Merge "power/stats: Correct language surrounding timestamps"
diff --git a/atrace/1.0/default/AtraceDevice.cpp b/atrace/1.0/default/AtraceDevice.cpp
index 4e82b0a..9f0c4c4 100644
--- a/atrace/1.0/default/AtraceDevice.cpp
+++ b/atrace/1.0/default/AtraceDevice.cpp
@@ -16,6 +16,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
#include "AtraceDevice.h"
@@ -39,15 +40,11 @@
// gfx
{
"gfx",
- {"Graphics",
- {{"/sys/kernel/debug/tracing/events/mdss/enable", false},
- {"/sys/kernel/debug/tracing/events/sde/enable", false},
- {"/sys/kernel/debug/tracing/events/mali_systrace/enable", false}}},
+ {"Graphics", {{"mdss", false}, {"sde", false}, {"mali_systrace", false}}},
},
{
"ion",
- {"ION allocation",
- {{"/sys/kernel/debug/tracing/events/kmem/ion_alloc_buffer_start/enable", false}}},
+ {"ION allocation", {{"kmem/ion_alloc_buffer_start", false}}},
},
};
@@ -65,16 +62,31 @@
return Void();
}
+AtraceDevice::AtraceDevice() {
+ struct stat st;
+
+ tracefs_event_root_ = "/sys/kernel/tracing/events/";
+ if (stat(tracefs_event_root_.c_str(), &st) != 0) {
+ tracefs_event_root_ = "/sys/kernel/debug/tracing/events/";
+ CHECK(stat(tracefs_event_root_.c_str(), &st) == 0) << "tracefs must be mounted at either"
+ "/sys/kernel/tracing or "
+ "/sys/kernel/debug/tracing";
+ }
+}
+
Return<::android::hardware::atrace::V1_0::Status> AtraceDevice::enableCategories(
- const hidl_vec<hidl_string>& categories) {
+ const hidl_vec<hidl_string>& categories) {
if (!categories.size()) {
return Status::ERROR_INVALID_ARGUMENT;
}
+
for (auto& c : categories) {
if (kTracingMap.count(c)) {
for (auto& p : kTracingMap.at(c).paths) {
- if (!android::base::WriteStringToFile("1", p.first)) {
- LOG(ERROR) << "Failed to enable tracing on: " << p.first;
+ std::string tracefs_event_enable_path = android::base::StringPrintf(
+ "%s%s/enable", tracefs_event_root_.c_str(), p.first.c_str());
+ if (!android::base::WriteStringToFile("1", tracefs_event_enable_path)) {
+ LOG(ERROR) << "Failed to enable tracing on: " << tracefs_event_enable_path;
if (p.second) {
// disable before return
disableAllCategories();
@@ -91,10 +103,13 @@
Return<::android::hardware::atrace::V1_0::Status> AtraceDevice::disableAllCategories() {
auto ret = Status::SUCCESS;
+
for (auto& c : kTracingMap) {
for (auto& p : c.second.paths) {
- if (!android::base::WriteStringToFile("0", p.first)) {
- LOG(ERROR) << "Failed to disable tracing on: " << p.first;
+ std::string tracefs_event_enable_path = android::base::StringPrintf(
+ "%s%s/enable", tracefs_event_root_.c_str(), p.first.c_str());
+ if (!android::base::WriteStringToFile("0", tracefs_event_enable_path)) {
+ LOG(ERROR) << "Failed to disable tracing on: " << tracefs_event_enable_path;
if (p.second) {
ret = Status::ERROR_TRACING_POINT;
}
diff --git a/atrace/1.0/default/AtraceDevice.h b/atrace/1.0/default/AtraceDevice.h
index e700f89..ab87c65 100644
--- a/atrace/1.0/default/AtraceDevice.h
+++ b/atrace/1.0/default/AtraceDevice.h
@@ -36,12 +36,16 @@
using ::android::hardware::Void;
struct AtraceDevice : public IAtraceDevice {
+ AtraceDevice();
// Methods from ::android::hardware::atrace::V1_0::IAtraceDevice follow.
Return<void> listCategories(listCategories_cb _hidl_cb) override;
Return<::android::hardware::atrace::V1_0::Status> enableCategories(
const hidl_vec<hidl_string>& categories) override;
Return<::android::hardware::atrace::V1_0::Status> disableAllCategories() override;
+ private:
+ std::string tracefs_event_root_;
+
// Methods from ::android::hidl::base::V1_0::IBase follow.
};
diff --git a/atrace/1.0/default/android.hardware.atrace@1.0-service.rc b/atrace/1.0/default/android.hardware.atrace@1.0-service.rc
index eb54c39..7110b45 100644
--- a/atrace/1.0/default/android.hardware.atrace@1.0-service.rc
+++ b/atrace/1.0/default/android.hardware.atrace@1.0-service.rc
@@ -1,10 +1,14 @@
on late-init
# vendor graphics trace points
chmod 0666 /sys/kernel/debug/tracing/events/sde/enable
+ chmod 0666 /sys/kernel/tracing/events/sde/enable
chmod 0666 /sys/kernel/debug/tracing/events/mdss/enable
+ chmod 0666 /sys/kernel/tracing/events/mdss/enable
chmod 0666 /sys/kernel/debug/tracing/events/mali_systrace/enable
+ chmod 0666 /sys/kernel/tracing/events/mali_systrace/enable
# ion allocation trace point
chmod 0666 /sys/kernel/debug/tracing/events/kmem/ion_alloc_buffer_start/enable
+ chmod 0666 /sys/kernel/tracing/events/kmem/ion_alloc_buffer_start/enable
service vendor.atrace-hal-1-0 /vendor/bin/hw/android.hardware.atrace@1.0-service
interface android.hardware.atrace@1.0::IAtraceDevice default
diff --git a/audio/7.0/config/api/current.txt b/audio/7.0/config/api/current.txt
index eb8c2dd..c2585bd 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;
@@ -104,6 +106,7 @@
enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_FM_TUNER;
enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_HDMI;
enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_HDMI_ARC;
+ enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_HDMI_EARC;
enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_IP;
enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_LINE;
enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_LOOPBACK;
@@ -138,6 +141,7 @@
enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_FM;
enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_HDMI;
enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_HDMI_ARC;
+ enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_HDMI_EARC;
enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_HEARING_AID;
enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_IP;
enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_LINE;
@@ -206,9 +210,11 @@
enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_CELT;
enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_DEFAULT;
enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_DOLBY_TRUEHD;
+ enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_DRA;
enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_DSD;
enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_DTS;
enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_DTS_HD;
+ enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_DTS_UHD;
enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_EVRC;
enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_EVRCB;
enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_EVRCNW;
diff --git a/audio/7.0/config/audio_policy_configuration.xsd b/audio/7.0/config/audio_policy_configuration.xsd
index 007e250..e0df359 100644
--- a/audio/7.0/config/audio_policy_configuration.xsd
+++ b/audio/7.0/config/audio_policy_configuration.xsd
@@ -252,6 +252,7 @@
<xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES"/>
<xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER"/>
<xs:enumeration value="AUDIO_DEVICE_OUT_HDMI"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_HDMI_EARC"/>
<xs:enumeration value="AUDIO_DEVICE_OUT_AUX_DIGITAL"/>
<xs:enumeration value="AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET"/>
<xs:enumeration value="AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET"/>
@@ -303,6 +304,7 @@
<xs:enumeration value="AUDIO_DEVICE_IN_USB_HEADSET"/>
<xs:enumeration value="AUDIO_DEVICE_IN_BLUETOOTH_BLE"/>
<xs:enumeration value="AUDIO_DEVICE_IN_HDMI_ARC"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_HDMI_EARC"/>
<xs:enumeration value="AUDIO_DEVICE_IN_ECHO_REFERENCE"/>
<xs:enumeration value="AUDIO_DEVICE_IN_BLE_HEADSET"/>
<xs:enumeration value="AUDIO_DEVICE_IN_DEFAULT"/>
@@ -411,6 +413,8 @@
<xs:enumeration value="AUDIO_FORMAT_MPEGH_LC_L3"/>
<xs:enumeration value="AUDIO_FORMAT_MPEGH_LC_L4"/>
<xs:enumeration value="AUDIO_FORMAT_IEC60958"/>
+ <xs:enumeration value="AUDIO_FORMAT_DTS_UHD"/>
+ <xs:enumeration value="AUDIO_FORMAT_DRA"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="extendableAudioFormat">
@@ -504,6 +508,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..a92a277 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:
@@ -155,6 +157,7 @@
case AudioDevice::AUDIO_DEVICE_OUT_TELEPHONY_TX:
case AudioDevice::AUDIO_DEVICE_OUT_LINE:
case AudioDevice::AUDIO_DEVICE_OUT_HDMI_ARC:
+ case AudioDevice::AUDIO_DEVICE_OUT_HDMI_EARC:
case AudioDevice::AUDIO_DEVICE_OUT_SPDIF:
case AudioDevice::AUDIO_DEVICE_OUT_FM:
case AudioDevice::AUDIO_DEVICE_OUT_AUX_LINE:
@@ -197,6 +200,7 @@
case AudioDevice::AUDIO_DEVICE_IN_USB_HEADSET:
case AudioDevice::AUDIO_DEVICE_IN_BLUETOOTH_BLE:
case AudioDevice::AUDIO_DEVICE_IN_HDMI_ARC:
+ case AudioDevice::AUDIO_DEVICE_IN_HDMI_EARC:
case AudioDevice::AUDIO_DEVICE_IN_ECHO_REFERENCE:
case AudioDevice::AUDIO_DEVICE_IN_BLE_HEADSET:
case AudioDevice::AUDIO_DEVICE_IN_DEFAULT:
diff --git a/audio/core/all-versions/default/Android.bp b/audio/core/all-versions/default/Android.bp
index f61964e..615655d 100644
--- a/audio/core/all-versions/default/Android.bp
+++ b/audio/core/all-versions/default/Android.bp
@@ -56,6 +56,7 @@
"android.hardware.audio-impl_headers",
"android.hardware.audio.common.util@all-versions",
"libaudioclient_headers",
+ "libaudioutils_headers",
"libaudio_system_headers",
"libhardware_headers",
"libmedia_headers",
diff --git a/audio/core/all-versions/default/StreamOut.cpp b/audio/core/all-versions/default/StreamOut.cpp
index a089f6b..4fe6601 100644
--- a/audio/core/all-versions/default/StreamOut.cpp
+++ b/audio/core/all-versions/default/StreamOut.cpp
@@ -28,6 +28,7 @@
#include <HidlUtils.h>
#include <android/log.h>
+#include <audio_utils/Metadata.h>
#include <hardware/audio.h>
#include <util/CoreUtils.h>
#include <utils/Trace.h>
@@ -742,7 +743,11 @@
switch (event) {
case STREAM_EVENT_CBK_TYPE_CODEC_FORMAT_CHANGED: {
hidl_vec<uint8_t> audioMetadata;
- audioMetadata.setToExternal((uint8_t*)param, strlen((char*)param));
+ // void* param is the byte string buffer from byte_string_from_audio_metadata().
+ // As the byte string buffer may have embedded zeroes, we cannot use strlen()
+ // but instead use audio_utils::metadata::dataByteStringLen().
+ audioMetadata.setToExternal((uint8_t*)param, audio_utils::metadata::dataByteStringLen(
+ (const uint8_t*)param));
result = eventCallback->onCodecFormatChanged(audioMetadata);
} break;
default:
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/Android.bp b/bluetooth/1.0/vts/functional/Android.bp
index 4806fef..768142c 100644
--- a/bluetooth/1.0/vts/functional/Android.bp
+++ b/bluetooth/1.0/vts/functional/Android.bp
@@ -31,6 +31,7 @@
"android.hardware.bluetooth@1.0",
"libbluetooth-types",
],
+ test_config: "VtsHalBluetoothV1_0TargetTest.xml",
test_suites: [
"general-tests",
"vts",
diff --git a/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.xml b/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.xml
new file mode 100644
index 0000000..09463c9
--- /dev/null
+++ b/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.xml
@@ -0,0 +1,38 @@
+<?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.
+-->
+<configuration description="Runs VtsHalBluetoothV1_0TargetTest.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.StopServicesSetup">
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="bluetooth" value="off" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="VtsHalBluetoothV1_0TargetTest->/data/local/tmp/VtsHalBluetoothV1_0TargetTest" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="VtsHalBluetoothV1_0TargetTest" />
+ </test>
+</configuration>
diff --git a/bluetooth/1.1/vts/functional/Android.bp b/bluetooth/1.1/vts/functional/Android.bp
index e64d5a9..7f56647 100644
--- a/bluetooth/1.1/vts/functional/Android.bp
+++ b/bluetooth/1.1/vts/functional/Android.bp
@@ -32,5 +32,6 @@
"android.hardware.bluetooth@1.0",
"libbluetooth-types",
],
+ test_config: "VtsHalBluetoothV1_1TargetTest.xml",
test_suites: ["general-tests", "vts"],
}
diff --git a/bluetooth/1.1/vts/functional/VtsHalBluetoothV1_1TargetTest.xml b/bluetooth/1.1/vts/functional/VtsHalBluetoothV1_1TargetTest.xml
new file mode 100644
index 0000000..d64751a
--- /dev/null
+++ b/bluetooth/1.1/vts/functional/VtsHalBluetoothV1_1TargetTest.xml
@@ -0,0 +1,36 @@
+<?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.
+-->
+<configuration description="Runs VtsHalBluetoothV1_1TargetTest.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="bluetooth" value="off" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="VtsHalBluetoothV1_1TargetTest->/data/local/tmp/VtsHalBluetoothV1_1TargetTest" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="VtsHalBluetoothV1_1TargetTest" />
+ </test>
+</configuration>
diff --git a/compatibility_matrices/compatibility_matrix.3.xml b/compatibility_matrices/compatibility_matrix.3.xml
index 608890b..a75ed25 100644
--- a/compatibility_matrices/compatibility_matrix.3.xml
+++ b/compatibility_matrices/compatibility_matrix.3.xml
@@ -223,7 +223,7 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="false">
+ <hal format="hidl" optional="true">
<name>android.hardware.keymaster</name>
<version>3.0</version>
<version>4.0</version>
diff --git a/compatibility_matrices/compatibility_matrix.4.xml b/compatibility_matrices/compatibility_matrix.4.xml
index e5e012c..3b8ee21 100644
--- a/compatibility_matrices/compatibility_matrix.4.xml
+++ b/compatibility_matrices/compatibility_matrix.4.xml
@@ -245,7 +245,7 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="false">
+ <hal format="hidl" optional="true">
<name>android.hardware.keymaster</name>
<version>3.0</version>
<version>4.0</version>
diff --git a/compatibility_matrices/compatibility_matrix.5.xml b/compatibility_matrices/compatibility_matrix.5.xml
index 8e175f0..0fb21a7 100644
--- a/compatibility_matrices/compatibility_matrix.5.xml
+++ b/compatibility_matrices/compatibility_matrix.5.xml
@@ -284,7 +284,7 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="false">
+ <hal format="hidl" optional="true">
<name>android.hardware.keymaster</name>
<version>3.0</version>
<version>4.0-1</version>
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 30af25f..039ea84 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -297,7 +297,7 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="false">
+ <hal format="hidl" optional="true">
<name>android.hardware.keymaster</name>
<version>3.0</version>
<version>4.0-1</version>
diff --git a/current.txt b/current.txt
index 454d43e..2bd03ba 100644
--- a/current.txt
+++ b/current.txt
@@ -776,8 +776,8 @@
dabe23dde7c9e3ad65c61def7392f186d7efe7f4216f9b6f9cf0863745b1a9f4 android.hardware.keymaster@4.1::IKeymasterDevice
cd84ab19c590e0e73dd2307b591a3093ee18147ef95e6d5418644463a6620076 android.hardware.neuralnetworks@1.2::IDevice
f729ee6a5f136b25d79ea6895d24700fce413df555baaecf2c39e4440d15d043 android.hardware.neuralnetworks@1.0::types
-a84f8dac7a9b75de1cc2936a9b429b9b62b32a31ea88ca52c29f98f5ddc0fa95 android.hardware.neuralnetworks@1.2::types
-cd331b92312d16ab89f475c39296abbf539efc4114a8c5c2b136ad99b904ef33 android.hardware.neuralnetworks@1.3::types
+38c1a3eb5c3dfa4cc40b7cf4be0e9850440e2c57197fba7407081679b358aa22 android.hardware.neuralnetworks@1.2::types
+550619f876cadbea1f718edce120f0e1dd4a6f4bd4c28b59d479677dc86b0aec 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
@@ -793,5 +793,45 @@
2b5afef68e3e2ff1dab63e4f2ee57337ef2635ec812f49080cadfce966d33b52 android.hardware.radio@1.2::IRadio
# HALs released in Android S
-# NOTE: waiting to freeze HALs until later in the release
-# NOTE: new HALs are recommended to be in AIDL
+59fa68432e374c8d3b7ec098a91a1e023a2c728110bb733237c551afa5929725 android.hardware.audio@7.0::IDevice
+2207948ca127b801c94f667c99dfd139f150b50671e1408d3e855d03efbf631d android.hardware.audio@7.0::IDevicesFactory
+1d201e15c553cd44c62864ac8d7039351ddf048a7ee61e380f6efb0904442eb8 android.hardware.audio@7.0::IPrimaryDevice
+38afa920e6d36013b5a800e8c82eefeebd24602de24441e2f8ce5b3bdf62d3af android.hardware.audio@7.0::IStream
+77d84330418abba5a92b0cdc4e27fa7c85c27344eaf7eeef441b8e88829ee475 android.hardware.audio@7.0::IStreamIn
+9e4d79ed8f3c7e18455f371342ea1802f080bae38f64db746cc433305ee1978b android.hardware.audio@7.0::IStreamOut
+54cbc3c637fe8d4b889ccb5690e5e3069ca8efd9c6607ce1d021a3f47576c67e android.hardware.audio@7.0::IStreamOutCallback
+8036ae0a68a698a79207218018de5f41aed344723f644112ffc99e20e5e2e9ff android.hardware.audio@7.0::IStreamOutEventCallback
+84978dbd15d4fa8be6073d0974755f7718ee0cde519ce71449fb734f53cee46b android.hardware.audio@7.0::types
+6a03a9d8cc917da00e8b88f4abc42db2f741e2d50901e8ab6dea32084a238fbd android.hardware.audio.common@7.0::types
+842b4485a00005fb938f674b12445cb592cd1636f56c7cc447966119070811bd android.hardware.audio.effect@7.0::IAcousticEchoCancelerEffect
+b62a85e5d745dc35b5a60464c6b33a5bb7a2b8b95863a1374aee77ea29cf8f49 android.hardware.audio.effect@7.0::IAutomaticGainControlEffect
+c8d5e30848191713db7cffccc482e4427816f33c98a24734c8769962f79f855b android.hardware.audio.effect@7.0::IBassBoostEffect
+7d021ecdf5bb6a61eb9ad193585d4986d1a64cb7fb4b52f219d7380145f2c6f1 android.hardware.audio.effect@7.0::IDownmixEffect
+7fee1e7c7bb3d513a524c8963d1f8f7c2ad856f26c745b4ebc286b40d503264a android.hardware.audio.effect@7.0::IEffect
+7596050ccc00234458dcb4e692056ed3c16f3618c11d7b17cb749cfd5713705d android.hardware.audio.effect@7.0::IEffectBufferProviderCallback
+f2e41467bcf1140a11b219c2e8f77981b955c2941befe66e1cc685b7863ae4c9 android.hardware.audio.effect@7.0::IEffectsFactory
+af66fb4addbc477f9fea65fb63475203122a9189624ca8d14e757bc7826d60a4 android.hardware.audio.effect@7.0::IEnvironmentalReverbEffect
+2878d007ed55e1a4149ddcd29606962c948d8610642276f91dffd5ed32281824 android.hardware.audio.effect@7.0::IEqualizerEffect
+0260ef9e2a3e077de366ebebc0c117c7ee13f46a1eabd4abd66cc6245d0bed98 android.hardware.audio.effect@7.0::ILoudnessEnhancerEffect
+3586bbc3a7cbe30f9aff0a522524eea9b78eea78280f09c35d43dbab48a1193e android.hardware.audio.effect@7.0::INoiseSuppressionEffect
+a7d74d7e7e0b1e3b739f233b7776bf01e868856a536f5cdac0f307e9c2850e64 android.hardware.audio.effect@7.0::IPresetReverbEffect
+b4cbc1f2d38787f2ad069a8e4d10c0896287531a2596f0de0283e390b0ecf05d android.hardware.audio.effect@7.0::IVirtualizerEffect
+2b5681e1ea6a2db0dc1e84edb96d3de2f7daf306046543e7956be76dcb8f20fb android.hardware.audio.effect@7.0::IVisualizerEffect
+fa1e2d78e66fd662de93cb479ffd55947fe54f51cb53915814b3d3e3036c86a5 android.hardware.audio.effect@7.0::types
+4baf8e0eca4aa896cc9ceb7bb676aaf4fa21372ef8b49eed68eced1221c3dc0d android.hardware.bluetooth.audio@2.1::IBluetoothAudioProvider
+d417a9212c8f96e3a06a2f221c8c5756c765355b2b81de2b2a65d4c9eee85401 android.hardware.bluetooth.audio@2.1::IBluetoothAudioProvidersFactory
+c17d9e27abd37ae5a8ff8da08fc5c9b13a264670feef6bbbc9d3ab1915216130 android.hardware.bluetooth.audio@2.1::types
+6763dd2273b1b47f3ac68af9b66870287eba33fb5b4d66e8fe1d30ae18ce24cb android.hardware.boot@1.2::IBootControl
+0c0657fad2239c2c7ec363d3b13f2e002d1c267ca89d2cc96d2b1de0475386cb android.hardware.fastboot@1.1::IFastboot
+3e8866987de4ecb48807c09d4c88ec38365930a22415f1b74edf8b14da17846b android.hardware.radio@1.6::IRadio
+715789427a44cc78f9d123b0ceb9e035e4ac2b1049501337c23a512e85b87850 android.hardware.radio@1.6::IRadioIndication
+2e9c08c4bc9539d8da28d7de33500f87148f7fa2e377238ee898b41752ac4f29 android.hardware.radio@1.6::IRadioResponse
+6475887a9cd5cc8cb803e3a78956d84d7a5fde571407ede2396f3ea5e0c0d3ad android.hardware.radio@1.6::types
+f22813615be1445ddd817655c054fc69dc9efea56c9035cd0757f3cbed190641 android.hardware.radio.config@1.3::IRadioConfig
+c9ad18729268593d14681d88ffad1c97e707444a45e1b4ed804dab949edbd84f android.hardware.radio.config@1.3::IRadioConfigResponse
+78dcb9a6975e8b377cb90bbe952078162960941468c992dcd2e1830a477b8c03 android.hardware.radio.config@1.3::types
+fd43298c43f70130c747a642ee43b0c242ac0cebffb377faa24f2725f0aa6caf android.hardware.tetheroffload.control@1.1::IOffloadControl
+ead4ec8713a2cb40906fe31ba793d21a6b1190143c446690d16a6ea686aa2fea android.hardware.tetheroffload.control@1.1::ITetheringOffloadCallback
+e34b4c7bec5e032c14804707ca924dd6b99ed5ba139da7505fe7d698d0fe178f android.hardware.tetheroffload.control@1.1::types
+
+# There should be no more HIDL HALs - please use AIDL instead.
diff --git a/gatekeeper/1.0/vts/functional/VtsHalGatekeeperV1_0TargetTest.cpp b/gatekeeper/1.0/vts/functional/VtsHalGatekeeperV1_0TargetTest.cpp
index e74cca9..7d32ced 100644
--- a/gatekeeper/1.0/vts/functional/VtsHalGatekeeperV1_0TargetTest.cpp
+++ b/gatekeeper/1.0/vts/functional/VtsHalGatekeeperV1_0TargetTest.cpp
@@ -306,6 +306,8 @@
if (first != nullptr && second != nullptr) {
EXPECT_NE(first->user_id, second->user_id);
}
+ // the old enrollment should be invalid now
+ verifyPassword(password, enrollRsp.data, 0, verifyRsp, false);
ALOGI("Testing Untrusted Reenroll done");
}
diff --git a/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/HardwareBufferDescription.aidl b/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/HardwareBufferDescription.aidl
index 8b12169..232e023 100644
--- a/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/HardwareBufferDescription.aidl
+++ b/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/HardwareBufferDescription.aidl
@@ -21,7 +21,7 @@
int width;
int height;
int layers;
- android.hardware.graphics.common.PixelFormat format;
- android.hardware.graphics.common.BufferUsage usage;
+ android.hardware.graphics.common.PixelFormat format = android.hardware.graphics.common.PixelFormat.UNSPECIFIED;
+ android.hardware.graphics.common.BufferUsage usage = android.hardware.graphics.common.BufferUsage.CPU_READ_NEVER;
int stride;
}
diff --git a/graphics/common/aidl/android/hardware/graphics/common/HardwareBufferDescription.aidl b/graphics/common/aidl/android/hardware/graphics/common/HardwareBufferDescription.aidl
index e1e3492..078c512 100644
--- a/graphics/common/aidl/android/hardware/graphics/common/HardwareBufferDescription.aidl
+++ b/graphics/common/aidl/android/hardware/graphics/common/HardwareBufferDescription.aidl
@@ -29,7 +29,7 @@
int width;
int height;
int layers;
- PixelFormat format;
- BufferUsage usage;
+ PixelFormat format = PixelFormat.UNSPECIFIED;
+ BufferUsage usage = BufferUsage.CPU_READ_NEVER;
int stride;
}
diff --git a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
index 50f282f..46f95dd 100644
--- a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
+++ b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
@@ -73,10 +73,15 @@
IComposerClient::Rect getFrameRect() const { return {0, 0, mDisplayWidth, mDisplayHeight}; }
+ void setDimensions(int32_t displayWidth, int32_t displayHeight) {
+ mDisplayWidth = displayWidth;
+ mDisplayHeight = displayHeight;
+ }
+
private:
const Display mDisplay;
- const int32_t mDisplayWidth;
- const int32_t mDisplayHeight;
+ int32_t mDisplayWidth;
+ int32_t mDisplayHeight;
};
class GraphicsComposerHidlTest : public ::testing::TestWithParam<std::string> {
@@ -194,6 +199,31 @@
const std::vector<ContentType>& capabilities,
const ContentType& contentType, const char* contentTypeStr);
+ Error setActiveConfigWithConstraints(
+ VtsDisplay& display, Config config,
+ const IComposerClient::VsyncPeriodChangeConstraints& constraints,
+ VsyncPeriodChangeTimeline* timeline) {
+ const auto error = mComposerClient->setActiveConfigWithConstraints(display.get(), config,
+ constraints, timeline);
+ if (error == Error::NONE) {
+ const int32_t displayWidth = mComposerClient->getDisplayAttribute_2_4(
+ display.get(), config, IComposerClient::Attribute::WIDTH);
+ const int32_t displayHeight = mComposerClient->getDisplayAttribute_2_4(
+ display.get(), config, IComposerClient::Attribute::HEIGHT);
+ display.setDimensions(displayWidth, displayHeight);
+ }
+ return error;
+ }
+
+ void setActiveConfig(VtsDisplay& display, Config config) {
+ mComposerClient->setActiveConfig(display.get(), config);
+ const int32_t displayWidth = mComposerClient->getDisplayAttribute_2_4(
+ display.get(), config, IComposerClient::Attribute::WIDTH);
+ const int32_t displayHeight = mComposerClient->getDisplayAttribute_2_4(
+ display.get(), config, IComposerClient::Attribute::HEIGHT);
+ display.setDimensions(displayWidth, displayHeight);
+ }
+
private:
// use the slot count usually set by SF
static constexpr uint32_t kBufferSlotCount = 64;
@@ -347,7 +377,7 @@
}
TEST_P(GraphicsComposerHidlTest, getDisplayVsyncPeriod) {
- for (const auto& display : mDisplays) {
+ for (VtsDisplay& display : mDisplays) {
for (Config config : mComposerClient->getDisplayConfigs(display.get())) {
VsyncPeriodNanos expectedVsyncPeriodNanos = mComposerClient->getDisplayAttribute_2_4(
display.get(), config,
@@ -358,8 +388,8 @@
constraints.desiredTimeNanos = systemTime();
constraints.seamlessRequired = false;
- EXPECT_EQ(Error::NONE, mComposerClient->setActiveConfigWithConstraints(
- display.get(), config, constraints, &timeline));
+ EXPECT_EQ(Error::NONE,
+ setActiveConfigWithConstraints(display, config, constraints, &timeline));
if (timeline.refreshRequired) {
sendRefreshFrame(display, &timeline);
@@ -411,11 +441,10 @@
constraints.seamlessRequired = false;
constraints.desiredTimeNanos = systemTime();
- for (const auto& display : mDisplays) {
+ for (VtsDisplay& display : mDisplays) {
Config invalidConfigId = GetInvalidConfigId(display.get());
EXPECT_EQ(Error::BAD_CONFIG,
- mComposerClient->setActiveConfigWithConstraints(display.get(), invalidConfigId,
- constraints, &timeline));
+ setActiveConfigWithConstraints(display, invalidConfigId, constraints, &timeline));
}
}
@@ -426,7 +455,7 @@
constraints.seamlessRequired = true;
constraints.desiredTimeNanos = systemTime();
- for (const auto& display : mDisplays) {
+ for (VtsDisplay& display : mDisplays) {
forEachTwoConfigs(display.get(), [&](Config config1, Config config2) {
const auto configGroup1 = mComposerClient->getDisplayAttribute_2_4(
display.get(), config1,
@@ -435,11 +464,10 @@
display.get(), config2,
IComposerClient::IComposerClient::Attribute::CONFIG_GROUP);
if (configGroup1 != configGroup2) {
- mComposerClient->setActiveConfig(display.get(), config1);
+ setActiveConfig(display, config1);
sendRefreshFrame(display, nullptr);
EXPECT_EQ(Error::SEAMLESS_NOT_ALLOWED,
- mComposerClient->setActiveConfigWithConstraints(display.get(), config2,
- constraints, &timeline));
+ setActiveConfigWithConstraints(display, config2, constraints, &timeline));
}
});
}
@@ -502,6 +530,8 @@
mWriter->presentDisplay();
execute();
+
+ ASSERT_NO_FATAL_FAILURE(mComposerClient->destroyLayer(display.get(), layer));
}
void GraphicsComposerHidlTest::waitForVsyncPeriodChange(Display display,
@@ -523,9 +553,9 @@
}
void GraphicsComposerHidlTest::Test_setActiveConfigWithConstraints(const TestParameters& params) {
- for (const auto& display : mDisplays) {
+ for (VtsDisplay& display : mDisplays) {
forEachTwoConfigs(display.get(), [&](Config config1, Config config2) {
- mComposerClient->setActiveConfig(display.get(), config1);
+ setActiveConfig(display, config1);
sendRefreshFrame(display, nullptr);
int32_t vsyncPeriod1 = mComposerClient->getDisplayAttribute_2_4(
@@ -543,8 +573,8 @@
IComposerClient::VsyncPeriodChangeConstraints constraints = {
.desiredTimeNanos = systemTime() + params.delayForChange,
.seamlessRequired = false};
- EXPECT_EQ(Error::NONE, mComposerClient->setActiveConfigWithConstraints(
- display.get(), config2, constraints, &timeline));
+ EXPECT_EQ(Error::NONE,
+ setActiveConfigWithConstraints(display, config2, constraints, &timeline));
EXPECT_TRUE(timeline.newVsyncAppliedTimeNanos >= constraints.desiredTimeNanos);
// Refresh rate should change within a reasonable time
diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredential.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredential.aidl
index a097895..3224e4b 100644
--- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredential.aidl
+++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredential.aidl
@@ -43,8 +43,8 @@
void startRetrieval(in android.hardware.identity.SecureAccessControlProfile[] accessControlProfiles, in android.hardware.keymaster.HardwareAuthToken authToken, in byte[] itemsRequest, in byte[] signingKeyBlob, in byte[] sessionTranscript, in byte[] readerSignature, in int[] requestCounts);
void startRetrieveEntryValue(in @utf8InCpp String nameSpace, in @utf8InCpp String name, in int entrySize, in int[] accessControlProfileIds);
byte[] retrieveEntryValue(in byte[] encryptedContent);
- void finishRetrieval(out byte[] mac, out byte[] deviceNameSpaces);
- android.hardware.identity.Certificate generateSigningKeyPair(out byte[] signingKeyBlob);
+ @SuppressWarnings(value={"out-array"}) void finishRetrieval(out byte[] mac, out byte[] deviceNameSpaces);
+ @SuppressWarnings(value={"out-array"}) android.hardware.identity.Certificate generateSigningKeyPair(out byte[] signingKeyBlob);
void setRequestedNamespaces(in android.hardware.identity.RequestNamespace[] requestNamespaces);
void setVerificationToken(in android.hardware.keymaster.VerificationToken verificationToken);
byte[] deleteCredentialWithChallenge(in byte[] challenge);
diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IWritableIdentityCredential.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IWritableIdentityCredential.aidl
index a713462..19a29ec 100644
--- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IWritableIdentityCredential.aidl
+++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IWritableIdentityCredential.aidl
@@ -38,6 +38,6 @@
android.hardware.identity.SecureAccessControlProfile addAccessControlProfile(in int id, in android.hardware.identity.Certificate readerCertificate, in boolean userAuthenticationRequired, in long timeoutMillis, in long secureUserId);
void beginAddEntry(in int[] accessControlProfileIds, in @utf8InCpp String nameSpace, in @utf8InCpp String name, in int entrySize);
byte[] addEntryValue(in byte[] content);
- void finishAddingEntries(out byte[] credentialData, out byte[] proofOfProvisioningSignature);
+ @SuppressWarnings(value={"out-array"}) void finishAddingEntries(out byte[] credentialData, out byte[] proofOfProvisioningSignature);
void setExpectedProofOfProvisioningSize(in int expectedProofOfProvisioningSize);
}
diff --git a/identity/aidl/android/hardware/identity/IIdentityCredential.aidl b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
index d23f88c..8ae293b 100644
--- a/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
+++ b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
@@ -324,6 +324,7 @@
*
* @param out deviceNameSpaces the bytes of DeviceNameSpaces.
*/
+ @SuppressWarnings(value={"out-array"})
void finishRetrieval(out byte[] mac, out byte[] deviceNameSpaces);
/**
@@ -376,6 +377,7 @@
*
* @return an X.509 certificate for the new signing key, signed by the credential key.
*/
+ @SuppressWarnings(value={"out-array"})
Certificate generateSigningKeyPair(out byte[] signingKeyBlob);
/**
diff --git a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
index 5f878ee..22bcf61 100644
--- a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
+++ b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
@@ -320,6 +320,7 @@
* "accessControlProfiles" : [ * uint ],
* }
*/
+ @SuppressWarnings(value={"out-array"})
void finishAddingEntries(out byte[] credentialData,
out byte[] proofOfProvisioningSignature);
diff --git a/identity/aidl/default/Android.bp b/identity/aidl/default/Android.bp
index a124b1e..7c68aee 100644
--- a/identity/aidl/default/Android.bp
+++ b/identity/aidl/default/Android.bp
@@ -32,6 +32,7 @@
static_libs: [
"libbase",
"libcppbor_external",
+ "libcppcose_rkp",
"libutils",
"libsoft_attestation_cert",
"libkeymaster_portable",
@@ -92,6 +93,7 @@
static_libs: [
"libbase",
"libcppbor_external",
+ "libcppcose_rkp",
"libutils",
"libsoft_attestation_cert",
"libkeymaster_portable",
diff --git a/identity/aidl/vts/Android.bp b/identity/aidl/vts/Android.bp
index 3592d3e..61d15d2 100644
--- a/identity/aidl/vts/Android.bp
+++ b/identity/aidl/vts/Android.bp
@@ -35,6 +35,7 @@
],
static_libs: [
"libcppbor_external",
+ "libcppcose_rkp",
"libkeymaster_portable",
"libpuresoftkeymasterdevice",
"android.hardware.keymaster@4.0",
diff --git a/identity/support/Android.bp b/identity/support/Android.bp
index 774bc40..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",
diff --git a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
index e0d60fc..9e37ed0 100644
--- a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
+++ b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
@@ -921,6 +921,23 @@
.Authorization(TAG_MIN_MAC_LENGTH, 128)));
}
+/**
+ * NewKeyGenerationTest.AesInvalidKeySize
+ *
+ * Verifies that specifying an invalid key size for AES key generation returns
+ * UNSUPPORTED_KEY_SIZE.
+ */
+TEST_P(NewKeyGenerationTest, AesInvalidKeySize) {
+ for (auto key_size : InvalidKeySizes(Algorithm::AES)) {
+ ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+ GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(key_size)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+ .Padding(PaddingMode::NONE)));
+ }
+}
+
INSTANTIATE_KEYMASTER_HIDL_TEST(NewKeyGenerationTest);
typedef KeymasterHidlTest SigningOperationsTest;
diff --git a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/HardwareAuthToken.aidl b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/HardwareAuthToken.aidl
index db1df2b..4f21cba 100644
--- a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/HardwareAuthToken.aidl
+++ b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/HardwareAuthToken.aidl
@@ -21,7 +21,7 @@
long challenge;
long userId;
long authenticatorId;
- android.hardware.keymaster.HardwareAuthenticatorType authenticatorType;
+ android.hardware.keymaster.HardwareAuthenticatorType authenticatorType = android.hardware.keymaster.HardwareAuthenticatorType.NONE;
android.hardware.keymaster.Timestamp timestamp;
byte[] mac;
}
diff --git a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/VerificationToken.aidl b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/VerificationToken.aidl
index 0633765..b116dac 100644
--- a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/VerificationToken.aidl
+++ b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/VerificationToken.aidl
@@ -20,6 +20,6 @@
parcelable VerificationToken {
long challenge;
android.hardware.keymaster.Timestamp timestamp;
- android.hardware.keymaster.SecurityLevel securityLevel;
+ android.hardware.keymaster.SecurityLevel securityLevel = android.hardware.keymaster.SecurityLevel.SOFTWARE;
byte[] mac;
}
diff --git a/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl b/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl
index 58602aa..99b036a 100644
--- a/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl
+++ b/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl
@@ -55,7 +55,7 @@
* authenticatorType describes the type of authentication that took place, e.g. password or
* fingerprint.
*/
- HardwareAuthenticatorType authenticatorType;
+ HardwareAuthenticatorType authenticatorType = HardwareAuthenticatorType.NONE;
/**
* timestamp indicates when the user authentication took place, in milliseconds since some
diff --git a/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl b/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl
index f053254..5efd937 100644
--- a/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl
+++ b/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl
@@ -43,7 +43,7 @@
/**
* SecurityLevel of the secure environment that generated the token.
*/
- SecurityLevel securityLevel;
+ SecurityLevel securityLevel = SecurityLevel.SOFTWARE;
/**
* 32-byte HMAC-SHA256 of the above values, computed as:
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 7849ca7..8bd2fbe 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
@@ -48,6 +48,10 @@
const nn::OptionalTimePoint& deadline,
const nn::OptionalDuration& loopTimeoutDuration) const override;
+ nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& loopTimeoutDuration) const override;
+
private:
const nn::SharedPreparedModel kPreparedModel;
};
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h
index 4681b9e..db3b2ad 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h
@@ -52,7 +52,6 @@
const std::string& getVersionString() const override;
nn::Version getFeatureLevel() const override;
nn::DeviceType getType() const override;
- bool isUpdatable() const override;
const std::vector<nn::Extension>& getSupportedExtensions() const override;
const nn::Capabilities& getCapabilities() const override;
std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const override;
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Execution.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Execution.h
new file mode 100644
index 0000000..e201e25
--- /dev/null
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Execution.h
@@ -0,0 +1,64 @@
+/*
+ * 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_0_UTILS_EXECUTION_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_EXECUTION_H
+
+#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include "PreparedModel.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+class Execution final : public nn::IExecution, public std::enable_shared_from_this<Execution> {
+ struct PrivateConstructorTag {};
+
+ public:
+ static nn::GeneralResult<std::shared_ptr<const Execution>> create(
+ std::shared_ptr<const PreparedModel> preparedModel, Request request,
+ hal::utils::RequestRelocation relocation);
+
+ Execution(PrivateConstructorTag tag, std::shared_ptr<const PreparedModel> preparedModel,
+ Request request, hal::utils::RequestRelocation relocation);
+
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+ const nn::OptionalTimePoint& deadline) const override;
+
+ nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+ const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+ private:
+ const std::shared_ptr<const PreparedModel> kPreparedModel;
+ const Request kRequest;
+ const hal::utils::RequestRelocation kRelocation;
+};
+
+} // namespace android::hardware::neuralnetworks::V1_0::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_EXECUTION_H
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h
index 8853eea..48be595 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h
@@ -57,10 +57,17 @@
const nn::OptionalDuration& loopTimeoutDuration,
const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+ nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& loopTimeoutDuration) const override;
+
nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
std::any getUnderlyingResource() const override;
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeInternal(
+ const V1_0::Request& request, const hal::utils::RequestRelocation& relocation) const;
+
private:
const sp<V1_0::IPreparedModel> kPreparedModel;
const hal::utils::DeathHandler kDeathHandler;
diff --git a/neuralnetworks/1.0/utils/src/Burst.cpp b/neuralnetworks/1.0/utils/src/Burst.cpp
index e3a9757..1284721 100644
--- a/neuralnetworks/1.0/utils/src/Burst.cpp
+++ b/neuralnetworks/1.0/utils/src/Burst.cpp
@@ -55,4 +55,10 @@
return kPreparedModel->execute(request, measure, deadline, loopTimeoutDuration);
}
+nn::GeneralResult<nn::SharedExecution> Burst::createReusableExecution(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& loopTimeoutDuration) const {
+ return kPreparedModel->createReusableExecution(request, measure, loopTimeoutDuration);
+}
+
} // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.0/utils/src/Device.cpp b/neuralnetworks/1.0/utils/src/Device.cpp
index bb31a26..93bd81a 100644
--- a/neuralnetworks/1.0/utils/src/Device.cpp
+++ b/neuralnetworks/1.0/utils/src/Device.cpp
@@ -106,10 +106,6 @@
return nn::DeviceType::OTHER;
}
-bool Device::isUpdatable() const {
- return false;
-}
-
const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
return kExtensions;
}
diff --git a/neuralnetworks/1.0/utils/src/Execution.cpp b/neuralnetworks/1.0/utils/src/Execution.cpp
new file mode 100644
index 0000000..7a3216b
--- /dev/null
+++ b/neuralnetworks/1.0/utils/src/Execution.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 "Execution.h"
+
+#include "Callbacks.h"
+#include "Conversions.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+nn::GeneralResult<std::shared_ptr<const Execution>> Execution::create(
+ std::shared_ptr<const PreparedModel> preparedModel, Request request,
+ hal::utils::RequestRelocation relocation) {
+ if (preparedModel == nullptr) {
+ return NN_ERROR() << "V1_0::utils::Execution::create must have non-null preparedModel";
+ }
+
+ return std::make_shared<const Execution>(PrivateConstructorTag{}, std::move(preparedModel),
+ std::move(request), std::move(relocation));
+}
+
+Execution::Execution(PrivateConstructorTag /*tag*/,
+ std::shared_ptr<const PreparedModel> preparedModel, Request request,
+ hal::utils::RequestRelocation relocation)
+ : kPreparedModel(std::move(preparedModel)),
+ kRequest(std::move(request)),
+ kRelocation(std::move(relocation)) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Execution::compute(
+ const nn::OptionalTimePoint& /*deadline*/) const {
+ return kPreparedModel->executeInternal(kRequest, kRelocation);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> Execution::computeFenced(
+ const std::vector<nn::SyncFence>& /*waitFor*/, const nn::OptionalTimePoint& /*deadline*/,
+ const nn::OptionalDuration& /*timeoutDurationAfterFence*/) const {
+ return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+ << "IExecution::computeFenced is not supported on 1.0 HAL service";
+}
+
+} // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.0/utils/src/PreparedModel.cpp b/neuralnetworks/1.0/utils/src/PreparedModel.cpp
index 858571d..7987ab4 100644
--- a/neuralnetworks/1.0/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.0/utils/src/PreparedModel.cpp
@@ -19,6 +19,7 @@
#include "Burst.h"
#include "Callbacks.h"
#include "Conversions.h"
+#include "Execution.h"
#include "Utils.h"
#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
@@ -61,22 +62,34 @@
const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
// 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)));
+ hal::utils::RequestRelocation relocation;
+ const nn::Request& requestInShared =
+ NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
+ &request, &maybeRequestInShared, &relocation)));
const auto hidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
+ return executeInternal(hidlRequest, relocation);
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+PreparedModel::executeInternal(const V1_0::Request& request,
+ const hal::utils::RequestRelocation& relocation) const {
+ if (relocation.input) {
+ relocation.input->flush();
+ }
+
const auto cb = sp<ExecutionCallback>::make();
const auto scoped = kDeathHandler.protectCallback(cb.get());
- const auto ret = kPreparedModel->execute(hidlRequest, cb);
+ const auto ret = kPreparedModel->execute(request, cb);
const auto status = HANDLE_TRANSPORT_FAILURE(ret);
HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
auto result = NN_TRY(cb->get());
- NN_TRY(hal::utils::makeExecutionFailure(
- hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
-
+ if (relocation.output) {
+ relocation.output->flush();
+ }
return result;
}
@@ -91,6 +104,19 @@
<< "IPreparedModel::executeFenced is not supported on 1.0 HAL service";
}
+nn::GeneralResult<nn::SharedExecution> PreparedModel::createReusableExecution(
+ const nn::Request& request, nn::MeasureTiming /*measure*/,
+ const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
+ // Ensure that request is ready for IPC.
+ std::optional<nn::Request> maybeRequestInShared;
+ hal::utils::RequestRelocation relocation;
+ const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+ &request, &maybeRequestInShared, &relocation));
+
+ auto hidlRequest = NN_TRY(convert(requestInShared));
+ return Execution::create(shared_from_this(), std::move(hidlRequest), std::move(relocation));
+}
+
nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
return Burst::create(shared_from_this());
}
diff --git a/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp b/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp
index f19ed77..7820c06 100644
--- a/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp
+++ b/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp
@@ -19,6 +19,7 @@
#include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <nnapi/IExecution.h>
#include <nnapi/IPreparedModel.h>
#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
@@ -224,6 +225,150 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
+TEST(PreparedModelTest, reusableExecute) {
+ // setup call
+ const uint32_t kNumberOfComputations = 2;
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ EXPECT_CALL(*mockPreparedModel, execute(_, _))
+ .Times(kNumberOfComputations)
+ .WillRepeatedly(Invoke(makeExecute(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE)));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute repeatedly
+ for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+ const auto computeResult = createResult.value()->compute({});
+ EXPECT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+ << ": " << computeResult.error().message;
+ }
+}
+
+TEST(PreparedModelTest, reusableExecuteLaunchError) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ EXPECT_CALL(*mockPreparedModel, execute(_, _))
+ .Times(1)
+ .WillOnce(Invoke(makeExecute(V1_0::ErrorStatus::GENERAL_FAILURE,
+ V1_0::ErrorStatus::GENERAL_FAILURE)));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->compute({});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteReturnError) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ EXPECT_CALL(*mockPreparedModel, execute(_, _))
+ .Times(1)
+ .WillOnce(Invoke(
+ makeExecute(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::GENERAL_FAILURE)));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->compute({});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteTransportFailure) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ EXPECT_CALL(*mockPreparedModel, execute(_, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->compute({});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteDeadObject) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ EXPECT_CALL(*mockPreparedModel, execute(_, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->compute({});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteCrash) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ const auto ret = [&mockPreparedModel]() -> hardware::Return<V1_0::ErrorStatus> {
+ mockPreparedModel->simulateCrash();
+ return V1_0::ErrorStatus::NONE;
+ };
+ EXPECT_CALL(*mockPreparedModel, execute(_, _)).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->compute({});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedNotSupported) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
TEST(PreparedModelTest, configureExecutionBurst) {
// setup test
const auto mockPreparedModel = MockPreparedModel::create();
diff --git a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h
index 3aec8ee..5e224b5 100644
--- a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h
+++ b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h
@@ -52,7 +52,6 @@
const std::string& getVersionString() const override;
nn::Version getFeatureLevel() const override;
nn::DeviceType getType() const override;
- bool isUpdatable() const override;
const std::vector<nn::Extension>& getSupportedExtensions() const override;
const nn::Capabilities& getCapabilities() const override;
std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const override;
diff --git a/neuralnetworks/1.1/utils/src/Device.cpp b/neuralnetworks/1.1/utils/src/Device.cpp
index d2ef57f..3197ef4 100644
--- a/neuralnetworks/1.1/utils/src/Device.cpp
+++ b/neuralnetworks/1.1/utils/src/Device.cpp
@@ -106,10 +106,6 @@
return nn::DeviceType::UNKNOWN;
}
-bool Device::isUpdatable() const {
- return false;
-}
-
const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
return kExtensions;
}
diff --git a/neuralnetworks/1.2/types.hal b/neuralnetworks/1.2/types.hal
index 03aed86..f5b6ead 100644
--- a/neuralnetworks/1.2/types.hal
+++ b/neuralnetworks/1.2/types.hal
@@ -3618,7 +3618,7 @@
* front of dimension i.
* padding[i, 1] specifies the number of elements to be padded after
* the end of dimension i.
- * * 2: An scalar specifying the value to use for padding input0.
+ * * 2: A scalar specifying the value to use for padding input0.
* For input tensor of {@link OperandType::TENSOR_FLOAT16}, the
* pad value must be of {@link OperandType::FLOAT16}.
* For input tensor of {@link OperandType::TENSOR_FLOAT32}, the
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h
index 489f857..b4bef5e 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h
@@ -71,7 +71,6 @@
const std::string& getVersionString() const override;
nn::Version getFeatureLevel() const override;
nn::DeviceType getType() const override;
- bool isUpdatable() const override;
const std::vector<nn::Extension>& getSupportedExtensions() const override;
const nn::Capabilities& getCapabilities() const override;
std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const override;
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Execution.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Execution.h
new file mode 100644
index 0000000..9c66446
--- /dev/null
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Execution.h
@@ -0,0 +1,66 @@
+/*
+ * 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_EXECUTION_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_H
+
+#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include "PreparedModel.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+class Execution final : public nn::IExecution, public std::enable_shared_from_this<Execution> {
+ struct PrivateConstructorTag {};
+
+ public:
+ static nn::GeneralResult<std::shared_ptr<const Execution>> create(
+ std::shared_ptr<const PreparedModel> preparedModel, V1_0::Request request,
+ hal::utils::RequestRelocation relocation, V1_2::MeasureTiming measure);
+
+ Execution(PrivateConstructorTag tag, std::shared_ptr<const PreparedModel> preparedModel,
+ V1_0::Request request, hal::utils::RequestRelocation relocation,
+ V1_2::MeasureTiming measure);
+
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+ const nn::OptionalTimePoint& deadline) const override;
+
+ nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+ const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+ private:
+ const std::shared_ptr<const PreparedModel> kPreparedModel;
+ const V1_0::Request kRequest;
+ const hal::utils::RequestRelocation kRelocation;
+ const MeasureTiming kMeasure;
+};
+
+} // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_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 9669d8c0..dae1ff3 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
@@ -28,9 +28,11 @@
#include <fmq/MessageQueue.h>
#include <hidl/MQDescriptor.h>
#include <nnapi/IBurst.h>
+#include <nnapi/IExecution.h>
#include <nnapi/IPreparedModel.h>
#include <nnapi/Result.h>
#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
#include <nnapi/hal/ProtectCallback.h>
#include <atomic>
@@ -51,14 +53,14 @@
* across FMQ, making it appear to the runtime as a regular synchronous inference. Additionally,
* this class manages the burst's memory cache.
*/
-class ExecutionBurstController final : public nn::IBurst {
+class ExecutionBurstController final
+ : public nn::IBurst,
+ public std::enable_shared_from_this<ExecutionBurstController> {
struct PrivateConstructorTag {};
public:
- using FallbackFunction =
- std::function<nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>(
- const nn::Request&, nn::MeasureTiming, const nn::OptionalTimePoint&,
- const nn::OptionalDuration&)>;
+ using FallbackFunction = std::function<
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>()>;
/**
* NN runtime memory cache.
@@ -154,10 +156,10 @@
* @return ExecutionBurstController Execution burst controller object.
*/
static nn::GeneralResult<std::shared_ptr<const ExecutionBurstController>> create(
- const sp<IPreparedModel>& preparedModel, FallbackFunction fallback,
+ nn::SharedPreparedModel preparedModel, const sp<IPreparedModel>& hidlPreparedModel,
std::chrono::microseconds pollingTimeWindow);
- ExecutionBurstController(PrivateConstructorTag tag, FallbackFunction fallback,
+ ExecutionBurstController(PrivateConstructorTag tag, nn::SharedPreparedModel preparedModel,
std::unique_ptr<RequestChannelSender> requestChannelSender,
std::unique_ptr<ResultChannelReceiver> resultChannelReceiver,
sp<ExecutionBurstCallback> callback, sp<IBurstContext> burstContext,
@@ -173,9 +175,21 @@
const nn::OptionalTimePoint& deadline,
const nn::OptionalDuration& loopTimeoutDuration) const override;
+ // See IBurst::createReusableExecution for information on this method.
+ nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& loopTimeoutDuration) const override;
+
+ // If fallback is not nullptr, this method will invoke the fallback function to try another
+ // execution path if the packet could not be sent. Otherwise, failing to send the packet will
+ // result in an error.
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeInternal(
+ const std::vector<FmqRequestDatum>& requestPacket,
+ const hal::utils::RequestRelocation& relocation, FallbackFunction fallback) const;
+
private:
mutable std::atomic_flag mExecutionInFlight = ATOMIC_FLAG_INIT;
- const FallbackFunction kFallback;
+ const nn::SharedPreparedModel kPreparedModel;
const std::unique_ptr<RequestChannelSender> mRequestChannelSender;
const std::unique_ptr<ResultChannelReceiver> mResultChannelReceiver;
const sp<ExecutionBurstCallback> mBurstCallback;
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h
index fb11130..35abd79 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h
@@ -58,10 +58,18 @@
const nn::OptionalDuration& loopTimeoutDuration,
const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+ nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& loopTimeoutDuration) const override;
+
nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
std::any getUnderlyingResource() const override;
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeInternal(
+ const V1_0::Request& request, MeasureTiming measure,
+ const hal::utils::RequestRelocation& relocation) const;
+
private:
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeSynchronously(
const V1_0::Request& request, MeasureTiming measure) const;
diff --git a/neuralnetworks/1.2/utils/src/Device.cpp b/neuralnetworks/1.2/utils/src/Device.cpp
index 1954dfa..9fe0de2 100644
--- a/neuralnetworks/1.2/utils/src/Device.cpp
+++ b/neuralnetworks/1.2/utils/src/Device.cpp
@@ -199,10 +199,6 @@
return kDeviceType;
}
-bool Device::isUpdatable() const {
- return false;
-}
-
const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
return kExtensions;
}
diff --git a/neuralnetworks/1.2/utils/src/Execution.cpp b/neuralnetworks/1.2/utils/src/Execution.cpp
new file mode 100644
index 0000000..18d1c90
--- /dev/null
+++ b/neuralnetworks/1.2/utils/src/Execution.cpp
@@ -0,0 +1,74 @@
+/*
+ * 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 "Execution.h"
+
+#include "Callbacks.h"
+#include "Conversions.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+nn::GeneralResult<std::shared_ptr<const Execution>> Execution::create(
+ std::shared_ptr<const PreparedModel> preparedModel, V1_0::Request request,
+ hal::utils::RequestRelocation relocation, V1_2::MeasureTiming measure) {
+ if (preparedModel == nullptr) {
+ return NN_ERROR() << "V1_2::utils::Execution::create must have non-null preparedModel";
+ }
+
+ return std::make_shared<const Execution>(PrivateConstructorTag{}, std::move(preparedModel),
+ std::move(request), std::move(relocation), measure);
+}
+
+Execution::Execution(PrivateConstructorTag /*tag*/,
+ std::shared_ptr<const PreparedModel> preparedModel, V1_0::Request request,
+ hal::utils::RequestRelocation relocation, V1_2::MeasureTiming measure)
+ : kPreparedModel(std::move(preparedModel)),
+ kRequest(std::move(request)),
+ kRelocation(std::move(relocation)),
+ kMeasure(measure) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Execution::compute(
+ const nn::OptionalTimePoint& /*deadline*/) const {
+ return kPreparedModel->executeInternal(kRequest, kMeasure, kRelocation);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> Execution::computeFenced(
+ const std::vector<nn::SyncFence>& /*waitFor*/, const nn::OptionalTimePoint& /*deadline*/,
+ const nn::OptionalDuration& /*timeoutDurationAfterFence*/) const {
+ return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+ << "IExecution::computeFenced is not supported on 1.2 HAL service";
+}
+
+} // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp b/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
index 7a17f25..8e82d25 100644
--- a/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
+++ b/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
@@ -28,6 +28,7 @@
#include <nnapi/Types.h>
#include <nnapi/Validation.h>
#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/CommonUtils.h>
#include <nnapi/hal/HandleError.h>
#include <nnapi/hal/ProtectCallback.h>
#include <nnapi/hal/TransferValue.h>
@@ -50,6 +51,35 @@
namespace android::hardware::neuralnetworks::V1_2::utils {
namespace {
+class BurstExecution final : public nn::IExecution,
+ public std::enable_shared_from_this<BurstExecution> {
+ struct PrivateConstructorTag {};
+
+ public:
+ static nn::GeneralResult<std::shared_ptr<const BurstExecution>> create(
+ std::shared_ptr<const ExecutionBurstController> controller,
+ std::vector<FmqRequestDatum> request, hal::utils::RequestRelocation relocation,
+ std::vector<ExecutionBurstController::OptionalCacheHold> cacheHolds);
+
+ BurstExecution(PrivateConstructorTag tag,
+ std::shared_ptr<const ExecutionBurstController> controller,
+ std::vector<FmqRequestDatum> request, hal::utils::RequestRelocation relocation,
+ std::vector<ExecutionBurstController::OptionalCacheHold> cacheHolds);
+
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+ const nn::OptionalTimePoint& deadline) const override;
+
+ nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+ const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+ private:
+ const std::shared_ptr<const ExecutionBurstController> kController;
+ const std::vector<FmqRequestDatum> kRequest;
+ const hal::utils::RequestRelocation kRelocation;
+ const std::vector<ExecutionBurstController::OptionalCacheHold> kCacheHolds;
+};
+
nn::GeneralResult<sp<IBurstContext>> executionBurstResultCallback(
V1_0::ErrorStatus status, const sp<IBurstContext>& burstContext) {
HANDLE_HAL_STATUS(status) << "IPreparedModel::configureExecutionBurst failed with status "
@@ -209,10 +239,10 @@
// ExecutionBurstController methods
nn::GeneralResult<std::shared_ptr<const ExecutionBurstController>> ExecutionBurstController::create(
- const sp<V1_2::IPreparedModel>& preparedModel, FallbackFunction fallback,
+ nn::SharedPreparedModel preparedModel, const sp<V1_2::IPreparedModel>& hidlPreparedModel,
std::chrono::microseconds pollingTimeWindow) {
// check inputs
- if (preparedModel == nullptr) {
+ if (preparedModel == nullptr || hidlPreparedModel == nullptr) {
return NN_ERROR() << "ExecutionBurstController::create passed a nullptr";
}
@@ -236,7 +266,7 @@
auto cb = hal::utils::CallbackValue(executionBurstResultCallback);
// configure burst
- const Return<void> ret = preparedModel->configureExecutionBurst(
+ const Return<void> ret = hidlPreparedModel->configureExecutionBurst(
burstCallback, *requestChannelDescriptor, *resultChannelDescriptor, cb);
HANDLE_TRANSPORT_FAILURE(ret);
@@ -250,18 +280,18 @@
// make and return controller
return std::make_shared<const ExecutionBurstController>(
- PrivateConstructorTag{}, std::move(fallback), std::move(requestChannelSender),
+ PrivateConstructorTag{}, std::move(preparedModel), std::move(requestChannelSender),
std::move(resultChannelReceiver), std::move(burstCallback), std::move(burstContext),
std::move(memoryCache), std::move(deathHandler));
}
ExecutionBurstController::ExecutionBurstController(
- PrivateConstructorTag /*tag*/, FallbackFunction fallback,
+ PrivateConstructorTag /*tag*/, nn::SharedPreparedModel preparedModel,
std::unique_ptr<RequestChannelSender> requestChannelSender,
std::unique_ptr<ResultChannelReceiver> resultChannelReceiver,
sp<ExecutionBurstCallback> callback, sp<IBurstContext> burstContext,
std::shared_ptr<MemoryCache> memoryCache, neuralnetworks::utils::DeathHandler deathHandler)
- : kFallback(std::move(fallback)),
+ : kPreparedModel(std::move(preparedModel)),
mRequestChannelSender(std::move(requestChannelSender)),
mResultChannelReceiver(std::move(resultChannelReceiver)),
mBurstCallback(std::move(callback)),
@@ -283,26 +313,96 @@
// 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
// ExecutionBurstServer collects systraces at different points in the code.
- NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION, "ExecutionBurstController::execute");
+ NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ExecutionBurstController::execute");
// if the request is valid but of a higher version than what's supported in burst execution,
// fall back to another execution path
if (const auto version = NN_TRY(hal::utils::makeExecutionFailure(nn::validate(request)));
version > nn::Version::ANDROID_Q) {
// fallback to another execution path if the packet could not be sent
- if (kFallback) {
- return kFallback(request, measure, deadline, loopTimeoutDuration);
- }
- return NN_ERROR() << "Request object has features not supported by IBurst::execute";
+ return kPreparedModel->execute(request, measure, deadline, loopTimeoutDuration);
}
+ // ensure that request is ready for IPC
+ std::optional<nn::Request> maybeRequestInShared;
+ hal::utils::RequestRelocation relocation;
+ const nn::Request& requestInShared =
+ NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
+ &request, &maybeRequestInShared, &relocation)));
+
// clear pools field of request, as they will be provided via slots
- const auto requestWithoutPools =
- nn::Request{.inputs = request.inputs, .outputs = request.outputs, .pools = {}};
+ const auto requestWithoutPools = nn::Request{
+ .inputs = requestInShared.inputs, .outputs = requestInShared.outputs, .pools = {}};
auto hidlRequest = NN_TRY(
hal::utils::makeExecutionFailure(V1_0::utils::unvalidatedConvert(requestWithoutPools)));
const auto hidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
+ std::vector<int32_t> slots;
+ std::vector<OptionalCacheHold> holds;
+ slots.reserve(requestInShared.pools.size());
+ holds.reserve(requestInShared.pools.size());
+ for (const auto& memoryPool : requestInShared.pools) {
+ auto [slot, hold] = mMemoryCache->cacheMemory(std::get<nn::SharedMemory>(memoryPool));
+ slots.push_back(slot);
+ holds.push_back(std::move(hold));
+ }
+
+ // send request packet
+ const auto requestPacket = serialize(hidlRequest, hidlMeasure, slots);
+ const auto fallback = [this, &request, measure, &deadline, &loopTimeoutDuration] {
+ return kPreparedModel->execute(request, measure, deadline, loopTimeoutDuration);
+ };
+ return executeInternal(requestPacket, relocation, fallback);
+}
+
+// See IBurst::createReusableExecution for information on this method.
+nn::GeneralResult<nn::SharedExecution> ExecutionBurstController::createReusableExecution(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& loopTimeoutDuration) const {
+ NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ExecutionBurstController::createReusableExecution");
+
+ // if the request is valid but of a higher version than what's supported in burst execution,
+ // fall back to another execution path
+ if (const auto version = NN_TRY(hal::utils::makeGeneralFailure(nn::validate(request)));
+ version > nn::Version::ANDROID_Q) {
+ // fallback to another execution path if the packet could not be sent
+ return kPreparedModel->createReusableExecution(request, measure, loopTimeoutDuration);
+ }
+
+ // ensure that request is ready for IPC
+ std::optional<nn::Request> maybeRequestInShared;
+ hal::utils::RequestRelocation relocation;
+ const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+ &request, &maybeRequestInShared, &relocation));
+
+ // clear pools field of request, as they will be provided via slots
+ const auto requestWithoutPools = nn::Request{
+ .inputs = requestInShared.inputs, .outputs = requestInShared.outputs, .pools = {}};
+ auto hidlRequest = NN_TRY(V1_0::utils::unvalidatedConvert(requestWithoutPools));
+ const auto hidlMeasure = NN_TRY(convert(measure));
+
+ std::vector<int32_t> slots;
+ std::vector<OptionalCacheHold> holds;
+ slots.reserve(requestInShared.pools.size());
+ holds.reserve(requestInShared.pools.size());
+ for (const auto& memoryPool : requestInShared.pools) {
+ auto [slot, hold] = mMemoryCache->cacheMemory(std::get<nn::SharedMemory>(memoryPool));
+ slots.push_back(slot);
+ holds.push_back(std::move(hold));
+ }
+
+ const auto requestPacket = serialize(hidlRequest, hidlMeasure, slots);
+ return BurstExecution::create(shared_from_this(), std::move(requestPacket),
+ std::move(relocation), std::move(holds));
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+ExecutionBurstController::executeInternal(const std::vector<FmqRequestDatum>& requestPacket,
+ const hal::utils::RequestRelocation& relocation,
+ FallbackFunction fallback) const {
+ NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION,
+ "ExecutionBurstController::executeInternal");
+
// Ensure that at most one execution is in flight at any given time.
const bool alreadyInFlight = mExecutionInFlight.test_and_set();
if (alreadyInFlight) {
@@ -310,22 +410,16 @@
}
const auto guard = base::make_scope_guard([this] { mExecutionInFlight.clear(); });
- std::vector<int32_t> slots;
- std::vector<OptionalCacheHold> holds;
- slots.reserve(request.pools.size());
- holds.reserve(request.pools.size());
- for (const auto& memoryPool : request.pools) {
- auto [slot, hold] = mMemoryCache->cacheMemory(std::get<nn::SharedMemory>(memoryPool));
- slots.push_back(slot);
- holds.push_back(std::move(hold));
+ if (relocation.input) {
+ relocation.input->flush();
}
// send request packet
- const auto sendStatus = mRequestChannelSender->send(hidlRequest, hidlMeasure, slots);
+ const auto sendStatus = mRequestChannelSender->sendPacket(requestPacket);
if (!sendStatus.ok()) {
// fallback to another execution path if the packet could not be sent
- if (kFallback) {
- return kFallback(request, measure, deadline, loopTimeoutDuration);
+ if (fallback) {
+ return fallback();
}
return NN_ERROR() << "Error sending FMQ packet: " << sendStatus.error();
}
@@ -333,7 +427,47 @@
// get result packet
const auto [status, outputShapes, timing] =
NN_TRY(hal::utils::makeExecutionFailure(mResultChannelReceiver->getBlocking()));
+
+ if (relocation.output) {
+ relocation.output->flush();
+ }
return executionCallback(status, outputShapes, timing);
}
+nn::GeneralResult<std::shared_ptr<const BurstExecution>> BurstExecution::create(
+ std::shared_ptr<const ExecutionBurstController> controller,
+ std::vector<FmqRequestDatum> request, hal::utils::RequestRelocation relocation,
+ std::vector<ExecutionBurstController::OptionalCacheHold> cacheHolds) {
+ if (controller == nullptr) {
+ return NN_ERROR() << "V1_2::utils::BurstExecution::create must have non-null controller";
+ }
+
+ return std::make_shared<const BurstExecution>(PrivateConstructorTag{}, std::move(controller),
+ std::move(request), std::move(relocation),
+ std::move(cacheHolds));
+}
+
+BurstExecution::BurstExecution(PrivateConstructorTag /*tag*/,
+ std::shared_ptr<const ExecutionBurstController> controller,
+ std::vector<FmqRequestDatum> request,
+ hal::utils::RequestRelocation relocation,
+ std::vector<ExecutionBurstController::OptionalCacheHold> cacheHolds)
+ : kController(std::move(controller)),
+ kRequest(std::move(request)),
+ kRelocation(std::move(relocation)),
+ kCacheHolds(std::move(cacheHolds)) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> BurstExecution::compute(
+ const nn::OptionalTimePoint& /*deadline*/) const {
+ return kController->executeInternal(kRequest, kRelocation, /*fallback=*/nullptr);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+BurstExecution::computeFenced(const std::vector<nn::SyncFence>& /*waitFor*/,
+ const nn::OptionalTimePoint& /*deadline*/,
+ const nn::OptionalDuration& /*timeoutDurationAfterFence*/) const {
+ return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+ << "IExecution::computeFenced is not supported on burst object";
+}
+
} // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/PreparedModel.cpp b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
index b209a44..1d87937 100644
--- a/neuralnetworks/1.2/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
@@ -18,6 +18,7 @@
#include "Callbacks.h"
#include "Conversions.h"
+#include "Execution.h"
#include "ExecutionBurstController.h"
#include "ExecutionBurstUtils.h"
#include "Utils.h"
@@ -93,19 +94,31 @@
const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
// 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)));
+ hal::utils::RequestRelocation relocation;
+ const nn::Request& requestInShared =
+ NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
+ &request, &maybeRequestInShared, &relocation)));
const auto hidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
const auto hidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
- auto result = kExecuteSynchronously ? executeSynchronously(hidlRequest, hidlMeasure)
- : executeAsynchronously(hidlRequest, hidlMeasure);
+ return executeInternal(hidlRequest, hidlMeasure, relocation);
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+PreparedModel::executeInternal(const V1_0::Request& request, MeasureTiming measure,
+ const hal::utils::RequestRelocation& relocation) const {
+ if (relocation.input) {
+ relocation.input->flush();
+ }
+
+ auto result = kExecuteSynchronously ? executeSynchronously(request, measure)
+ : executeAsynchronously(request, measure);
auto [outputShapes, timing] = NN_TRY(std::move(result));
- NN_TRY(hal::utils::makeExecutionFailure(
- hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
-
+ if (relocation.output) {
+ relocation.output->flush();
+ }
return std::make_pair(std::move(outputShapes), timing);
}
@@ -120,6 +133,21 @@
<< "IPreparedModel::executeFenced is not supported on 1.2 HAL service";
}
+nn::GeneralResult<nn::SharedExecution> PreparedModel::createReusableExecution(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
+ // Ensure that request is ready for IPC.
+ std::optional<nn::Request> maybeRequestInShared;
+ hal::utils::RequestRelocation relocation;
+ const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+ &request, &maybeRequestInShared, &relocation));
+
+ auto hidlRequest = NN_TRY(convert(requestInShared));
+ auto hidlMeasure = NN_TRY(convert(measure));
+ return Execution::create(shared_from_this(), std::move(hidlRequest), std::move(relocation),
+ hidlMeasure);
+}
+
nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
auto self = shared_from_this();
auto fallback = [preparedModel = std::move(self)](
@@ -130,7 +158,7 @@
return preparedModel->execute(request, measure, deadline, loopTimeoutDuration);
};
const auto pollingTimeWindow = getBurstControllerPollingTimeWindow();
- return ExecutionBurstController::create(kPreparedModel, std::move(fallback), pollingTimeWindow);
+ return ExecutionBurstController::create(shared_from_this(), kPreparedModel, pollingTimeWindow);
}
std::any PreparedModel::getUnderlyingResource() const {
diff --git a/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp b/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp
index d297b1a..5e2ad79 100644
--- a/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp
+++ b/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp
@@ -21,6 +21,7 @@
#include <android/hardware/neuralnetworks/1.2/IExecutionCallback.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <nnapi/IExecution.h>
#include <nnapi/IPreparedModel.h>
#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
@@ -334,6 +335,248 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
+TEST(PreparedModelTest, reusableExecuteSync) {
+ // setup call
+ const uint32_t kNumberOfComputations = 2;
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _))
+ .Times(kNumberOfComputations)
+ .WillRepeatedly(
+ Invoke(makeExecuteSynchronously(V1_0::ErrorStatus::NONE, {}, kNoTiming)));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute repeatedly
+ for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+ const auto computeResult = createResult.value()->compute({});
+ EXPECT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+ << ": " << computeResult.error().message;
+ }
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncError) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _))
+ .Times(1)
+ .WillOnce(Invoke(
+ makeExecuteSynchronously(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming)));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->compute({});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncTransportFailure) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->compute({});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncDeadObject) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->compute({});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsync) {
+ // setup call
+ const uint32_t kNumberOfComputations = 2;
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+ .Times(kNumberOfComputations)
+ .WillRepeatedly(Invoke(makeExecuteAsynchronously(
+ V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE, {}, kNoTiming)));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute repeatedly
+ for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+ const auto computeResult = createResult.value()->compute({});
+ EXPECT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+ << ": " << computeResult.error().message;
+ }
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncLaunchError) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makeExecuteAsynchronously(V1_0::ErrorStatus::GENERAL_FAILURE,
+ V1_0::ErrorStatus::GENERAL_FAILURE, {},
+ kNoTiming)));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->compute({});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncReturnError) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makeExecuteAsynchronously(
+ V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming)));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->compute({});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncTransportFailure) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->compute({});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncDeadObject) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->compute({});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncCrash) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ const auto ret = [&mockPreparedModel]() -> hardware::Return<V1_0::ErrorStatus> {
+ mockPreparedModel->simulateCrash();
+ return V1_0::ErrorStatus::NONE;
+ };
+ EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _)).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->compute({});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedNotSupported) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
TEST(PreparedModelTest, configureExecutionBurst) {
// setup test
const auto mockPreparedModel = MockPreparedModel::create();
diff --git a/neuralnetworks/1.3/types.hal b/neuralnetworks/1.3/types.hal
index a5dbd5e..a26b858 100644
--- a/neuralnetworks/1.3/types.hal
+++ b/neuralnetworks/1.3/types.hal
@@ -3834,7 +3834,7 @@
* front of dimension i.
* padding[i, 1] specifies the number of elements to be padded after
* the end of dimension i.
- * * 2: An scalar specifying the value to use for padding input0.
+ * * 2: A scalar specifying the value to use for padding input0.
* For input tensor of {@link OperandType::TENSOR_FLOAT16}, the
* pad value must be of {@link OperandType::FLOAT16}.
* For input tensor of {@link OperandType::TENSOR_FLOAT32}, the
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h
index f36b6c0..84f606a 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h
@@ -54,7 +54,6 @@
const std::string& getVersionString() const override;
nn::Version getFeatureLevel() const override;
nn::DeviceType getType() const override;
- bool isUpdatable() const override;
const std::vector<nn::Extension>& getSupportedExtensions() const override;
const nn::Capabilities& getCapabilities() const override;
std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const override;
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Execution.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Execution.h
new file mode 100644
index 0000000..06c33d4
--- /dev/null
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Execution.h
@@ -0,0 +1,66 @@
+/*
+ * 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_EXECUTION_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_EXECUTION_H
+
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+
+#include "PreparedModel.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+class Execution final : public nn::IExecution, public std::enable_shared_from_this<Execution> {
+ struct PrivateConstructorTag {};
+
+ public:
+ static nn::GeneralResult<std::shared_ptr<const Execution>> create(
+ std::shared_ptr<const PreparedModel> preparedModel, Request request,
+ hal::utils::RequestRelocation relocation, V1_2::MeasureTiming measure,
+ OptionalTimeoutDuration loopTimeoutDuration);
+
+ Execution(PrivateConstructorTag tag, std::shared_ptr<const PreparedModel> preparedModel,
+ Request request, hal::utils::RequestRelocation relocation,
+ V1_2::MeasureTiming measure, OptionalTimeoutDuration loopTimeoutDuration);
+
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+ const nn::OptionalTimePoint& deadline) const override;
+
+ nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+ const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+ private:
+ const std::shared_ptr<const PreparedModel> kPreparedModel;
+ const Request kRequest;
+ const hal::utils::RequestRelocation kRelocation;
+ const V1_2::MeasureTiming kMeasure;
+ const OptionalTimeoutDuration kLoopTimeoutDuration;
+};
+
+} // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_EXECUTION_H
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h
index 690fecc..5acba71 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h
@@ -57,10 +57,26 @@
const nn::OptionalDuration& loopTimeoutDuration,
const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+ nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& loopTimeoutDuration) const override;
+
nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
std::any getUnderlyingResource() const override;
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeInternal(
+ const Request& request, V1_2::MeasureTiming measure, const OptionalTimePoint& deadline,
+ const OptionalTimeoutDuration& loopTimeoutDuration,
+ const hal::utils::RequestRelocation& relocation) const;
+
+ nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+ executeFencedInternal(const Request& request, const hidl_vec<hidl_handle>& waitFor,
+ V1_2::MeasureTiming measure, const OptionalTimePoint& deadline,
+ const OptionalTimeoutDuration& loopTimeoutDuration,
+ const OptionalTimeoutDuration& timeoutDurationAfterFence,
+ const hal::utils::RequestRelocation& relocation) const;
+
private:
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeSynchronously(
const Request& request, V1_2::MeasureTiming measure, const OptionalTimePoint& deadline,
diff --git a/neuralnetworks/1.3/utils/src/Device.cpp b/neuralnetworks/1.3/utils/src/Device.cpp
index 87c9f32..d710b85 100644
--- a/neuralnetworks/1.3/utils/src/Device.cpp
+++ b/neuralnetworks/1.3/utils/src/Device.cpp
@@ -150,10 +150,6 @@
return kDeviceType;
}
-bool Device::isUpdatable() const {
- return false;
-}
-
const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
return kExtensions;
}
diff --git a/neuralnetworks/1.3/utils/src/Execution.cpp b/neuralnetworks/1.3/utils/src/Execution.cpp
new file mode 100644
index 0000000..3d17cc3
--- /dev/null
+++ b/neuralnetworks/1.3/utils/src/Execution.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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 "Execution.h"
+
+#include "Conversions.h"
+#include "PreparedModel.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <android/hardware/neuralnetworks/1.3/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+nn::GeneralResult<std::shared_ptr<const Execution>> Execution::create(
+ std::shared_ptr<const PreparedModel> preparedModel, Request request,
+ hal::utils::RequestRelocation relocation, V1_2::MeasureTiming measure,
+ OptionalTimeoutDuration loopTimeoutDuration) {
+ if (preparedModel == nullptr) {
+ return NN_ERROR() << "V1_3::utils::Execution::create must have non-null preparedModel";
+ }
+
+ return std::make_shared<const Execution>(PrivateConstructorTag{}, std::move(preparedModel),
+ std::move(request), std::move(relocation), measure,
+ std::move(loopTimeoutDuration));
+}
+
+Execution::Execution(PrivateConstructorTag /*tag*/,
+ std::shared_ptr<const PreparedModel> preparedModel, Request request,
+ hal::utils::RequestRelocation relocation, V1_2::MeasureTiming measure,
+ OptionalTimeoutDuration loopTimeoutDuration)
+ : kPreparedModel(std::move(preparedModel)),
+ kRequest(std::move(request)),
+ kRelocation(std::move(relocation)),
+ kMeasure(measure),
+ kLoopTimeoutDuration(std::move(loopTimeoutDuration)) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Execution::compute(
+ const nn::OptionalTimePoint& deadline) const {
+ const auto hidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
+ return kPreparedModel->executeInternal(kRequest, kMeasure, hidlDeadline, kLoopTimeoutDuration,
+ kRelocation);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> Execution::computeFenced(
+ const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& timeoutDurationAfterFence) const {
+ const auto hidlWaitFor = NN_TRY(hal::utils::convertSyncFences(waitFor));
+ const auto hidlDeadline = NN_TRY(convert(deadline));
+ const auto hidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence));
+ return kPreparedModel->executeFencedInternal(kRequest, hidlWaitFor, kMeasure, hidlDeadline,
+ kLoopTimeoutDuration,
+ hidlTimeoutDurationAfterFence, kRelocation);
+}
+
+} // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/src/PreparedModel.cpp b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
index fd7f8f2..cb56bdc 100644
--- a/neuralnetworks/1.3/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
@@ -18,6 +18,7 @@
#include "Callbacks.h"
#include "Conversions.h"
+#include "Execution.h"
#include "Utils.h"
#include <android/hardware/neuralnetworks/1.0/types.h>
@@ -139,8 +140,10 @@
const nn::OptionalDuration& loopTimeoutDuration) const {
// 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)));
+ hal::utils::RequestRelocation relocation;
+ const nn::Request& requestInShared =
+ NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
+ &request, &maybeRequestInShared, &relocation)));
const auto hidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
const auto hidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
@@ -148,16 +151,27 @@
const auto hidlLoopTimeoutDuration =
NN_TRY(hal::utils::makeExecutionFailure(convert(loopTimeoutDuration)));
+ return executeInternal(hidlRequest, hidlMeasure, hidlDeadline, hidlLoopTimeoutDuration,
+ relocation);
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+PreparedModel::executeInternal(const Request& request, V1_2::MeasureTiming measure,
+ const OptionalTimePoint& deadline,
+ const OptionalTimeoutDuration& loopTimeoutDuration,
+ const hal::utils::RequestRelocation& relocation) const {
+ if (relocation.input) {
+ relocation.input->flush();
+ }
+
auto result = kExecuteSynchronously
- ? executeSynchronously(hidlRequest, hidlMeasure, hidlDeadline,
- hidlLoopTimeoutDuration)
- : executeAsynchronously(hidlRequest, hidlMeasure, hidlDeadline,
- hidlLoopTimeoutDuration);
+ ? executeSynchronously(request, measure, deadline, loopTimeoutDuration)
+ : executeAsynchronously(request, measure, deadline, loopTimeoutDuration);
auto [outputShapes, timing] = NN_TRY(std::move(result));
- NN_TRY(hal::utils::makeExecutionFailure(
- hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
-
+ if (relocation.output) {
+ relocation.output->flush();
+ }
return std::make_pair(std::move(outputShapes), timing);
}
@@ -168,8 +182,9 @@
const nn::OptionalDuration& timeoutDurationAfterFence) const {
// Ensure that request is ready for IPC.
std::optional<nn::Request> maybeRequestInShared;
- const nn::Request& requestInShared =
- NN_TRY(hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared));
+ hal::utils::RequestRelocation relocation;
+ const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+ &request, &maybeRequestInShared, &relocation));
const auto hidlRequest = NN_TRY(convert(requestInShared));
const auto hidlWaitFor = NN_TRY(hal::utils::convertSyncFences(waitFor));
@@ -178,27 +193,58 @@
const auto hidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
const auto hidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence));
+ return executeFencedInternal(hidlRequest, hidlWaitFor, hidlMeasure, hidlDeadline,
+ hidlLoopTimeoutDuration, hidlTimeoutDurationAfterFence,
+ relocation);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+PreparedModel::executeFencedInternal(const Request& request, const hidl_vec<hidl_handle>& waitFor,
+ V1_2::MeasureTiming measure, const OptionalTimePoint& deadline,
+ const OptionalTimeoutDuration& loopTimeoutDuration,
+ const OptionalTimeoutDuration& timeoutDurationAfterFence,
+ const hal::utils::RequestRelocation& relocation) const {
+ if (relocation.input) {
+ relocation.input->flush();
+ }
+
auto cb = hal::utils::CallbackValue(fencedExecutionCallback);
- const auto ret = kPreparedModel->executeFenced(hidlRequest, hidlWaitFor, hidlMeasure,
- hidlDeadline, hidlLoopTimeoutDuration,
- hidlTimeoutDurationAfterFence, cb);
+ const auto ret =
+ kPreparedModel->executeFenced(request, waitFor, measure, deadline, loopTimeoutDuration,
+ timeoutDurationAfterFence, cb);
HANDLE_TRANSPORT_FAILURE(ret);
auto [syncFence, callback] = NN_TRY(cb.take());
// If executeFenced required the request memory to be moved into shared memory, block here until
// the fenced execution has completed and flush the memory back.
- if (maybeRequestInShared.has_value()) {
+ if (relocation.output) {
const auto state = syncFence.syncWait({});
if (state != nn::SyncFence::FenceState::SIGNALED) {
return NN_ERROR() << "syncWait failed with " << state;
}
- NN_TRY(hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared));
+ relocation.output->flush();
}
return std::make_pair(std::move(syncFence), std::move(callback));
}
+nn::GeneralResult<nn::SharedExecution> PreparedModel::createReusableExecution(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& loopTimeoutDuration) const {
+ // Ensure that request is ready for IPC.
+ std::optional<nn::Request> maybeRequestInShared;
+ hal::utils::RequestRelocation relocation;
+ const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+ &request, &maybeRequestInShared, &relocation));
+
+ auto hidlRequest = NN_TRY(convert(requestInShared));
+ auto hidlMeasure = NN_TRY(convert(measure));
+ auto hidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
+ return Execution::create(shared_from_this(), std::move(hidlRequest), std::move(relocation),
+ hidlMeasure, std::move(hidlLoopTimeoutDuration));
+}
+
nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
auto self = shared_from_this();
auto fallback = [preparedModel = std::move(self)](
@@ -209,7 +255,7 @@
return preparedModel->execute(request, measure, deadline, loopTimeoutDuration);
};
const auto pollingTimeWindow = V1_2::utils::getBurstControllerPollingTimeWindow();
- return V1_2::utils::ExecutionBurstController::create(kPreparedModel, std::move(fallback),
+ return V1_2::utils::ExecutionBurstController::create(shared_from_this(), kPreparedModel,
pollingTimeWindow);
}
diff --git a/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp b/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp
index 5303c2a..6dbbd6b 100644
--- a/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp
+++ b/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp
@@ -22,6 +22,7 @@
#include <android/hardware/neuralnetworks/1.3/IFencedExecutionCallback.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <nnapi/IExecution.h>
#include <nnapi/IPreparedModel.h>
#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
@@ -462,6 +463,363 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
+TEST(PreparedModelTest, reusableExecuteSync) {
+ // setup call
+ const uint32_t kNumberOfComputations = 2;
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, executeSynchronously_1_3(_, _, _, _, _))
+ .Times(kNumberOfComputations)
+ .WillRepeatedly(
+ Invoke(makeExecuteSynchronously(V1_3::ErrorStatus::NONE, {}, kNoTiming)));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute repeatedly
+ for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+ const auto computeResult = createResult.value()->compute({});
+ EXPECT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+ << ": " << computeResult.error().message;
+ }
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncError) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, executeSynchronously_1_3(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(
+ makeExecuteSynchronously(V1_3::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming)));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->compute({});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncTransportFailure) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, executeSynchronously_1_3(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->compute({});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncDeadObject) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, executeSynchronously_1_3(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->compute({});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsync) {
+ // setup call
+ const uint32_t kNumberOfComputations = 2;
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+ .Times(kNumberOfComputations)
+ .WillRepeatedly(Invoke(makeExecuteAsynchronously(
+ V1_3::ErrorStatus::NONE, V1_3::ErrorStatus::NONE, {}, kNoTiming)));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute repeatedly
+ for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+ const auto computeResult = createResult.value()->compute({});
+ EXPECT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+ << ": " << computeResult.error().message;
+ }
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncLaunchError) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makeExecuteAsynchronously(V1_3::ErrorStatus::GENERAL_FAILURE,
+ V1_3::ErrorStatus::GENERAL_FAILURE, {},
+ kNoTiming)));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->compute({});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncReturnError) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makeExecuteAsynchronously(
+ V1_3::ErrorStatus::NONE, V1_3::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming)));
+
+ // run test
+ const auto result = preparedModel->execute({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncTransportFailure) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->compute({});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncDeadObject) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->compute({});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncCrash) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ const auto ret = [&mockPreparedModel]() -> hardware::Return<V1_3::ErrorStatus> {
+ mockPreparedModel->simulateCrash();
+ return V1_3::ErrorStatus::NONE;
+ };
+ EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(ret));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->compute({});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteFenced) {
+ // setup call
+ const uint32_t kNumberOfComputations = 2;
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ const auto mockCallback = MockFencedExecutionCallback::create();
+ EXPECT_CALL(*mockCallback, getExecutionInfo(_))
+ .Times(kNumberOfComputations)
+ .WillRepeatedly(Invoke(makeExecuteFencedCallbackReturn(V1_3::ErrorStatus::NONE,
+ kNoTiming, kNoTiming)));
+ EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+ .Times(kNumberOfComputations)
+ .WillRepeatedly(
+ Invoke(makeExecuteFencedReturn(V1_3::ErrorStatus::NONE, {}, mockCallback)));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute repeatedly
+ for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+ const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+ ASSERT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+ << ": " << computeResult.error().message;
+ const auto& [syncFence, callback] = computeResult.value();
+ EXPECT_EQ(syncFence.syncWait({}), nn::SyncFence::FenceState::SIGNALED);
+ ASSERT_NE(callback, nullptr);
+
+ // get results from callback
+ const auto callbackResult = callback();
+ ASSERT_TRUE(callbackResult.has_value()) << "Failed with " << callbackResult.error().code
+ << ": " << callbackResult.error().message;
+ }
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedCallbackError) {
+ // setup call
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ const auto mockCallback = MockFencedExecutionCallback::create();
+ EXPECT_CALL(*mockCallback, getExecutionInfo(_))
+ .Times(1)
+ .WillOnce(Invoke(makeExecuteFencedCallbackReturn(V1_3::ErrorStatus::GENERAL_FAILURE,
+ kNoTiming, kNoTiming)));
+ EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makeExecuteFencedReturn(V1_3::ErrorStatus::NONE, {}, mockCallback)));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+ ASSERT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code << ": "
+ << computeResult.error().message;
+ const auto& [syncFence, callback] = computeResult.value();
+ EXPECT_NE(syncFence.syncWait({}), nn::SyncFence::FenceState::ACTIVE);
+ ASSERT_NE(callback, nullptr);
+
+ // verify callback failure
+ const auto callbackResult = callback();
+ ASSERT_FALSE(callbackResult.has_value());
+ EXPECT_EQ(callbackResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedError) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(
+ makeExecuteFencedReturn(V1_3::ErrorStatus::GENERAL_FAILURE, {}, nullptr)));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedTransportFailure) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedDeadObject) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
TEST(PreparedModelTest, configureExecutionBurst) {
// setup test
const auto mockPreparedModel = MockPreparedModel::create();
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/Operand.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operand.aidl
index 5a9f4ff..1d9bdd8 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operand.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operand.aidl
@@ -34,11 +34,11 @@
package android.hardware.neuralnetworks;
@VintfStability
parcelable Operand {
- android.hardware.neuralnetworks.OperandType type;
+ android.hardware.neuralnetworks.OperandType type = android.hardware.neuralnetworks.OperandType.FLOAT32;
int[] dimensions;
float scale;
int zeroPoint;
- android.hardware.neuralnetworks.OperandLifeTime lifetime;
+ android.hardware.neuralnetworks.OperandLifeTime lifetime = android.hardware.neuralnetworks.OperandLifeTime.TEMPORARY_VARIABLE;
android.hardware.neuralnetworks.DataLocation location;
@nullable android.hardware.neuralnetworks.OperandExtraParams extraParams;
}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandPerformance.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandPerformance.aidl
index de93d8b..ebb361b 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandPerformance.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandPerformance.aidl
@@ -34,6 +34,6 @@
package android.hardware.neuralnetworks;
@VintfStability
parcelable OperandPerformance {
- android.hardware.neuralnetworks.OperandType type;
+ android.hardware.neuralnetworks.OperandType type = android.hardware.neuralnetworks.OperandType.FLOAT32;
android.hardware.neuralnetworks.PerformanceInfo info;
}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operation.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operation.aidl
index 33fcd60..a4a3fbe 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operation.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operation.aidl
@@ -34,7 +34,7 @@
package android.hardware.neuralnetworks;
@VintfStability
parcelable Operation {
- android.hardware.neuralnetworks.OperationType type;
+ android.hardware.neuralnetworks.OperationType type = android.hardware.neuralnetworks.OperationType.ADD;
int[] inputs;
int[] outputs;
}
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/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 c5b4ab1..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 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 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 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 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 bfab906..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 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 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 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 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/Operand.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Operand.aidl
index 4d2260f..998e06d 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/Operand.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Operand.aidl
@@ -33,7 +33,7 @@
* {@link IDevice::OPERAND_TYPE_BASE_MAX} is possible and should be interpreted as an extension
* type according to {@link Model::extensionNameToPrefix}.
*/
- OperandType type;
+ OperandType type = OperandType.FLOAT32;
/**
* Dimensions of the operand.
*
@@ -86,7 +86,7 @@
/**
* How the operand is used.
*/
- OperandLifeTime lifetime;
+ OperandLifeTime lifetime = OperandLifeTime.TEMPORARY_VARIABLE;
/**
* Where to find the data for this operand.
* If the lifetime is TEMPORARY_VARIABLE, SUBGRAPH_INPUT, SUBGRAPH_OUTPUT, or NO_VALUE:
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandPerformance.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandPerformance.aidl
index 7fd86f9..7f53967 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandPerformance.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandPerformance.aidl
@@ -25,6 +25,6 @@
*/
@VintfStability
parcelable OperandPerformance {
- OperandType type;
+ OperandType type = OperandType.FLOAT32;
PerformanceInfo info;
}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Operation.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Operation.aidl
index 0c6032f..366d9a4 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/Operation.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Operation.aidl
@@ -30,7 +30,7 @@
* {@link IDevice::OPERATION_TYPE_BASE_MAX} is possible and should be interpreted as an
* extension type according to {@link Model::extensionNameToPrefix}.
*/
- OperationType type;
+ OperationType type = OperationType.ADD;
/**
* Describes the table that contains the indexes of the inputs of the operation. The offset is
* the index in the operandIndexes table.
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl
index 3f49154..e7fb90d 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl
@@ -3693,7 +3693,7 @@
* front of dimension i.
* padding[i, 1] specifies the number of elements to be padded after
* the end of dimension i.
- * * 2: An scalar specifying the value to use for padding input0.
+ * * 2: A scalar specifying the value to use for padding input0.
* For input tensor of {@link OperandType::TENSOR_FLOAT16}, the
* pad value must be of {@link OperandType::FLOAT16}.
* For input tensor of {@link OperandType::TENSOR_FLOAT32}, the
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/include/nnapi/hal/aidl/Burst.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Burst.h
index 008e4e4..0cc78d4 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Burst.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Burst.h
@@ -38,7 +38,7 @@
namespace aidl::android::hardware::neuralnetworks::utils {
// Class that adapts aidl_hal::IBurst to nn::IBurst.
-class Burst final : public nn::IBurst {
+class Burst final : public nn::IBurst, public std::enable_shared_from_this<Burst> {
struct PrivateConstructorTag {};
public:
@@ -100,6 +100,16 @@
const nn::OptionalTimePoint& deadline,
const nn::OptionalDuration& loopTimeoutDuration) const override;
+ // See IBurst::createReusableExecution for information.
+ nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& loopTimeoutDuration) const override;
+
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeInternal(
+ const aidl_hal::Request& request, const std::vector<int64_t>& memoryIdentifierTokens,
+ bool measure, int64_t deadline, int64_t loopTimeoutDuration,
+ const hal::utils::RequestRelocation& relocation) const;
+
private:
mutable std::atomic_flag mExecutionInFlight = ATOMIC_FLAG_INIT;
const std::shared_ptr<aidl_hal::IBurst> kBurst;
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Device.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Device.h
index eb194e3..1457646 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Device.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Device.h
@@ -54,7 +54,6 @@
const std::string& getVersionString() const override;
nn::Version getFeatureLevel() const override;
nn::DeviceType getType() const override;
- bool isUpdatable() const override;
const std::vector<nn::Extension>& getSupportedExtensions() const override;
const nn::Capabilities& getCapabilities() const override;
std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const override;
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Execution.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Execution.h
new file mode 100644
index 0000000..a77ea98
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Execution.h
@@ -0,0 +1,65 @@
+/*
+ * 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_EXECUTION_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_EXECUTION_H
+
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+
+#include "PreparedModel.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+class Execution final : public nn::IExecution, public std::enable_shared_from_this<Execution> {
+ struct PrivateConstructorTag {};
+
+ public:
+ static nn::GeneralResult<std::shared_ptr<const Execution>> create(
+ std::shared_ptr<const PreparedModel> preparedModel, Request request,
+ hal::utils::RequestRelocation relocation, bool measure, int64_t loopTimeoutDuration);
+
+ Execution(PrivateConstructorTag tag, std::shared_ptr<const PreparedModel> preparedModel,
+ Request request, hal::utils::RequestRelocation relocation, bool measure,
+ int64_t loopTimeoutDuration);
+
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+ const nn::OptionalTimePoint& deadline) const override;
+
+ nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+ const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+ private:
+ const std::shared_ptr<const PreparedModel> kPreparedModel;
+ const Request kRequest;
+ const hal::utils::RequestRelocation kRelocation;
+ const bool kMeasure;
+ const int64_t kLoopTimeoutDuration;
+};
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_EXECUTION_H
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h
index abce6cc..4035764 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h
@@ -18,6 +18,7 @@
#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_PREPARED_MODEL_H
#include <aidl/android/hardware/neuralnetworks/IPreparedModel.h>
+#include <aidl/android/hardware/neuralnetworks/Request.h>
#include <nnapi/IPreparedModel.h>
#include <nnapi/Result.h>
#include <nnapi/Types.h>
@@ -34,7 +35,8 @@
namespace aidl::android::hardware::neuralnetworks::utils {
// Class that adapts aidl_hal::IPreparedModel to nn::IPreparedModel.
-class PreparedModel final : public nn::IPreparedModel {
+class PreparedModel final : public nn::IPreparedModel,
+ public std::enable_shared_from_this<PreparedModel> {
struct PrivateConstructorTag {};
public:
@@ -55,10 +57,25 @@
const nn::OptionalDuration& loopTimeoutDuration,
const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+ nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& loopTimeoutDuration) const override;
+
nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
std::any getUnderlyingResource() const override;
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeInternal(
+ const Request& request, bool measure, int64_t deadline, int64_t loopTimeoutDuration,
+ const hal::utils::RequestRelocation& relocation) const;
+
+ nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+ executeFencedInternal(const Request& request,
+ const std::vector<ndk::ScopedFileDescriptor>& waitFor, bool measure,
+ int64_t deadline, int64_t loopTimeoutDuration,
+ int64_t timeoutDurationAfterFence,
+ const hal::utils::RequestRelocation& relocation) const;
+
private:
const std::shared_ptr<aidl_hal::IPreparedModel> kPreparedModel;
};
diff --git a/neuralnetworks/aidl/utils/src/Burst.cpp b/neuralnetworks/aidl/utils/src/Burst.cpp
index 0b475bc..3cbba4d 100644
--- a/neuralnetworks/aidl/utils/src/Burst.cpp
+++ b/neuralnetworks/aidl/utils/src/Burst.cpp
@@ -22,6 +22,7 @@
#include <android-base/logging.h>
#include <android/binder_auto_utils.h>
#include <nnapi/IBurst.h>
+#include <nnapi/IExecution.h>
#include <nnapi/Result.h>
#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
@@ -35,6 +36,39 @@
namespace aidl::android::hardware::neuralnetworks::utils {
namespace {
+class BurstExecution final : public nn::IExecution,
+ public std::enable_shared_from_this<BurstExecution> {
+ struct PrivateConstructorTag {};
+
+ public:
+ static nn::GeneralResult<std::shared_ptr<const BurstExecution>> create(
+ std::shared_ptr<const Burst> burst, Request request,
+ std::vector<int64_t> memoryIdentifierTokens, bool measure, int64_t loopTimeoutDuration,
+ hal::utils::RequestRelocation relocation,
+ std::vector<Burst::OptionalCacheHold> cacheHolds);
+
+ BurstExecution(PrivateConstructorTag tag, std::shared_ptr<const Burst> burst, Request request,
+ std::vector<int64_t> memoryIdentifierTokens, bool measure,
+ int64_t loopTimeoutDuration, hal::utils::RequestRelocation relocation,
+ std::vector<Burst::OptionalCacheHold> cacheHolds);
+
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+ const nn::OptionalTimePoint& deadline) const override;
+
+ nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+ const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+ private:
+ const std::shared_ptr<const Burst> kBurst;
+ const Request kRequest;
+ const std::vector<int64_t>& kMemoryIdentifierTokens;
+ const bool kMeasure;
+ const int64_t kLoopTimeoutDuration;
+ const hal::utils::RequestRelocation kRelocation;
+ const std::vector<Burst::OptionalCacheHold> kCacheHolds;
+};
+
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)));
@@ -139,17 +173,12 @@
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)));
+ hal::utils::RequestRelocation relocation;
+ const nn::Request& requestInShared =
+ NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
+ &request, &maybeRequestInShared, &relocation)));
const auto aidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
const auto aidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
@@ -159,9 +188,9 @@
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) {
+ memoryIdentifierTokens.reserve(requestInShared.pools.size());
+ holds.reserve(requestInShared.pools.size());
+ for (const auto& memoryPool : requestInShared.pools) {
if (const auto* memory = std::get_if<nn::SharedMemory>(&memoryPool)) {
if (auto cached = kMemoryCache->getMemoryIfAvailable(*memory)) {
auto& [identifier, hold] = *cached;
@@ -172,12 +201,30 @@
}
memoryIdentifierTokens.push_back(-1);
}
- CHECK_EQ(request.pools.size(), memoryIdentifierTokens.size());
+ CHECK_EQ(requestInShared.pools.size(), memoryIdentifierTokens.size());
+
+ return executeInternal(aidlRequest, memoryIdentifierTokens, aidlMeasure, aidlDeadline,
+ aidlLoopTimeoutDuration, relocation);
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst::executeInternal(
+ const Request& request, const std::vector<int64_t>& memoryIdentifierTokens, bool measure,
+ int64_t deadline, int64_t loopTimeoutDuration,
+ const hal::utils::RequestRelocation& relocation) 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(); });
+
+ if (relocation.input) {
+ relocation.input->flush();
+ }
ExecutionResult executionResult;
- const auto ret =
- kBurst->executeSynchronously(aidlRequest, memoryIdentifierTokens, aidlMeasure,
- aidlDeadline, aidlLoopTimeoutDuration, &executionResult);
+ const auto ret = kBurst->executeSynchronously(request, memoryIdentifierTokens, measure,
+ deadline, loopTimeoutDuration, &executionResult);
HANDLE_ASTATUS(ret) << "execute failed";
if (!executionResult.outputSufficientSize) {
auto canonicalOutputShapes =
@@ -188,10 +235,88 @@
auto [outputShapes, timing] = NN_TRY(hal::utils::makeExecutionFailure(
convertExecutionResults(executionResult.outputShapes, executionResult.timing)));
- NN_TRY(hal::utils::makeExecutionFailure(
- hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
-
+ if (relocation.output) {
+ relocation.output->flush();
+ }
return std::make_pair(std::move(outputShapes), timing);
}
+nn::GeneralResult<nn::SharedExecution> Burst::createReusableExecution(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& loopTimeoutDuration) const {
+ // Ensure that request is ready for IPC.
+ std::optional<nn::Request> maybeRequestInShared;
+ hal::utils::RequestRelocation relocation;
+ const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+ &request, &maybeRequestInShared, &relocation));
+
+ auto aidlRequest = NN_TRY(convert(requestInShared));
+ const auto aidlMeasure = NN_TRY(convert(measure));
+ const auto aidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
+
+ std::vector<int64_t> memoryIdentifierTokens;
+ std::vector<OptionalCacheHold> holds;
+ memoryIdentifierTokens.reserve(requestInShared.pools.size());
+ holds.reserve(requestInShared.pools.size());
+ for (const auto& memoryPool : requestInShared.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(requestInShared.pools.size(), memoryIdentifierTokens.size());
+
+ return BurstExecution::create(shared_from_this(), std::move(aidlRequest),
+ std::move(memoryIdentifierTokens), aidlMeasure,
+ aidlLoopTimeoutDuration, std::move(relocation), std::move(holds));
+}
+
+nn::GeneralResult<std::shared_ptr<const BurstExecution>> BurstExecution::create(
+ std::shared_ptr<const Burst> burst, Request request,
+ std::vector<int64_t> memoryIdentifierTokens, bool measure, int64_t loopTimeoutDuration,
+ hal::utils::RequestRelocation relocation,
+ std::vector<Burst::OptionalCacheHold> cacheHolds) {
+ if (burst == nullptr) {
+ return NN_ERROR() << "aidl::utils::BurstExecution::create must have non-null burst";
+ }
+
+ return std::make_shared<const BurstExecution>(
+ PrivateConstructorTag{}, std::move(burst), std::move(request),
+ std::move(memoryIdentifierTokens), measure, loopTimeoutDuration, std::move(relocation),
+ std::move(cacheHolds));
+}
+
+BurstExecution::BurstExecution(PrivateConstructorTag /*tag*/, std::shared_ptr<const Burst> burst,
+ Request request, std::vector<int64_t> memoryIdentifierTokens,
+ bool measure, int64_t loopTimeoutDuration,
+ hal::utils::RequestRelocation relocation,
+ std::vector<Burst::OptionalCacheHold> cacheHolds)
+ : kBurst(std::move(burst)),
+ kRequest(std::move(request)),
+ kMemoryIdentifierTokens(std::move(memoryIdentifierTokens)),
+ kMeasure(measure),
+ kLoopTimeoutDuration(loopTimeoutDuration),
+ kRelocation(std::move(relocation)),
+ kCacheHolds(std::move(cacheHolds)) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> BurstExecution::compute(
+ const nn::OptionalTimePoint& deadline) const {
+ const auto aidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
+ return kBurst->executeInternal(kRequest, kMemoryIdentifierTokens, kMeasure, aidlDeadline,
+ kLoopTimeoutDuration, kRelocation);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+BurstExecution::computeFenced(const std::vector<nn::SyncFence>& /*waitFor*/,
+ const nn::OptionalTimePoint& /*deadline*/,
+ const nn::OptionalDuration& /*timeoutDurationAfterFence*/) const {
+ return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+ << "IExecution::computeFenced is not supported on burst object";
+}
+
} // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/src/Conversions.cpp b/neuralnetworks/aidl/utils/src/Conversions.cpp
index 93ac51c..4b263ee 100644
--- a/neuralnetworks/aidl/utils/src/Conversions.cpp
+++ b/neuralnetworks/aidl/utils/src/Conversions.cpp
@@ -410,11 +410,11 @@
}
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) {
@@ -422,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) {
@@ -964,8 +964,8 @@
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)),
};
}
diff --git a/neuralnetworks/aidl/utils/src/Device.cpp b/neuralnetworks/aidl/utils/src/Device.cpp
index 02ca861..0fd453b 100644
--- a/neuralnetworks/aidl/utils/src/Device.cpp
+++ b/neuralnetworks/aidl/utils/src/Device.cpp
@@ -178,10 +178,6 @@
return kDeviceType;
}
-bool Device::isUpdatable() const {
- return false;
-}
-
const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
return kExtensions;
}
diff --git a/neuralnetworks/aidl/utils/src/Execution.cpp b/neuralnetworks/aidl/utils/src/Execution.cpp
new file mode 100644
index 0000000..2aee8a6
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/Execution.cpp
@@ -0,0 +1,79 @@
+/*
+ * 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 "Execution.h"
+
+#include "Conversions.h"
+#include "PreparedModel.h"
+#include "Utils.h"
+
+#include <aidl/android/hardware/neuralnetworks/Request.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+nn::GeneralResult<std::shared_ptr<const Execution>> Execution::create(
+ std::shared_ptr<const PreparedModel> preparedModel, Request request,
+ hal::utils::RequestRelocation relocation, bool measure, int64_t loopTimeoutDuration) {
+ if (preparedModel == nullptr) {
+ return NN_ERROR() << "aidl::utils::Execution::create must have non-null preparedModel";
+ }
+
+ return std::make_shared<const Execution>(PrivateConstructorTag{}, std::move(preparedModel),
+ std::move(request), std::move(relocation), measure,
+ loopTimeoutDuration);
+}
+
+Execution::Execution(PrivateConstructorTag /*tag*/,
+ std::shared_ptr<const PreparedModel> preparedModel, Request request,
+ hal::utils::RequestRelocation relocation, bool measure,
+ int64_t loopTimeoutDuration)
+ : kPreparedModel(std::move(preparedModel)),
+ kRequest(std::move(request)),
+ kRelocation(std::move(relocation)),
+ kMeasure(measure),
+ kLoopTimeoutDuration(loopTimeoutDuration) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Execution::compute(
+ const nn::OptionalTimePoint& deadline) const {
+ const auto aidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
+ return kPreparedModel->executeInternal(kRequest, kMeasure, aidlDeadline, kLoopTimeoutDuration,
+ kRelocation);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> Execution::computeFenced(
+ const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& timeoutDurationAfterFence) const {
+ const auto aidlWaitFor = NN_TRY(convert(waitFor));
+ const auto aidlDeadline = NN_TRY(convert(deadline));
+ const auto aidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence));
+ return kPreparedModel->executeFencedInternal(kRequest, aidlWaitFor, kMeasure, aidlDeadline,
+ kLoopTimeoutDuration,
+ aidlTimeoutDurationAfterFence, kRelocation);
+}
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/src/PreparedModel.cpp b/neuralnetworks/aidl/utils/src/PreparedModel.cpp
index 003965b..1915607 100644
--- a/neuralnetworks/aidl/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/aidl/utils/src/PreparedModel.cpp
@@ -19,8 +19,11 @@
#include "Burst.h"
#include "Callbacks.h"
#include "Conversions.h"
+#include "Execution.h"
+#include "ProtectCallback.h"
#include "Utils.h"
+#include <aidl/android/hardware/neuralnetworks/Request.h>
#include <android/binder_auto_utils.h>
#include <nnapi/IPreparedModel.h>
#include <nnapi/Result.h>
@@ -74,18 +77,31 @@
const nn::OptionalDuration& loopTimeoutDuration) const {
// 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)));
+ hal::utils::RequestRelocation relocation;
+ const nn::Request& requestInShared =
+ NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
+ &request, &maybeRequestInShared, &relocation)));
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)));
+ return executeInternal(aidlRequest, aidlMeasure, aidlDeadline, aidlLoopTimeoutDuration,
+ relocation);
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+PreparedModel::executeInternal(const Request& request, bool measure, int64_t deadline,
+ int64_t loopTimeoutDuration,
+ const hal::utils::RequestRelocation& relocation) const {
+ if (relocation.input) {
+ relocation.input->flush();
+ }
ExecutionResult executionResult;
- const auto ret = kPreparedModel->executeSynchronously(
- aidlRequest, aidlMeasure, aidlDeadline, aidlLoopTimeoutDuration, &executionResult);
+ const auto ret = kPreparedModel->executeSynchronously(request, measure, deadline,
+ loopTimeoutDuration, &executionResult);
HANDLE_ASTATUS(ret) << "executeSynchronously failed";
if (!executionResult.outputSufficientSize) {
auto canonicalOutputShapes =
@@ -96,9 +112,9 @@
auto [outputShapes, timing] = NN_TRY(hal::utils::makeExecutionFailure(
convertExecutionResults(executionResult.outputShapes, executionResult.timing)));
- NN_TRY(hal::utils::makeExecutionFailure(
- hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
-
+ if (relocation.output) {
+ relocation.output->flush();
+ }
return std::make_pair(std::move(outputShapes), timing);
}
@@ -109,8 +125,9 @@
const nn::OptionalDuration& timeoutDurationAfterFence) const {
// Ensure that request is ready for IPC.
std::optional<nn::Request> maybeRequestInShared;
- const nn::Request& requestInShared =
- NN_TRY(hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared));
+ hal::utils::RequestRelocation relocation;
+ const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+ &request, &maybeRequestInShared, &relocation));
const auto aidlRequest = NN_TRY(convert(requestInShared));
const auto aidlWaitFor = NN_TRY(convert(waitFor));
@@ -118,11 +135,25 @@
const auto aidlDeadline = NN_TRY(convert(deadline));
const auto aidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
const auto aidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence));
+ return executeFencedInternal(aidlRequest, aidlWaitFor, aidlMeasure, aidlDeadline,
+ aidlLoopTimeoutDuration, aidlTimeoutDurationAfterFence,
+ relocation);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+PreparedModel::executeFencedInternal(const Request& request,
+ const std::vector<ndk::ScopedFileDescriptor>& waitFor,
+ bool measure, int64_t deadline, int64_t loopTimeoutDuration,
+ int64_t timeoutDurationAfterFence,
+ const hal::utils::RequestRelocation& relocation) const {
+ if (relocation.input) {
+ relocation.input->flush();
+ }
FencedExecutionResult result;
- const auto ret = kPreparedModel->executeFenced(aidlRequest, aidlWaitFor, aidlMeasure,
- aidlDeadline, aidlLoopTimeoutDuration,
- aidlTimeoutDurationAfterFence, &result);
+ const auto ret =
+ kPreparedModel->executeFenced(request, waitFor, measure, deadline, loopTimeoutDuration,
+ timeoutDurationAfterFence, &result);
HANDLE_ASTATUS(ret) << "executeFenced failed";
auto resultSyncFence = nn::SyncFence::createAsSignaled();
@@ -137,12 +168,12 @@
// If executeFenced required the request memory to be moved into shared memory, block here until
// the fenced execution has completed and flush the memory back.
- if (maybeRequestInShared.has_value()) {
+ if (relocation.output) {
const auto state = resultSyncFence.syncWait({});
if (state != nn::SyncFence::FenceState::SIGNALED) {
return NN_ERROR() << "syncWait failed with " << state;
}
- NN_TRY(hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared));
+ relocation.output->flush();
}
// Create callback which can be used to retrieve the execution error status and timings.
@@ -159,6 +190,22 @@
return std::make_pair(std::move(resultSyncFence), std::move(resultCallback));
}
+nn::GeneralResult<nn::SharedExecution> PreparedModel::createReusableExecution(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& loopTimeoutDuration) const {
+ // Ensure that request is ready for IPC.
+ std::optional<nn::Request> maybeRequestInShared;
+ hal::utils::RequestRelocation relocation;
+ const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+ &request, &maybeRequestInShared, &relocation));
+
+ auto aidlRequest = NN_TRY(convert(requestInShared));
+ auto aidlMeasure = NN_TRY(convert(measure));
+ auto aidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
+ return Execution::create(shared_from_this(), std::move(aidlRequest), std::move(relocation),
+ aidlMeasure, aidlLoopTimeoutDuration);
+}
+
nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
std::shared_ptr<IBurst> burst;
const auto ret = kPreparedModel->configureExecutionBurst(&burst);
diff --git a/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp b/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp
index 630a460..8bb5c90 100644
--- a/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp
+++ b/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp
@@ -21,6 +21,7 @@
#include <aidl/android/hardware/neuralnetworks/IFencedExecutionCallback.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <nnapi/IExecution.h>
#include <nnapi/IPreparedModel.h>
#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
@@ -39,7 +40,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(); };
@@ -253,6 +254,225 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
+TEST(PreparedModelTest, reusableExecuteSync) {
+ // setup call
+ const uint32_t kNumberOfComputations = 2;
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ const auto mockExecutionResult = ExecutionResult{
+ .outputSufficientSize = true,
+ .outputShapes = {},
+ .timing = kNoTiming,
+ };
+ EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
+ .Times(kNumberOfComputations)
+ .WillRepeatedly(
+ DoAll(SetArgPointee<4>(mockExecutionResult), InvokeWithoutArgs(makeStatusOk)));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute repeatedly
+ for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+ const auto computeResult = createResult.value()->compute({});
+ EXPECT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+ << ": " << computeResult.error().message;
+ }
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncError) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makeGeneralFailure));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->compute({});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncTransportFailure) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->compute({});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncDeadObject) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->compute({});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteFenced) {
+ // setup call
+ const uint32_t kNumberOfComputations = 2;
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ const auto mockCallback = MockFencedExecutionCallback::create();
+ EXPECT_CALL(*mockCallback, getExecutionInfo(_, _, _))
+ .Times(kNumberOfComputations)
+ .WillRepeatedly(DoAll(SetArgPointee<0>(kNoTiming), SetArgPointee<1>(kNoTiming),
+ SetArgPointee<2>(ErrorStatus::NONE), Invoke(makeStatusOk)));
+ EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+ .Times(kNumberOfComputations)
+ .WillRepeatedly(Invoke(makeFencedExecutionResult(mockCallback)));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute repeatedly
+ for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+ const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+ ASSERT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+ << ": " << computeResult.error().message;
+ const auto& [syncFence, callback] = computeResult.value();
+ EXPECT_EQ(syncFence.syncWait({}), nn::SyncFence::FenceState::SIGNALED);
+ ASSERT_NE(callback, nullptr);
+
+ // get results from callback
+ const auto callbackResult = callback();
+ ASSERT_TRUE(callbackResult.has_value()) << "Failed with " << callbackResult.error().code
+ << ": " << callbackResult.error().message;
+ }
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedCallbackError) {
+ // setup call
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ const auto mockCallback = MockFencedExecutionCallback::create();
+ EXPECT_CALL(*mockCallback, getExecutionInfo(_, _, _))
+ .Times(1)
+ .WillOnce(Invoke(DoAll(SetArgPointee<0>(kNoTiming), SetArgPointee<1>(kNoTiming),
+ SetArgPointee<2>(ErrorStatus::GENERAL_FAILURE),
+ Invoke(makeStatusOk))));
+ EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makeFencedExecutionResult(mockCallback)));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+ ASSERT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code << ": "
+ << computeResult.error().message;
+ const auto& [syncFence, callback] = computeResult.value();
+ EXPECT_NE(syncFence.syncWait({}), nn::SyncFence::FenceState::ACTIVE);
+ ASSERT_NE(callback, nullptr);
+
+ // verify callback failure
+ const auto callbackResult = callback();
+ ASSERT_FALSE(callbackResult.has_value());
+ EXPECT_EQ(callbackResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedError) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedTransportFailure) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedDeadObject) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // create execution
+ const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+ ASSERT_TRUE(createResult.has_value())
+ << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+ ASSERT_NE(createResult.value(), nullptr);
+
+ // invoke compute
+ const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+ ASSERT_FALSE(computeResult.has_value());
+ EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
TEST(PreparedModelTest, configureExecutionBurst) {
// setup test
const auto mockPreparedModel = MockPreparedModel::create();
diff --git a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
index 1440429..d3b041d 100644
--- a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
@@ -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/QualityOfServiceTests.cpp b/neuralnetworks/aidl/vts/functional/QualityOfServiceTests.cpp
index e803e38..bbba887 100644
--- a/neuralnetworks/aidl/vts/functional/QualityOfServiceTests.cpp
+++ b/neuralnetworks/aidl/vts/functional/QualityOfServiceTests.cpp
@@ -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 {
@@ -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/utils/common/include/nnapi/hal/CommonUtils.h b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
index 8fe6b90..fdc90df 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
@@ -20,6 +20,7 @@
#include <cutils/native_handle.h>
#include <hidl/HidlSupport.h>
#include <nnapi/Result.h>
+#include <nnapi/SharedMemory.h>
#include <nnapi/Types.h>
#include <functional>
#include <vector>
@@ -59,19 +60,70 @@
nn::GeneralResult<std::reference_wrapper<const nn::Model>> flushDataFromPointerToShared(
const nn::Model* model, std::optional<nn::Model>* maybeModelInSharedOut);
+// Record a relocation mapping between pointer-based data and shared memory.
+// Only two specializations of this template may exist:
+// - RelocationInfo<const void*> for request inputs
+// - RelocationInfo<void*> for request outputs
+template <typename PointerType>
+struct RelocationInfo {
+ PointerType data;
+ size_t length;
+ size_t offset;
+};
+using InputRelocationInfo = RelocationInfo<const void*>;
+using OutputRelocationInfo = RelocationInfo<void*>;
+
+// Keep track of the relocation mapping between pointer-based data and shared memory pool,
+// and provide method to copy the data between pointers and the shared memory pool.
+// Only two specializations of this template may exist:
+// - RelocationTracker<InputRelocationInfo> for request inputs
+// - RelocationTracker<OutputRelocationInfo> for request outputs
+template <typename RelocationInfoType>
+class RelocationTracker {
+ public:
+ static nn::GeneralResult<std::unique_ptr<RelocationTracker>> create(
+ std::vector<RelocationInfoType> relocationInfos, nn::SharedMemory memory) {
+ auto mapping = NN_TRY(map(memory));
+ return std::make_unique<RelocationTracker<RelocationInfoType>>(
+ std::move(relocationInfos), std::move(memory), std::move(mapping));
+ }
+
+ RelocationTracker(std::vector<RelocationInfoType> relocationInfos, nn::SharedMemory memory,
+ nn::Mapping mapping)
+ : kRelocationInfos(std::move(relocationInfos)),
+ kMemory(std::move(memory)),
+ kMapping(std::move(mapping)) {}
+
+ // Specializations defined in CommonUtils.cpp.
+ // For InputRelocationTracker, this method will copy pointer data to the shared memory pool.
+ // For OutputRelocationTracker, this method will copy shared memory data to the pointers.
+ void flush() const;
+
+ private:
+ const std::vector<RelocationInfoType> kRelocationInfos;
+ const nn::SharedMemory kMemory;
+ const nn::Mapping kMapping;
+};
+using InputRelocationTracker = RelocationTracker<InputRelocationInfo>;
+using OutputRelocationTracker = RelocationTracker<OutputRelocationInfo>;
+
+struct RequestRelocation {
+ std::unique_ptr<InputRelocationTracker> input;
+ std::unique_ptr<OutputRelocationTracker> output;
+};
+
// Relocate pointer-based data to shared memory. If `request` has no
// Request::Argument::LifeTime::POINTER data, the function returns with a reference to `request`. If
// `request` has Request::Argument::LifeTime::POINTER data, the request is copied to
// `maybeRequestInSharedOut` with the POINTER data relocated to a memory pool, and the function
-// returns with a reference to `*maybeRequestInSharedOut`.
-nn::GeneralResult<std::reference_wrapper<const nn::Request>> flushDataFromPointerToShared(
- const nn::Request* request, std::optional<nn::Request>* maybeRequestInSharedOut);
-
-// Undoes `flushDataFromPointerToShared` on a Request object. More specifically,
-// `unflushDataFromSharedToPointer` copies the output shared memory data from the transformed
-// Request object back to the output pointer-based memory in the original Request object.
-nn::GeneralResult<void> unflushDataFromSharedToPointer(
- const nn::Request& request, const std::optional<nn::Request>& maybeRequestInShared);
+// returns with a reference to `*maybeRequestInSharedOut`. The `relocationOut` will be set to track
+// the input and output relocations.
+//
+// Unlike `flushDataFromPointerToShared`, this method will not copy the input pointer data to the
+// shared memory pool. Use `relocationOut` to flush the input or output data after the call.
+nn::GeneralResult<std::reference_wrapper<const nn::Request>> convertRequestFromPointerToShared(
+ const nn::Request* request, std::optional<nn::Request>* maybeRequestInSharedOut,
+ RequestRelocation* relocationOut);
nn::GeneralResult<std::vector<uint32_t>> countNumberOfConsumers(
size_t numberOfOperands, const std::vector<nn::Operation>& operations);
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h
index 17b3fd9..e86edda 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h
@@ -35,6 +35,10 @@
const nn::Request& request, nn::MeasureTiming measure,
const nn::OptionalTimePoint& deadline,
const nn::OptionalDuration& loopTimeoutDuration) const override;
+
+ nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& loopTimeoutDuration) const override;
};
} // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h
index d843526..5e62b9a 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h
@@ -32,7 +32,7 @@
class InvalidDevice final : public nn::IDevice {
public:
InvalidDevice(std::string name, std::string versionString, nn::Version featureLevel,
- nn::DeviceType type, bool isUpdatable, std::vector<nn::Extension> extensions,
+ nn::DeviceType type, std::vector<nn::Extension> extensions,
nn::Capabilities capabilities,
std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded);
@@ -40,7 +40,6 @@
const std::string& getVersionString() const override;
nn::Version getFeatureLevel() const override;
nn::DeviceType getType() const override;
- bool isUpdatable() const override;
const std::vector<nn::Extension>& getSupportedExtensions() const override;
const nn::Capabilities& getCapabilities() const override;
std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const override;
@@ -71,7 +70,6 @@
const std::string kVersionString;
const nn::Version kFeatureLevel;
const nn::DeviceType kType;
- const bool kIsUpdatable;
const std::vector<nn::Extension> kExtensions;
const nn::Capabilities kCapabilities;
const std::pair<uint32_t, uint32_t> kNumberOfCacheFilesNeeded;
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidExecution.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidExecution.h
new file mode 100644
index 0000000..5b00221
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidExecution.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_UTILS_COMMON_INVALID_EXECUTION_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_EXECUTION_H
+
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class InvalidExecution final : public nn::IExecution {
+ public:
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+ const nn::OptionalTimePoint& deadline) const override;
+
+ nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+ const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+};
+
+} // namespace android::hardware::neuralnetworks::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_EXECUTION_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h
index 3e1dca7..de30aae 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h
@@ -40,6 +40,10 @@
const nn::OptionalDuration& loopTimeoutDuration,
const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+ nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& loopTimeoutDuration) const override;
+
nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
std::any getUnderlyingResource() const override;
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h
index c92cc41..fde2486 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h
@@ -51,7 +51,16 @@
const nn::OptionalTimePoint& deadline,
const nn::OptionalDuration& loopTimeoutDuration) const override;
+ nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& loopTimeoutDuration) const override;
+
private:
+ bool isValidInternal() const EXCLUDES(mMutex);
+ nn::GeneralResult<nn::SharedExecution> createReusableExecutionInternal(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& loopTimeoutDuration) const;
+
const Factory kMakeBurst;
mutable std::mutex mMutex;
mutable nn::SharedBurst mBurst GUARDED_BY(mMutex);
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h
index 8199c52..84ae799 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h
@@ -53,7 +53,6 @@
const std::string& getVersionString() const override;
nn::Version getFeatureLevel() const override;
nn::DeviceType getType() const override;
- bool isUpdatable() const override;
const std::vector<nn::Extension>& getSupportedExtensions() const override;
const nn::Capabilities& getCapabilities() const override;
std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const override;
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientExecution.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientExecution.h
new file mode 100644
index 0000000..d0084e8
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientExecution.h
@@ -0,0 +1,66 @@
+/*
+ * 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_UTILS_COMMON_RESILIENT_EXECUTION_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_EXECUTION_H
+
+#include <android-base/thread_annotations.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class ResilientExecution final : public nn::IExecution,
+ public std::enable_shared_from_this<ResilientExecution> {
+ struct PrivateConstructorTag {};
+
+ public:
+ using Factory = std::function<nn::GeneralResult<nn::SharedExecution>()>;
+
+ static nn::GeneralResult<std::shared_ptr<const ResilientExecution>> create(
+ Factory makeExecution);
+
+ ResilientExecution(PrivateConstructorTag tag, Factory makeExecution,
+ nn::SharedExecution execution);
+
+ nn::SharedExecution getExecution() const;
+ nn::GeneralResult<nn::SharedExecution> recover(const nn::IExecution* failingExecution) const;
+
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+ const nn::OptionalTimePoint& deadline) const override;
+
+ nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+ const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+ private:
+ bool isValidInternal() const EXCLUDES(mMutex);
+
+ const Factory kMakeExecution;
+ mutable std::mutex mMutex;
+ mutable nn::SharedExecution mExecution GUARDED_BY(mMutex);
+};
+
+} // namespace android::hardware::neuralnetworks::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_EXECUTION_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h
index a6c1b19..86533ed 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h
@@ -58,12 +58,19 @@
const nn::OptionalDuration& loopTimeoutDuration,
const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+ nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& loopTimeoutDuration) const override;
+
nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
std::any getUnderlyingResource() const override;
private:
bool isValidInternal() const EXCLUDES(mMutex);
+ nn::GeneralResult<nn::SharedExecution> createReusableExecutionInternal(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& loopTimeoutDuration) const;
nn::GeneralResult<nn::SharedBurst> configureExecutionBurstInternal() const;
const Factory kMakePreparedModel;
diff --git a/neuralnetworks/utils/common/src/CommonUtils.cpp b/neuralnetworks/utils/common/src/CommonUtils.cpp
index 4d26795..eaeb9ad 100644
--- a/neuralnetworks/utils/common/src/CommonUtils.cpp
+++ b/neuralnetworks/utils/common/src/CommonUtils.cpp
@@ -200,10 +200,31 @@
return **maybeModelInSharedOut;
}
-nn::GeneralResult<std::reference_wrapper<const nn::Request>> flushDataFromPointerToShared(
- const nn::Request* request, std::optional<nn::Request>* maybeRequestInSharedOut) {
+template <>
+void InputRelocationTracker::flush() const {
+ // Copy from pointers to shared memory.
+ uint8_t* memoryPtr = static_cast<uint8_t*>(std::get<void*>(kMapping.pointer));
+ for (const auto& [data, length, offset] : kRelocationInfos) {
+ std::memcpy(memoryPtr + offset, data, length);
+ }
+}
+
+template <>
+void OutputRelocationTracker::flush() const {
+ // Copy from shared memory to pointers.
+ const uint8_t* memoryPtr = static_cast<const uint8_t*>(
+ std::visit([](auto ptr) { return static_cast<const void*>(ptr); }, kMapping.pointer));
+ for (const auto& [data, length, offset] : kRelocationInfos) {
+ std::memcpy(data, memoryPtr + offset, length);
+ }
+}
+
+nn::GeneralResult<std::reference_wrapper<const nn::Request>> convertRequestFromPointerToShared(
+ const nn::Request* request, std::optional<nn::Request>* maybeRequestInSharedOut,
+ RequestRelocation* relocationOut) {
CHECK(request != nullptr);
CHECK(maybeRequestInSharedOut != nullptr);
+ CHECK(relocationOut != nullptr);
if (hasNoPointerData(*request)) {
return *request;
@@ -213,8 +234,11 @@
// to the caller through `maybeRequestInSharedOut` if the function succeeds.
nn::Request requestInShared = *request;
+ RequestRelocation relocation;
+
// Change input pointers to shared memory.
- nn::ConstantMemoryBuilder inputBuilder(requestInShared.pools.size());
+ nn::MutableMemoryBuilder inputBuilder(requestInShared.pools.size());
+ std::vector<InputRelocationInfo> inputRelocationInfos;
for (auto& input : requestInShared.inputs) {
const auto& location = input.location;
if (input.lifetime != nn::Request::Argument::LifeTime::POINTER) {
@@ -225,17 +249,21 @@
const void* data = std::visit([](auto ptr) { return static_cast<const void*>(ptr); },
location.pointer);
CHECK(data != nullptr);
- input.location = inputBuilder.append(data, location.length);
+ input.location = inputBuilder.append(location.length);
+ inputRelocationInfos.push_back({data, input.location.length, input.location.offset});
}
// Allocate input memory.
if (!inputBuilder.empty()) {
auto memory = NN_TRY(inputBuilder.finish());
- requestInShared.pools.push_back(std::move(memory));
+ requestInShared.pools.push_back(memory);
+ relocation.input = NN_TRY(
+ InputRelocationTracker::create(std::move(inputRelocationInfos), std::move(memory)));
}
// Change output pointers to shared memory.
nn::MutableMemoryBuilder outputBuilder(requestInShared.pools.size());
+ std::vector<OutputRelocationInfo> outputRelocationInfos;
for (auto& output : requestInShared.outputs) {
const auto& location = output.location;
if (output.lifetime != nn::Request::Argument::LifeTime::POINTER) {
@@ -243,62 +271,25 @@
}
output.lifetime = nn::Request::Argument::LifeTime::POOL;
+ void* data = std::get<void*>(location.pointer);
+ CHECK(data != nullptr);
output.location = outputBuilder.append(location.length);
+ outputRelocationInfos.push_back({data, output.location.length, output.location.offset});
}
// Allocate output memory.
if (!outputBuilder.empty()) {
auto memory = NN_TRY(outputBuilder.finish());
- requestInShared.pools.push_back(std::move(memory));
+ requestInShared.pools.push_back(memory);
+ relocation.output = NN_TRY(OutputRelocationTracker::create(std::move(outputRelocationInfos),
+ std::move(memory)));
}
*maybeRequestInSharedOut = requestInShared;
+ *relocationOut = std::move(relocation);
return **maybeRequestInSharedOut;
}
-nn::GeneralResult<void> unflushDataFromSharedToPointer(
- const nn::Request& request, const std::optional<nn::Request>& maybeRequestInShared) {
- if (!maybeRequestInShared.has_value() || maybeRequestInShared->pools.empty() ||
- !std::holds_alternative<nn::SharedMemory>(maybeRequestInShared->pools.back())) {
- return {};
- }
- const auto& requestInShared = *maybeRequestInShared;
-
- // Map the memory.
- const auto& outputMemory = std::get<nn::SharedMemory>(requestInShared.pools.back());
- const auto [pointer, size, context] = NN_TRY(map(outputMemory));
- const uint8_t* constantPointer =
- std::visit([](const auto& o) { return static_cast<const uint8_t*>(o); }, pointer);
-
- // Flush each output pointer.
- CHECK_EQ(request.outputs.size(), requestInShared.outputs.size());
- for (size_t i = 0; i < request.outputs.size(); ++i) {
- const auto& location = request.outputs[i].location;
- const auto& locationInShared = requestInShared.outputs[i].location;
- if (!std::holds_alternative<void*>(location.pointer)) {
- continue;
- }
-
- // Get output pointer and size.
- void* data = std::get<void*>(location.pointer);
- CHECK(data != nullptr);
- const size_t length = location.length;
-
- // Get output pool location.
- CHECK(requestInShared.outputs[i].lifetime == nn::Request::Argument::LifeTime::POOL);
- const size_t index = locationInShared.poolIndex;
- const size_t offset = locationInShared.offset;
- const size_t outputPoolIndex = requestInShared.pools.size() - 1;
- CHECK(locationInShared.length == length);
- CHECK(index == outputPoolIndex);
-
- // Flush memory.
- std::memcpy(data, constantPointer + offset, length);
- }
-
- return {};
-}
-
nn::GeneralResult<std::vector<uint32_t>> countNumberOfConsumers(
size_t numberOfOperands, const std::vector<nn::Operation>& operations) {
return makeGeneralFailure(nn::countNumberOfConsumers(numberOfOperands, operations));
diff --git a/neuralnetworks/utils/common/src/InvalidBurst.cpp b/neuralnetworks/utils/common/src/InvalidBurst.cpp
index 0c34f05..0191533 100644
--- a/neuralnetworks/utils/common/src/InvalidBurst.cpp
+++ b/neuralnetworks/utils/common/src/InvalidBurst.cpp
@@ -38,4 +38,10 @@
return NN_ERROR() << "InvalidBurst";
}
+nn::GeneralResult<nn::SharedExecution> InvalidBurst::createReusableExecution(
+ const nn::Request& /*request*/, nn::MeasureTiming /*measure*/,
+ const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
+ return NN_ERROR() << "InvalidBurst";
+}
+
} // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/InvalidDevice.cpp b/neuralnetworks/utils/common/src/InvalidDevice.cpp
index 81bca7f..535ccb4 100644
--- a/neuralnetworks/utils/common/src/InvalidDevice.cpp
+++ b/neuralnetworks/utils/common/src/InvalidDevice.cpp
@@ -32,14 +32,13 @@
namespace android::hardware::neuralnetworks::utils {
InvalidDevice::InvalidDevice(std::string name, std::string versionString, nn::Version featureLevel,
- nn::DeviceType type, bool isUpdatable,
- std::vector<nn::Extension> extensions, nn::Capabilities capabilities,
+ nn::DeviceType type, std::vector<nn::Extension> extensions,
+ nn::Capabilities capabilities,
std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded)
: kName(std::move(name)),
kVersionString(std::move(versionString)),
kFeatureLevel(featureLevel),
kType(type),
- kIsUpdatable(isUpdatable),
kExtensions(std::move(extensions)),
kCapabilities(std::move(capabilities)),
kNumberOfCacheFilesNeeded(numberOfCacheFilesNeeded) {}
@@ -60,10 +59,6 @@
return kType;
}
-bool InvalidDevice::isUpdatable() const {
- return kIsUpdatable;
-}
-
const std::vector<nn::Extension>& InvalidDevice::getSupportedExtensions() const {
return kExtensions;
}
diff --git a/neuralnetworks/utils/common/src/InvalidExecution.cpp b/neuralnetworks/utils/common/src/InvalidExecution.cpp
new file mode 100644
index 0000000..c4edd25
--- /dev/null
+++ b/neuralnetworks/utils/common/src/InvalidExecution.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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 "InvalidExecution.h"
+
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> InvalidExecution::compute(
+ const nn::OptionalTimePoint& /*deadline*/) const {
+ return NN_ERROR() << "InvalidExecution";
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+InvalidExecution::computeFenced(const std::vector<nn::SyncFence>& /*waitFor*/,
+ const nn::OptionalTimePoint& /*deadline*/,
+ const nn::OptionalDuration& /*timeoutDurationAfterFence*/) const {
+ return NN_ERROR() << "InvalidExecution";
+}
+
+} // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp b/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp
index 9081e1f..8195462 100644
--- a/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp
+++ b/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp
@@ -42,6 +42,12 @@
return NN_ERROR() << "InvalidPreparedModel";
}
+nn::GeneralResult<nn::SharedExecution> InvalidPreparedModel::createReusableExecution(
+ const nn::Request& /*request*/, nn::MeasureTiming /*measure*/,
+ const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
+ return NN_ERROR() << "InvalidPreparedModel";
+}
+
nn::GeneralResult<nn::SharedBurst> InvalidPreparedModel::configureExecutionBurst() const {
return NN_ERROR() << "InvalidPreparedModel";
}
diff --git a/neuralnetworks/utils/common/src/ResilientBurst.cpp b/neuralnetworks/utils/common/src/ResilientBurst.cpp
index 38ccc62..79cbe39 100644
--- a/neuralnetworks/utils/common/src/ResilientBurst.cpp
+++ b/neuralnetworks/utils/common/src/ResilientBurst.cpp
@@ -19,6 +19,7 @@
#include <android-base/logging.h>
#include <android-base/thread_annotations.h>
#include <nnapi/IBurst.h>
+#include <nnapi/IPreparedModel.h>
#include <nnapi/Result.h>
#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
@@ -29,6 +30,9 @@
#include <optional>
#include <utility>
+#include "InvalidExecution.h"
+#include "ResilientExecution.h"
+
namespace android::hardware::neuralnetworks::utils {
namespace {
@@ -46,11 +50,11 @@
// Attempt recovery and return if it fails.
auto maybeBurst = resilientBurst.recover(burst.get());
if (!maybeBurst.has_value()) {
- auto [resultErrorMessage, resultErrorCode, resultOutputShapes] = std::move(result).error();
- const auto& [recoveryErrorMessage, recoveryErrorCode] = maybeBurst.error();
- return nn::error(resultErrorCode, std::move(resultOutputShapes))
- << resultErrorMessage << ", and failed to recover dead burst object with error "
- << recoveryErrorCode << ": " << recoveryErrorMessage;
+ const auto& [message, code] = maybeBurst.error();
+ std::ostringstream oss;
+ oss << ", and failed to recover dead burst object with error " << code << ": " << message;
+ result.error().message += oss.str();
+ return result;
}
burst = std::move(maybeBurst).value();
@@ -109,4 +113,35 @@
return protect(*this, fn);
}
+nn::GeneralResult<nn::SharedExecution> ResilientBurst::createReusableExecution(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& loopTimeoutDuration) const {
+#if 0
+ auto self = shared_from_this();
+ ResilientExecution::Factory makeExecution =
+ [burst = std::move(self), request, measure, loopTimeoutDuration] {
+ return burst->createReusableExecutionInternal(request, measure, loopTimeoutDuration);
+ };
+ return ResilientExecution::create(std::move(makeExecution));
+#else
+ return createReusableExecutionInternal(request, measure, loopTimeoutDuration);
+#endif
+}
+
+nn::GeneralResult<nn::SharedExecution> ResilientBurst::createReusableExecutionInternal(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& loopTimeoutDuration) const {
+ if (!isValidInternal()) {
+ return std::make_shared<const InvalidExecution>();
+ }
+ const auto fn = [&request, measure, &loopTimeoutDuration](const nn::IBurst& burst) {
+ return burst.createReusableExecution(request, measure, loopTimeoutDuration);
+ };
+ return protect(*this, fn);
+}
+
+bool ResilientBurst::isValidInternal() const {
+ return true;
+}
+
} // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/ResilientDevice.cpp b/neuralnetworks/utils/common/src/ResilientDevice.cpp
index 13965af..2023c9a 100644
--- a/neuralnetworks/utils/common/src/ResilientDevice.cpp
+++ b/neuralnetworks/utils/common/src/ResilientDevice.cpp
@@ -122,14 +122,12 @@
};
if (compare(&IDevice::getName) || compare(&IDevice::getVersionString) ||
compare(&IDevice::getFeatureLevel) || compare(&IDevice::getType) ||
- compare(&IDevice::isUpdatable) || compare(&IDevice::getSupportedExtensions) ||
- compare(&IDevice::getCapabilities)) {
+ compare(&IDevice::getSupportedExtensions) || compare(&IDevice::getCapabilities)) {
LOG(ERROR) << "Recovered device has different metadata than what is cached. Marking "
"IDevice object as invalid.";
device = std::make_shared<const InvalidDevice>(
- kName, kVersionString, mDevice->getFeatureLevel(), mDevice->getType(),
- mDevice->isUpdatable(), kExtensions, kCapabilities,
- mDevice->getNumberOfCacheFilesNeeded());
+ kName, kVersionString, mDevice->getFeatureLevel(), mDevice->getType(), kExtensions,
+ kCapabilities, mDevice->getNumberOfCacheFilesNeeded());
mIsValid = false;
}
@@ -153,10 +151,6 @@
return getDevice()->getType();
}
-bool ResilientDevice::isUpdatable() const {
- return getDevice()->isUpdatable();
-}
-
const std::vector<nn::Extension>& ResilientDevice::getSupportedExtensions() const {
return kExtensions;
}
diff --git a/neuralnetworks/utils/common/src/ResilientExecution.cpp b/neuralnetworks/utils/common/src/ResilientExecution.cpp
new file mode 100644
index 0000000..46b404a
--- /dev/null
+++ b/neuralnetworks/utils/common/src/ResilientExecution.cpp
@@ -0,0 +1,126 @@
+/*
+ * 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 "ResilientExecution.h"
+
+#include "InvalidBurst.h"
+#include "ResilientBurst.h"
+
+#include <android-base/logging.h>
+#include <android-base/thread_annotations.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <sstream>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+namespace {
+
+template <typename FnType>
+auto protect(const ResilientExecution& resilientExecution, const FnType& fn)
+ -> decltype(fn(*resilientExecution.getExecution())) {
+ auto execution = resilientExecution.getExecution();
+ auto result = fn(*execution);
+
+ // Immediately return if prepared model is not dead.
+ if (result.has_value() || result.error().code != nn::ErrorStatus::DEAD_OBJECT) {
+ return result;
+ }
+
+ // Attempt recovery and return if it fails.
+ auto maybeExecution = resilientExecution.recover(execution.get());
+ if (!maybeExecution.has_value()) {
+ const auto& [message, code] = maybeExecution.error();
+ std::ostringstream oss;
+ oss << ", and failed to recover dead prepared model with error " << code << ": " << message;
+ result.error().message += oss.str();
+ return result;
+ }
+ execution = std::move(maybeExecution).value();
+
+ return fn(*execution);
+}
+
+} // namespace
+
+nn::GeneralResult<std::shared_ptr<const ResilientExecution>> ResilientExecution::create(
+ Factory makeExecution) {
+ if (makeExecution == nullptr) {
+ return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+ << "utils::ResilientExecution::create must have non-empty makeExecution";
+ }
+ auto execution = NN_TRY(makeExecution());
+ CHECK(execution != nullptr);
+ return std::make_shared<ResilientExecution>(PrivateConstructorTag{}, std::move(makeExecution),
+ std::move(execution));
+}
+
+ResilientExecution::ResilientExecution(PrivateConstructorTag /*tag*/, Factory makeExecution,
+ nn::SharedExecution execution)
+ : kMakeExecution(std::move(makeExecution)), mExecution(std::move(execution)) {
+ CHECK(kMakeExecution != nullptr);
+ CHECK(mExecution != nullptr);
+}
+
+nn::SharedExecution ResilientExecution::getExecution() const {
+ std::lock_guard guard(mMutex);
+ return mExecution;
+}
+
+nn::GeneralResult<nn::SharedExecution> ResilientExecution::recover(
+ const nn::IExecution* failingExecution) const {
+ std::lock_guard guard(mMutex);
+
+ // Another caller updated the failing prepared model.
+ if (mExecution.get() != failingExecution) {
+ return mExecution;
+ }
+
+ mExecution = NN_TRY(kMakeExecution());
+ return mExecution;
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+ResilientExecution::compute(const nn::OptionalTimePoint& deadline) const {
+ const auto fn = [&deadline](const nn::IExecution& execution) {
+ return execution.compute(deadline);
+ };
+ return protect(*this, fn);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+ResilientExecution::computeFenced(const std::vector<nn::SyncFence>& waitFor,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& timeoutDurationAfterFence) const {
+ const auto fn = [&waitFor, &deadline,
+ &timeoutDurationAfterFence](const nn::IExecution& execution) {
+ return execution.computeFenced(waitFor, deadline, timeoutDurationAfterFence);
+ };
+ return protect(*this, fn);
+}
+
+bool ResilientExecution::isValidInternal() const {
+ return true;
+}
+
+} // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp b/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp
index 5dd5f99..1ae19bc 100644
--- a/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp
+++ b/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp
@@ -17,7 +17,9 @@
#include "ResilientPreparedModel.h"
#include "InvalidBurst.h"
+#include "InvalidExecution.h"
#include "ResilientBurst.h"
+#include "ResilientExecution.h"
#include <android-base/logging.h>
#include <android-base/thread_annotations.h>
@@ -127,6 +129,21 @@
return protect(*this, fn);
}
+nn::GeneralResult<nn::SharedExecution> ResilientPreparedModel::createReusableExecution(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& loopTimeoutDuration) const {
+#if 0
+ auto self = shared_from_this();
+ ResilientExecution::Factory makeExecution =
+ [preparedModel = std::move(self), request, measure, loopTimeoutDuration] {
+ return preparedModel->createReusableExecutionInternal(request, measure, loopTimeoutDuration);
+ };
+ return ResilientExecution::create(std::move(makeExecution));
+#else
+ return createReusableExecutionInternal(request, measure, loopTimeoutDuration);
+#endif
+}
+
nn::GeneralResult<nn::SharedBurst> ResilientPreparedModel::configureExecutionBurst() const {
#if 0
auto self = shared_from_this();
@@ -140,6 +157,19 @@
#endif
}
+nn::GeneralResult<nn::SharedExecution> ResilientPreparedModel::createReusableExecutionInternal(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& loopTimeoutDuration) const {
+ if (!isValidInternal()) {
+ return std::make_shared<const InvalidExecution>();
+ }
+ const auto fn = [&request, measure,
+ &loopTimeoutDuration](const nn::IPreparedModel& preparedModel) {
+ return preparedModel.createReusableExecution(request, measure, loopTimeoutDuration);
+ };
+ return protect(*this, fn);
+}
+
std::any ResilientPreparedModel::getUnderlyingResource() const {
return getPreparedModel()->getUnderlyingResource();
}
diff --git a/neuralnetworks/utils/common/test/MockDevice.h b/neuralnetworks/utils/common/test/MockDevice.h
index b274716..a9428bc 100644
--- a/neuralnetworks/utils/common/test/MockDevice.h
+++ b/neuralnetworks/utils/common/test/MockDevice.h
@@ -29,7 +29,6 @@
MOCK_METHOD(const std::string&, getVersionString, (), (const, override));
MOCK_METHOD(Version, getFeatureLevel, (), (const, override));
MOCK_METHOD(DeviceType, getType, (), (const, override));
- MOCK_METHOD(bool, isUpdatable, (), (const, override));
MOCK_METHOD(const std::vector<Extension>&, getSupportedExtensions, (), (const, override));
MOCK_METHOD(const Capabilities&, getCapabilities, (), (const, override));
MOCK_METHOD((std::pair<uint32_t, uint32_t>), getNumberOfCacheFilesNeeded, (),
diff --git a/neuralnetworks/utils/common/test/MockExecution.h b/neuralnetworks/utils/common/test/MockExecution.h
new file mode 100644
index 0000000..91e3428
--- /dev/null
+++ b/neuralnetworks/utils/common/test/MockExecution.h
@@ -0,0 +1,38 @@
+/*
+ * 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_UTILS_COMMON_TEST_MOCK_EXECUTION
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_EXECUTION
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IExecution.h>
+
+namespace android::nn {
+
+class MockExecution final : public IExecution {
+ public:
+ MOCK_METHOD((ExecutionResult<std::pair<std::vector<OutputShape>, Timing>>), compute,
+ (const OptionalTimePoint& deadline), (const, override));
+ MOCK_METHOD((GeneralResult<std::pair<SyncFence, ExecuteFencedInfoCallback>>), computeFenced,
+ (const std::vector<SyncFence>& waitFor, const OptionalTimePoint& deadline,
+ const OptionalDuration& timeoutDurationAfterFence),
+ (const, override));
+};
+
+} // namespace android::nn
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_EXECUTION
diff --git a/neuralnetworks/utils/common/test/MockPreparedModel.h b/neuralnetworks/utils/common/test/MockPreparedModel.h
index c004861..c8ce006 100644
--- a/neuralnetworks/utils/common/test/MockPreparedModel.h
+++ b/neuralnetworks/utils/common/test/MockPreparedModel.h
@@ -35,6 +35,10 @@
const OptionalDuration& loopTimeoutDuration,
const OptionalDuration& timeoutDurationAfterFence),
(const, override));
+ MOCK_METHOD((GeneralResult<SharedExecution>), createReusableExecution,
+ (const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalDuration& loopTimeoutDuration),
+ (const, override));
MOCK_METHOD(GeneralResult<SharedBurst>, configureExecutionBurst, (), (const, override));
MOCK_METHOD(std::any, getUnderlyingResource, (), (const, override));
};
diff --git a/neuralnetworks/utils/common/test/ResilientExecution.cpp b/neuralnetworks/utils/common/test/ResilientExecution.cpp
new file mode 100644
index 0000000..c0737fb
--- /dev/null
+++ b/neuralnetworks/utils/common/test/ResilientExecution.cpp
@@ -0,0 +1,260 @@
+/*
+ * 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 <gmock/gmock.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ResilientExecution.h>
+#include <utility>
+#include "MockExecution.h"
+
+namespace android::hardware::neuralnetworks::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+
+using SharedMockExecution = std::shared_ptr<const nn::MockExecution>;
+using MockExecutionFactory = ::testing::MockFunction<nn::GeneralResult<nn::SharedExecution>()>;
+
+SharedMockExecution createMockExecution() {
+ return std::make_shared<const nn::MockExecution>();
+}
+
+std::tuple<SharedMockExecution, std::unique_ptr<MockExecutionFactory>,
+ std::shared_ptr<const ResilientExecution>>
+setup() {
+ auto mockExecution = std::make_shared<const nn::MockExecution>();
+
+ auto mockExecutionFactory = std::make_unique<MockExecutionFactory>();
+ EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(Return(mockExecution));
+
+ auto buffer = ResilientExecution::create(mockExecutionFactory->AsStdFunction()).value();
+ return std::make_tuple(std::move(mockExecution), std::move(mockExecutionFactory),
+ std::move(buffer));
+}
+
+constexpr auto makeError = [](nn::ErrorStatus status) {
+ return [status](const auto&... /*args*/) { return nn::error(status); };
+};
+const auto kReturnGeneralFailure = makeError(nn::ErrorStatus::GENERAL_FAILURE);
+const auto kReturnDeadObject = makeError(nn::ErrorStatus::DEAD_OBJECT);
+
+const auto kNoExecutionError =
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>{};
+const auto kNoFencedExecutionError =
+ nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>(
+ std::make_pair(nn::SyncFence::createAsSignaled(), nullptr));
+
+} // namespace
+
+TEST(ResilientExecutionTest, invalidExecutionFactory) {
+ // setup call
+ const auto invalidExecutionFactory = ResilientExecution::Factory{};
+
+ // run test
+ const auto result = ResilientExecution::create(invalidExecutionFactory);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST(ResilientExecutionTest, executionFactoryFailure) {
+ // setup call
+ const auto invalidExecutionFactory = kReturnGeneralFailure;
+
+ // run test
+ const auto result = ResilientExecution::create(invalidExecutionFactory);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientExecutionTest, getExecution) {
+ // setup call
+ const auto [mockExecution, mockExecutionFactory, execution] = setup();
+
+ // run test
+ const auto result = execution->getExecution();
+
+ // verify result
+ EXPECT_TRUE(result == mockExecution);
+}
+
+TEST(ResilientExecutionTest, compute) {
+ // setup call
+ const auto [mockExecution, mockExecutionFactory, execution] = setup();
+ EXPECT_CALL(*mockExecution, compute(_)).Times(1).WillOnce(Return(kNoExecutionError));
+
+ // run test
+ const auto result = execution->compute({});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientExecutionTest, computeError) {
+ // setup call
+ const auto [mockExecution, mockExecutionFactory, execution] = setup();
+ EXPECT_CALL(*mockExecution, compute(_)).Times(1).WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = execution->compute({});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientExecutionTest, computeDeadObjectFailedRecovery) {
+ // setup call
+ const auto [mockExecution, mockExecutionFactory, execution] = setup();
+ EXPECT_CALL(*mockExecution, compute(_)).Times(1).WillOnce(kReturnDeadObject);
+ EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = execution->compute({});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientExecutionTest, computeDeadObjectSuccessfulRecovery) {
+ // setup call
+ const auto [mockExecution, mockExecutionFactory, execution] = setup();
+ EXPECT_CALL(*mockExecution, compute(_)).Times(1).WillOnce(kReturnDeadObject);
+ const auto recoveredMockExecution = createMockExecution();
+ EXPECT_CALL(*recoveredMockExecution, compute(_)).Times(1).WillOnce(Return(kNoExecutionError));
+ EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(Return(recoveredMockExecution));
+
+ // run test
+ const auto result = execution->compute({});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientExecutionTest, computeFenced) {
+ // setup call
+ const auto [mockExecution, mockExecutionFactory, execution] = setup();
+ EXPECT_CALL(*mockExecution, computeFenced(_, _, _))
+ .Times(1)
+ .WillOnce(Return(kNoFencedExecutionError));
+
+ // run test
+ const auto result = execution->computeFenced({}, {}, {});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientExecutionTest, computeFencedError) {
+ // setup call
+ const auto [mockExecution, mockExecutionFactory, execution] = setup();
+ EXPECT_CALL(*mockExecution, computeFenced(_, _, _)).Times(1).WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = execution->computeFenced({}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientExecutionTest, computeFencedDeadObjectFailedRecovery) {
+ // setup call
+ const auto [mockExecution, mockExecutionFactory, execution] = setup();
+ EXPECT_CALL(*mockExecution, computeFenced(_, _, _)).Times(1).WillOnce(kReturnDeadObject);
+ EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = execution->computeFenced({}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientExecutionTest, computeFencedDeadObjectSuccessfulRecovery) {
+ // setup call
+ const auto [mockExecution, mockExecutionFactory, execution] = setup();
+ EXPECT_CALL(*mockExecution, computeFenced(_, _, _)).Times(1).WillOnce(kReturnDeadObject);
+ const auto recoveredMockExecution = createMockExecution();
+ EXPECT_CALL(*recoveredMockExecution, computeFenced(_, _, _))
+ .Times(1)
+ .WillOnce(Return(kNoFencedExecutionError));
+ EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(Return(recoveredMockExecution));
+
+ // run test
+ const auto result = execution->computeFenced({}, {}, {});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientExecutionTest, recover) {
+ // setup call
+ const auto [mockExecution, mockExecutionFactory, execution] = setup();
+ const auto recoveredMockExecution = createMockExecution();
+ EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(Return(recoveredMockExecution));
+
+ // run test
+ const auto result = execution->recover(mockExecution.get());
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_TRUE(result.value() == recoveredMockExecution);
+}
+
+TEST(ResilientExecutionTest, recoverFailure) {
+ // setup call
+ const auto [mockExecution, mockExecutionFactory, execution] = setup();
+ const auto recoveredMockExecution = createMockExecution();
+ EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = execution->recover(mockExecution.get());
+
+ // verify result
+ EXPECT_FALSE(result.has_value());
+}
+
+TEST(ResilientExecutionTest, someoneElseRecovered) {
+ // setup call
+ const auto [mockExecution, mockExecutionFactory, execution] = setup();
+ const auto recoveredMockExecution = createMockExecution();
+ EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(Return(recoveredMockExecution));
+ execution->recover(mockExecution.get());
+
+ // run test
+ const auto result = execution->recover(mockExecution.get());
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_TRUE(result.value() == recoveredMockExecution);
+}
+
+} // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/test/ResilientPreparedModelTest.cpp b/neuralnetworks/utils/common/test/ResilientPreparedModelTest.cpp
index 6d86e10..d396ca8 100644
--- a/neuralnetworks/utils/common/test/ResilientPreparedModelTest.cpp
+++ b/neuralnetworks/utils/common/test/ResilientPreparedModelTest.cpp
@@ -55,6 +55,7 @@
const auto kReturnGeneralFailure = makeError(nn::ErrorStatus::GENERAL_FAILURE);
const auto kReturnDeadObject = makeError(nn::ErrorStatus::DEAD_OBJECT);
+const auto kNoCreateReusableExecutionError = nn::GeneralResult<nn::SharedExecution>{};
const auto kNoExecutionError =
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>{};
const auto kNoFencedExecutionError =
@@ -231,6 +232,36 @@
<< "Failed with " << result.error().code << ": " << result.error().message;
}
+TEST(ResilientPreparedModelTest, createReusableExecution) {
+ // setup call
+ const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+ EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _))
+ .Times(1)
+ .WillOnce(Return(kNoCreateReusableExecutionError));
+
+ // run test
+ const auto result = preparedModel->createReusableExecution({}, {}, {});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientPreparedModelTest, createReusableExecutionError) {
+ // setup call
+ const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+ EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _))
+ .Times(1)
+ .WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = preparedModel->createReusableExecution({}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
TEST(ResilientPreparedModelTest, getUnderlyingResource) {
// setup call
const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
diff --git a/neuralnetworks/utils/service/include/nnapi/hal/Service.h b/neuralnetworks/utils/service/include/nnapi/hal/Service.h
index e339627..2fd5237 100644
--- a/neuralnetworks/utils/service/include/nnapi/hal/Service.h
+++ b/neuralnetworks/utils/service/include/nnapi/hal/Service.h
@@ -22,10 +22,15 @@
#include <memory>
#include <vector>
-namespace android::nn::hal {
+namespace android::hardware::neuralnetworks::service {
-std::vector<nn::SharedDevice> getDevices();
+struct SharedDeviceAndUpdatability {
+ nn::SharedDevice device;
+ bool isDeviceUpdatable = false;
+};
-} // namespace android::nn::hal
+std::vector<SharedDeviceAndUpdatability> getDevices(bool includeUpdatableDrivers);
+
+} // namespace android::hardware::neuralnetworks::service
#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_SERVICE_H
diff --git a/neuralnetworks/utils/service/src/Service.cpp b/neuralnetworks/utils/service/src/Service.cpp
index 75ab1f9..2286288 100644
--- a/neuralnetworks/utils/service/src/Service.cpp
+++ b/neuralnetworks/utils/service/src/Service.cpp
@@ -35,6 +35,7 @@
#include <nnapi/hal/1.2/Service.h>
#include <nnapi/hal/1.3/Service.h>
#include <nnapi/hal/aidl/Service.h>
+#include <nnapi/hal/aidl/Utils.h>
#include <functional>
#include <memory>
@@ -50,7 +51,7 @@
using getDeviceFn = std::add_pointer_t<nn::GeneralResult<nn::SharedDevice>(const std::string&)>;
void getHidlDevicesForVersion(const std::string& descriptor, getDeviceFn getDevice,
- std::vector<nn::SharedDevice>* devices,
+ std::vector<SharedDeviceAndUpdatability>* devices,
std::unordered_set<std::string>* registeredDevices) {
CHECK(devices != nullptr);
CHECK(registeredDevices != nullptr);
@@ -62,7 +63,7 @@
if (maybeDevice.has_value()) {
auto device = std::move(maybeDevice).value();
CHECK(device != nullptr);
- devices->push_back(std::move(device));
+ devices->push_back({.device = std::move(device)});
} else {
LOG(ERROR) << "getDevice(" << name << ") failed with " << maybeDevice.error().code
<< ": " << maybeDevice.error().message;
@@ -71,8 +72,9 @@
}
}
-void getAidlDevices(std::vector<nn::SharedDevice>* devices,
- std::unordered_set<std::string>* registeredDevices) {
+void getAidlDevices(std::vector<SharedDeviceAndUpdatability>* devices,
+ std::unordered_set<std::string>* registeredDevices,
+ bool includeUpdatableDrivers) {
CHECK(devices != nullptr);
CHECK(registeredDevices != nullptr);
@@ -89,12 +91,21 @@
}
for (const auto& name : names) {
+ bool isDeviceUpdatable = false;
+ if (__builtin_available(android __NNAPI_AIDL_MIN_ANDROID_API__, *)) {
+ const auto instance = std::string(aidl_hal::IDevice::descriptor) + '/' + name;
+ isDeviceUpdatable = AServiceManager_isUpdatableViaApex(instance.c_str());
+ }
+ if (isDeviceUpdatable && !includeUpdatableDrivers) {
+ continue;
+ }
if (const auto [it, unregistered] = registeredDevices->insert(name); unregistered) {
auto maybeDevice = aidl_hal::utils::getDevice(name);
if (maybeDevice.has_value()) {
auto device = std::move(maybeDevice).value();
CHECK(device != nullptr);
- devices->push_back(std::move(device));
+ devices->push_back(
+ {.device = std::move(device), .isDeviceUpdatable = isDeviceUpdatable});
} else {
LOG(ERROR) << "getDevice(" << name << ") failed with " << maybeDevice.error().code
<< ": " << maybeDevice.error().message;
@@ -103,11 +114,13 @@
}
}
-std::vector<nn::SharedDevice> getDevices() {
- std::vector<nn::SharedDevice> devices;
+} // namespace
+
+std::vector<SharedDeviceAndUpdatability> getDevices(bool includeUpdatableDrivers) {
+ std::vector<SharedDeviceAndUpdatability> devices;
std::unordered_set<std::string> registeredDevices;
- getAidlDevices(&devices, ®isteredDevices);
+ getAidlDevices(&devices, ®isteredDevices, includeUpdatableDrivers);
getHidlDevicesForVersion(V1_3::IDevice::descriptor, &V1_3::utils::getDevice, &devices,
®isteredDevices);
@@ -121,13 +134,4 @@
return devices;
}
-} // namespace
} // namespace android::hardware::neuralnetworks::service
-
-namespace android::nn::hal {
-
-std::vector<nn::SharedDevice> getDevices() {
- return hardware::neuralnetworks::service::getDevices();
-}
-
-} // namespace android::nn::hal
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..7c05984 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}));
@@ -432,6 +433,8 @@
::android::hardware::radio::V1_6::RadioError::NONE,
::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS}));
}
+
+ sleep(1);
serial = GetRandomSerialNumber();
res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::THROTTLE_ANCHOR_CARRIER,
@@ -440,7 +443,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}));
@@ -452,6 +455,8 @@
::android::hardware::radio::V1_6::RadioError::NONE,
::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS}));
}
+
+ sleep(1);
serial = GetRandomSerialNumber();
res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::HOLD, 60000);
@@ -460,7 +465,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}));
@@ -472,6 +477,8 @@
::android::hardware::radio::V1_6::RadioError::NONE,
::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS}));
}
+
+ sleep(1);
serial = GetRandomSerialNumber();
res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::NO_DATA_THROTTLING, 60000);
@@ -479,7 +486,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/rebootescrow/aidl/default/Android.bp b/rebootescrow/aidl/default/Android.bp
index b9fb2a9..1f67a3e 100644
--- a/rebootescrow/aidl/default/Android.bp
+++ b/rebootescrow/aidl/default/Android.bp
@@ -87,7 +87,9 @@
],
static_libs: [
"libhadamardutils",
- "libgtest_prod",
+ ],
+ header_libs: [
+ "libgtest_prod_headers",
],
shared_libs: [
"liblog",
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 bf30999..fa643fc 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
@@ -44,10 +44,10 @@
void deleteKey(in byte[] keyBlob);
void deleteAllKeys();
void destroyAttestationIds();
- android.hardware.security.keymint.BeginResult begin(in android.hardware.security.keymint.KeyPurpose purpose, in byte[] keyBlob, in android.hardware.security.keymint.KeyParameter[] params, in android.hardware.security.keymint.HardwareAuthToken authToken);
+ android.hardware.security.keymint.BeginResult begin(in android.hardware.security.keymint.KeyPurpose purpose, in byte[] keyBlob, in android.hardware.security.keymint.KeyParameter[] params, in @nullable android.hardware.security.keymint.HardwareAuthToken authToken);
void deviceLocked(in boolean passwordOnly, in @nullable android.hardware.security.secureclock.TimeStampToken timestampToken);
void earlyBootEnded();
byte[] convertStorageKeyToEphemeral(in byte[] storageKeyBlob);
- byte[] performOperation(in byte[] request);
+ android.hardware.security.keymint.KeyCharacteristics[] getKeyCharacteristics(in byte[] keyBlob, in byte[] appId, in byte[] appData);
const int AUTH_TOKEN_MAC_LENGTH = 32;
}
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/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Tag.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Tag.aidl
index 7591318..e310b44 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Tag.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Tag.aidl
@@ -48,7 +48,6 @@
RSA_PUBLIC_EXPONENT = 1342177480,
INCLUDE_UNIQUE_ID = 1879048394,
RSA_OAEP_MGF_DIGEST = 536871115,
- BLOB_USAGE_REQUIREMENTS = 268435757,
BOOTLOADER_ONLY = 1879048494,
ROLLBACK_RESISTANCE = 1879048495,
HARDWARE_TYPE = 268435760,
diff --git a/security/keymint/aidl/android/hardware/security/keymint/AttestationKey.aidl b/security/keymint/aidl/android/hardware/security/keymint/AttestationKey.aidl
index b4bc60c..4e3008f 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/AttestationKey.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/AttestationKey.aidl
@@ -27,7 +27,19 @@
@VintfStability
@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
parcelable AttestationKey {
+ /**
+ * Key blob containing a key pair with KeyPurpose::ATTEST_KEY
+ */
byte[] keyBlob;
+
+ /**
+ * Key parameters needed to use the key in keyBlob, notably Tag::APPLICATION_ID and
+ * Tag::APPLICATION_DATA, if they were provided during generation of the key in keyBlob.
+ */
KeyParameter[] attestKeyParams;
+
+ /**
+ * The issuerSubjectName to use in the generated attestation.
+ */
byte[] issuerSubjectName;
}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/BeginResult.aidl b/security/keymint/aidl/android/hardware/security/keymint/BeginResult.aidl
index 2304a58..b5336b9 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/BeginResult.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/BeginResult.aidl
@@ -25,7 +25,10 @@
*/
@VintfStability
parcelable BeginResult {
- /* This is the challenge used in verifyAuthorization. It must be a nonce. */
+ /**
+ * This is the challenge used to verify authorization of an operation.
+ * See IKeyMintOperation.aidl entrypoints updateAad() and update().
+ */
long challenge;
/**
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
index 1c503c2..b6af813 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -20,6 +20,7 @@
import android.hardware.security.keymint.BeginResult;
import android.hardware.security.keymint.HardwareAuthToken;
import android.hardware.security.keymint.IKeyMintOperation;
+import android.hardware.security.keymint.KeyCharacteristics;
import android.hardware.security.keymint.KeyCreationResult;
import android.hardware.security.keymint.KeyFormat;
import android.hardware.security.keymint.KeyMintHardwareInfo;
@@ -107,7 +108,6 @@
*
* - 168-bit keys.
* - CBC and ECB mode.
-
* - CBC and ECB modes must support unpadded and PKCS7 padding modes. With no padding CBC and
* ECB-mode operations must fail with ErrorCode::INVALID_INPUT_LENGTH if the input isn't a
* multiple of the DES block size.
@@ -150,8 +150,8 @@
*
* The IKeyMintDevice must ignore unknown tags.
*
- * The caller must always provide the current date time in the keyParameter CREATION_DATETIME
- * tags.
+ * The caller may provide the current date time in the keyParameter CREATION_DATETIME tag, but
+ * this is optional and informational only.
*
* All authorization tags and their values enforced by an IKeyMintDevice must be cryptographically
* bound to the private/secret key material such that any modification of the portion of the key
@@ -185,7 +185,7 @@
* startup, preferably by the bootloader. This bitstring must be cryptographically bound to every
* key managed by the IKeyMintDevice. As above, the recommended mechanism for this cryptographic
* binding is to include the Root of Trust data in the input to the key derivation function used to
- * derive a key that is used to encryp the private/secret key material.
+ * derive a key that is used to encrypt the private/secret key material.
*
* The root of trust consists of a bitstring that must be derived from the public key used by
* Verified Boot to verify the signature on the boot image and from the lock state of the
@@ -247,7 +247,7 @@
* Generates a new cryptographic key, specifying associated parameters, which must be
* cryptographically bound to the key. IKeyMintDevice implementations must disallow any use
* of a key in any way inconsistent with the authorizations specified at generation time. With
- * respect to parameters that the secure environment cannot enforce, the secure envionment's
+ * respect to parameters that the secure environment cannot enforce, the secure environment's
* obligation is limited to ensuring that the unenforceable parameters associated with the key
* cannot be modified. In addition, the characteristics returned by generateKey places
* parameters correctly in the tee-enforced and strongbox-enforced lists.
@@ -257,9 +257,6 @@
*
* o Tag::ORIGIN with the value KeyOrigin::GENERATED.
*
- * o Tag::BLOB_USAGE_REQUIREMENTS with the appropriate value (see KeyBlobUsageRequirements in
- * Tag.aidl).
- *
* o Tag::OS_VERSION, Tag::OS_PATCHLEVEL, Tag::VENDOR_PATCHLEVEL and Tag::BOOT_PATCHLEVEL with
* appropriate values.
*
@@ -271,15 +268,14 @@
*
* The following parameters are required to generate an RSA key:
*
- * o Tag::Key_SIZE specifies the size of the public modulus, in bits. If omitted, generateKey
+ * o Tag::KEY_SIZE specifies the size of the public modulus, in bits. If omitted, generateKey
* must return ErrorCode::UNSUPPORTED_KEY_SIZE. Required values for TEE IKeyMintDevice
* implementations are 1024, 2048, 3072 and 4096. StrongBox IKeyMintDevice implementations
* must support 2048.
*
* o Tag::RSA_PUBLIC_EXPONENT specifies the RSA public exponent value. If omitted, generateKey
* must return ErrorCode::INVALID_ARGUMENT. The values 3 and 65537 must be supported. It is
- * recommended to support all prime values up to 2^64. If provided with a non-prime value,
- * generateKey must return ErrorCode::INVALID_ARGUMENT.
+ * recommended to support all prime values up to 2^64.
*
* The following parameters are not necessary to generate a usable RSA key, but generateKey must
* not return an error if they are omitted:
@@ -288,7 +284,7 @@
* except AGREE_KEY must be supported for RSA keys.
*
* o Tag::DIGEST specifies digest algorithms that may be used with the new key. TEE
- * IKeyMintDevice implementatiosn must support all Digest values (see digest.aidl) for RSA
+ * IKeyMintDevice implementations must support all Digest values (see digest.aidl) for RSA
* keys. StrongBox IKeyMintDevice implementations must support SHA_2_256.
*
* o Tag::PADDING specifies the padding modes that may be used with the new
@@ -298,11 +294,9 @@
*
* == ECDSA Keys ==
*
- * Either Tag::KEY_SIZE or Tag::EC_CURVE must be provided to generate an ECDSA key. If neither
- * is provided, generateKey must return ErrorCode::UNSUPPORTED_KEY_SIZE. If Tag::KEY_SIZE is
- * provided, the possible values are 224, 256, 384 and 521, and must be mapped to Tag::EC_CURVE
- * values P_224, P_256, P_384 and P_521, respectively. TEE IKeyMintDevice implementations
- * must support all curves. StrongBox implementations must support P_256.
+ * Tag::EC_CURVE must be provided to generate an ECDSA key. If it is not provided, generateKey
+ * must return ErrorCode::UNSUPPORTED_KEY_SIZE. TEE IKeyMintDevice implementations must support
+ * all curves. StrongBox implementations must support P_256.
*
* == AES Keys ==
*
@@ -312,6 +306,10 @@
* If Tag::BLOCK_MODE is specified with value BlockMode::GCM, then the caller must also provide
* Tag::MIN_MAC_LENGTH. If omitted, generateKey must return ErrorCode::MISSING_MIN_MAC_LENGTH.
*
+ * == 3DES Keys ==
+ *
+ * Only Tag::KEY_SIZE is required to generate an 3DES key, and its value must be 168. If
+ * omitted, generateKey must return ErrorCode::UNSUPPORTED_KEY_SIZE.
*
* @param keyParams Key generation parameters are defined as KeyMintDevice tag/value pairs,
* provided in params. See above for detailed specifications of which tags are required
@@ -325,6 +323,10 @@
* return ErrorCode::INCOMPATIBLE_PURPOSE. If the provided AttestationKey has an empty
* issuer subject name, the IKeyMintDevice must return ErrorCode::INVALID_ARGUMENT.
*
+ * If `attestationKey` is null and `keyParams` contains Tag::ATTESTATION_CHALLENGE but
+ * the KeyMint implementation does not have factory-provisioned attestation keys, it must
+ * return ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED.
+ *
* @return The result of key creation. See KeyCreationResult.aidl.
*/
KeyCreationResult generateKey(
@@ -348,13 +350,12 @@
*
* o Tag::ORIGIN (returned in keyCharacteristics) must have the value KeyOrigin::IMPORTED.
*
- * @param inKeyParams Key generation parameters are defined as KeyMintDevice tag/value pairs,
+ * @param keyParams Key generation parameters are defined as KeyMintDevice tag/value pairs,
* provided in params.
*
- * @param inKeyFormat The format of the key material to import. See KeyFormat in
- * keyformat.aidl.
+ * @param keyFormat The format of the key material to import. See KeyFormat in keyformat.aidl.
*
- * @param inKeyData The key material to import, in the format specified in keyFormat.
+ * @param keyData The key material to import, in the format specified in keyFormat.
*
* @param attestationKey, if provided, specifies the key that must be used to sign the
* attestation certificate. If `keyParams` does not contain a Tag::ATTESTATION_CHALLENGE
@@ -364,6 +365,10 @@
* return ErrorCode::INCOMPATIBLE_PURPOSE. If the provided AttestationKey has an empty
* issuer subject name, the IKeyMintDevice must return ErrorCode::INVALID_ARGUMENT.
*
+ * If `attestationKey` is null and `keyParams` contains Tag::ATTESTATION_CHALLENGE but
+ * the KeyMint implementation does not have factory-provisioned attestation keys, it must
+ * return ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED.
+ *
* @return The result of key creation. See KeyCreationResult.aidl.
*/
KeyCreationResult importKey(in KeyParameter[] keyParams, in KeyFormat keyFormat,
@@ -373,9 +378,8 @@
* Securely imports a key, or key pair, returning a key blob and a description of the imported
* key.
*
- * @param inWrappedKeyData The wrapped key material to import.
- * TODO(seleneh) Decide if we want the wrapped key in DER-encoded ASN.1 format or CBOR
- * format or both. And specify the standarized format.
+ * @param wrappedKeyData The wrapped key material to import, as ASN.1 DER-encoded data
+ * corresponding to the following schema.
*
* KeyDescription ::= SEQUENCE(
* keyFormat INTEGER, # Values from KeyFormat enum.
@@ -393,20 +397,20 @@
*
* Where:
*
- * o keyFormat is an integer from the KeyFormat enum, defining the format of the plaintext
+ * - keyFormat is an integer from the KeyFormat enum, defining the format of the plaintext
* key material.
- * o keyParams is the characteristics of the key to be imported (as with generateKey or
+ * - keyParams is the characteristics of the key to be imported (as with generateKey or
* importKey). If the secure import is successful, these characteristics must be
* associated with the key exactly as if the key material had been insecurely imported
- * with the IKeyMintDevice::importKey. See attestKey() for documentation of the
- * AuthorizationList schema.
- * o encryptedTransportKey is a 256-bit AES key, XORed with a masking key and then encrypted
+ * with the IKeyMintDevice::importKey. See KeyCreationResult.aidl for documentation of
+ * the AuthorizationList schema.
+ * - encryptedTransportKey is a 256-bit AES key, XORed with a masking key and then encrypted
* with the wrapping key specified by wrappingKeyBlob.
- * o keyDescription is a KeyDescription, above.
- * o encryptedKey is the key material of the key to be imported, in format keyFormat, and
+ * - keyDescription is a KeyDescription, above.
+ * - encryptedKey is the key material of the key to be imported, in format keyFormat, and
* encrypted with encryptedEphemeralKey in AES-GCM mode, with the DER-encoded
* representation of keyDescription provided as additional authenticated data.
- * o tag is the tag produced by the AES-GCM encryption of encryptedKey.
+ * - tag is the tag produced by the AES-GCM encryption of encryptedKey.
*
* So, importWrappedKey does the following:
*
@@ -435,7 +439,7 @@
*
* @param passwordSid specifies the password secure ID (SID) of the user that owns the key being
* installed. If the authorization list in wrappedKeyData contains a
- * Tag::USER_SECURE_IDwith a value that has the HardwareAuthenticatorType::PASSWORD bit
+ * Tag::USER_SECURE_ID with a value that has the HardwareAuthenticatorType::PASSWORD bit
* set, the constructed key must be bound to the SID value provided by this argument. If
* the wrappedKeyData does not contain such a tag and value, this argument must be
* ignored.
@@ -478,9 +482,9 @@
* patch level and OS version. This requirement is relaxed for 4.0::IKeymasterDevice and
* IKeyMintDevice, and the OS version in the boot image footer is no longer used.
*
- * @param inKeyBlobToUpgrade The opaque descriptor returned by generateKey() or importKey();
+ * @param keyBlobToUpgrade The opaque descriptor returned by generateKey() or importKey().
*
- * @param inUpgradeParams A parameter list containing any parameters needed to complete the
+ * @param upgradeParams A parameter list containing any parameters needed to complete the
* upgrade, including Tag::APPLICATION_ID and Tag::APPLICATION_DATA.
*
* @return A new key blob that references the same key as keyBlobToUpgrade, but is in the new
@@ -494,7 +498,9 @@
* render the key permanently unusable. Keys without Tag::ROLLBACK_RESISTANCE may or
* may not be rendered unusable.
*
- * @param inKeyBlob The opaque descriptor returned by generateKey() or importKey();
+ * @param keyBlob The opaque descriptor returned by generateKey() or importKey();
+ *
+ * @return error See the ErrorCode enum.
*/
void deleteKey(in byte[] keyBlob);
@@ -503,8 +509,6 @@
* this function is called all keys with Tag::ROLLBACK_RESISTANCE in their hardware-enforced
* authorization lists must be rendered permanently unusable. Keys without
* Tag::ROLLBACK_RESISTANCE may or may not be rendered unusable.
- *
- * @return error See the ErrorCode enum.
*/
void deleteAllKeys();
@@ -523,28 +527,27 @@
/**
* Begins a cryptographic operation using the specified key. If all is well, begin() must
- * return ErrorCode::OK and create an operation handle which must be passed to subsequent calls
- * to update(), finish() or abort().
+ * return ErrorCode::OK and create an IKeyMintOperation handle which will be used to perform
+ * the cryptographic operation.
*
- * It is critical that each call to begin() be paired with a subsequent call to finish() or
- * abort(), to allow the IKeyMintDevice implementation to clean up any internal operation
- * state. The caller's failure to do this may leak internal state space or other internal
- * resources and may eventually cause begin() to return ErrorCode::TOO_MANY_OPERATIONS when it
- * runs out of space for operations. Any result other than ErrorCode::OK from begin(), update()
- * or finish() implicitly aborts the operation, in which case abort() need not be called (and
- * must return ErrorCode::INVALID_OPERATION_HANDLE if called). IKeyMintDevice implementations
- * must support 32 concurrent operations.
+ * It is critical that each successful call to begin() be paired with a subsequent call to
+ * finish() or abort() on the resulting IKeyMintOperation, to allow the IKeyMintDevice
+ * implementation to clean up any internal operation state. The caller's failure to do this may
+ * leak internal state space or other internal resources and may eventually cause begin() to
+ * return ErrorCode::TOO_MANY_OPERATIONS when it runs out of space for operations. Any result
+ * other than ErrorCode::OK from begin() will not return an IKeyMintOperation (in which case
+ * calling finish() or abort() is neither possible nor necessary). IKeyMintDevice
+ * implementations must support 32 concurrent operations.
*
* If Tag::APPLICATION_ID or Tag::APPLICATION_DATA were specified during key generation or
* import, calls to begin must include those tags with the originally-specified values in the
- * inParams argument to this method. If not, begin() must return ErrorCode::INVALID_KEY_BLOB.
+ * params argument to this method. If not, begin() must return ErrorCode::INVALID_KEY_BLOB.
*
* == Authorization Enforcement ==
*
* The following key authorization parameters must be enforced by the IKeyMintDevice secure
* environment if the tags were returned in the "hardwareEnforced" list in the
- * KeyCharacteristics. Public key operations, meaning KeyPurpose::ENCRYPT and
- * KeyPurpose::VERIFY must be allowed to succeed even if authorization requirements are not met.
+ * KeyCharacteristics.
*
* -- All Key Types --
*
@@ -573,9 +576,9 @@
*
* o Tag::USER_SECURE_ID must be enforced by this method if and only if the key also has
* Tag::AUTH_TIMEOUT (if it does not have Tag::AUTH_TIMEOUT, the Tag::USER_SECURE_ID
- * requirement must be enforced by update() and finish()). If the key has both, then this
- * method must receive a non-empty HardwareAuthToken in the authToken argument. For the auth
- * token to be valid, all of the following have to be true:
+ * requirement must be enforced by updateAad(), update() and finish()). If the key has both,
+ * then this method must receive a non-empty HardwareAuthToken in the authToken argument. For
+ * the auth token to be valid, all of the following have to be true:
*
* o The HMAC field must validate correctly.
*
@@ -602,32 +605,30 @@
*
* -- RSA Keys --
*
- * All RSA key operations must specify exactly one padding mode in inParams. If unspecified or
+ * All RSA key operations must specify exactly one padding mode in params. If unspecified or
* specified more than once, the begin() must return ErrorCode::UNSUPPORTED_PADDING_MODE.
*
- * RSA signing and verification operations need a digest, as do RSA encryption and decryption
- * operations with OAEP padding mode. For those cases, the caller must specify exactly one
- * digest in inParams. If unspecified or specified more than once, begin() must return
+ * RSA signing operations need a digest, as do RSA encryption and decryption operations with
+ * OAEP padding mode. For those cases, the caller must specify exactly one digest in params.
+ * If unspecified or specified more than once, begin() must return
* ErrorCode::UNSUPPORTED_DIGEST.
*
* Private key operations (KeyPurpose::DECRYPT and KeyPurpose::SIGN) need authorization of
* digest and padding, which means that the key authorizations need to contain the specified
* values. If not, begin() must return ErrorCode::INCOMPATIBLE_DIGEST or
- * ErrorCode::INCOMPATIBLE_PADDING, as appropriate. Public key operations (KeyPurpose::ENCRYPT
- * and KeyPurpose::VERIFY) are permitted with unauthorized digest or padding modes.
+ * ErrorCode::INCOMPATIBLE_PADDING_MODE, as appropriate.
*
* With the exception of PaddingMode::NONE, all RSA padding modes are applicable only to certain
* purposes. Specifically, PaddingMode::RSA_PKCS1_1_5_SIGN and PaddingMode::RSA_PSS only
- * support signing and verification, while PaddingMode::RSA_PKCS1_1_5_ENCRYPT and
- * PaddingMode::RSA_OAEP only support encryption and decryption. begin() must return
- * ErrorCode::UNSUPPORTED_PADDING_MODE if the specified mode does not support the specified
- * purpose.
+ * support signing, while PaddingMode::RSA_PKCS1_1_5_ENCRYPT and PaddingMode::RSA_OAEP only
+ * support encryption and decryption. begin() must return ErrorCode::UNSUPPORTED_PADDING_MODE
+ * if the specified mode does not support the specified purpose.
*
* There are some important interactions between padding modes and digests:
*
- * o PaddingMode::NONE indicates that a "raw" RSA operation is performed. If signing or
- * verifying, Digest::NONE is specified for the digest. No digest is necessary for unpadded
- * encryption or decryption.
+ * o PaddingMode::NONE indicates that a "raw" RSA operation is performed. If signing,
+ * Digest::NONE is specified for the digest. No digest is necessary for unpadded encryption
+ * or decryption.
*
* o PaddingMode::RSA_PKCS1_1_5_SIGN padding requires a digest. The digest may be Digest::NONE,
* in which case the KeyMint implementation cannot build a proper PKCS#1 v1.5 signature
@@ -639,37 +640,37 @@
*
* o PaddingMode::RSA_PKCS1_1_1_5_ENCRYPT padding does not require a digest.
*
- * o PaddingMode::RSA_PSS padding requires a digest, which may not be Digest::NONE. If
- * Digest::NONE is specified, the begin() must return ErrorCode::INCOMPATIBLE_DIGEST. In
- * addition, the size of the RSA key must be at least 2 + D bytes larger than the output size
- * of the digest, where D is the size of the digest, in bytes. Otherwise begin() must
- * return ErrorCode::INCOMPATIBLE_DIGEST. The salt size must be D.
+ * o PaddingMode::RSA_PSS padding requires a digest, which must match one of the padding values
+ * in the key authorizations, and which may not be Digest::NONE. begin() must return
+ * ErrorCode::INCOMPATIBLE_DIGEST if this is not the case. In addition, the size of the RSA
+ * key must be at least 2 + D bytes larger than the output size of the digest, where D is the
+ * size of the digest, in bytes. Otherwise begin() must return
+ * ErrorCode::INCOMPATIBLE_DIGEST. The salt size must be D.
*
- * o PaddingMode::RSA_OAEP padding requires a digest, which may not be Digest::NONE. If
- * Digest::NONE is specified, begin() must return ErrorCode::INCOMPATIBLE_DIGEST. The OAEP
- * mask generation function must be MGF1 and the MGF1 digest must be SHA1, regardless of the
- * OAEP digest specified.
+ * o PaddingMode::RSA_OAEP padding requires a digest, which must match one of the padding values
+ * in the key authorizations, and which may not be Digest::NONE. begin() must return
+ * ErrorCode::INCOMPATIBLE_DIGEST if this is not the case. RSA_OAEP padding also requires an
+ * MGF1 digest, specified with Tag::RSA_OAEP_MGF_DIGEST, which must match one of the MGF1
+ * padding values in the key authorizations and which may not be Digest::NONE. begin() must
+ * return ErrorCode::INCOMPATIBLE_MGF_DIGEST if this is not the case. The OAEP mask generation
+ * function must be MGF1.
*
* -- EC Keys --
*
- * EC key operations must specify exactly one padding mode in inParams. If unspecified or
- * specified more than once, begin() must return ErrorCode::UNSUPPORTED_PADDING_MODE.
- *
* Private key operations (KeyPurpose::SIGN) need authorization of digest and padding, which
* means that the key authorizations must contain the specified values. If not, begin() must
- * return ErrorCode::INCOMPATIBLE_DIGEST. Public key operations (KeyPurpose::VERIFY) are
- * permitted with unauthorized digest or padding.
+ * return ErrorCode::INCOMPATIBLE_DIGEST.
*
* -- AES Keys --
*
* AES key operations must specify exactly one block mode (Tag::BLOCK_MODE) and one padding mode
- * (Tag::PADDING) in inParams. If either value is unspecified or specified more than once,
+ * (Tag::PADDING) in params. If either value is unspecified or specified more than once,
* begin() must return ErrorCode::UNSUPPORTED_BLOCK_MODE or
* ErrorCode::UNSUPPORTED_PADDING_MODE. The specified modes must be authorized by the key,
* otherwise begin() must return ErrorCode::INCOMPATIBLE_BLOCK_MODE or
* ErrorCode::INCOMPATIBLE_PADDING_MODE.
*
- * If the block mode is BlockMode::GCM, inParams must specify Tag::MAC_LENGTH, and the specified
+ * If the block mode is BlockMode::GCM, params must specify Tag::MAC_LENGTH, and the specified
* value must be a multiple of 8 that is not greater than 128 or less than the value of
* Tag::MIN_MAC_LENGTH in the key authorizations. For MAC lengths greater than 128 or
* non-multiples of 8, begin() must return ErrorCode::UNSUPPORTED_MAC_LENGTH. For values less
@@ -682,48 +683,66 @@
*
* If the block mode is BlockMode::CBC, BlockMode::CTR, or BlockMode::GCM, an initialization
* vector or nonce is required. In most cases, callers shouldn't provide an IV or nonce and the
- * IKeyMintDevice implementation must generate a random IV or nonce and return it via
- * Tag::NONCE in outParams. CBC and CTR IVs are 16 bytes. GCM nonces are 12 bytes. If the key
+ * IKeyMintDevice implementation must generate a random IV or nonce and return it via Tag::NONCE
+ * in outParams. CBC and CTR IVs are 16 bytes. GCM nonces are 12 bytes. If the key
* authorizations contain Tag::CALLER_NONCE, then the caller may provide an IV/nonce with
- * Tag::NONCE in inParams. If a nonce is provided when Tag::CALLER_NONCE is not authorized,
+ * Tag::NONCE in params, which must be of the correct size (if not, return
+ * ErrorCode::INVALID_NONCE). If a nonce is provided when Tag::CALLER_NONCE is not authorized,
* begin() must return ErrorCode::CALLER_NONCE_PROHIBITED. If a nonce is not provided when
- * Tag::CALLER_NONCE is authorized, IKeyMintDevice msut generate a random IV/nonce.
+ * Tag::CALLER_NONCE is authorized, IKeyMintDevice must generate a random IV/nonce.
+ *
+ * -- 3DES Keys --
+ *
+ * 3DES key operations must specify exactly one block mode (Tag::BLOCK_MODE) and one padding
+ * mode (Tag::PADDING) in params. If either value is unspecified or specified more than once,
+ * begin() must return ErrorCode::UNSUPPORTED_BLOCK_MODE or
+ * ErrorCode::UNSUPPORTED_PADDING_MODE. The specified modes must be authorized by the key,
+ * otherwise begin() must return ErrorCode::INCOMPATIBLE_BLOCK_MODE or
+ * ErrorCode::INCOMPATIBLE_PADDING_MODE.
+ *
+ * If the block mode is BlockMode::CBC, an initialization vector or nonce is required. In most
+ * cases, callers shouldn't provide an IV or nonce and the IKeyMintDevice implementation must
+ * generate a random IV or nonce and return it via Tag::NONCE in outParams. CBC IVs are 8
+ * bytes. If the key authorizations contain Tag::CALLER_NONCE, then the caller may provide an
+ * IV/nonce with Tag::NONCE in params, which must be of the correct size (if not, return
+ * ErrorCode::INVALID_NONCE). If a nonce is provided when Tag::CALLER_NONCE is not authorized,
+ * begin() must return ErrorCode::CALLER_NONCE_PROHIBITED. If a nonce is not provided when
+ * Tag::CALLER_NONCE is authorized, IKeyMintDevice must generate a random IV/nonce.
+ *
*
* -- HMAC keys --
*
- * HMAC key operations must specify Tag::MAC_LENGTH in inParams. The specified value must be a
+ * HMAC key operations must specify Tag::MAC_LENGTH in params. The specified value must be a
* multiple of 8 that is not greater than the digest length or less than the value of
* Tag::MIN_MAC_LENGTH in the key authorizations. For MAC lengths greater than the digest
* length or non-multiples of 8, begin() must return ErrorCode::UNSUPPORTED_MAC_LENGTH. For
* values less than the key's minimum length, begin() must return ErrorCode::INVALID_MAC_LENGTH.
*
- * @param inPurpose The purpose of the operation, one of KeyPurpose::ENCRYPT,
- * KeyPurpose::DECRYPT, KeyPurpose::SIGN, KeyPurpose::VERIFY, or KeyPurpose::AGREE_KEY.
- * Note that for AEAD modes, encryption and decryption imply signing and verification,
- * respectively, but must be specified as KeyPurpose::ENCRYPT and KeyPurpose::DECRYPT.
+ * @param purpose The purpose of the operation, one of KeyPurpose::ENCRYPT, KeyPurpose::DECRYPT,
+ * KeyPurpose::SIGN, KeyPurpose::VERIFY, or KeyPurpose::AGREE_KEY. Note that for AEAD
+ * modes, encryption and decryption imply signing and verification, respectively, but
+ * must be specified as KeyPurpose::ENCRYPT and KeyPurpose::DECRYPT.
*
- * @param inKeyBlob The opaque key descriptor returned by generateKey() or importKey(). The key
+ * @param keyBlob The opaque key descriptor returned by generateKey() or importKey(). The key
* must have a purpose compatible with purpose and all of its usage requirements must be
* satisfied, or begin() must return an appropriate error code (see above).
*
- * @param inParams Additional parameters for the operation. If Tag::APPLICATION_ID or
+ * @param params Additional parameters for the operation. If Tag::APPLICATION_ID or
* Tag::APPLICATION_DATA were provided during generation, they must be provided here, or
* the operation must fail with ErrorCode::INVALID_KEY_BLOB. For operations that require
- * a nonce or IV, on keys that were generated with Tag::CALLER_NONCE, inParams may
+ * a nonce or IV, on keys that were generated with Tag::CALLER_NONCE, params may
* contain a tag Tag::NONCE. If Tag::NONCE is provided for a key without
* Tag:CALLER_NONCE, ErrorCode::CALLER_NONCE_PROHIBITED must be returned.
*
- * @param inAuthToken Authentication token. Callers that provide no token must set all numeric
- * fields to zero and the MAC must be an empty vector. TODO: make this field nullable.
- * b/173483024.
+ * @param authToken Authentication token.
*
* @return BeginResult as output, which contains the challenge, KeyParameters which haves
* additional data from the operation initialization, notably to return the IV or nonce
* from operations that generate an IV or nonce, and IKeyMintOperation object pointer
- * which is used to perform update(), finish() or abort() operations.
+ * which is used to perform updateAad(), update(), finish() or abort() operations.
*/
BeginResult begin(in KeyPurpose purpose, in byte[] keyBlob, in KeyParameter[] params,
- in HardwareAuthToken authToken);
+ in @nullable HardwareAuthToken authToken);
/**
* Called by client to notify the IKeyMintDevice that the device is now locked, and keys with
@@ -737,7 +756,7 @@
* Note that the IKeyMintDevice UNLOCKED_DEVICE_REQUIRED semantics are slightly different from
* the UNLOCKED_DEVICE_REQUIRED semantics enforced by keystore. Keystore handles device locking
* on a per-user basis. Because auth tokens do not contain an Android user ID, it's not
- * possible to replicate the keystore enformcement logic in IKeyMintDevice. So from the
+ * possible to replicate the keystore enforcement logic in IKeyMintDevice. So from the
* IKeyMintDevice perspective, any user unlock unlocks all UNLOCKED_DEVICE_REQUIRED keys.
* Keystore will continue enforcing the per-user device locking.
*
@@ -763,7 +782,7 @@
*/
void earlyBootEnded();
- /*
+ /**
* Called by the client to get a wrapped per-boot ephemeral key from a wrapped storage key.
* Clients will then use the returned per-boot ephemeral key in place of the wrapped storage
* key. Whenever the hardware is presented with a per-boot ephemeral key for an operation, it
@@ -785,16 +804,24 @@
byte[] convertStorageKeyToEphemeral(in byte[] storageKeyBlob);
/**
- * Called by the client to perform a KeyMint operation.
+ * Returns parameters associated with the provided key. This should match the
+ * KeyCharacteristics present in the KeyCreationResult returned by generateKey(),
+ * importKey(), or importWrappedKey().
*
- * This method is added primarily as a placeholder. Details will be fleshed before the KeyMint
- * V1 interface is frozen. Until then, implementations must return ErrorCode::UNIMPLEMENTED.
+ * @param keyBlob The opaque descriptor returned by generateKey, importKey or importWrappedKey.
*
- * @param request is an encrypted buffer containing a description of the operation the client
- * wishes to perform. Structure, content and encryption are TBD.
+ * @param appId An opaque byte string identifying the client. This value must match the
+ * Tag::APPLICATION_ID data provided during key generation/import. Without the correct
+ * value, it must be computationally infeasible for the secure hardware to obtain the
+ * key material.
*
- * @return an encrypted buffer containing the result of the operation. Structure, content and
- * encryption are TBD.
+ * @param appData An opaque byte string provided by the application. This value must match the
+ * Tag::APPLICATION_DATA data provided during key generation/import. Without the
+ * correct value, it must be computationally infeasible for the secure hardware to
+ * obtain the key material.
+ *
+ * @return Characteristics of the generated key. See KeyCreationResult for details.
*/
- byte[] performOperation(in byte[] request);
+ KeyCharacteristics[] getKeyCharacteristics(
+ in byte[] keyBlob, in byte[] appId, in byte[] appData);
}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
index d2a993f..aa7b492 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
@@ -31,12 +31,12 @@
* update() is called. If updateAad() is called after update(), it must return
* ErrorCode::INVALID_TAG.
*
- * If operation is in an invalid state (was aborted or had an error) update() must return
+ * If the operation is in an invalid state (was aborted or had an error) update() must return
* ErrorCode::INVALID_OPERATION_HANDLE.
*
* If this method returns an error code other than ErrorCode::OK, the operation is aborted and
- * the operation handle must be invalidated. Any future use of the handle, with this method,
- * finish, or abort, must return ErrorCode::INVALID_OPERATION_HANDLE.
+ * the operation handle must be invalidated. Any future use of this object must return
+ * ErrorCode::INVALID_OPERATION_HANDLE.
*
* == Authorization Enforcement ==
*
@@ -58,9 +58,10 @@
*
* o The key must have a Tag::USER_AUTH_TYPE that matches the auth type in the token.
*
- * o The challenge field in the auth token must contain the operationHandle
+ * o The challenge field in the auth token must contain the value returned from
+ * IKeyMintDevice::begin(), given by the challenge field of the BeginResult structure.
*
- * If any of these conditions are not met, update() must return
+ * If any of these conditions are not met, updateAad() must return
* ErrorCode::KEY_USER_NOT_AUTHENTICATED.
*
* The caller must provide the auth token on every call to updateAad(), update() and finish().
@@ -74,7 +75,7 @@
*
* @param input Additional Authentication Data to be processed.
*
- * @param authToken Authentication token. Can be nullable if not provided.
+ * @param authToken Authentication token, if provided.
*
* @param timeStampToken timestamp token, certifies the freshness of an auth token in case
* the security domain of this KeyMint instance has a different clock than the
@@ -93,18 +94,9 @@
* If operation is in an invalid state (was aborted or had an error) update() must return
* ErrorCode::INVALID_OPERATION_HANDLE.
*
- * To provide more flexibility for buffer handling, implementations of this method have the
- * option of consuming less data than was provided. The caller is responsible for looping to
- * feed the rest of the data in subsequent calls. The amount of input consumed must be returned
- * in the inputConsumed parameter. Implementations must always consume at least one byte,
- * unless the operation cannot accept any more; if more than zero bytes are provided and zero
- * bytes are consumed, callers must consider this an error and abort the operation.
- * TODO(seleneh) update the code to always consume alll the input data. b/168665179.
- *
- * Implementations may also choose how much data to return, as a result of the update. This is
- * only relevant for encryption and decryption operations, because signing and verification
- * return no data until finish. It is recommended to return data as early as possible, rather
- * than buffer it.
+ * Implementations may choose how much data to return as a result of the update. This is
+ * only relevant for encryption and decryption operations, because signing returns no data
+ * until finish. It is recommended to return data as early as possible, rather than buffer it.
*
* If this method returns an error code other than ErrorCode::OK, the operation is aborted and
* the operation handle must be invalidated. Any future use of the handle, with this method,
@@ -112,8 +104,8 @@
*
* == Authorization Enforcement ==
*
- * Key authorization enforcement is performed primarily in begin(). The one exception is the
- * case where the key has:
+ * Key authorization enforcement is performed primarily in IKeyMintDevice::begin(). The one
+ * exception is the case where the key has:
*
* o One or more Tag::USER_SECURE_IDs, and
*
@@ -130,7 +122,8 @@
*
* o The key must have a Tag::USER_AUTH_TYPE that matches the auth type in the token.
*
- * o The challenge field in the auth token must contain the operationHandle
+ * o The challenge field in the auth token must contain the challenge value contained in the
+ * BeginResult returned from IKeyMintDevice::begin().
*
* If any of these conditions are not met, update() must return
* ErrorCode::KEY_USER_NOT_AUTHENTICATED.
@@ -139,22 +132,20 @@
*
* -- RSA keys --
*
- * For signing and verification operations with Digest::NONE, this method must accept the entire
- * block to be signed or verified in a single update. It may not consume only a portion of the
- * block in these cases. However, the caller may choose to provide the data in multiple
- * updates, and update() must accept the data this way as well. If the caller provides more
- * data to sign than can be used (length of data exceeds RSA key size), update() must return
- * ErrorCode::INVALID_INPUT_LENGTH.
+ * For signing operations with Digest::NONE, this method must accept the entire block to be
+ * signed in a single update. It may not consume only a portion of the block in these cases.
+ * However, the caller may choose to provide the data in multiple updates, and update() must
+ * accept the data this way as well. If the caller provides more data to sign than can be used
+ * (length of data exceeds RSA key size), update() must return ErrorCode::INVALID_INPUT_LENGTH.
*
* -- ECDSA keys --
*
- * For signing and verification operations with Digest::NONE, this method must accept the entire
- * block to be signed or verified in a single update. This method may not consume only a
- * portion of the block. However, the caller may choose to provide the data in multiple updates
- * and update() must accept the data this way as well. If the caller provides more data to sign
- * than can be used, the data is silently truncated. (This differs from the handling of excess
- * data provided in similar RSA operations. The reason for this is compatibility with legacy
- * clients.)
+ * For signing operations with Digest::NONE, this method must accept the entire block to be
+ * signed in a single update. This method may not consume only a portion of the block.
+ * However, the caller may choose to provide the data in multiple updates and update() must
+ * accept the data this way as well. If the caller provides more data to sign than can be used,
+ * the data is silently truncated. (This differs from the handling of excess data provided in
+ * similar RSA operations. The reason for this is compatibility with legacy clients.)
*
* -- AES keys --
*
@@ -182,7 +173,7 @@
in @nullable TimeStampToken timeStampToken);
/**
- * Finalizes a cryptographic operation begun with begin() and invalidates operation.
+ * Finalizes a cryptographic operation begun with begin() and invalidates the operation.
*
* This method is the last one called in an operation, so all processed data must be returned.
*
@@ -190,8 +181,7 @@
* Any future use of the operation, with finish(), update(), or abort(), must return
* ErrorCode::INVALID_OPERATION_HANDLE.
*
- * Signing operations return the signature as the output. Verification operations accept the
- * signature in the signature parameter, and return no output.
+ * Signing operations return the signature as the output.
*
* == Authorization enforcement ==
*
@@ -230,44 +220,35 @@
* Some additional requirements, depending on the padding mode:
*
* o PaddingMode::NONE. For unpadded signing and encryption operations, if the provided data is
- * shorter than the key, the data must be zero-padded on the left before
- * signing/encryption. If the data is the same length as the key, but numerically larger,
- * finish() must return ErrorCode::INVALID_ARGUMENT. For verification and decryption
- * operations, the data must be exactly as long as the key. Otherwise, return
- * ErrorCode::INVALID_INPUT_LENGTH.
+ * shorter than the key, the data must be zero-padded on the left before signing/encryption.
+ * If the data is the same length as the key, but numerically larger, finish() must return
+ * ErrorCode::INVALID_ARGUMENT. For decryption operations, the data must be exactly as long
+ * as the key. Otherwise, return ErrorCode::INVALID_INPUT_LENGTH.
*
* o PaddingMode::RSA_PSS. For PSS-padded signature operations, the PSS salt length must match
- * the size of the PSS digest selected. The digest specified with Tag::DIGEST in inputParams
+ * the size of the PSS digest selected. The digest specified with Tag::DIGEST in params
* on begin() must be used as the PSS digest algorithm, MGF1 must be used as the mask
* generation function and SHA1 must be used as the MGF1 digest algorithm.
*
- * o PaddingMode::RSA_OAEP. The digest specified with Tag::DIGEST in inputParams on begin is
- * used as the OAEP digest algorithm, MGF1 must be used as the mask generation function and
- * and SHA1 must be used as the MGF1 digest algorithm.
- *
* -- ECDSA keys --
*
- * If the data provided for unpadded signing or verification is too long, truncate it.
+ * If the data provided for undigested signing is too long, truncate it.
*
* -- AES keys --
*
* Some additional conditions, depending on block mode:
*
* o BlockMode::ECB or BlockMode::CBC. If padding is PaddingMode::NONE and the data length is
- * not a multiple of the AES block size, finish() must return
- * ErrorCode::INVALID_INPUT_LENGTH. If padding is PaddingMode::PKCS7, pad the data per the
- * PKCS#7 specification, including adding an additional padding block if the data is a multiple
- * of the block length.
+ * not a multiple of the AES block size, finish() must return
+ * ErrorCode::INVALID_INPUT_LENGTH. If padding is PaddingMode::PKCS7, pad the data per the
+ * PKCS#7 specification, including adding an additional padding block if the data is a
+ * multiple of the block length.
*
* o BlockMode::GCM. During encryption, after processing all plaintext, compute the tag
* (Tag::MAC_LENGTH bytes) and append it to the returned ciphertext. During decryption,
* process the last Tag::MAC_LENGTH bytes as the tag. If tag verification fails, finish()
* must return ErrorCode::VERIFICATION_FAILED.
*
- * TODO: update() will need to be refactored into 2 function. b/168665179.
- *
- * @param inParams Additional parameters for the operation.
- *
* @param input Data to be processed, per the parameters established in the call to begin().
* finish() must consume all provided data or return ErrorCode::INVALID_INPUT_LENGTH.
*
@@ -281,11 +262,9 @@
* token.
*
* @param confirmationToken is the confirmation token required by keys with
- * Tag::TRUSTED_CONFIRMATION_REQUIRED.
+ * Tag::TRUSTED_CONFIRMATION_REQUIRED.
*
* @return The output data, if any.
- *
- * @return outParams Any output parameters generated by finish().
*/
byte[] finish(in @nullable byte[] input, in @nullable byte[] signature,
in @nullable HardwareAuthToken authToken,
@@ -293,13 +272,10 @@
in @nullable byte[] confirmationToken);
/**
- * Aborts a cryptographic operation begun with begin(), freeing all internal resources. If an
- * operation was finalized, calling update, finish, or abort yields
- * ErrorCode::INVALID_OPERATION_HANDLE. An operation is finalized if finish or abort was
- * called on it, or if update returned an ErrorCode.
- *
- * @param operationHandle The operation handle returned by begin(). This handle must be
- * invalid when abort() returns.
+ * Aborts a cryptographic operation begun with IKeyMintDevice::begin(), freeing all internal
+ * resources. If an operation was finalized, calling updateAad, update, finish, or abort yields
+ * ErrorCode::INVALID_OPERATION_HANDLE. An operation is finalized if finish or abort was called
+ * on it, or if updateAad or update returned an ErrorCode.
*
* @return error See the ErrorCode enum in ErrorCode.aidl.
*/
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl b/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
index 1ae6762..04d91d0 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
@@ -77,7 +78,7 @@
* While a proper BCC, as described above, reflects the complete boot sequence from boot ROM to the
* secure area image of the IRemotelyProvisionedComponent, it's also possible to use a "degenerate"
* BCC which consists only of a single, self-signed certificate containing the public key of a
- * hardware-bound key pair. This is an appopriate solution for devices which haven't implemented
+ * hardware-bound key pair. This is an appropriate solution for devices which haven't implemented
* everything necessary to produce a proper BCC, but can derive a unique key pair in the secure
* area. In this degenerate case, DK_pub is the same as KM_pub.
*
@@ -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 c2e21b6..f93dbba 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
@@ -58,7 +58,7 @@
* There are a few variations in what is contained in `certificateChain`, depending on whether
* the caller requested attestation, whether they provided an attestation key (via the
* `attestationKey` parameter of `generateKey()`, `importKey()` or `importWrappedKey()`), and in
- * the non-attestaion case, whether the key can self-sign.
+ * the non-attestation case, whether the key can self-sign.
*
* 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
@@ -66,10 +66,11 @@
* 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.
+ * returned. KeyMint implementations are not required to support factory-provisioned
+ * attestation keys.
*
* 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
+ * provided and the `attestationKey` parameter on the generate/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::
@@ -90,6 +91,110 @@
* 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.
+ *
+ * In all cases except the symmetric key, the contents of certificate chain must be DER-encoded
+ * X.509 certificates ordered such that each certificate is signed by the subsequent one, up to
+ * the root which must be self-signed (or contain a fake signature in the case of case 4 above).
+ * The first certificate in the chain signs the public key info of the newly-generated or
+ * newly-imported key pair. In the attestation cases (1 and 2 above), the first certificate
+ * must also satisfy some other requirements:
+ *
+ * o It must have the serial number provided in Tag::CERTIFICATE_SERIAL, or default to 1 if the
+ * tag is not provided.
+ *
+ * o It must have the subject provided in Tag::CERTIFICATE_SUBJECT, or default to CN="Android
+ * Keystore Key", if the tag is not provided.
+ *
+ * o It must contain the notBefore and notAfter date-times specified in
+ * Tag::CERTIFICATE_NOT_BEFORE and Tag::CERTIFICATE_NOT_AFTER, respectively.
+ *
+ * o It must contain a Key Usage extension with:
+ *
+ * - the digitalSignature bit set iff the attested key has KeyPurpose::SIGN,
+ * - the dataEncipherment bit set iff the attested key has KeyPurpose::DECRYPT,
+ * - the keyEncipherment bit set iff the attested key has KeyPurpose::WRAP_KEY,
+ * - the keyAgreement bit set iff the attested key has KeyPurpose::AGREE_KEY, and
+ * - the keyCertSignBit set iff the attested key has KeyPurpose::ATTEST_KEY.
+ *
+ * o it must contain a KeyDescription attestation extension with OID 1.3.6.1.4.1.11129.2.1.17.
+ *
+ * The KeyDescription content is defined by the following ASN.1 schema, which is mostly a
+ * straightforward translation of the KeyMint tag/value parameter lists to ASN.1.
+ *
+ * KeyDescription ::= SEQUENCE {
+ * attestationVersion INTEGER, # Value 100
+ * attestationSecurityLevel SecurityLevel, # See below
+ * keyMintVersion INTEGER, # Value 100
+ * keymintSecurityLevel SecurityLevel, # See below
+ * attestationChallenge OCTET_STRING, # Tag::ATTESTATION_CHALLENGE from attestParams
+ * uniqueId OCTET_STRING, # Empty unless key has Tag::INCLUDE_UNIQUE_ID
+ * softwareEnforced AuthorizationList, # See below
+ * hardwareEnforced AuthorizationList, # See below
+ * }
+ *
+ * SecurityLevel ::= ENUMERATED {
+ * Software (0),
+ * TrustedEnvironment (1),
+ * StrongBox (2),
+ * }
+ *
+ * RootOfTrust ::= SEQUENCE {
+ * verifiedBootKey OCTET_STRING,
+ * deviceLocked BOOLEAN,
+ * verifiedBootState VerifiedBootState,
+ * # verifiedBootHash must contain 32-byte value that represents the state of all binaries
+ * # or other components validated by verified boot. Updating any verified binary or
+ * # component must cause this value to change.
+ * verifiedBootHash OCTET_STRING,
+ * }
+ *
+ * VerifiedBootState ::= ENUMERATED {
+ * Verified (0),
+ * SelfSigned (1),
+ * Unverified (2),
+ * Failed (3),
+ * }
+ *
+ * AuthorizationList ::= SEQUENCE {
+ * purpose [1] EXPLICIT SET OF INTEGER OPTIONAL,
+ * algorithm [2] EXPLICIT INTEGER OPTIONAL,
+ * keySize [3] EXPLICIT INTEGER OPTIONAL,
+ * blockMode [4] EXPLICIT SET OF INTEGER OPTIONAL,
+ * digest [5] EXPLICIT SET OF INTEGER OPTIONAL,
+ * padding [6] EXPLICIT SET OF INTEGER OPTIONAL,
+ * callerNonce [7] EXPLICIT NULL OPTIONAL,
+ * minMacLength [8] EXPLICIT INTEGER OPTIONAL,
+ * ecCurve [10] EXPLICIT INTEGER OPTIONAL,
+ * rsaPublicExponent [200] EXPLICIT INTEGER OPTIONAL,
+ * rollbackResistance [303] EXPLICIT NULL OPTIONAL,
+ * activeDateTime [400] EXPLICIT INTEGER OPTIONAL,
+ * originationExpireDateTime [401] EXPLICIT INTEGER OPTIONAL,
+ * usageExpireDateTime [402] EXPLICIT INTEGER OPTIONAL,
+ * userSecureId [502] EXPLICIT INTEGER OPTIONAL,
+ * noAuthRequired [503] EXPLICIT NULL OPTIONAL,
+ * userAuthType [504] EXPLICIT INTEGER OPTIONAL,
+ * authTimeout [505] EXPLICIT INTEGER OPTIONAL,
+ * allowWhileOnBody [506] EXPLICIT NULL OPTIONAL,
+ * trustedUserPresenceReq [507] EXPLICIT NULL OPTIONAL,
+ * trustedConfirmationReq [508] EXPLICIT NULL OPTIONAL,
+ * unlockedDeviceReq [509] EXPLICIT NULL OPTIONAL,
+ * creationDateTime [701] EXPLICIT INTEGER OPTIONAL,
+ * origin [702] EXPLICIT INTEGER OPTIONAL,
+ * rootOfTrust [704] EXPLICIT RootOfTrust OPTIONAL,
+ * osVersion [705] EXPLICIT INTEGER OPTIONAL,
+ * osPatchLevel [706] EXPLICIT INTEGER OPTIONAL,
+ * attestationApplicationId [709] EXPLICIT OCTET_STRING OPTIONAL,
+ * attestationIdBrand [710] EXPLICIT OCTET_STRING OPTIONAL,
+ * attestationIdDevice [711] EXPLICIT OCTET_STRING OPTIONAL,
+ * attestationIdProduct [712] EXPLICIT OCTET_STRING OPTIONAL,
+ * attestationIdSerial [713] EXPLICIT OCTET_STRING OPTIONAL,
+ * attestationIdImei [714] EXPLICIT OCTET_STRING OPTIONAL,
+ * attestationIdMeid [715] EXPLICIT OCTET_STRING OPTIONAL,
+ * attestationIdManufacturer [716] EXPLICIT OCTET_STRING OPTIONAL,
+ * attestationIdModel [717] EXPLICIT OCTET_STRING OPTIONAL,
+ * vendorPatchLevel [718] EXPLICIT INTEGER OPTIONAL,
+ * bootPatchLevel [719] EXPLICIT INTEGER OPTIONAL,
+ * }
*/
Certificate[] certificateChain;
}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyOrigin.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyOrigin.aidl
index f896125..5840c6b 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyOrigin.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyOrigin.aidl
@@ -20,7 +20,7 @@
* The origin of a key (or pair), i.e. where it was generated. Note that ORIGIN can be found in
* either the hardware-enforced or software-enforced list for a key, indicating whether the key is
* hardware or software-based. Specifically, a key with GENERATED in the hardware-enforced list
- * must be guaranteed never to have existed outide the secure hardware.
+ * must be guaranteed never to have existed outside the secure hardware.
* @hide
*/
@VintfStability
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
index c874fc3..e141e55 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
@@ -23,10 +23,10 @@
@VintfStability
@Backing(type="int")
enum KeyPurpose {
- /* Usable with RSA, EC and AES keys. */
+ /* Usable with RSA, 3DES and AES keys. */
ENCRYPT = 0,
- /* Usable with RSA, EC and AES keys. */
+ /* Usable with RSA, 3DES and AES keys. */
DECRYPT = 1,
/* Usable with RSA, EC and HMAC keys. */
@@ -36,6 +36,7 @@
VERIFY = 3,
/* 4 is reserved */
+
/* Usable with wrapping keys. */
WRAP_KEY = 5,
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/android/hardware/security/keymint/Tag.aidl b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
index cde1fc0..8fbc91a 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
@@ -19,7 +19,7 @@
import android.hardware.security.keymint.TagType;
// TODO(seleneh) : note aidl currently does not support double nested enum definitions such as
-// ROOT_OF_TRUST = TagType:BYTES | 704. So we are forced to write definations as
+// ROOT_OF_TRUST = TagType:BYTES | 704. So we are forced to write definitions as
// ROOT_OF_TRUST = (9 << 28) for now. Will need to flip this back later when aidl support is added.
/**
@@ -59,7 +59,7 @@
ALGORITHM = (1 << 28) /* TagType:ENUM */ | 2,
/**
- * Tag::KEY_SIZE pecifies the size, in bits, of the key, measuring in the normal way for the
+ * Tag::KEY_SIZE specifies the size, in bits, of the key, measuring in the normal way for the
* key's algorithm. For example, for RSA keys, Tag::KEY_SIZE specifies the size of the public
* modulus. For AES keys it specifies the length of the secret key material. For 3DES keys it
* specifies the length of the key material, not counting parity bits (though parity bits must
@@ -75,9 +75,9 @@
* is only relevant to AES and 3DES keys. Possible values are defined by the BlockMode enum.
*
* This tag is repeatable for key generation/import. For AES and 3DES operations the caller
- * must specify a Tag::BLOCK_MODE in the additionalParams argument of begin(). If the mode is
- * missing or the specified mode is not in the modes specified for the key during
- * generation/import, the operation must fail with ErrorCode::INCOMPATIBLE_BLOCK_MODE.
+ * must specify a Tag::BLOCK_MODE in the params argument of begin(). If the mode is missing or
+ * the specified mode is not in the modes specified for the key during generation/import, the
+ * operation must fail with ErrorCode::INCOMPATIBLE_BLOCK_MODE.
*
* Must be hardware-enforced.
*/
@@ -89,9 +89,9 @@
* values are defined by the Digest enum.
*
* This tag is repeatable for key generation/import. For signing and verification operations,
- * the caller must specify a digest in the additionalParams argument of begin(). If the digest
- * is missing or the specified digest is not in the digests associated with the key, the
- * operation must fail with ErrorCode::INCOMPATIBLE_DIGEST.
+ * the caller must specify a digest in the params argument of begin(). If the digest is missing
+ * or the specified digest is not in the digests associated with the key, the operation must
+ * fail with ErrorCode::INCOMPATIBLE_DIGEST.
*
* Must be hardware-enforced.
*/
@@ -145,7 +145,7 @@
* This value is the minimum MAC length, in bits. It must be a multiple of 8 bits. For HMAC
* keys, the value must be least 64 and no more than 512. For GCM keys, the value must be at
* least 96 and no more than 128. If the provided value violates these requirements,
- * generateKey() or importKey() must return ErrorCode::UNSUPPORTED_KEY_SIZE.
+ * generateKey() or importKey() must return ErrorCode::UNSUPPORTED_MIN_MAC_LENGTH.
*
* Must be hardware-enforced.
*/
@@ -154,9 +154,8 @@
// Tag 9 reserved
/**
- * Tag::EC_CURVE specifies the elliptic curve. EC key generation requests may have
- * Tag:EC_CURVE, Tag::KEY_SIZE, or both. If both are provided and the size and curve do not
- * match, IKeyMintDevice must return ErrorCode::INVALID_ARGUMENT.
+ * Tag::EC_CURVE specifies the elliptic curve. Possible values are defined in the EcCurve
+ * enumeration.
*
* Must be hardware-enforced.
*/
@@ -188,37 +187,19 @@
INCLUDE_UNIQUE_ID = (7 << 28) /* TagType:BOOL */ | 202,
/**
- * Tag::RSA_OAEP_MGF_DIGEST specifies the MGF1 digest algorithms that may be used with
- * RSA encryption/decryption with OAEP padding. If the key characteristics supports OAEP
- * and this tag is absent then SHA1 digest is selected by default for MGF1.
+ * Tag::RSA_OAEP_MGF_DIGEST specifies the MGF1 digest algorithms that may be used with RSA
+ * encryption/decryption with OAEP padding. Possible values are defined by the Digest enum.
*
- * This tag is repeatable for key generation/import. If this tag is present in the key
- * characteristics with one or more values from @4.0::Digest, then for RSA cipher
- * operations with OAEP Padding, the caller must specify a digest in the additionalParams
- * argument of begin operation. If this tag is missing or the specified digest is not in
- * the digests associated with the key then begin operation must fail with
- * ErrorCode::INCOMPATIBLE_MGF_DIGEST.
+ * This tag is repeatable for key generation/import. RSA cipher operations with OAEP padding
+ * must specify an MGF1 digest in the params argument of begin(). If this tag is missing or the
+ * specified digest is not in the MGF1 digests associated with the key then begin operation must
+ * fail with ErrorCode::INCOMPATIBLE_MGF_DIGEST.
*
* Must be hardware-enforced.
*/
RSA_OAEP_MGF_DIGEST = (2 << 28) /* TagType:ENUM_REP */ | 203,
- /**
- * TODO(seleneh) this tag needs to be deleted from all codes.
- *
- * Tag::BLOB_USAGE_REQUIREMENTS specifies the necessary system environment conditions for the
- * generated key to be used. Possible values are defined by the KeyBlobUsageRequirements enum.
- *
- * This tag is specified by the caller during key generation or import to require that the key
- * is usable in the specified condition. If the caller specifies Tag::BLOB_USAGE_REQUIREMENTS
- * with value KeyBlobUsageRequirements::STANDALONE the IKeyMintDevice must return a key blob
- * that can be used without file system support. This is critical for devices with encrypted
- * disks, where the file system may not be available until after a KeyMint key is used to
- * decrypt the disk.
- *
- * Must be hardware-enforced.
- */
- BLOB_USAGE_REQUIREMENTS = (1 << 28) /* TagType:ENUM */ | 301,
+ // Tag 301 reserved
/**
* Tag::BOOTLOADER_ONLY specifies only the bootloader can use the key.
@@ -241,7 +222,7 @@
* ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE. IKeyMintDevice implementations are not
* required to support rollback resistance.
*
- * Must be hardwared-enforced.
+ * Must be hardware-enforced.
*/
ROLLBACK_RESISTANCE = (7 << 28) /* TagType:BOOL */ | 303,
@@ -251,7 +232,7 @@
/**
* Keys tagged with EARLY_BOOT_ONLY may only be used during early boot, until
* IKeyMintDevice::earlyBootEnded() is called. Early boot keys may be created after
- * early boot. Early boot keys may not be imprted at all, if Tag::EARLY_BOOT_ONLY is
+ * early boot. Early boot keys may not be imported at all, if Tag::EARLY_BOOT_ONLY is
* provided to IKeyMintDevice::importKey, the import must fail with
* ErrorCode::INVALID_ARGUMENT.
*/
@@ -307,7 +288,7 @@
* with ErrorCode::KEY_RATE_LIMIT_EXCEEDED. This implies that the IKeyMintDevice must keep a
* table of use counters for keys with this tag. Because memory is often limited, this table
* may have a fixed maximum size and KeyMint may fail operations that attempt to use keys with
- * this tag when the table is full. The table must acommodate at least 8 in-use keys and
+ * this tag when the table is full. The table must accommodate at least 8 in-use keys and
* aggressively reuse table slots when key minimum-usage intervals expire. If an operation
* fails because the table is full, KeyMint returns ErrorCode::TOO_MANY_OPERATIONS.
*
@@ -327,7 +308,7 @@
* device is restarted. This implies that the IKeyMintDevice must keep a table of use
* counters for keys with this tag. Because KeyMint memory is often limited, this table can
* have a fixed maximum size and KeyMint can fail operations that attempt to use keys with
- * this tag when the table is full. The table needs to acommodate at least 8 keys. If an
+ * this tag when the table is full. The table needs to accommodate at least 8 keys. If an
* operation fails because the table is full, IKeyMintDevice must
* ErrorCode::TOO_MANY_OPERATIONS.
*
@@ -386,14 +367,14 @@
* key, and may only be used if the difference between the current time when begin() is called
* and the timestamp in the HardwareAuthToken is less than the value in Tag::AUTH_TIMEOUT * 1000
* (the multiplier is because Tag::AUTH_TIMEOUT is in seconds, but the HardwareAuthToken
- * timestamp is in milliseconds). Otherwise the IKeyMintDevice must returrn
+ * timestamp is in milliseconds). Otherwise the IKeyMintDevice must return
* ErrorCode::KEY_USER_NOT_AUTHENTICATED.
*
* If Tag::AUTH_TIMEOUT is not present, then the key is an "auth-per-operation" key. In this
* case, begin() must not require a HardwareAuthToken with appropriate contents. Instead,
* update() and finish() must receive a HardwareAuthToken with Tag::USER_SECURE_ID value in
* userId or authenticatorId fields, and the current operation's operation handle in the
- * challenge field. Otherwise the IKeyMintDevice must returrn
+ * challenge field. Otherwise the IKeyMintDevice must return
* ErrorCode::KEY_USER_NOT_AUTHENTICATED.
*
* This tag is repeatable. If repeated, and any one of the values matches the HardwareAuthToken
@@ -434,7 +415,7 @@
/**
* Tag::AUTH_TIMEOUT specifies the time in seconds for which the key is authorized for use,
* after user authentication. If
- * Tag::USER_SECURE_ID is present and this tag is not, then the key requies authentication for
+ * Tag::USER_SECURE_ID is present and this tag is not, then the key requires authentication for
* every usage (see begin() for the details of the authentication-per-operation flow).
*
* The value is a 32-bit integer specifying the time in seconds after a successful
@@ -504,7 +485,7 @@
* Tag::TRUSTED_CONFIRMATION_REQUIRED is only applicable to keys with KeyPurpose SIGN, and
* specifies that this key must not be usable unless the user provides confirmation of the data
* to be signed. Confirmation is proven to keyMint via an approval token. See
- * CONFIRMATION_TOKEN, as well as the ConfirmatinUI HAL.
+ * CONFIRMATION_TOKEN, as well as the ConfirmationUI HAL.
*
* If an attempt to use a key with this tag does not have a cryptographically valid
* CONFIRMATION_TOKEN provided to finish() or if the data provided to update()/finish() does not
@@ -526,7 +507,7 @@
* Tag::APPLICATION_ID. When provided to generateKey or importKey, this tag specifies data
* that is necessary during all uses of the key. In particular, calls to exportKey() and
* getKeyCharacteristics() must provide the same value to the clientId parameter, and calls to
- * begin must provide this tag and the same associated data as part of the inParams set. If
+ * begin() must provide this tag and the same associated data as part of the inParams set. If
* the correct data is not provided, the method must return ErrorCode::INVALID_KEY_BLOB.
*
* The content of this tag must be bound to the key cryptographically, meaning it must not be
@@ -550,7 +531,7 @@
* provide this tag and the same associated data as part of the inParams set. If the correct
* data is not provided, the method must return ErrorCode::INVALID_KEY_BLOB.
*
- * The content of this tag msut be bound to the key cryptographically, meaning it must not be
+ * The content of this tag must be bound to the key cryptographically, meaning it must not be
* possible for an adversary who has access to all of the secure world secrets but does not have
* access to the tag content to decrypt the key without brute-forcing the tag content, which
* applications can prevent by specifying sufficiently high-entropy content.
@@ -561,10 +542,9 @@
/**
* Tag::CREATION_DATETIME specifies the date and time the key was created, in milliseconds since
- * January 1, 1970. This tag is optional and informational only.
+ * January 1, 1970. This tag is optional and informational only, and not enforced by anything.
*
- * Tag::CREATED is informational only, and not enforced by anything. Must be in the
- * software-enforced list, if provided.
+ * Must be in the software-enforced list, if provided.
*/
CREATION_DATETIME = (6 << 28) /* TagType:DATE */ | 701,
@@ -650,10 +630,11 @@
* Tag::CREATION_DATETIME by 2592000000, dropping any remainder. T changes every 30 days
* (2592000000 = 30 * 24 * 60 * 60 * 1000).
*
- * C is the value of Tag::ATTESTATION_APPLICATION_ID that is provided to attestKey().
+ * C is the value of Tag::ATTESTATION_APPLICATION_ID that is provided to attested key
+ * generation/import operations.
*
- * R is 1 if Tag::RESET_SINCE_ID_ROTATION was provided to attestKey or 0 if the tag was not
- * provided.
+ * R is 1 if Tag::RESET_SINCE_ID_ROTATION was provided to attested key generation/import or 0
+ * if the tag was not provided.
*
* HBK is a unique hardware-bound secret known to the secure environment and never revealed
* by it. The secret must contain at least 128 bits of entropy and be unique to the
@@ -668,9 +649,9 @@
UNIQUE_ID = (9 << 28) /* TagType:BYTES */ | 707,
/**
- * Tag::ATTESTATION_CHALLENGE is used to deliver a "challenge" value to the attestKey() method,
- * which must place the value in the KeyDescription SEQUENCE of the attestation extension. See
- * attestKey().
+ * Tag::ATTESTATION_CHALLENGE is used to deliver a "challenge" value to the attested key
+ * generation/import methods, which must place the value in the KeyDescription SEQUENCE of the
+ * attestation extension.
*
* Must never appear in KeyCharacteristics.
*/
@@ -678,7 +659,7 @@
/**
* Tag::ATTESTATION_APPLICATION_ID identifies the set of applications which may use a key, used
- * only with attestKey().
+ * only with attested key generation/import operations.
*
* The content of Tag::ATTESTATION_APPLICATION_ID is a DER-encoded ASN.1 structure, with the
* following schema:
@@ -704,8 +685,8 @@
/**
* Tag::ATTESTATION_ID_BRAND provides the device's brand name, as returned by Build.BRAND in
- * Android, to attestKey(). This field must be set only when requesting attestation of the
- * device's identifiers.
+ * Android, to attested key generation/import operations. This field must be set only when
+ * requesting attestation of the device's identifiers.
*
* If the device does not support ID attestation (or destroyAttestationIds() was previously
* called and the device can no longer attest its IDs), any key attestation request that
@@ -717,8 +698,8 @@
/**
* Tag::ATTESTATION_ID_DEVICE provides the device's device name, as returned by Build.DEVICE in
- * Android, to attestKey(). This field must be set only when requesting attestation of the
- * device's identifiers.
+ * Android, to attested key generation/import operations. This field must be set only when
+ * requesting attestation of the device's identifiers.
*
* If the device does not support ID attestation (or destroyAttestationIds() was previously
* called and the device can no longer attest its IDs), any key attestation request that
@@ -730,8 +711,8 @@
/**
* Tag::ATTESTATION_ID_PRODUCT provides the device's product name, as returned by Build.PRODUCT
- * in Android, to attestKey(). This field must be set only when requesting attestation of the
- * device's identifiers.
+ * in Android, to attested key generation/import operations. This field must be set only when
+ * requesting attestation of the device's identifiers.
*
* If the device does not support ID attestation (or destroyAttestationIds() was previously
* called and the device can no longer attest its IDs), any key attestation request that
@@ -754,8 +735,9 @@
ATTESTATION_ID_SERIAL = (9 << 28) /* TagType:BYTES */ | 713,
/**
- * Tag::ATTESTATION_ID_IMEI provides the IMEIs for all radios on the device to attestKey().
- * This field must be set only when requesting attestation of the device's identifiers.
+ * Tag::ATTESTATION_ID_IMEI provides the IMEIs for all radios on the device to attested key
+ * generation/import operations. This field must be set only when requesting attestation of the
+ * device's identifiers.
*
* If the device does not support ID attestation (or destroyAttestationIds() was previously
* called and the device can no longer attest its IDs), any key attestation request that
@@ -766,8 +748,9 @@
ATTESTATION_ID_IMEI = (9 << 28) /* TagType:BYTES */ | 714,
/**
- * Tag::ATTESTATION_ID_MEID provides the MEIDs for all radios on the device to attestKey().
- * This field must be set only when requesting attestation of the device's identifiers.
+ * Tag::ATTESTATION_ID_MEID provides the MEIDs for all radios on the device to attested key
+ * generation/import operations. This field must be set only when requesting attestation of the
+ * device's identifiers.
*
* If the device does not support ID attestation (or destroyAttestationIds() was previously
* called and the device can no longer attest its IDs), any key attestation request that
@@ -779,8 +762,8 @@
/**
* Tag::ATTESTATION_ID_MANUFACTURER provides the device's manufacturer name, as returned by
- * Build.MANUFACTURER in Android, to attstKey(). This field must be set only when requesting
- * attestation of the device's identifiers.
+ * Build.MANUFACTURER in Android, to attested key generation/import operations. This field must
+ * be set only when requesting attestation of the device's identifiers.
*
* If the device does not support ID attestation (or destroyAttestationIds() was previously
* called and the device can no longer attest its IDs), any key attestation request that
@@ -792,8 +775,8 @@
/**
* Tag::ATTESTATION_ID_MODEL provides the device's model name, as returned by Build.MODEL in
- * Android, to attestKey(). This field must be set only when requesting attestation of the
- * device's identifiers.
+ * Android, to attested key generation/import operations. This field must be set only when
+ * requesting attestation of the device's identifiers.
*
* If the device does not support ID attestation (or destroyAttestationIds() was previously
* called and the device can no longer attest its IDs), any key attestation request that
@@ -839,20 +822,20 @@
* the value would be 20180605. If the day is not known, 00 may be substituted.
*
* During each boot, the bootloader must provide the patch level of the boot image to the secure
- * envirionment (mechanism is implementation-defined).
+ * environment (mechanism is implementation-defined).
*
* Must be hardware-enforced.
*/
BOOT_PATCHLEVEL = (3 << 28) /* TagType:UINT */ | 719,
/**
- * DEVICE_UNIQUE_ATTESTATION is an argument to IKeyMintDevice::attestKey(). It indicates that
- * attestation using a device-unique key is requested, rather than a batch key. When a
- * device-unique key is used, only the attestation certificate is returned; no additional
- * chained certificates are provided. It's up to the caller to recognize the device-unique
- * signing key. Only SecurityLevel::STRONGBOX IKeyMintDevices may support device-unique
- * attestations. SecurityLevel::TRUSTED_ENVIRONMENT IKeyMintDevices must return
- * ErrorCode::INVALID_ARGUMENT if they receive DEVICE_UNIQUE_ATTESTATION.
+ * DEVICE_UNIQUE_ATTESTATION is an argument to IKeyMintDevice::attested key generation/import
+ * operations. It indicates that attestation using a device-unique key is requested, rather
+ * than a batch key. When a device-unique key is used, only the attestation certificate is
+ * returned; no additional chained certificates are provided. It's up to the caller to
+ * recognize the device-unique signing key. Only SecurityLevel::STRONGBOX IKeyMintDevices may
+ * support device-unique attestations. SecurityLevel::TRUSTED_ENVIRONMENT IKeyMintDevices must
+ * return ErrorCode::INVALID_ARGUMENT if they receive DEVICE_UNIQUE_ATTESTATION.
* SecurityLevel::STRONGBOX IKeyMintDevices need not support DEVICE_UNIQUE_ATTESTATION, and
* return ErrorCode::CANNOT_ATTEST_IDS if they do not support it.
*
@@ -935,33 +918,34 @@
CONFIRMATION_TOKEN = (9 << 28) /* TagType:BYTES */ | 1005,
/**
- * Tag::CERTIFICATE_SERIAL specifies the serial number to be assigned to the
- * attestation certificate to be generated for the given key. This parameter should only
- * be passed to keyMint in the attestation parameters during generateKey() and importKey().
+ * Tag::CERTIFICATE_SERIAL specifies the serial number to be assigned to the attestation
+ * certificate to be generated for the given key. This parameter should only be passed to
+ * keyMint in the attestation parameters during generateKey() and importKey(). If not provided,
+ * the serial shall default to 1.
*/
CERTIFICATE_SERIAL = (8 << 28) /* TagType:BIGNUM */ | 1006,
/**
- * Tag::CERTIFICATE_SUBJECT the certificate subject. The value is a DER encoded X509 NAME.
- * This value is used when generating a self signed certificates. This tag may be specified
+ * Tag::CERTIFICATE_SUBJECT the certificate subject. The value is a DER encoded X509 NAME.
+ * This value is used when generating a self signed certificates. This tag may be specified
* during generateKey and importKey. If not provided the subject name shall default to
- * <TODO default subject here>.
+ * CN="Android Keystore Key".
*/
CERTIFICATE_SUBJECT = (9 << 28) /* TagType:BYTES */ | 1007,
/**
* Tag::CERTIFICATE_NOT_BEFORE the beginning of the validity of the certificate in UNIX epoch
- * time in seconds. This value is used when generating attestation or self signed certificates.
- * ErrorCode::MISSING_NOT_BEFORE must be returned if this tag is not provided if this tag is
- * not provided to generateKey or importKey.
+ * time in seconds. This value is used when generating attestation or self signed certificates.
+ * ErrorCode::MISSING_NOT_BEFORE must be returned if this tag is not provided if this tag is not
+ * provided to generateKey or importKey.
*/
CERTIFICATE_NOT_BEFORE = (6 << 28) /* TagType:DATE */ | 1008,
/**
- * Tag::CERTIFICATE_NOT_AFTER the end of the validity of the certificate in UNIX epoch
- * time in seconds. This value is used when generating attestation or self signed certificates.
- * ErrorCode::MISSING_NOT_AFTER must be returned if this tag is not provided to generateKey
- * or importKey.
+ * Tag::CERTIFICATE_NOT_AFTER the end of the validity of the certificate in UNIX epoch time in
+ * seconds. This value is used when generating attestation or self signed certificates.
+ * ErrorCode::MISSING_NOT_AFTER must be returned if this tag is not provided to generateKey or
+ * importKey.
*/
CERTIFICATE_NOT_AFTER = (6 << 28) /* TagType:DATE */ | 1009,
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 6c7309e..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,7 +44,7 @@
"android.hardware.security.keymint-V1-ndk_platform",
"android.hardware.security.secureclock-V1-ndk_platform",
"libcppbor_external",
- "libcppcose",
+ "libcppcose_rkp",
"libkeymint_remote_prov_support",
"libkeymint_vts_test_utils",
],
@@ -75,7 +76,7 @@
"android.hardware.security.keymint-V1-ndk_platform",
"android.hardware.security.secureclock-V1-ndk_platform",
"libcppbor_external",
- "libcppcose",
+ "libcppcose_rkp",
"libgmock_ndk",
"libkeymint_remote_prov_support",
],
@@ -92,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 daa3e18..881354d 100644
--- a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
+++ b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
@@ -35,10 +35,16 @@
using AttestKeyTest = KeyMintAidlTestBase;
+/*
+ * AttestKeyTest.AllRsaSizes
+ *
+ * This test creates self signed RSA attestation keys of various sizes, and verify they can be
+ * used to sign other RSA and EC keys.
+ */
TEST_P(AttestKeyTest, AllRsaSizes) {
for (auto size : ValidKeySizes(Algorithm::RSA)) {
/*
- * Create attestaton key.
+ * Create attestation key.
*/
AttestationKey attest_key;
vector<KeyCharacteristics> attest_key_characteristics;
@@ -50,11 +56,12 @@
{} /* attestation signing key */, &attest_key.keyBlob,
&attest_key_characteristics, &attest_key_cert_chain));
+ ASSERT_GT(attest_key_cert_chain.size(), 0);
EXPECT_EQ(attest_key_cert_chain.size(), 1);
EXPECT_TRUE(IsSelfSigned(attest_key_cert_chain)) << "Failed on size " << size;
/*
- * Use attestation key to sign RSA key
+ * Use attestation key to sign RSA signing key
*/
attest_key.issuerSubjectName = make_name_from_str("Android Keystore Key");
vector<uint8_t> attested_key_blob;
@@ -81,20 +88,55 @@
EXPECT_FALSE(ChainSignaturesAreValid(attested_key_cert_chain));
// Appending the attest_key chain to the attested_key_chain should yield a valid chain.
- if (attest_key_cert_chain.size() > 0) {
- attested_key_cert_chain.push_back(attest_key_cert_chain[0]);
- }
+ attested_key_cert_chain.push_back(attest_key_cert_chain[0]);
EXPECT_TRUE(ChainSignaturesAreValid(attested_key_cert_chain));
+ EXPECT_EQ(attested_key_cert_chain.size(), 2);
/*
- * Use attestation key to sign EC key
+ * Use attestation key to sign RSA decryption key
*/
+ attested_key_characteristics.resize(0);
+ attested_key_cert_chain.resize(0);
+ EXPECT_EQ(ErrorCode::OK,
+ GenerateKey(AuthorizationSetBuilder()
+ .RsaEncryptionKey(2048, 65537)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AttestationChallenge("foo2")
+ .AttestationApplicationId("bar2")
+ .SetDefaultValidity(),
+ attest_key, &attested_key_blob, &attested_key_characteristics,
+ &attested_key_cert_chain));
+
+ CheckedDeleteKey(&attested_key_blob);
+
+ hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
+ sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+ EXPECT_TRUE(verify_attestation_record("foo2", "bar2", sw_enforced, hw_enforced, SecLevel(),
+ attested_key_cert_chain[0].encodedCertificate));
+
+ // Attestation by itself is not valid (last entry is not self-signed).
+ EXPECT_FALSE(ChainSignaturesAreValid(attested_key_cert_chain));
+
+ // Appending the attest_key chain to the attested_key_chain should yield a valid chain.
+ attested_key_cert_chain.push_back(attest_key_cert_chain[0]);
+ EXPECT_TRUE(ChainSignaturesAreValid(attested_key_cert_chain));
+ EXPECT_EQ(attested_key_cert_chain.size(), 2);
+
+ /*
+ * Use attestation key to sign EC key. Specify a CREATION_DATETIME for this one.
+ */
+ attested_key_characteristics.resize(0);
+ attested_key_cert_chain.resize(0);
+ uint64_t timestamp = 1619621648000;
EXPECT_EQ(ErrorCode::OK,
GenerateKey(AuthorizationSetBuilder()
.EcdsaSigningKey(EcCurve::P_256)
.Authorization(TAG_NO_AUTH_REQUIRED)
.AttestationChallenge("foo")
.AttestationApplicationId("bar")
+ .Authorization(TAG_CREATION_DATETIME, timestamp)
.SetDefaultValidity(),
attest_key, &attested_key_blob, &attested_key_characteristics,
&attested_key_cert_chain));
@@ -104,6 +146,12 @@
hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+ // The client-specified CREATION_DATETIME should be in sw_enforced.
+ // Its presence will also trigger verify_attestation_record() to check that it
+ // is in the attestation extension with a matching value.
+ EXPECT_TRUE(sw_enforced.Contains(TAG_CREATION_DATETIME, timestamp))
+ << "expected CREATION_TIMESTAMP in sw_enforced:" << sw_enforced
+ << " not in hw_enforced:" << hw_enforced;
EXPECT_TRUE(verify_attestation_record("foo", "bar", sw_enforced, hw_enforced, SecLevel(),
attested_key_cert_chain[0].encodedCertificate));
@@ -111,9 +159,7 @@
EXPECT_FALSE(ChainSignaturesAreValid(attested_key_cert_chain));
// Appending the attest_key chain to the attested_key_chain should yield a valid chain.
- if (attest_key_cert_chain.size() > 0) {
- attested_key_cert_chain.push_back(attest_key_cert_chain[0]);
- }
+ attested_key_cert_chain.push_back(attest_key_cert_chain[0]);
EXPECT_TRUE(ChainSignaturesAreValid(attested_key_cert_chain));
// Bail early if anything failed.
@@ -121,10 +167,378 @@
}
}
+/*
+ * AttestKeyTest.RsaAttestedAttestKeys
+ *
+ * This test creates an RSA attestation key signed by factory keys, and varifies it can be
+ * used to sign other RSA and EC keys.
+ */
+TEST_P(AttestKeyTest, RsaAttestedAttestKeys) {
+ auto challenge = "hello";
+ auto app_id = "foo";
+
+ auto subject = "cert subj 2";
+ vector<uint8_t> subject_der(make_name_from_str(subject));
+
+ uint64_t serial_int = 66;
+ vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+ /*
+ * Create attestation key.
+ */
+ AttestationKey attest_key;
+ vector<KeyCharacteristics> attest_key_characteristics;
+ vector<Certificate> attest_key_cert_chain;
+ ASSERT_EQ(ErrorCode::OK,
+ GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(2048, 65537)
+ .AttestKey()
+ .AttestationChallenge(challenge)
+ .AttestationApplicationId(app_id)
+ .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+ .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .SetDefaultValidity(),
+ {} /* attestation signing key */, &attest_key.keyBlob,
+ &attest_key_characteristics, &attest_key_cert_chain));
+
+ EXPECT_GT(attest_key_cert_chain.size(), 1);
+ verify_subject_and_serial(attest_key_cert_chain[0], serial_int, subject, false);
+ EXPECT_TRUE(ChainSignaturesAreValid(attest_key_cert_chain));
+
+ AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attest_key_characteristics);
+ AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attest_key_characteristics);
+ EXPECT_TRUE(verify_attestation_record(challenge, app_id, //
+ sw_enforced, hw_enforced, SecLevel(),
+ attest_key_cert_chain[0].encodedCertificate));
+
+ /*
+ * Use attestation key to sign RSA key
+ */
+ attest_key.issuerSubjectName = subject_der;
+ vector<uint8_t> attested_key_blob;
+ vector<KeyCharacteristics> attested_key_characteristics;
+ vector<Certificate> attested_key_cert_chain;
+
+ auto subject2 = "cert subject";
+ vector<uint8_t> subject_der2(make_name_from_str(subject2));
+
+ uint64_t serial_int2 = 987;
+ vector<uint8_t> serial_blob2(build_serial_blob(serial_int2));
+
+ EXPECT_EQ(ErrorCode::OK,
+ GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(2048, 65537)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AttestationChallenge("foo")
+ .AttestationApplicationId("bar")
+ .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob2)
+ .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der2)
+ .SetDefaultValidity(),
+ attest_key, &attested_key_blob, &attested_key_characteristics,
+ &attested_key_cert_chain));
+
+ CheckedDeleteKey(&attested_key_blob);
+ CheckedDeleteKey(&attest_key.keyBlob);
+
+ AuthorizationSet hw_enforced2 = HwEnforcedAuthorizations(attested_key_characteristics);
+ AuthorizationSet sw_enforced2 = SwEnforcedAuthorizations(attested_key_characteristics);
+ EXPECT_TRUE(verify_attestation_record("foo", "bar", sw_enforced2, hw_enforced2, SecLevel(),
+ attested_key_cert_chain[0].encodedCertificate));
+
+ // Attestation by itself is not valid (last entry is not self-signed).
+ EXPECT_FALSE(ChainSignaturesAreValid(attested_key_cert_chain));
+
+ // Appending the attest_key chain to the attested_key_chain should yield a valid chain.
+ attested_key_cert_chain.insert(attested_key_cert_chain.end(), attest_key_cert_chain.begin(),
+ attest_key_cert_chain.end());
+
+ EXPECT_TRUE(ChainSignaturesAreValid(attested_key_cert_chain));
+ EXPECT_GT(attested_key_cert_chain.size(), 2);
+ verify_subject_and_serial(attested_key_cert_chain[0], serial_int2, subject2, false);
+}
+
+/*
+ * AttestKeyTest.RsaAttestKeyChaining
+ *
+ * This test creates a chain of multiple RSA attest keys, each used to sign the next attest key,
+ * with the last attest key signed by the factory chain.
+ */
+TEST_P(AttestKeyTest, RsaAttestKeyChaining) {
+ const int chain_size = 6;
+ vector<vector<uint8_t>> key_blob_list(chain_size);
+ vector<vector<Certificate>> cert_chain_list(chain_size);
+
+ for (int i = 0; i < chain_size; i++) {
+ string sub = "attest key chaining ";
+ char index = '1' + i;
+ string subject = sub + index;
+ vector<uint8_t> subject_der(make_name_from_str(subject));
+
+ uint64_t serial_int = 7000 + i;
+ vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+ vector<KeyCharacteristics> attested_key_characteristics;
+ AttestationKey attest_key;
+ optional<AttestationKey> attest_key_opt;
+
+ if (i > 0) {
+ attest_key.issuerSubjectName = make_name_from_str(sub + (char)(index - 1));
+ attest_key.keyBlob = key_blob_list[i - 1];
+ attest_key_opt = attest_key;
+ }
+
+ EXPECT_EQ(ErrorCode::OK,
+ GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(2048, 65537)
+ .AttestKey()
+ .AttestationChallenge("foo")
+ .AttestationApplicationId("bar")
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+ .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+ .SetDefaultValidity(),
+ attest_key_opt, &key_blob_list[i], &attested_key_characteristics,
+ &cert_chain_list[i]));
+
+ AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
+ AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+ EXPECT_TRUE(verify_attestation_record("foo", "bar", sw_enforced, hw_enforced, SecLevel(),
+ cert_chain_list[i][0].encodedCertificate));
+
+ if (i > 0) {
+ /*
+ * The first key is attestated with factory chain, but all the rest of the keys are
+ * not supposed to be returned in attestation certificate chains.
+ */
+ EXPECT_FALSE(ChainSignaturesAreValid(cert_chain_list[i]));
+
+ // Appending the attest_key chain to the attested_key_chain should yield a valid chain.
+ cert_chain_list[i].insert(cert_chain_list[i].end(), //
+ cert_chain_list[i - 1].begin(), //
+ cert_chain_list[i - 1].end());
+ }
+
+ EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_list[i]));
+ EXPECT_GT(cert_chain_list[i].size(), i + 1);
+ verify_subject_and_serial(cert_chain_list[i][0], serial_int, subject, false);
+ }
+
+ for (int i = 0; i < chain_size; i++) {
+ CheckedDeleteKey(&key_blob_list[i]);
+ }
+}
+
+/*
+ * AttestKeyTest.EcAttestKeyChaining
+ *
+ * This test creates a chain of multiple Ec attest keys, each used to sign the next attest key,
+ * with the last attest key signed by the factory chain.
+ */
+TEST_P(AttestKeyTest, EcAttestKeyChaining) {
+ const int chain_size = 6;
+ vector<vector<uint8_t>> key_blob_list(chain_size);
+ vector<vector<Certificate>> cert_chain_list(chain_size);
+
+ for (int i = 0; i < chain_size; i++) {
+ string sub = "Ec attest key chaining ";
+ char index = '1' + i;
+ string subject = sub + index;
+ vector<uint8_t> subject_der(make_name_from_str(subject));
+
+ uint64_t serial_int = 800000 + i;
+ vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+ vector<KeyCharacteristics> attested_key_characteristics;
+ AttestationKey attest_key;
+ optional<AttestationKey> attest_key_opt;
+
+ if (i > 0) {
+ attest_key.issuerSubjectName = make_name_from_str(sub + (char)(index - 1));
+ attest_key.keyBlob = key_blob_list[i - 1];
+ attest_key_opt = attest_key;
+ }
+
+ EXPECT_EQ(ErrorCode::OK,
+ GenerateKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(224)
+ .AttestKey()
+ .AttestationChallenge("foo")
+ .AttestationApplicationId("bar")
+ .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+ .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .SetDefaultValidity(),
+ attest_key_opt, &key_blob_list[i], &attested_key_characteristics,
+ &cert_chain_list[i]));
+
+ AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
+ AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+ EXPECT_TRUE(verify_attestation_record("foo", "bar", sw_enforced, hw_enforced, SecLevel(),
+ cert_chain_list[i][0].encodedCertificate));
+
+ if (i > 0) {
+ /*
+ * The first key is attestated with factory chain, but all the rest of the keys are
+ * not supposed to be returned in attestation certificate chains.
+ */
+ EXPECT_FALSE(ChainSignaturesAreValid(cert_chain_list[i]));
+
+ // Appending the attest_key chain to the attested_key_chain should yield a valid chain.
+ cert_chain_list[i].insert(cert_chain_list[i].end(), //
+ cert_chain_list[i - 1].begin(), //
+ cert_chain_list[i - 1].end());
+ }
+
+ EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_list[i]));
+ EXPECT_GT(cert_chain_list[i].size(), i + 1);
+ verify_subject_and_serial(cert_chain_list[i][0], serial_int, subject, false);
+ }
+
+ for (int i = 0; i < chain_size; i++) {
+ CheckedDeleteKey(&key_blob_list[i]);
+ }
+}
+
+/*
+ * AttestKeyTest.AlternateAttestKeyChaining
+ *
+ * This test creates a chain of multiple attest keys, in the order Ec - RSA - Ec - RSA ....
+ * Each attest key is used to sign the next attest key, with the last attest key signed by
+ * the factory chain. This is to verify different algorithms of attest keys can
+ * cross sign each other and be chained together.
+ */
+TEST_P(AttestKeyTest, AlternateAttestKeyChaining) {
+ const int chain_size = 6;
+ vector<vector<uint8_t>> key_blob_list(chain_size);
+ vector<vector<Certificate>> cert_chain_list(chain_size);
+
+ for (int i = 0; i < chain_size; i++) {
+ string sub = "Alt attest key chaining ";
+ char index = '1' + i;
+ string subject = sub + index;
+ vector<uint8_t> subject_der(make_name_from_str(subject));
+
+ uint64_t serial_int = 90000000 + i;
+ vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+ vector<KeyCharacteristics> attested_key_characteristics;
+ AttestationKey attest_key;
+ optional<AttestationKey> attest_key_opt;
+
+ if (i > 0) {
+ attest_key.issuerSubjectName = make_name_from_str(sub + (char)(index - 1));
+ attest_key.keyBlob = key_blob_list[i - 1];
+ attest_key_opt = attest_key;
+ }
+
+ if ((i & 0x1) == 1) {
+ EXPECT_EQ(ErrorCode::OK,
+ GenerateKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(224)
+ .AttestKey()
+ .AttestationChallenge("foo")
+ .AttestationApplicationId("bar")
+ .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+ .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .SetDefaultValidity(),
+ attest_key_opt, &key_blob_list[i], &attested_key_characteristics,
+ &cert_chain_list[i]));
+ } else {
+ EXPECT_EQ(ErrorCode::OK,
+ GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(2048, 65537)
+ .AttestKey()
+ .AttestationChallenge("foo")
+ .AttestationApplicationId("bar")
+ .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+ .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .SetDefaultValidity(),
+ attest_key_opt, &key_blob_list[i], &attested_key_characteristics,
+ &cert_chain_list[i]));
+ }
+
+ AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
+ AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+ EXPECT_TRUE(verify_attestation_record("foo", "bar", sw_enforced, hw_enforced, SecLevel(),
+ cert_chain_list[i][0].encodedCertificate));
+
+ if (i > 0) {
+ /*
+ * The first key is attestated with factory chain, but all the rest of the keys are
+ * not supposed to be returned in attestation certificate chains.
+ */
+ EXPECT_FALSE(ChainSignaturesAreValid(cert_chain_list[i]));
+
+ // Appending the attest_key chain to the attested_key_chain should yield a valid chain.
+ cert_chain_list[i].insert(cert_chain_list[i].end(), //
+ cert_chain_list[i - 1].begin(), //
+ cert_chain_list[i - 1].end());
+ }
+
+ EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_list[i]));
+ EXPECT_GT(cert_chain_list[i].size(), i + 1);
+ verify_subject_and_serial(cert_chain_list[i][0], serial_int, subject, false);
+ }
+
+ for (int i = 0; i < chain_size; i++) {
+ CheckedDeleteKey(&key_blob_list[i]);
+ }
+}
+
+TEST_P(AttestKeyTest, MissingChallenge) {
+ for (auto size : ValidKeySizes(Algorithm::RSA)) {
+ /*
+ * Create attestation key.
+ */
+ AttestationKey attest_key;
+ vector<KeyCharacteristics> attest_key_characteristics;
+ vector<Certificate> attest_key_cert_chain;
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(size, 65537)
+ .AttestKey()
+ .SetDefaultValidity(),
+ {} /* attestation signing key */, &attest_key.keyBlob,
+ &attest_key_characteristics, &attest_key_cert_chain));
+
+ EXPECT_EQ(attest_key_cert_chain.size(), 1);
+ EXPECT_TRUE(IsSelfSigned(attest_key_cert_chain)) << "Failed on size " << size;
+
+ /*
+ * Use attestation key to sign RSA / ECDSA key but forget to provide a challenge
+ */
+ attest_key.issuerSubjectName = make_name_from_str("Android Keystore Key");
+ vector<uint8_t> attested_key_blob;
+ vector<KeyCharacteristics> attested_key_characteristics;
+ vector<Certificate> attested_key_cert_chain;
+ EXPECT_EQ(ErrorCode::INVALID_ARGUMENT,
+ GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(2048, 65537)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AttestationApplicationId("bar")
+ .SetDefaultValidity(),
+ attest_key, &attested_key_blob, &attested_key_characteristics,
+ &attested_key_cert_chain));
+
+ EXPECT_EQ(ErrorCode::INVALID_ARGUMENT,
+ GenerateKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(EcCurve::P_256)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AttestationApplicationId("bar")
+ .SetDefaultValidity(),
+ attest_key, &attested_key_blob, &attested_key_characteristics,
+ &attested_key_cert_chain));
+
+ CheckedDeleteKey(&attest_key.keyBlob);
+ }
+}
+
TEST_P(AttestKeyTest, AllEcCurves) {
for (auto curve : ValidCurves()) {
/*
- * Create attestaton key.
+ * Create attestation key.
*/
AttestationKey attest_key;
vector<KeyCharacteristics> attest_key_characteristics;
@@ -136,6 +550,7 @@
{} /* attestation siging key */, &attest_key.keyBlob,
&attest_key_characteristics, &attest_key_cert_chain));
+ ASSERT_GT(attest_key_cert_chain.size(), 0);
EXPECT_EQ(attest_key_cert_chain.size(), 1);
EXPECT_TRUE(IsSelfSigned(attest_key_cert_chain)) << "Failed on curve " << curve;
@@ -208,7 +623,7 @@
}
TEST_P(AttestKeyTest, AttestWithNonAttestKey) {
- // Create non-attestaton key.
+ // Create non-attestation key.
AttestationKey non_attest_key;
vector<KeyCharacteristics> non_attest_key_characteristics;
vector<Certificate> non_attest_key_cert_chain;
@@ -219,6 +634,7 @@
{} /* attestation siging key */, &non_attest_key.keyBlob,
&non_attest_key_characteristics, &non_attest_key_cert_chain));
+ ASSERT_GT(non_attest_key_cert_chain.size(), 0);
EXPECT_EQ(non_attest_key_cert_chain.size(), 1);
EXPECT_TRUE(IsSelfSigned(non_attest_key_cert_chain));
diff --git a/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp b/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
new file mode 100644
index 0000000..6f0ee4e
--- /dev/null
+++ b/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
@@ -0,0 +1,191 @@
+/*
+ * 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)
+ .AttestationChallenge("challenge")
+ .AttestationApplicationId("foo")
+ .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION),
+ &key_blob, &key_characteristics);
+
+ ASSERT_EQ(result, ErrorCode::INVALID_ARGUMENT);
+}
+
+/*
+ * 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_EQ(result, ErrorCode::INVALID_ARGUMENT);
+}
+
+/*
+ * 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)
+ .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 3da0484..4789204 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -23,12 +23,12 @@
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <cppbor_parse.h>
-#include <cppcose/cppcose.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>
@@ -119,7 +119,6 @@
// Attestations don't contain everything in key authorization lists, so we need to filter the key
// lists to produce the lists that we expect to match the attestations.
auto kTagsToFilter = {
- Tag::BLOB_USAGE_REQUIREMENTS, //
Tag::CREATION_DATETIME, //
Tag::EC_CURVE,
Tag::HARDWARE_TYPE,
@@ -168,9 +167,11 @@
securityLevel_ = info.securityLevel;
name_.assign(info.keyMintName.begin(), info.keyMintName.end());
author_.assign(info.keyMintAuthorName.begin(), info.keyMintAuthorName.end());
+ timestamp_token_required_ = info.timestampTokenRequired;
os_version_ = getOsVersion();
os_patch_level_ = getOsPatchlevel();
+ vendor_patch_level_ = getVendorPatchlevel();
}
void KeyMintAidlTestBase::SetUp() {
@@ -273,7 +274,8 @@
ErrorCode KeyMintAidlTestBase::ImportWrappedKey(string wrapped_key, string wrapping_key,
const AuthorizationSet& wrapping_key_desc,
string masking_key,
- const AuthorizationSet& unwrapping_params) {
+ const AuthorizationSet& unwrapping_params,
+ int64_t password_sid, int64_t biometric_sid) {
EXPECT_EQ(ErrorCode::OK, ImportKey(wrapping_key_desc, KeyFormat::PKCS8, wrapping_key));
key_characteristics_.clear();
@@ -282,8 +284,7 @@
Status result = keymint_->importWrappedKey(
vector<uint8_t>(wrapped_key.begin(), wrapped_key.end()), key_blob_,
vector<uint8_t>(masking_key.begin(), masking_key.end()),
- unwrapping_params.vector_data(), 0 /* passwordSid */, 0 /* biometricSid */,
- &creationResult);
+ unwrapping_params.vector_data(), password_sid, biometric_sid, &creationResult);
if (result.isOk()) {
EXPECT_PRED2(KeyCharacteristicsBasicallyValid, SecLevel(),
@@ -332,6 +333,11 @@
return GetReturnErrorCode(result);
}
+ErrorCode KeyMintAidlTestBase::DestroyAttestationIds() {
+ Status result = keymint_->destroyAttestationIds();
+ return GetReturnErrorCode(result);
+}
+
void KeyMintAidlTestBase::CheckedDeleteKey(vector<uint8_t>* key_blob, bool keep_key_blob) {
ErrorCode result = DeleteKey(key_blob, keep_key_blob);
EXPECT_TRUE(result == ErrorCode::OK || result == ErrorCode::UNIMPLEMENTED) << result << endl;
@@ -348,7 +354,7 @@
SCOPED_TRACE("Begin");
Status result;
BeginResult out;
- result = keymint_->begin(purpose, key_blob, in_params.vector_data(), HardwareAuthToken(), &out);
+ result = keymint_->begin(purpose, key_blob, in_params.vector_data(), std::nullopt, &out);
if (result.isOk()) {
*out_params = out.params;
@@ -366,7 +372,7 @@
Status result;
BeginResult out;
- result = keymint_->begin(purpose, key_blob, in_params.vector_data(), HardwareAuthToken(), &out);
+ result = keymint_->begin(purpose, key_blob, in_params.vector_data(), std::nullopt, &out);
if (result.isOk()) {
*out_params = out.params;
@@ -654,6 +660,18 @@
return ciphertext;
}
+string KeyMintAidlTestBase::EncryptMessage(const string& message, BlockMode block_mode,
+ PaddingMode padding, uint8_t mac_length_bits) {
+ SCOPED_TRACE("EncryptMessage");
+ auto params = AuthorizationSetBuilder()
+ .BlockMode(block_mode)
+ .Padding(padding)
+ .Authorization(TAG_MAC_LENGTH, mac_length_bits);
+ AuthorizationSet out_params;
+ string ciphertext = EncryptMessage(message, params, &out_params);
+ return ciphertext;
+}
+
string KeyMintAidlTestBase::DecryptMessage(const vector<uint8_t>& key_blob,
const string& ciphertext,
const AuthorizationSet& params) {
@@ -744,6 +762,15 @@
return {224, 384, 521};
case Algorithm::AES:
return {192};
+ case Algorithm::TRIPLE_DES:
+ return {56};
+ default:
+ return {};
+ }
+ } else {
+ switch (algorithm) {
+ case Algorithm::TRIPLE_DES:
+ return {56};
default:
return {};
}
@@ -751,6 +778,68 @@
return {};
}
+vector<BlockMode> KeyMintAidlTestBase::ValidBlockModes(Algorithm algorithm) {
+ switch (algorithm) {
+ case Algorithm::AES:
+ return {
+ BlockMode::CBC,
+ BlockMode::CTR,
+ BlockMode::ECB,
+ BlockMode::GCM,
+ };
+ case Algorithm::TRIPLE_DES:
+ return {
+ BlockMode::CBC,
+ BlockMode::ECB,
+ };
+ default:
+ return {};
+ }
+}
+
+vector<PaddingMode> KeyMintAidlTestBase::ValidPaddingModes(Algorithm algorithm,
+ BlockMode blockMode) {
+ switch (algorithm) {
+ case Algorithm::AES:
+ switch (blockMode) {
+ case BlockMode::CBC:
+ case BlockMode::ECB:
+ return {PaddingMode::NONE, PaddingMode::PKCS7};
+ case BlockMode::CTR:
+ case BlockMode::GCM:
+ return {PaddingMode::NONE};
+ default:
+ return {};
+ };
+ case Algorithm::TRIPLE_DES:
+ switch (blockMode) {
+ case BlockMode::CBC:
+ case BlockMode::ECB:
+ return {PaddingMode::NONE, PaddingMode::PKCS7};
+ default:
+ return {};
+ };
+ default:
+ return {};
+ }
+}
+
+vector<PaddingMode> KeyMintAidlTestBase::InvalidPaddingModes(Algorithm algorithm,
+ BlockMode blockMode) {
+ switch (algorithm) {
+ case Algorithm::AES:
+ switch (blockMode) {
+ case BlockMode::CTR:
+ case BlockMode::GCM:
+ return {PaddingMode::PKCS7};
+ default:
+ return {};
+ };
+ default:
+ return {};
+ }
+}
+
vector<EcCurve> KeyMintAidlTestBase::ValidCurves() {
if (securityLevel_ == SecurityLevel::STRONGBOX) {
return {EcCurve::P_256};
@@ -845,6 +934,66 @@
return result;
}
+void verify_serial(X509* cert, const uint64_t expected_serial) {
+ BIGNUM_Ptr ser(BN_new());
+ EXPECT_TRUE(ASN1_INTEGER_to_BN(X509_get_serialNumber(cert), ser.get()));
+
+ uint64_t serial;
+ EXPECT_TRUE(BN_get_u64(ser.get(), &serial));
+ EXPECT_EQ(serial, expected_serial);
+}
+
+// Please set self_signed to true for fake certificates or self signed
+// certificates
+void verify_subject(const X509* cert, //
+ const string& subject, //
+ bool self_signed) {
+ char* cert_issuer = //
+ X509_NAME_oneline(X509_get_issuer_name(cert), nullptr, 0);
+
+ char* cert_subj = X509_NAME_oneline(X509_get_subject_name(cert), nullptr, 0);
+
+ string expected_subject("/CN=");
+ if (subject.empty()) {
+ expected_subject.append("Android Keystore Key");
+ } else {
+ expected_subject.append(subject);
+ }
+
+ EXPECT_STREQ(expected_subject.c_str(), cert_subj) << "Cert has wrong subject." << cert_subj;
+
+ if (self_signed) {
+ EXPECT_STREQ(cert_issuer, cert_subj)
+ << "Cert issuer and subject mismatch for self signed certificate.";
+ }
+
+ OPENSSL_free(cert_subj);
+ OPENSSL_free(cert_issuer);
+}
+
+vector<uint8_t> build_serial_blob(const uint64_t serial_int) {
+ BIGNUM_Ptr serial(BN_new());
+ EXPECT_TRUE(BN_set_u64(serial.get(), serial_int));
+
+ int len = BN_num_bytes(serial.get());
+ vector<uint8_t> serial_blob(len);
+ if (BN_bn2bin(serial.get(), serial_blob.data()) != len) {
+ return {};
+ }
+
+ return serial_blob;
+}
+
+void verify_subject_and_serial(const Certificate& certificate, //
+ const uint64_t expected_serial, //
+ const string& subject, bool self_signed) {
+ X509_Ptr cert(parse_cert_blob(certificate.encodedCertificate));
+ ASSERT_TRUE(!!cert.get());
+
+ verify_serial(cert.get(), expected_serial);
+ verify_subject(cert.get(), subject, self_signed);
+}
+
bool verify_attestation_record(const string& challenge, //
const string& app_id, //
AuthorizationSet expected_sw_enforced, //
@@ -882,17 +1031,21 @@
EXPECT_EQ(ErrorCode::OK, error);
if (error != ErrorCode::OK) return false;
- EXPECT_GE(att_attestation_version, 3U);
+ EXPECT_EQ(att_attestation_version, 100U);
+ 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()));
- EXPECT_GE(att_keymaster_version, 4U);
+ expected_sw_enforced.push_back(TAG_ATTESTATION_APPLICATION_ID, appId);
+ }
+
+ EXPECT_EQ(att_keymaster_version, 100U);
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
@@ -1078,17 +1231,10 @@
string cert_issuer = x509NameToStr(X509_get_issuer_name(key_cert.get()));
string signer_subj = x509NameToStr(X509_get_subject_name(signing_cert.get()));
if (cert_issuer != signer_subj) {
- return AssertionFailure() << "Cert " << i << " has wrong issuer.\n" << cert_data.str();
- }
-
- if (i == 0) {
- string cert_sub = x509NameToStr(X509_get_subject_name(key_cert.get()));
- if ("/CN=Android Keystore Key" != cert_sub) {
- return AssertionFailure()
- << "Leaf cert has wrong subject, should be CN=Android Keystore Key, was "
- << cert_sub << '\n'
- << cert_data.str();
- }
+ return AssertionFailure() << "Cert " << i << " has wrong issuer.\n"
+ << " Signer subject is " << signer_subj
+ << " Issuer subject is " << cert_issuer << endl
+ << cert_data.str();
}
}
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
index da174ce..cb38938 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -71,6 +71,7 @@
IKeyMintDevice& keyMint() { return *keymint_; }
uint32_t os_version() { return os_version_; }
uint32_t os_patch_level() { return os_patch_level_; }
+ uint32_t vendor_patch_level() { return vendor_patch_level_; }
ErrorCode GetReturnErrorCode(const Status& result);
@@ -94,13 +95,22 @@
ErrorCode ImportWrappedKey(string wrapped_key, string wrapping_key,
const AuthorizationSet& wrapping_key_desc, string masking_key,
- const AuthorizationSet& unwrapping_params);
+ const AuthorizationSet& unwrapping_params, int64_t password_sid,
+ int64_t biometric_sid);
+ ErrorCode ImportWrappedKey(string wrapped_key, string wrapping_key,
+ const AuthorizationSet& wrapping_key_desc, string masking_key,
+ const AuthorizationSet& unwrapping_params) {
+ return ImportWrappedKey(wrapped_key, wrapping_key, wrapping_key_desc, masking_key,
+ unwrapping_params, 0 /* password_sid */, 0 /* biometric_sid */);
+ }
ErrorCode DeleteKey(vector<uint8_t>* key_blob, bool keep_key_blob = false);
ErrorCode DeleteKey(bool keep_key_blob = false);
ErrorCode DeleteAllKeys();
+ ErrorCode DestroyAttestationIds();
+
void CheckedDeleteKey(vector<uint8_t>* key_blob, bool keep_key_blob = false);
void CheckedDeleteKey();
@@ -165,6 +175,8 @@
const vector<uint8_t>& iv_in);
string EncryptMessage(const string& message, BlockMode block_mode, PaddingMode padding,
uint8_t mac_length_bits, const vector<uint8_t>& iv_in);
+ string EncryptMessage(const string& message, BlockMode block_mode, PaddingMode padding,
+ uint8_t mac_length_bits);
string DecryptMessage(const vector<uint8_t>& key_blob, const string& ciphertext,
const AuthorizationSet& params);
@@ -230,6 +242,10 @@
vector<uint32_t> ValidKeySizes(Algorithm algorithm);
vector<uint32_t> InvalidKeySizes(Algorithm algorithm);
+ vector<BlockMode> ValidBlockModes(Algorithm algorithm);
+ vector<PaddingMode> ValidPaddingModes(Algorithm algorithm, BlockMode blockMode);
+ vector<PaddingMode> InvalidPaddingModes(Algorithm algorithm, BlockMode blockMode);
+
vector<EcCurve> ValidCurves();
vector<EcCurve> InvalidCurves();
@@ -262,6 +278,8 @@
std::shared_ptr<IKeyMintDevice> keymint_;
uint32_t os_version_;
uint32_t os_patch_level_;
+ uint32_t vendor_patch_level_;
+ bool timestamp_token_required_;
SecurityLevel securityLevel_;
string name_;
@@ -269,12 +287,20 @@
long challenge_;
};
+vector<uint8_t> build_serial_blob(const uint64_t serial_int);
+void verify_subject(const X509* cert, const string& subject, bool self_signed);
+void verify_serial(X509* cert, const uint64_t expected_serial);
+void verify_subject_and_serial(const Certificate& certificate, //
+ const uint64_t expected_serial, //
+ const string& subject, bool self_signed);
+
bool verify_attestation_record(const string& challenge, //
const string& app_id, //
AuthorizationSet expected_sw_enforced, //
AuthorizationSet expected_hw_enforced, //
SecurityLevel security_level,
const vector<uint8_t>& attestation_cert);
+
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);
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index 2d28845..cd7d603 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -67,6 +67,8 @@
namespace {
+bool check_patchLevels = false;
+
template <TagType tag_type, Tag tag, typename ValueT>
bool contains(const vector<KeyParameter>& set, TypedTag<tag_type, tag> ttag,
ValueT expected_value) {
@@ -113,109 +115,296 @@
return b;
}
-string rsa_key =
- hex2str("30820275020100300d06092a864886f70d01010105000482025f3082025b"
- "02010002818100c6095409047d8634812d5a218176e45c41d60a75b13901"
- "f234226cffe776521c5a77b9e389417b71c0b6a44d13afe4e4a2805d46c9"
- "da2935adb1ff0c1f24ea06e62b20d776430a4d435157233c6f916783c30e"
- "310fcbd89b85c2d56771169785ac12bca244abda72bfb19fc44d27c81e1d"
- "92de284f4061edfd99280745ea6d2502030100010281801be0f04d9cae37"
- "18691f035338308e91564b55899ffb5084d2460e6630257e05b3ceab0297"
- "2dfabcd6ce5f6ee2589eb67911ed0fac16e43a444b8c861e544a05933657"
- "72f8baf6b22fc9e3c5f1024b063ac080a7b2234cf8aee8f6c47bbf4fd3ac"
- "e7240290bef16c0b3f7f3cdd64ce3ab5912cf6e32f39ab188358afcccd80"
- "81024100e4b49ef50f765d3b24dde01aceaaf130f2c76670a91a61ae08af"
- "497b4a82be6dee8fcdd5e3f7ba1cfb1f0c926b88f88c92bfab137fba2285"
- "227b83c342ff7c55024100ddabb5839c4c7f6bf3d4183231f005b31aa58a"
- "ffdda5c79e4cce217f6bc930dbe563d480706c24e9ebfcab28a6cdefd324"
- "b77e1bf7251b709092c24ff501fd91024023d4340eda3445d8cd26c14411"
- "da6fdca63c1ccd4b80a98ad52b78cc8ad8beb2842c1d280405bc2f6c1bea"
- "214a1d742ab996b35b63a82a5e470fa88dbf823cdd02401b7b57449ad30d"
- "1518249a5f56bb98294d4b6ac12ffc86940497a5a5837a6cf946262b4945"
- "26d328c11e1126380fde04c24f916dec250892db09a6d77cdba351024077"
- "62cd8f4d050da56bd591adb515d24d7ccd32cca0d05f866d583514bd7324"
- "d5f33645e8ed8b4a1cb3cc4a1d67987399f2a09f5b3fb68c88d5e5d90ac3"
- "3492d6");
+string rsa_key = hex2str(
+ // RFC 5208 s5
+ "30820275" // SEQUENCE length 0x275 (PrivateKeyInfo) {
+ "020100" // INTEGER length 1 value 0x00 (version)
+ "300d" // SEQUENCE length 0x0d (AlgorithmIdentifier) {
+ "0609" // OBJECT IDENTIFIER length 9 (algorithm)
+ "2a864886f70d010101" // 1.2.840.113549.1.1.1 (rsaEncryption)
+ "0500" // NULL (parameters)
+ // } end SEQUENCE (AlgorithmIdentifier)
+ "0482025f" // OCTET STRING length 0x25f (privateKey) holding...
+ // RFC 8017 A.1.2
+ "3082025b" // SEQUENCE length 0x25b (RSAPrivateKey) {
+ "020100" // INTEGER length 1 value 0x00 (version)
+ "028181" // INTEGER length 0x81 value (modulus) ...
+ "00c6095409047d8634812d5a218176e4"
+ "5c41d60a75b13901f234226cffe77652"
+ "1c5a77b9e389417b71c0b6a44d13afe4"
+ "e4a2805d46c9da2935adb1ff0c1f24ea"
+ "06e62b20d776430a4d435157233c6f91"
+ "6783c30e310fcbd89b85c2d567711697"
+ "85ac12bca244abda72bfb19fc44d27c8"
+ "1e1d92de284f4061edfd99280745ea6d"
+ "25"
+ "0203010001" // INTEGER length 3 value 0x10001 (publicExponent)
+ "028180" // INTEGER length 0x80 (privateExponent) value...
+ "1be0f04d9cae3718691f035338308e91"
+ "564b55899ffb5084d2460e6630257e05"
+ "b3ceab02972dfabcd6ce5f6ee2589eb6"
+ "7911ed0fac16e43a444b8c861e544a05"
+ "93365772f8baf6b22fc9e3c5f1024b06"
+ "3ac080a7b2234cf8aee8f6c47bbf4fd3"
+ "ace7240290bef16c0b3f7f3cdd64ce3a"
+ "b5912cf6e32f39ab188358afcccd8081"
+ "0241" // INTEGER length 0x41 (prime1)
+ "00e4b49ef50f765d3b24dde01aceaaf1"
+ "30f2c76670a91a61ae08af497b4a82be"
+ "6dee8fcdd5e3f7ba1cfb1f0c926b88f8"
+ "8c92bfab137fba2285227b83c342ff7c"
+ "55"
+ "0241" // INTEGER length 0x41 (prime2)
+ "00ddabb5839c4c7f6bf3d4183231f005"
+ "b31aa58affdda5c79e4cce217f6bc930"
+ "dbe563d480706c24e9ebfcab28a6cdef"
+ "d324b77e1bf7251b709092c24ff501fd"
+ "91"
+ "0240" // INTEGER length 0x40 (exponent1)
+ "23d4340eda3445d8cd26c14411da6fdc"
+ "a63c1ccd4b80a98ad52b78cc8ad8beb2"
+ "842c1d280405bc2f6c1bea214a1d742a"
+ "b996b35b63a82a5e470fa88dbf823cdd"
+ "0240" // INTEGER length 0x40 (exponent2)
+ "1b7b57449ad30d1518249a5f56bb9829"
+ "4d4b6ac12ffc86940497a5a5837a6cf9"
+ "46262b494526d328c11e1126380fde04"
+ "c24f916dec250892db09a6d77cdba351"
+ "0240" // INTEGER length 0x40 (coefficient)
+ "7762cd8f4d050da56bd591adb515d24d"
+ "7ccd32cca0d05f866d583514bd7324d5"
+ "f33645e8ed8b4a1cb3cc4a1d67987399"
+ "f2a09f5b3fb68c88d5e5d90ac33492d6"
+ // } end SEQUENCE (PrivateKey)
+ // } end SEQUENCE (PrivateKeyInfo)
+);
/*
* 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 rsa_2048_key = hex2str(
+ // RFC 5208 s5
+ "308204BD" // SEQUENCE length 0x4bd (PrivateKeyInfo) {
+ "020100" // INTEGER length 1 value 0x00 (version)
+ "300D" // SEQUENCE length 0x0d (AlgorithmIdentifier) {
+ "0609" // OBJECT IDENTIFIER length 9 (algorithm)
+ "2A864886F70D010101" // 1.2.840.113549.1.1.1 (rsaEncryption)
+ "0500" // NULL (parameters)
+ // } end SEQUENCE (AlgorithmIdentifier)
+ "048204A7" // OCTET STRING length 0x25f (privateKey) holding...
+ // RFC 8017 A.1.2
+ "308204A3" // SEQUENCE length 0x4a3 (RSAPrivateKey) {
+ "020100" // INTEGER length 1 value 0x00 (version)
+ "02820101" // INTEGER length 0x101 value (modulus) ...
+ "00BEBC342B56D443B1299F9A6A7056E8"
+ "0A897E318476A5A18029E63B2ED739A6"
+ "1791D339F58DC763D9D14911F2EDEC38"
+ "3DEE11F6319B44510E7A3ECD9B79B973"
+ "82E49500ACF8117DC89CAF0E621F7775"
+ "6554A2FD4664BFE7AB8B59AB48340DBF"
+ "A27B93B5A81F6ECDEB02D0759307128D"
+ "F3E3BAD4055C8B840216DFAA5700670E"
+ "6C5126F0962FCB70FF308F25049164CC"
+ "F76CC2DA66A7DD9A81A714C2809D6918"
+ "6133D29D84568E892B6FFBF3199BDB14"
+ "383EE224407F190358F111A949552ABA"
+ "6714227D1BD7F6B20DD0CB88F9467B71"
+ "9339F33BFF35B3870B3F62204E4286B0"
+ "948EA348B524544B5F9838F29EE643B0"
+ "79EEF8A713B220D7806924CDF7295070"
+ "C5"
+ "0203010001" // INTEGER length 3 value 0x10001 (publicExponent)
+ "02820100" // INTEGER length 0x100 (privateExponent) value...
+ "69F377F35F2F584EF075353CCD1CA997"
+ "38DB3DBC7C7FF35F9366CE176DFD1B13"
+ "5AB10030344ABF5FBECF1D4659FDEF1C"
+ "0FC430834BE1BE3911951377BB3D563A"
+ "2EA9CA8F4AD9C48A8CE6FD516A735C66"
+ "2686C7B4B3C09A7B8354133E6F93F790"
+ "D59EAEB92E84C9A4339302CCE28FDF04"
+ "CCCAFA7DE3F3A827D4F6F7D38E68B0EC"
+ "6AB706645BF074A4E4090D06FB163124"
+ "365FD5EE7A20D350E9958CC30D91326E"
+ "1B292E9EF5DB408EC42DAF737D201497"
+ "04D0A678A0FB5B5446863B099228A352"
+ "D604BA8091A164D01D5AB05397C71EAD"
+ "20BE2A08FC528FE442817809C787FEE4"
+ "AB97F97B9130D022153EDC6EB6CBE7B0"
+ "F8E3473F2E901209B5DB10F93604DB01"
+ "028181" // INTEGER length 0x81 (prime1)
+ "00E83C0998214941EA4F9293F1B77E2E"
+ "99E6CF305FAF358238E126124FEAF2EB"
+ "9724B2EA7B78E6032343821A80E55D1D"
+ "88FB12D220C3F41A56142FEC85796D19"
+ "17F1E8C774F142B67D3D6E7B7E6B4383"
+ "E94DB5929089DBB346D5BDAB40CC2D96"
+ "EE0409475E175C63BF78CFD744136740"
+ "838127EA723FF3FE7FA368C1311B4A4E"
+ "05"
+ "028181" // INTEGER length 0x81 (prime2)
+ "00D240FCC0F5D7715CDE21CB2DC86EA1"
+ "46132EA3B06F61FF2AF54BF38473F59D"
+ "ADCCE32B5F4CC32DD0BA6F509347B4B5"
+ "B1B58C39F95E4798CCBB43E83D0119AC"
+ "F532F359CA743C85199F0286610E2009"
+ "97D7312917179AC9B67558773212EC96"
+ "1E8BCE7A3CC809BC5486A96E4B0E6AF3"
+ "94D94E066A0900B7B70E82A44FB30053"
+ "C1"
+ "028181" // INTEGER length 0x81 (exponent1)
+ "00AD15DA1CBD6A492B66851BA8C316D3"
+ "8AB700E2CFDDD926A658003513C54BAA"
+ "152B30021D667D20078F500F8AD3E7F3"
+ "945D74A891ED1A28EAD0FEEAEC8C14A8"
+ "E834CF46A13D1378C99D18940823CFDD"
+ "27EC5810D59339E0C34198AC638E09C8"
+ "7CBB1B634A9864AE9F4D5EB2D53514F6"
+ "7B4CAEC048C8AB849A02E397618F3271"
+ "35"
+ "028180" // INTEGER length 0x80 (exponent2)
+ "1FA2C1A5331880A92D8F3E281C617108"
+ "BF38244F16E352E69ED417C7153F9EC3"
+ "18F211839C643DCF8B4DD67CE2AC312E"
+ "95178D5D952F06B1BF779F4916924B70"
+ "F582A23F11304E02A5E7565AE22A35E7"
+ "4FECC8B6FDC93F92A1A37703E4CF0E63"
+ "783BD02EB716A7ECBBFA606B10B74D01"
+ "579522E7EF84D91FC522292108D902C1"
+ "028180" // INTEGER length 0x80 (coefficient)
+ "796FE3825F9DCC85DF22D58690065D93"
+ "898ACD65C087BEA8DA3A63BF4549B795"
+ "E2CD0E3BE08CDEBD9FCF1720D9CDC507"
+ "0D74F40DED8E1102C52152A31B6165F8"
+ "3A6722AECFCC35A493D7634664B888A0"
+ "8D3EB034F12EA28BFEE346E205D33482"
+ "7F778B16ED40872BD29FCB36536B6E93"
+ "FFB06778696B4A9D81BB0A9423E63DE5"
+ // } end SEQUENCE (PrivateKey)
+ // } end SEQUENCE (PrivateKeyInfo)
+);
-string ec_256_key =
- hex2str("308187020100301306072a8648ce3d020106082a8648ce3d030107046d30"
- "6b0201010420737c2ecd7b8d1940bf2930aa9b4ed3ff941eed09366bc032"
- "99986481f3a4d859a14403420004bf85d7720d07c25461683bc648b4778a"
- "9a14dd8a024e3bdd8c7ddd9ab2b528bbc7aa1b51f14ebbbb0bd0ce21bcc4"
- "1c6eb00083cf3376d11fd44949e0b2183bfe");
+string ec_256_key = hex2str(
+ // RFC 5208 s5
+ "308187" // SEQUENCE length 0x87 (PrivateKeyInfo) {
+ "020100" // INTEGER length 1 value 0 (version)
+ "3013" // SEQUENCE length 0x13 (AlgorithmIdentifier) {
+ "0607" // OBJECT IDENTIFIER length 7 (algorithm)
+ "2a8648ce3d0201" // 1.2.840.10045.2.1 (ecPublicKey)
+ "0608" // OBJECT IDENTIFIER length 8 (param)
+ "2a8648ce3d030107" // 1.2.840.10045.3.1.7 (secp256r1)
+ // } end SEQUENCE (AlgorithmIdentifier)
+ "046d" // OCTET STRING length 0x6d (privateKey) holding...
+ "306b" // SEQUENCE length 0x6b (ECPrivateKey)
+ "020101" // INTEGER length 1 value 1 (version)
+ "0420" // OCTET STRING length 0x20 (privateKey)
+ "737c2ecd7b8d1940bf2930aa9b4ed3ff"
+ "941eed09366bc03299986481f3a4d859"
+ "a144" // TAG [1] len 0x44 (publicKey) {
+ "03420004bf85d7720d07c25461683bc6"
+ "48b4778a9a14dd8a024e3bdd8c7ddd9a"
+ "b2b528bbc7aa1b51f14ebbbb0bd0ce21"
+ "bcc41c6eb00083cf3376d11fd44949e0"
+ "b2183bfe"
+ // } end SEQUENCE (ECPrivateKey)
+ // } end SEQUENCE (PrivateKeyInfo)
+);
-string ec_521_key =
- hex2str("3081EE020100301006072A8648CE3D020106052B810400230481D63081D3"
- "02010104420011458C586DB5DAA92AFAB03F4FE46AA9D9C3CE9A9B7A006A"
- "8384BEC4C78E8E9D18D7D08B5BCFA0E53C75B064AD51C449BAE0258D54B9"
- "4B1E885DED08ED4FB25CE9A1818903818600040149EC11C6DF0FA122C6A9"
- "AFD9754A4FA9513A627CA329E349535A5629875A8ADFBE27DCB932C05198"
- "6377108D054C28C6F39B6F2C9AF81802F9F326B842FF2E5F3C00AB7635CF"
- "B36157FC0882D574A10D839C1A0C049DC5E0D775E2EE50671A208431BB45"
- "E78E70BEFE930DB34818EE4D5C26259F5C6B8E28A652950F9F88D7B4B2C9"
- "D9");
+string ec_521_key = hex2str(
+ // RFC 5208 s5
+ "3081EE" // SEQUENCE length 0xee (PrivateKeyInfo) {
+ "020100" // INTEGER length 1 value 0 (version)
+ "3010" // SEQUENCE length 0x10 (AlgorithmIdentifier) {
+ "0607" // OBJECT IDENTIFIER length 7 (algorithm)
+ "2A8648CE3D0201" // 1.2.840.10045.2.1 (ecPublicKey)
+ "0605" // OBJECT IDENTIFIER length 5 (param)
+ "2B81040023" // 1.3.132.0.35 (secp521r1)
+ // } end SEQUENCE (AlgorithmIdentifier)
+ "0481D6" // OCTET STRING length 0xd6 (privateKey) holding...
+ "3081D3" // SEQUENCE length 0xd3 (ECPrivateKey)
+ "020101" // INTEGER length 1 value 1 (version)
+ "0442" // OCTET STRING length 0x42 (privateKey)
+ "0011458C586DB5DAA92AFAB03F4FE46A"
+ "A9D9C3CE9A9B7A006A8384BEC4C78E8E"
+ "9D18D7D08B5BCFA0E53C75B064AD51C4"
+ "49BAE0258D54B94B1E885DED08ED4FB2"
+ "5CE9"
+ "A18189" // TAG [1] len 0x89 (publicKey) {
+ "03818600040149EC11C6DF0FA122C6A9"
+ "AFD9754A4FA9513A627CA329E349535A"
+ "5629875A8ADFBE27DCB932C051986377"
+ "108D054C28C6F39B6F2C9AF81802F9F3"
+ "26B842FF2E5F3C00AB7635CFB36157FC"
+ "0882D574A10D839C1A0C049DC5E0D775"
+ "E2EE50671A208431BB45E78E70BEFE93"
+ "0DB34818EE4D5C26259F5C6B8E28A652"
+ "950F9F88D7B4B2C9D9"
+ // } end SEQUENCE (ECPrivateKey)
+ // } end SEQUENCE (PrivateKeyInfo)
+);
-string ec_256_key_rfc5915 =
- hex2str("308193020100301306072a8648ce3d020106082a8648ce3d030107047930"
- "770201010420782370a8c8ce5537baadd04dcff079c8158cfa9c67b818b3"
- "8e8d21c9fa750c1da00a06082a8648ce3d030107a14403420004e2cc561e"
- "e701da0ad0ef0d176bb0c919d42e79c393fdc1bd6c4010d85cf2cf8e68c9"
- "05464666f98dad4f01573ba81078b3428570a439ba3229fbc026c550682f");
+string ec_256_key_rfc5915 = hex2str(
+ // RFC 5208 s5
+ "308193" // SEQUENCE length 0x93 (PrivateKeyInfo) {
+ "020100" // INTEGER length 1 value 0 (version)
+ "3013" // SEQUENCE length 0x13 (AlgorithmIdentifier) {
+ "0607" // OBJECT IDENTIFIER length 7 (algorithm)
+ "2a8648ce3d0201" // 1.2.840.10045.2.1 (ecPublicKey)
+ "0608" // OBJECT IDENTIFIER length 8 (param)
+ "2a8648ce3d030107" // 1.2.840.10045.3.1.7 (secp256r1)
+ // } end SEQUENCE (AlgorithmIdentifier)
+ "0479" // OCTET STRING length 0x79 (privateKey) holding...
+ // RFC 5915 s3
+ "3077" // SEQUENCE length 0x77 (ECPrivateKey)
+ "020101" // INTEGER length 1 value 1 (version)
+ "0420" // OCTET STRING length 0x42 (privateKey)
+ "782370a8c8ce5537baadd04dcff079c8"
+ "158cfa9c67b818b38e8d21c9fa750c1d"
+ "a00a" // TAG [0] length 0xa (parameters)
+ "0608" // OBJECT IDENTIFIER length 8
+ "2a8648ce3d030107" // 1.2.840.10045.3.1.7 (secp256r1)
+ // } end TAG [0]
+ "a144" // TAG [1] length 0x44 (publicKey) {
+ "0342" // BIT STRING length 0x42
+ "00" // no pad bits
+ "04e2cc561ee701da0ad0ef0d176bb0c9"
+ "19d42e79c393fdc1bd6c4010d85cf2cf"
+ "8e68c905464666f98dad4f01573ba810"
+ "78b3428570a439ba3229fbc026c55068"
+ "2f"
+ // } end SEQUENCE (ECPrivateKey)
+ // } end SEQUENCE (PrivateKeyInfo)
+);
-string ec_256_key_sec1 =
- hex2str("308187020100301306072a8648ce3d020106082a8648ce3d030107046d30"
- "6b0201010420782370a8c8ce5537baadd04dcff079c8158cfa9c67b818b3"
- "8e8d21c9fa750c1da14403420004e2cc561ee701da0ad0ef0d176bb0c919"
- "d42e79c393fdc1bd6c4010d85cf2cf8e68c905464666f98dad4f01573ba8"
- "1078b3428570a439ba3229fbc026c550682f");
+string ec_256_key_sec1 = hex2str(
+ // RFC 5208 s5
+ "308187" // SEQUENCE length 0x87 (PrivateKeyInfo) {
+ "020100" // INTEGER length 1 value 0 (version)
+ "3013" // SEQUENCE length 0x13 (AlgorithmIdentifier) {
+ "0607" // OBJECT IDENTIFIER length 7 (algorithm)
+ "2a8648ce3d0201" // 1.2.840.10045.2.1 (ecPublicKey)
+ "0608" // OBJECT IDENTIFIER length 8 (param)
+ "2a8648ce3d030107" // 1.2.840.10045.3.1.7 (secp256r1)
+ // } end SEQUENCE (AlgorithmIdentifier)
+ "046d" // OCTET STRING length 0x6d (privateKey) holding...
+ // SEC1-v2 C.4
+ "306b" // SEQUENCE length 0x6b (ECPrivateKey)
+ "020101" // INTEGER length 1 value 0x01 (version)
+ "0420" // OCTET STRING length 0x20 (privateKey)
+ "782370a8c8ce5537baadd04dcff079c8"
+ "158cfa9c67b818b38e8d21c9fa750c1d"
+ "a144" // TAG [1] length 0x44 (publicKey) {
+ "0342" // BIT STRING length 0x42
+ "00" // no pad bits
+ "04e2cc561ee701da0ad0ef0d176bb0c9"
+ "19d42e79c393fdc1bd6c4010d85cf2cf"
+ "8e68c905464666f98dad4f01573ba810"
+ "78b3428570a439ba3229fbc026c55068"
+ "2f"
+ // } end TAG [1] (publicKey)
+ // } end SEQUENCE (PrivateKeyInfo)
+);
struct RSA_Delete {
void operator()(RSA* p) { RSA_free(p); }
@@ -291,37 +480,375 @@
class NewKeyGenerationTest : public KeyMintAidlTestBase {
protected:
void CheckBaseParams(const vector<KeyCharacteristics>& keyCharacteristics) {
- // TODO(swillden): Distinguish which params should be in which auth list.
-
- AuthorizationSet auths;
- for (auto& entry : keyCharacteristics) {
- auths.push_back(AuthorizationSet(entry.authorizations));
- }
-
- EXPECT_TRUE(auths.Contains(TAG_ORIGIN, KeyOrigin::GENERATED));
+ AuthorizationSet auths = CheckCommonParams(keyCharacteristics);
EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::SIGN));
EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::VERIFY));
- // 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::ENCRYPT));
EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::DECRYPT));
+ }
+
+ void CheckSymmetricParams(const vector<KeyCharacteristics>& keyCharacteristics) {
+ AuthorizationSet auths = CheckCommonParams(keyCharacteristics);
+ EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::ENCRYPT));
+ EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::DECRYPT));
+
+ EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::SIGN));
+ EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::VERIFY));
+ }
+
+ AuthorizationSet CheckCommonParams(const vector<KeyCharacteristics>& keyCharacteristics) {
+ // TODO(swillden): Distinguish which params should be in which auth list.
+ AuthorizationSet auths;
+ for (auto& entry : keyCharacteristics) {
+ auths.push_back(AuthorizationSet(entry.authorizations));
+ }
+ EXPECT_TRUE(auths.Contains(TAG_ORIGIN, KeyOrigin::GENERATED));
+
+ // Verify that App data, ROT and auth timeout are NOT included.
+ EXPECT_FALSE(auths.Contains(TAG_ROOT_OF_TRUST));
+ EXPECT_FALSE(auths.Contains(TAG_APPLICATION_DATA));
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());
+ // None of the tests specify CREATION_DATETIME so check that the KeyMint implementation
+ // never adds it.
+ EXPECT_FALSE(auths.Contains(TAG_CREATION_DATETIME));
+ // Check OS details match the original hardware info.
+ auto os_ver = auths.GetTagValue(TAG_OS_VERSION);
+ EXPECT_TRUE(os_ver);
+ EXPECT_EQ(*os_ver, os_version());
auto os_pl = auths.GetTagValue(TAG_OS_PATCHLEVEL);
- ASSERT_TRUE(os_pl);
+ EXPECT_TRUE(os_pl);
EXPECT_EQ(*os_pl, os_patch_level());
+
+ if (check_patchLevels) {
+ // Should include vendor and boot patchlevels.
+ auto vendor_pl = auths.GetTagValue(TAG_VENDOR_PATCHLEVEL);
+ EXPECT_TRUE(vendor_pl);
+ EXPECT_EQ(*vendor_pl, vendor_patch_level());
+ auto boot_pl = auths.GetTagValue(TAG_BOOT_PATCHLEVEL);
+ EXPECT_TRUE(boot_pl);
+ }
+
+ return auths;
}
};
/*
+ * NewKeyGenerationTest.Aes
+ *
+ * Verifies that keymint can generate all required AES key sizes, and that the resulting keys
+ * have correct characteristics.
+ */
+TEST_P(NewKeyGenerationTest, Aes) {
+ for (auto key_size : ValidKeySizes(Algorithm::AES)) {
+ for (auto block_mode : ValidBlockModes(Algorithm::AES)) {
+ for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+ SCOPED_TRACE(testing::Message()
+ << "AES-" << key_size << "-" << block_mode << "-" << padding_mode);
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ auto builder = AuthorizationSetBuilder()
+ .AesEncryptionKey(key_size)
+ .BlockMode(block_mode)
+ .Padding(padding_mode)
+ .SetDefaultValidity();
+ if (block_mode == BlockMode::GCM) {
+ builder.Authorization(TAG_MIN_MAC_LENGTH, 128);
+ }
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(builder, &key_blob, &key_characteristics));
+
+ EXPECT_GT(key_blob.size(), 0U);
+ CheckSymmetricParams(key_characteristics);
+
+ AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+ EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::AES));
+ EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+ << "Key size " << key_size << "missing";
+
+ CheckedDeleteKey(&key_blob);
+ }
+ }
+ }
+}
+
+/*
+ * NewKeyGenerationTest.AesInvalidSize
+ *
+ * Verifies that specifying an invalid key size for AES key generation returns
+ * UNSUPPORTED_KEY_SIZE.
+ */
+TEST_P(NewKeyGenerationTest, AesInvalidSize) {
+ for (auto key_size : InvalidKeySizes(Algorithm::AES)) {
+ for (auto block_mode : ValidBlockModes(Algorithm::AES)) {
+ for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+ SCOPED_TRACE(testing::Message()
+ << "AES-" << key_size << "-" << block_mode << "-" << padding_mode);
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ auto builder = AuthorizationSetBuilder()
+ .AesEncryptionKey(key_size)
+ .BlockMode(block_mode)
+ .Padding(padding_mode)
+ .SetDefaultValidity();
+ if (block_mode == BlockMode::GCM) {
+ builder.Authorization(TAG_MIN_MAC_LENGTH, 128);
+ }
+ EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+ GenerateKey(builder, &key_blob, &key_characteristics));
+ }
+ }
+ }
+
+ for (auto block_mode : ValidBlockModes(Algorithm::AES)) {
+ for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ // No key size specified
+ auto builder = AuthorizationSetBuilder()
+ .Authorization(TAG_ALGORITHM, Algorithm::AES)
+ .BlockMode(block_mode)
+ .Padding(padding_mode)
+ .SetDefaultValidity();
+ if (block_mode == BlockMode::GCM) {
+ builder.Authorization(TAG_MIN_MAC_LENGTH, 128);
+ }
+ EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+ GenerateKey(builder, &key_blob, &key_characteristics));
+ }
+ }
+}
+
+/*
+ * NewKeyGenerationTest.AesInvalidPadding
+ *
+ * Verifies that specifying an invalid padding on AES keys gives a failure
+ * somewhere along the way.
+ */
+TEST_P(NewKeyGenerationTest, AesInvalidPadding) {
+ for (auto key_size : ValidKeySizes(Algorithm::AES)) {
+ for (auto block_mode : ValidBlockModes(Algorithm::AES)) {
+ for (auto padding_mode : InvalidPaddingModes(Algorithm::AES, block_mode)) {
+ SCOPED_TRACE(testing::Message()
+ << "AES-" << key_size << "-" << block_mode << "-" << padding_mode);
+ auto builder = AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(key_size)
+ .BlockMode(block_mode)
+ .Padding(padding_mode)
+ .SetDefaultValidity();
+ if (block_mode == BlockMode::GCM) {
+ builder.Authorization(TAG_MIN_MAC_LENGTH, 128);
+ }
+
+ auto result = GenerateKey(builder);
+ if (result == ErrorCode::OK) {
+ // Key creation was OK but has generated a key that cannot be used.
+ auto params =
+ AuthorizationSetBuilder().BlockMode(block_mode).Padding(padding_mode);
+ if (block_mode == BlockMode::GCM) {
+ params.Authorization(TAG_MAC_LENGTH, 128);
+ }
+ auto result = Begin(KeyPurpose::ENCRYPT, params);
+ EXPECT_TRUE(result == ErrorCode::INCOMPATIBLE_PADDING_MODE ||
+ result == ErrorCode::INVALID_KEY_BLOB)
+ << "unexpected result: " << result;
+ } else {
+ // The KeyMint implementation detected that the generated key
+ // is unusable.
+ EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, result);
+ }
+ }
+ }
+ }
+}
+
+/*
+ * NewKeyGenerationTest.AesGcmMissingMinMac
+ *
+ * Verifies that specifying an invalid key size for AES key generation returns
+ * UNSUPPORTED_KEY_SIZE.
+ */
+TEST_P(NewKeyGenerationTest, AesGcmMissingMinMac) {
+ for (auto key_size : ValidKeySizes(Algorithm::AES)) {
+ BlockMode block_mode = BlockMode::GCM;
+ for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+ SCOPED_TRACE(testing::Message()
+ << "AES-" << key_size << "-" << block_mode << "-" << padding_mode);
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ // No MIN_MAC_LENGTH provided.
+ auto builder = AuthorizationSetBuilder()
+ .AesEncryptionKey(key_size)
+ .BlockMode(block_mode)
+ .Padding(padding_mode)
+ .SetDefaultValidity();
+ EXPECT_EQ(ErrorCode::MISSING_MIN_MAC_LENGTH,
+ GenerateKey(builder, &key_blob, &key_characteristics));
+ }
+ }
+}
+
+/*
+ * NewKeyGenerationTest.AesGcmMinMacOutOfRange
+ *
+ * Verifies that specifying an invalid min MAC size for AES key generation returns
+ * UNSUPPORTED_MIN_MAC_LENGTH.
+ */
+TEST_P(NewKeyGenerationTest, AesGcmMinMacOutOfRange) {
+ for (size_t min_mac_len : {88, 136}) {
+ for (auto key_size : ValidKeySizes(Algorithm::AES)) {
+ BlockMode block_mode = BlockMode::GCM;
+ for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+ SCOPED_TRACE(testing::Message()
+ << "AES-" << key_size << "-" << block_mode << "-" << padding_mode);
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ auto builder = AuthorizationSetBuilder()
+ .AesEncryptionKey(key_size)
+ .BlockMode(block_mode)
+ .Padding(padding_mode)
+ .Authorization(TAG_MIN_MAC_LENGTH, min_mac_len)
+ .SetDefaultValidity();
+ EXPECT_EQ(ErrorCode::UNSUPPORTED_MIN_MAC_LENGTH,
+ GenerateKey(builder, &key_blob, &key_characteristics));
+ }
+ }
+ }
+}
+
+/*
+ * NewKeyGenerationTest.TripleDes
+ *
+ * Verifies that keymint can generate all required 3DES key sizes, and that the resulting keys
+ * have correct characteristics.
+ */
+TEST_P(NewKeyGenerationTest, TripleDes) {
+ for (auto key_size : ValidKeySizes(Algorithm::TRIPLE_DES)) {
+ for (auto block_mode : ValidBlockModes(Algorithm::TRIPLE_DES)) {
+ for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+ SCOPED_TRACE(testing::Message()
+ << "3DES-" << key_size << "-" << block_mode << "-" << padding_mode);
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .TripleDesEncryptionKey(key_size)
+ .BlockMode(block_mode)
+ .Padding(padding_mode)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .SetDefaultValidity(),
+ &key_blob, &key_characteristics));
+
+ EXPECT_GT(key_blob.size(), 0U);
+ CheckSymmetricParams(key_characteristics);
+
+ AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+ EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::TRIPLE_DES));
+ EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+ << "Key size " << key_size << "missing";
+
+ CheckedDeleteKey(&key_blob);
+ }
+ }
+ }
+}
+
+/*
+ * NewKeyGenerationTest.TripleDesWithAttestation
+ *
+ * Verifies that keymint can generate all required 3DES key sizes, and that the resulting keys
+ * have correct characteristics.
+ *
+ * Request attestation, which doesn't help for symmetric keys (as there is no public key to
+ * put in a certificate) but which isn't an error.
+ */
+TEST_P(NewKeyGenerationTest, TripleDesWithAttestation) {
+ for (auto key_size : ValidKeySizes(Algorithm::TRIPLE_DES)) {
+ for (auto block_mode : ValidBlockModes(Algorithm::TRIPLE_DES)) {
+ for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+ SCOPED_TRACE(testing::Message()
+ << "3DES-" << key_size << "-" << block_mode << "-" << padding_mode);
+
+ auto challenge = "hello";
+ auto app_id = "foo";
+
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .TripleDesEncryptionKey(key_size)
+ .BlockMode(block_mode)
+ .Padding(padding_mode)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AttestationChallenge(challenge)
+ .AttestationApplicationId(app_id)
+ .SetDefaultValidity(),
+ &key_blob, &key_characteristics));
+
+ EXPECT_GT(key_blob.size(), 0U);
+ CheckSymmetricParams(key_characteristics);
+
+ AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+ EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::TRIPLE_DES));
+ EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+ << "Key size " << key_size << "missing";
+
+ CheckedDeleteKey(&key_blob);
+ }
+ }
+ }
+}
+
+/*
+ * NewKeyGenerationTest.TripleDesInvalidSize
+ *
+ * Verifies that specifying an invalid key size for 3-DES key generation returns
+ * UNSUPPORTED_KEY_SIZE.
+ */
+TEST_P(NewKeyGenerationTest, TripleDesInvalidSize) {
+ for (auto key_size : InvalidKeySizes(Algorithm::TRIPLE_DES)) {
+ for (auto block_mode : ValidBlockModes(Algorithm::TRIPLE_DES)) {
+ for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+ SCOPED_TRACE(testing::Message()
+ << "3DES-" << key_size << "-" << block_mode << "-" << padding_mode);
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+ GenerateKey(AuthorizationSetBuilder()
+ .TripleDesEncryptionKey(key_size)
+ .BlockMode(block_mode)
+ .Padding(padding_mode)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .SetDefaultValidity(),
+ &key_blob, &key_characteristics));
+ }
+ }
+ }
+
+ // Omitting the key size fails.
+ for (auto block_mode : ValidBlockModes(Algorithm::TRIPLE_DES)) {
+ for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+ SCOPED_TRACE(testing::Message()
+ << "3DES-default-" << block_mode << "-" << padding_mode);
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+ GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_ALGORITHM, Algorithm::TRIPLE_DES)
+ .BlockMode(block_mode)
+ .Padding(padding_mode)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .SetDefaultValidity(),
+ &key_blob, &key_characteristics));
+ }
+ }
+}
+
+/*
* NewKeyGenerationTest.Rsa
*
* Verifies that keymint can generate all required RSA key sizes, and that the resulting keys
@@ -355,25 +882,34 @@
/*
* NewKeyGenerationTest.RsaWithAttestation
*
- * Verifies that keymint can generate all required RSA key sizes, and that the resulting keys
- * have correct characteristics.
+ * Verifies that keymint can generate all required RSA key sizes with attestation, and that the
+ * resulting keys 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";
+ auto subject = "cert subj 2";
+ vector<uint8_t> subject_der(make_name_from_str(subject));
+
+ uint64_t serial_int = 66;
+ vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+ 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)
- .AttestationChallenge(challenge)
- .AttestationApplicationId(app_id)
- .Authorization(TAG_NO_AUTH_REQUIRED)
- .SetDefaultValidity(),
- &key_blob, &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)
+ .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+ .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+ .SetDefaultValidity(),
+ &key_blob, &key_characteristics));
ASSERT_GT(key_blob.size(), 0U);
CheckBaseParams(key_characteristics);
@@ -385,6 +921,7 @@
<< "Key size " << key_size << "missing";
EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
+ verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
ASSERT_GT(cert_chain_.size(), 0);
@@ -470,6 +1007,193 @@
}
/*
+ * 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";
+
+ auto subject = "subj 2";
+ vector<uint8_t> subject_der(make_name_from_str(subject));
+
+ uint64_t serial_int = 111166;
+ vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+ 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)
+ .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+ .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+ .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));
+
+ verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
+ 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) {
+ auto subject = "cert subj subj subj subj subj subj 22222222222222222222";
+ vector<uint8_t> subject_der(make_name_from_str(subject));
+
+ uint64_t serial_int = 0;
+ vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+ 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)
+ .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+ .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+ .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));
+
+ verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
+ 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";
+
+ auto subject = "cert subj 2";
+ vector<uint8_t> subject_der(make_name_from_str(subject));
+
+ uint64_t serial_int = 1;
+ vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+ 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)
+ .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+ .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+ .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));
+
+ verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
+ 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
@@ -516,22 +1240,31 @@
* 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";
+ auto subject = "cert subj 2";
+ vector<uint8_t> subject_der(make_name_from_str(subject));
+
+ uint64_t serial_int = 66;
+ vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+ 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)
- .AttestationChallenge(challenge)
- .AttestationApplicationId(app_id)
- .Authorization(TAG_NO_AUTH_REQUIRED)
- .Authorization(TAG_USAGE_COUNT_LIMIT, 1)
- .SetDefaultValidity(),
- &key_blob, &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)
+ .Authorization(TAG_USAGE_COUNT_LIMIT, 1)
+ .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+ .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+ .SetDefaultValidity(),
+ &key_blob, &key_characteristics));
ASSERT_GT(key_blob.size(), 0U);
CheckBaseParams(key_characteristics);
@@ -554,6 +1287,7 @@
// Check the usage count limit tag also appears in the attestation.
EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
ASSERT_GT(cert_chain_.size(), 0);
+ verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
@@ -600,6 +1334,20 @@
}
/*
+ * NewKeyGenerationTest.RsaMissingParams
+ *
+ * Verifies that omitting optional tags works.
+ */
+TEST_P(NewKeyGenerationTest, RsaMissingParams) {
+ for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
+ ASSERT_EQ(ErrorCode::OK,
+ GenerateKey(
+ AuthorizationSetBuilder().RsaKey(key_size, 65537).SetDefaultValidity()));
+ CheckedDeleteKey();
+ }
+}
+
+/*
* NewKeyGenerationTest.Ecdsa
*
* Verifies that keymint can generate all required EC key sizes, and that the resulting keys
@@ -628,6 +1376,208 @@
}
/*
+ * 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";
+
+ auto subject = "cert subj 2";
+ vector<uint8_t> subject_der(make_name_from_str(subject));
+
+ uint64_t serial_int = 0xFFFFFFFFFFFFFFFF;
+ vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+ 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)
+ .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+ .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+ .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);
+ verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
+
+ 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) {
+ auto subject = "cert subj 2";
+ vector<uint8_t> subject_der(make_name_from_str(subject));
+
+ uint64_t serial_int = 0x123456FFF1234;
+ vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+ 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)
+ .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+ .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+ .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_));
+ verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
+ 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
@@ -738,7 +1688,7 @@
}
/*
- * NewKeyGenerationTest.EcdsaInvalidCurves
+ * NewKeyGenerationTest.EcdsaAllValidCurves
*
* Verifies that keymint does not support any curve designated as unsupported.
*/
@@ -789,6 +1739,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
@@ -854,6 +1839,16 @@
CheckedDeleteKey();
}
}
+ if (SecLevel() == SecurityLevel::STRONGBOX) {
+ // STRONGBOX devices must not support keys larger than 512 bits.
+ size_t key_size = 520;
+ EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+ GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(key_size)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, 256)))
+ << "HMAC key size " << key_size << " unexpectedly valid";
+ }
}
/*
@@ -887,6 +1882,15 @@
CheckedDeleteKey();
}
}
+
+ // Minimum MAC length must be no more than 512 bits.
+ size_t min_mac_length = 520;
+ EXPECT_EQ(ErrorCode::UNSUPPORTED_MIN_MAC_LENGTH,
+ GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, min_mac_length)))
+ << "HMAC min mac length " << min_mac_length << " invalid.";
}
/*
@@ -922,6 +1926,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;
@@ -1196,6 +2241,38 @@
}
/*
+ * SigningOperationsTest.RsaNonUniqueParams
+ *
+ * Verifies that an operation with multiple padding modes is rejected.
+ */
+TEST_P(SigningOperationsTest, RsaNonUniqueParams) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(2048, 65537)
+ .Digest(Digest::NONE)
+ .Digest(Digest::SHA1)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Padding(PaddingMode::NONE)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+ .SetDefaultValidity()));
+
+ ASSERT_EQ(ErrorCode::UNSUPPORTED_PADDING_MODE,
+ Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+
+ ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST,
+ Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+ .Digest(Digest::NONE)
+ .Digest(Digest::SHA1)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+
+ ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST,
+ Begin(KeyPurpose::SIGN,
+ AuthorizationSetBuilder().Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+}
+
+/*
* SigningOperationsTest.RsaUnsupportedPadding
*
* Verifies that RSA operations fail with the correct error (but key gen succeeds) when used
@@ -1212,6 +2289,20 @@
ErrorCode::UNSUPPORTED_PADDING_MODE,
Begin(KeyPurpose::SIGN,
AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::PKCS7)));
+ CheckedDeleteKey();
+
+ ASSERT_EQ(ErrorCode::OK,
+ GenerateKey(
+ AuthorizationSetBuilder()
+ .RsaSigningKey(2048, 65537)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Digest(Digest::SHA_2_256 /* supported digest */)
+ .Padding(PaddingMode::RSA_OAEP) /* padding mode for encryption only */
+ .SetDefaultValidity()));
+ ASSERT_EQ(ErrorCode::UNSUPPORTED_PADDING_MODE,
+ Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_OAEP)));
}
/*
@@ -1235,7 +2326,7 @@
}
/*
- * SigningOperationsTest.RsaPssNoDigest
+ * SigningOperationsTest.RsaPssNoPadding
*
* Verifies that RSA operations fail when no padding mode is specified. PaddingMode::NONE is
* supported in some cases (as validated in other tests), but a mode must be specified.
@@ -1414,6 +2505,23 @@
}
/*
+ * SigningOperationsTest.EcdsaIncompatibleDigest
+ *
+ * Verifies that using an EC key requires compatible digest.
+ */
+TEST_P(SigningOperationsTest, EcdsaIncompatibleDigest) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(256)
+ .Digest(Digest::NONE)
+ .Digest(Digest::SHA1)
+ .SetDefaultValidity()));
+ EXPECT_EQ(ErrorCode::INCOMPATIBLE_DIGEST,
+ Begin(KeyPurpose::SIGN, AuthorizationSetBuilder().Digest(Digest::SHA_2_256)));
+ AbortIfNeeded();
+}
+
+/*
* SigningOperationsTest.AesEcbSign
*
* Verifies that attempts to use AES keys to sign fail in the correct way.
@@ -1474,6 +2582,26 @@
}
/*
+ * SigningOperationsTest.HmacSha256InvalidMacLength
+ *
+ * Verifies that HMAC fails in the correct way when asked to generate a MAC whose length is
+ * not a multiple of 8.
+ */
+TEST_P(SigningOperationsTest, HmacSha256InvalidMacLength) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .HmacKey(128)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, 160)));
+ AuthorizationSet output_params;
+ EXPECT_EQ(ErrorCode::UNSUPPORTED_MAC_LENGTH, Begin(KeyPurpose::SIGN, key_blob_,
+ AuthorizationSetBuilder()
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_MAC_LENGTH, 161),
+ &output_params));
+}
+
+/*
* SigningOperationsTest.HmacSha256TooSmallMacLength
*
* Verifies that HMAC fails in the correct way when asked to generate a MAC smaller than the
@@ -1590,7 +2718,7 @@
}
/*
- * VerificationOperationsTest.RsaSuccess
+ * VerificationOperationsTest.RsaAllPaddingsAndDigests
*
* Verifies RSA signature/verification for all padding modes and digests.
*/
@@ -1686,7 +2814,7 @@
}
/*
- * VerificationOperationsTest.RsaSuccess
+ * VerificationOperationsTest.RsaAllDigestsAndCurves
*
* Verifies ECDSA signature/verification for all digests and curves.
*/
@@ -1892,6 +3020,48 @@
}
/*
+ * ImportKeyTest.RsaSuccessWithoutParams
+ *
+ * Verifies that importing and using an RSA key pair without specifying parameters
+ * works correctly.
+ */
+TEST_P(ImportKeyTest, RsaSuccessWithoutParams) {
+ 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)
+ .SigningKey()
+ .Authorization(TAG_ALGORITHM, Algorithm::RSA)
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_PSS)
+ .SetDefaultValidity(),
+ KeyFormat::PKCS8, key));
+
+ // Key size and public exponent are determined from the imported key material.
+ CheckCryptoParam(TAG_KEY_SIZE, key_size);
+ CheckCryptoParam(TAG_RSA_PUBLIC_EXPONENT, 65537U);
+
+ CheckCryptoParam(TAG_ALGORITHM, Algorithm::RSA);
+ CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
+ CheckCryptoParam(TAG_PADDING, PaddingMode::RSA_PSS);
+ CheckOrigin();
+
+ string message(1024 / 8, 'a');
+ auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::RSA_PSS);
+ string signature = SignMessage(message, params);
+ VerifyMessage(message, signature, params);
+}
+
+/*
* ImportKeyTest.RsaKeySizeMismatch
*
* Verifies that importing an RSA key pair with a size that doesn't match the key fails in the
@@ -2086,7 +3256,113 @@
}
/*
- * ImportKeyTest.AesSuccess
+ * ImportKeyTest.AesFailure
+ *
+ * Verifies that importing an invalid AES key fails.
+ */
+TEST_P(ImportKeyTest, AesFailure) {
+ string key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ uint32_t bitlen = key.size() * 8;
+ for (uint32_t key_size : {bitlen - 1, bitlen + 1, bitlen - 8, bitlen + 8}) {
+ // Explicit key size doesn't match that of the provided key.
+ auto result = ImportKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(key_size)
+ .EcbMode()
+ .Padding(PaddingMode::PKCS7),
+ KeyFormat::RAW, key);
+ ASSERT_TRUE(result == ErrorCode::IMPORT_PARAMETER_MISMATCH ||
+ result == ErrorCode::UNSUPPORTED_KEY_SIZE)
+ << "unexpected result: " << result;
+ }
+
+ // Explicit key size matches that of the provided key, but it's not a valid size.
+ string long_key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+ ImportKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(long_key.size() * 8)
+ .EcbMode()
+ .Padding(PaddingMode::PKCS7),
+ KeyFormat::RAW, long_key));
+ string short_key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+ ImportKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(short_key.size() * 8)
+ .EcbMode()
+ .Padding(PaddingMode::PKCS7),
+ KeyFormat::RAW, short_key));
+}
+
+/*
+ * ImportKeyTest.TripleDesSuccess
+ *
+ * Verifies that importing and using a 3DES key works.
+ */
+TEST_P(ImportKeyTest, TripleDesSuccess) {
+ string key = hex2str("a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f7358");
+ ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .TripleDesEncryptionKey(168)
+ .EcbMode()
+ .Padding(PaddingMode::PKCS7),
+ KeyFormat::RAW, key));
+
+ CheckCryptoParam(TAG_ALGORITHM, Algorithm::TRIPLE_DES);
+ CheckCryptoParam(TAG_KEY_SIZE, 168U);
+ CheckCryptoParam(TAG_PADDING, PaddingMode::PKCS7);
+ CheckCryptoParam(TAG_BLOCK_MODE, BlockMode::ECB);
+ CheckOrigin();
+
+ string message = "Hello World!";
+ auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+ string ciphertext = EncryptMessage(message, params);
+ string plaintext = DecryptMessage(ciphertext, params);
+ EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * ImportKeyTest.TripleDesFailure
+ *
+ * Verifies that importing an invalid 3DES key fails.
+ */
+TEST_P(ImportKeyTest, TripleDesFailure) {
+ string key = hex2str("a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f7358");
+ uint32_t bitlen = key.size() * 8;
+ for (uint32_t key_size : {bitlen - 1, bitlen + 1, bitlen - 8, bitlen + 8}) {
+ // Explicit key size doesn't match that of the provided key.
+ auto result = ImportKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .TripleDesEncryptionKey(key_size)
+ .EcbMode()
+ .Padding(PaddingMode::PKCS7),
+ KeyFormat::RAW, key);
+ ASSERT_TRUE(result == ErrorCode::IMPORT_PARAMETER_MISMATCH ||
+ result == ErrorCode::UNSUPPORTED_KEY_SIZE)
+ << "unexpected result: " << result;
+ }
+ // Explicit key size matches that of the provided key, but it's not a valid size.
+ string long_key = hex2str("a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f7358");
+ ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+ ImportKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .TripleDesEncryptionKey(long_key.size() * 8)
+ .EcbMode()
+ .Padding(PaddingMode::PKCS7),
+ KeyFormat::RAW, long_key));
+ string short_key = hex2str("a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f7358");
+ ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+ ImportKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .TripleDesEncryptionKey(short_key.size() * 8)
+ .EcbMode()
+ .Padding(PaddingMode::PKCS7),
+ KeyFormat::RAW, short_key));
+}
+
+/*
+ * ImportKeyTest.HmacKeySuccess
*
* Verifies that importing and using an HMAC key works.
*/
@@ -2112,57 +3388,230 @@
INSTANTIATE_KEYMINT_AIDL_TEST(ImportKeyTest);
auto wrapped_key = hex2str(
- "3082017902010004820100934bf94e2aa28a3f83c9f79297250262fbe3276b5a1c91159bbfa3ef8957aac8"
- "4b59b30b455a79c2973480823d8b3863c3deef4a8e243590268d80e18751a0e130f67ce6a1ace9f79b95e0"
- "97474febc981195b1d13a69086c0863f66a7b7fdb48792227b1ac5e2489febdf087ab5486483033a6f001c"
- "a5d1ec1e27f5c30f4cec2642074a39ae68aee552e196627a8e3d867e67a8c01b11e75f13cca0a97ab668b5"
- "0cda07a8ecb7cd8e3dd7009c9636534f6f239cffe1fc8daa466f78b676c7119efb96bce4e69ca2a25d0b34"
- "ed9c3ff999b801597d5220e307eaa5bee507fb94d1fa69f9e519b2de315bac92c36f2ea1fa1df4478c0dde"
- "deae8c70e0233cd098040cd796b02c370f1fa4cc0124f1302e0201033029a1083106020100020101a20302"
- "0120a30402020100a4053103020101a6053103020140bf83770205000420ccd540855f833a5e1480bfd2d3"
- "6faf3aeee15df5beabe2691bc82dde2a7aa910041064c9f689c60ff6223ab6e6999e0eb6e5");
+ // IKeyMintDevice.aidl
+ "30820179" // SEQUENCE length 0x179 (SecureKeyWrapper) {
+ "020100" // INTEGER length 1 value 0x00 (version)
+ "04820100" // OCTET STRING length 0x100 (encryptedTransportKey)
+ "934bf94e2aa28a3f83c9f79297250262"
+ "fbe3276b5a1c91159bbfa3ef8957aac8"
+ "4b59b30b455a79c2973480823d8b3863"
+ "c3deef4a8e243590268d80e18751a0e1"
+ "30f67ce6a1ace9f79b95e097474febc9"
+ "81195b1d13a69086c0863f66a7b7fdb4"
+ "8792227b1ac5e2489febdf087ab54864"
+ "83033a6f001ca5d1ec1e27f5c30f4cec"
+ "2642074a39ae68aee552e196627a8e3d"
+ "867e67a8c01b11e75f13cca0a97ab668"
+ "b50cda07a8ecb7cd8e3dd7009c963653"
+ "4f6f239cffe1fc8daa466f78b676c711"
+ "9efb96bce4e69ca2a25d0b34ed9c3ff9"
+ "99b801597d5220e307eaa5bee507fb94"
+ "d1fa69f9e519b2de315bac92c36f2ea1"
+ "fa1df4478c0ddedeae8c70e0233cd098"
+ "040c" // OCTET STRING length 0x0c (initializationVector)
+ "d796b02c370f1fa4cc0124f1"
+ "302e" // SEQUENCE length 0x2e (KeyDescription) {
+ "020103" // INTEGER length 1 value 0x03 (keyFormat = RAW)
+ "3029" // SEQUENCE length 0x29 (AuthorizationList) {
+ "a108" // [1] context-specific constructed tag=1 length 0x08 { (purpose)
+ "3106" // SET length 0x06
+ "020100" // INTEGER length 1 value 0x00 (Encrypt)
+ "020101" // INTEGER length 1 value 0x01 (Decrypt)
+ // } end SET
+ // } end [1]
+ "a203" // [2] context-specific constructed tag=2 length 0x02 { (algorithm)
+ "020120" // INTEGER length 1 value 0x20 (AES)
+ // } end [2]
+ "a304" // [3] context-specific constructed tag=3 length 0x04 { (keySize)
+ "02020100" // INTEGER length 2 value 0x100
+ // } end [3]
+ "a405" // [4] context-specific constructed tag=4 length 0x05 { (blockMode)
+ "3103" // SET length 0x03 {
+ "020101" // INTEGER length 1 value 0x01 (ECB)
+ // } end SET
+ // } end [4]
+ "a605" // [6] context-specific constructed tag=6 length 0x05 { (padding)
+ "3103" // SET length 0x03 {
+ "020140" // INTEGER length 1 value 0x40 (PKCS7)
+ // } end SET
+ // } end [5]
+ "bf837702" // [503] context-specific constructed tag=503=0x1F7 length 0x02 {
+ // (noAuthRequired)
+ "0500" // NULL
+ // } end [503]
+ // } end SEQUENCE (AuthorizationList)
+ // } end SEQUENCE (KeyDescription)
+ "0420" // OCTET STRING length 0x20 (encryptedKey)
+ "ccd540855f833a5e1480bfd2d36faf3a"
+ "eee15df5beabe2691bc82dde2a7aa910"
+ "0410" // OCTET STRING length 0x10 (tag)
+ "64c9f689c60ff6223ab6e6999e0eb6e5"
+ // } SEQUENCE (SecureKeyWrapper)
+);
auto wrapped_key_masked = hex2str(
- "3082017902010004820100aad93ed5924f283b4bb5526fbe7a1412f9d9749ec30db9062b29e574a8546f33"
- "c88732452f5b8e6a391ee76c39ed1712c61d8df6213dec1cffbc17a8c6d04c7b30893d8daa9b2015213e21"
- "946821553207f8f9931c4caba23ed3bee28b36947e47f10e0a5c3dc51c988a628daad3e5e1f4005e79c2d5"
- "a96c284b4b8d7e4948f331e5b85dd5a236f85579f3ea1d1b848487470bdb0ab4f81a12bee42c99fe0df4be"
- "e3759453e69ad1d68a809ce06b949f7694a990429b2fe81e066ff43e56a21602db70757922a4bcc23ab89f"
- "1e35da77586775f423e519c2ea394caf48a28d0c8020f1dcf6b3a68ec246f615ae96dae9a079b1f6eb9590"
- "33c1af5c125fd94168040c6d9721d08589581ab49204a3302e0201033029a1083106020100020101a20302"
- "0120a30402020100a4053103020101a6053103020140bf83770205000420a61c6e247e25b3e6e69aa78eb0"
- "3c2d4ac20d1f99a9a024a76f35c8e2cab9b68d04102560c70109ae67c030f00b98b512a670");
+ // IKeyMintDevice.aidl
+ "30820179" // SEQUENCE length 0x179 (SecureKeyWrapper) {
+ "020100" // INTEGER length 1 value 0x00 (version)
+ "04820100" // OCTET STRING length 0x100 (encryptedTransportKey)
+ "aad93ed5924f283b4bb5526fbe7a1412"
+ "f9d9749ec30db9062b29e574a8546f33"
+ "c88732452f5b8e6a391ee76c39ed1712"
+ "c61d8df6213dec1cffbc17a8c6d04c7b"
+ "30893d8daa9b2015213e219468215532"
+ "07f8f9931c4caba23ed3bee28b36947e"
+ "47f10e0a5c3dc51c988a628daad3e5e1"
+ "f4005e79c2d5a96c284b4b8d7e4948f3"
+ "31e5b85dd5a236f85579f3ea1d1b8484"
+ "87470bdb0ab4f81a12bee42c99fe0df4"
+ "bee3759453e69ad1d68a809ce06b949f"
+ "7694a990429b2fe81e066ff43e56a216"
+ "02db70757922a4bcc23ab89f1e35da77"
+ "586775f423e519c2ea394caf48a28d0c"
+ "8020f1dcf6b3a68ec246f615ae96dae9"
+ "a079b1f6eb959033c1af5c125fd94168"
+ "040c" // OCTET STRING length 0x0c (initializationVector)
+ "6d9721d08589581ab49204a3"
+ "302e" // SEQUENCE length 0x2e (KeyDescription) {
+ "020103" // INTEGER length 1 value 0x03 (keyFormat = RAW)
+ "3029" // SEQUENCE length 0x29 (AuthorizationList) {
+ "a108" // [1] context-specific constructed tag=1 length 0x08 { (purpose)
+ "3106" // SET length 0x06
+ "020100" // INTEGER length 1 value 0x00 (Encrypt)
+ "020101" // INTEGER length 1 value 0x01 (Decrypt)
+ // } end SET
+ // } end [1]
+ "a203" // [2] context-specific constructed tag=2 length 0x02 { (algorithm)
+ "020120" // INTEGER length 1 value 0x20 (AES)
+ // } end [2]
+ "a304" // [3] context-specific constructed tag=3 length 0x04 { (keySize)
+ "02020100" // INTEGER length 2 value 0x100
+ // } end [3]
+ "a405" // [4] context-specific constructed tag=4 length 0x05 { (blockMode
+ "3103" // SET length 0x03 {
+ "020101" // INTEGER length 1 value 0x01 (ECB)
+ // } end SET
+ // } end [4]
+ "a605" // [6] context-specific constructed tag=6 length 0x05 { (padding)
+ "3103" // SET length 0x03 {
+ "020140" // INTEGER length 1 value 0x40 (PKCS7)
+ // } end SET
+ // } end [5]
+ "bf837702" // [503] context-specific constructed tag=503=0x1F7 length 0x02 {
+ // (noAuthRequired)
+ "0500" // NULL
+ // } end [503]
+ // } end SEQUENCE (AuthorizationList)
+ // } end SEQUENCE (KeyDescription)
+ "0420" // OCTET STRING length 0x20 (encryptedKey)
+ "a61c6e247e25b3e6e69aa78eb03c2d4a"
+ "c20d1f99a9a024a76f35c8e2cab9b68d"
+ "0410" // OCTET STRING length 0x10 (tag)
+ "2560c70109ae67c030f00b98b512a670"
+ // } SEQUENCE (SecureKeyWrapper)
+);
auto wrapping_key = hex2str(
- "308204be020100300d06092a864886f70d0101010500048204a8308204a40201000282010100aec367931d"
- "8900ce56b0067f7d70e1fc653f3f34d194c1fed50018fb43db937b06e673a837313d56b1c725150a3fef86"
- "acbddc41bb759c2854eae32d35841efb5c18d82bc90a1cb5c1d55adf245b02911f0b7cda88c421ff0ebafe"
- "7c0d23be312d7bd5921ffaea1347c157406fef718f682643e4e5d33c6703d61c0cf7ac0bf4645c11f5c137"
- "4c3886427411c449796792e0bef75dec858a2123c36753e02a95a96d7c454b504de385a642e0dfc3e60ac3"
- "a7ee4991d0d48b0172a95f9536f02ba13cecccb92b727db5c27e5b2f5cec09600b286af5cf14c42024c61d"
- "dfe71c2a8d7458f185234cb00e01d282f10f8fc6721d2aed3f4833cca2bd8fa62821dd5502030100010282"
- "0100431447b6251908112b1ee76f99f3711a52b6630960046c2de70de188d833f8b8b91e4d785caeeeaf4f"
- "0f74414e2cda40641f7fe24f14c67a88959bdb27766df9e710b630a03adc683b5d2c43080e52bee71e9eae"
- "b6de297a5fea1072070d181c822bccff087d63c940ba8a45f670feb29fb4484d1c95e6d2579ba02aae0a00"
- "900c3ebf490e3d2cd7ee8d0e20c536e4dc5a5097272888cddd7e91f228b1c4d7474c55b8fcd618c4a957bb"
- "ddd5ad7407cc312d8d98a5caf7e08f4a0d6b45bb41c652659d5a5ba05b663737a8696281865ba20fbdd7f8"
- "51e6c56e8cbe0ddbbf24dc03b2d2cb4c3d540fb0af52e034a2d06698b128e5f101e3b51a34f8d8b4f86181"
- "02818100de392e18d682c829266cc3454e1d6166242f32d9a1d10577753e904ea7d08bff841be5bac82a16"
- "4c5970007047b8c517db8f8f84e37bd5988561bdf503d4dc2bdb38f885434ae42c355f725c9a60f91f0788"
- "e1f1a97223b524b5357fdf72e2f696bab7d78e32bf92ba8e1864eab1229e91346130748a6e3c124f9149d7"
- "1c743502818100c95387c0f9d35f137b57d0d65c397c5e21cc251e47008ed62a542409c8b6b6ac7f8967b3"
- "863ca645fcce49582a9aa17349db6c4a95affdae0dae612e1afac99ed39a2d934c880440aed8832f984316"
- "3a47f27f392199dc1202f9a0f9bd08308007cb1e4e7f58309366a7de25f7c3c9b880677c068e1be936e812"
- "88815252a8a102818057ff8ca1895080b2cae486ef0adfd791fb0235c0b8b36cd6c136e52e4085f4ea5a06"
- "3212a4f105a3764743e53281988aba073f6e0027298e1c4378556e0efca0e14ece1af76ad0b030f27af6f0"
- "ab35fb73a060d8b1a0e142fa2647e93b32e36d8282ae0a4de50ab7afe85500a16f43a64719d6e2b9439823"
- "719cd08bcd03178102818100ba73b0bb28e3f81e9bd1c568713b101241acc607976c4ddccc90e65b6556ca"
- "31516058f92b6e09f3b160ff0e374ec40d78ae4d4979fde6ac06a1a400c61dd31254186af30b22c10582a8"
- "a43e34fe949c5f3b9755bae7baa7b7b7a6bd03b38cef55c86885fc6c1978b9cee7ef33da507c9df6b9277c"
- "ff1e6aaa5d57aca528466102818100c931617c77829dfb1270502be9195c8f2830885f57dba869536811e6"
- "864236d0c4736a0008a145af36b8357a7c3d139966d04c4e00934ea1aede3bb6b8ec841dc95e3f579751e2"
- "bfdfe27ae778983f959356210723287b0affcc9f727044d48c373f1babde0724fa17a4fd4da0902c7c9b9b"
- "f27ba61be6ad02dfddda8f4e6822");
+ // RFC 5208 s5
+ "308204be" // SEQUENCE length 0x4be (PrivateKeyInfo) {
+ "020100" // INTEGER length 1 value 0x00 (version)
+ "300d" // SEQUENCE length 0x0d (AlgorithmIdentifier) {
+ "0609" // OBJECT IDENTIFIER length 0x09 (algorithm)
+ "2a864886f70d010101" // 1.2.840.113549.1.1.1 (RSAES-PKCS1-v1_5 encryption scheme)
+ "0500" // NULL (parameters)
+ // } SEQUENCE (AlgorithmIdentifier)
+ "048204a8" // OCTET STRING len 0x4a8 (privateKey), which contains...
+ // RFC 8017 A.1.2
+ "308204a4" // SEQUENCE len 0x4a4 (RSAPrivateKey) {
+ "020100" // INTEGER length 1 value 0x00 (version)
+ "02820101" // INTEGER length 0x0101 (modulus) value...
+ "00aec367931d8900ce56b0067f7d70e1" // 0x10
+ "fc653f3f34d194c1fed50018fb43db93" // 0x20
+ "7b06e673a837313d56b1c725150a3fef" // 0x30
+ "86acbddc41bb759c2854eae32d35841e" // 0x40
+ "fb5c18d82bc90a1cb5c1d55adf245b02" // 0x50
+ "911f0b7cda88c421ff0ebafe7c0d23be" // 0x60
+ "312d7bd5921ffaea1347c157406fef71" // 0x70
+ "8f682643e4e5d33c6703d61c0cf7ac0b" // 0x80
+ "f4645c11f5c1374c3886427411c44979" // 0x90
+ "6792e0bef75dec858a2123c36753e02a" // 0xa0
+ "95a96d7c454b504de385a642e0dfc3e6" // 0xb0
+ "0ac3a7ee4991d0d48b0172a95f9536f0" // 0xc0
+ "2ba13cecccb92b727db5c27e5b2f5cec" // 0xd0
+ "09600b286af5cf14c42024c61ddfe71c" // 0xe0
+ "2a8d7458f185234cb00e01d282f10f8f" // 0xf0
+ "c6721d2aed3f4833cca2bd8fa62821dd" // 0x100
+ "55" // 0x101
+ "0203010001" // INTEGER length 3 value 0x10001 (publicExponent)
+ "02820100" // INTEGER length 0x100 (privateExponent) value...
+ "431447b6251908112b1ee76f99f3711a" // 0x10
+ "52b6630960046c2de70de188d833f8b8" // 0x20
+ "b91e4d785caeeeaf4f0f74414e2cda40" // 0x30
+ "641f7fe24f14c67a88959bdb27766df9" // 0x40
+ "e710b630a03adc683b5d2c43080e52be" // 0x50
+ "e71e9eaeb6de297a5fea1072070d181c" // 0x60
+ "822bccff087d63c940ba8a45f670feb2" // 0x70
+ "9fb4484d1c95e6d2579ba02aae0a0090" // 0x80
+ "0c3ebf490e3d2cd7ee8d0e20c536e4dc" // 0x90
+ "5a5097272888cddd7e91f228b1c4d747" // 0xa0
+ "4c55b8fcd618c4a957bbddd5ad7407cc" // 0xb0
+ "312d8d98a5caf7e08f4a0d6b45bb41c6" // 0xc0
+ "52659d5a5ba05b663737a8696281865b" // 0xd0
+ "a20fbdd7f851e6c56e8cbe0ddbbf24dc" // 0xe0
+ "03b2d2cb4c3d540fb0af52e034a2d066" // 0xf0
+ "98b128e5f101e3b51a34f8d8b4f86181" // 0x100
+ "028181" // INTEGER length 0x81 (prime1) value...
+ "00de392e18d682c829266cc3454e1d61" // 0x10
+ "66242f32d9a1d10577753e904ea7d08b" // 0x20
+ "ff841be5bac82a164c5970007047b8c5" // 0x30
+ "17db8f8f84e37bd5988561bdf503d4dc" // 0x40
+ "2bdb38f885434ae42c355f725c9a60f9" // 0x50
+ "1f0788e1f1a97223b524b5357fdf72e2" // 0x60
+ "f696bab7d78e32bf92ba8e1864eab122" // 0x70
+ "9e91346130748a6e3c124f9149d71c74" // 0x80
+ "35"
+ "028181" // INTEGER length 0x81 (prime2) value...
+ "00c95387c0f9d35f137b57d0d65c397c" // 0x10
+ "5e21cc251e47008ed62a542409c8b6b6" // 0x20
+ "ac7f8967b3863ca645fcce49582a9aa1" // 0x30
+ "7349db6c4a95affdae0dae612e1afac9" // 0x40
+ "9ed39a2d934c880440aed8832f984316" // 0x50
+ "3a47f27f392199dc1202f9a0f9bd0830" // 0x60
+ "8007cb1e4e7f58309366a7de25f7c3c9" // 0x70
+ "b880677c068e1be936e81288815252a8" // 0x80
+ "a1"
+ "028180" // INTEGER length 0x80 (exponent1) value...
+ "57ff8ca1895080b2cae486ef0adfd791" // 0x10
+ "fb0235c0b8b36cd6c136e52e4085f4ea" // 0x20
+ "5a063212a4f105a3764743e53281988a" // 0x30
+ "ba073f6e0027298e1c4378556e0efca0" // 0x40
+ "e14ece1af76ad0b030f27af6f0ab35fb" // 0x50
+ "73a060d8b1a0e142fa2647e93b32e36d" // 0x60
+ "8282ae0a4de50ab7afe85500a16f43a6" // 0x70
+ "4719d6e2b9439823719cd08bcd031781" // 0x80
+ "028181" // INTEGER length 0x81 (exponent2) value...
+ "00ba73b0bb28e3f81e9bd1c568713b10" // 0x10
+ "1241acc607976c4ddccc90e65b6556ca" // 0x20
+ "31516058f92b6e09f3b160ff0e374ec4" // 0x30
+ "0d78ae4d4979fde6ac06a1a400c61dd3" // 0x40
+ "1254186af30b22c10582a8a43e34fe94" // 0x50
+ "9c5f3b9755bae7baa7b7b7a6bd03b38c" // 0x60
+ "ef55c86885fc6c1978b9cee7ef33da50" // 0x70
+ "7c9df6b9277cff1e6aaa5d57aca52846" // 0x80
+ "61"
+ "028181" // INTEGER length 0x81 (coefficient) value...
+ "00c931617c77829dfb1270502be9195c" // 0x10
+ "8f2830885f57dba869536811e6864236" // 0x20
+ "d0c4736a0008a145af36b8357a7c3d13" // 0x30
+ "9966d04c4e00934ea1aede3bb6b8ec84" // 0x40
+ "1dc95e3f579751e2bfdfe27ae778983f" // 0x50
+ "959356210723287b0affcc9f727044d4" // 0x60
+ "8c373f1babde0724fa17a4fd4da0902c" // 0x70
+ "7c9b9bf27ba61be6ad02dfddda8f4e68" // 0x80
+ "22"
+ // } SEQUENCE
+ // } SEQUENCE ()
+);
string zero_masking_key =
hex2str("0000000000000000000000000000000000000000000000000000000000000000");
@@ -2191,6 +3640,36 @@
EXPECT_EQ(message, plaintext);
}
+/*
+ * ImportWrappedKeyTest.SuccessSidsIgnored
+ *
+ * Verifies that password_sid and biometric_sid are ignored on import if the authorizations don't
+ * include Tag:USER_SECURE_ID.
+ */
+TEST_P(ImportWrappedKeyTest, SuccessSidsIgnored) {
+ auto wrapping_key_desc = AuthorizationSetBuilder()
+ .RsaEncryptionKey(2048, 65537)
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_OAEP)
+ .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY)
+ .SetDefaultValidity();
+
+ int64_t password_sid = 42;
+ int64_t biometric_sid = 24;
+ ASSERT_EQ(ErrorCode::OK,
+ ImportWrappedKey(wrapped_key, wrapping_key, wrapping_key_desc, zero_masking_key,
+ AuthorizationSetBuilder()
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_OAEP),
+ password_sid, biometric_sid));
+
+ string message = "Hello World!";
+ auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+ string ciphertext = EncryptMessage(message, params);
+ string plaintext = DecryptMessage(ciphertext, params);
+ EXPECT_EQ(message, plaintext);
+}
+
TEST_P(ImportWrappedKeyTest, SuccessMasked) {
auto wrapping_key_desc = AuthorizationSetBuilder()
.RsaEncryptionKey(2048, 65537)
@@ -2237,6 +3716,36 @@
.Padding(PaddingMode::RSA_OAEP)));
}
+TEST_P(ImportWrappedKeyTest, WrongPaddingMode) {
+ auto wrapping_key_desc = AuthorizationSetBuilder()
+ .RsaEncryptionKey(2048, 65537)
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_PSS)
+ .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY)
+ .SetDefaultValidity();
+
+ ASSERT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE,
+ ImportWrappedKey(wrapped_key, wrapping_key, wrapping_key_desc, zero_masking_key,
+ AuthorizationSetBuilder()
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_OAEP)));
+}
+
+TEST_P(ImportWrappedKeyTest, WrongDigest) {
+ auto wrapping_key_desc = AuthorizationSetBuilder()
+ .RsaEncryptionKey(2048, 65537)
+ .Digest(Digest::SHA_2_512)
+ .Padding(PaddingMode::RSA_OAEP)
+ .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY)
+ .SetDefaultValidity();
+
+ ASSERT_EQ(ErrorCode::INCOMPATIBLE_DIGEST,
+ ImportWrappedKey(wrapped_key, wrapping_key, wrapping_key_desc, zero_masking_key,
+ AuthorizationSetBuilder()
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_OAEP)));
+}
+
INSTANTIATE_KEYMINT_AIDL_TEST(ImportWrappedKeyTest);
typedef KeyMintAidlTestBase EncryptionOperationsTest;
@@ -2247,22 +3756,26 @@
* Verifies that raw RSA encryption works.
*/
TEST_P(EncryptionOperationsTest, RsaNoPaddingSuccess) {
- ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
- .Authorization(TAG_NO_AUTH_REQUIRED)
- .RsaEncryptionKey(2048, 65537)
- .Padding(PaddingMode::NONE)
- .SetDefaultValidity()));
+ for (uint64_t exponent : {3, 65537}) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaEncryptionKey(2048, exponent)
+ .Padding(PaddingMode::NONE)
+ .SetDefaultValidity()));
- string message = string(2048 / 8, 'a');
- auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
- string ciphertext1 = EncryptMessage(message, params);
- EXPECT_EQ(2048U / 8, ciphertext1.size());
+ string message = string(2048 / 8, 'a');
+ auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
+ string ciphertext1 = EncryptMessage(message, params);
+ EXPECT_EQ(2048U / 8, ciphertext1.size());
- string ciphertext2 = EncryptMessage(message, params);
- EXPECT_EQ(2048U / 8, ciphertext2.size());
+ string ciphertext2 = EncryptMessage(message, params);
+ EXPECT_EQ(2048U / 8, ciphertext2.size());
- // Unpadded RSA is deterministic
- EXPECT_EQ(ciphertext1, ciphertext2);
+ // Unpadded RSA is deterministic
+ EXPECT_EQ(ciphertext1, ciphertext2);
+
+ CheckedDeleteKey();
+ }
}
/*
@@ -2389,14 +3902,31 @@
.Padding(PaddingMode::RSA_OAEP)
.Digest(Digest::NONE)
.SetDefaultValidity()));
- string message = "Hello World!";
auto params = AuthorizationSetBuilder().Padding(PaddingMode::RSA_OAEP).Digest(Digest::NONE);
EXPECT_EQ(ErrorCode::INCOMPATIBLE_DIGEST, Begin(KeyPurpose::ENCRYPT, params));
}
/*
- * EncryptionOperationsTest.RsaOaepInvalidDigest
+ * EncryptionOperationsTest.RsaOaepInvalidPadding
+ *
+ * Verifies that RSA-OAEP encryption operations fail in the correct way when asked to operate
+ * with a padding value that is only suitable for signing/verifying.
+ */
+TEST_P(EncryptionOperationsTest, RsaOaepInvalidPadding) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaEncryptionKey(2048, 65537)
+ .Padding(PaddingMode::RSA_PSS)
+ .Digest(Digest::NONE)
+ .SetDefaultValidity()));
+
+ auto params = AuthorizationSetBuilder().Padding(PaddingMode::RSA_PSS).Digest(Digest::NONE);
+ EXPECT_EQ(ErrorCode::UNSUPPORTED_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.RsaOaepDecryptWithWrongDigest
*
* Verifies that RSA-OAEP encryption operations fail in the correct way when asked to decrypt
* with a different digest than was used to encrypt.
@@ -2595,7 +4125,7 @@
/*
* EncryptionOperationsTest.RsaPkcs1TooLarge
*
- * Verifies that RSA PKCS encryption fails in the correct way when the mssage is too large.
+ * Verifies that RSA PKCS encryption fails in the correct way when the message is too large.
*/
TEST_P(EncryptionOperationsTest, RsaPkcs1TooLarge) {
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
@@ -2680,7 +4210,49 @@
}
/*
- * EncryptionOperationsTest.AesEcbRoundTripSuccess
+ * EncryptionOperationsTest.AesEcbUnknownTag
+ *
+ * Verifies that AES ECB operations ignore unknown tags.
+ */
+TEST_P(EncryptionOperationsTest, AesEcbUnknownTag) {
+ int32_t unknown_tag_value = ((7 << 28) /* TagType:BOOL */ | 150);
+ Tag unknown_tag = static_cast<Tag>(unknown_tag_value);
+ KeyParameter unknown_param;
+ unknown_param.tag = unknown_tag;
+
+ vector<KeyCharacteristics> key_characteristics;
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+ .Padding(PaddingMode::NONE)
+ .Authorization(unknown_param),
+ &key_blob_, &key_characteristics));
+ ASSERT_GT(key_blob_.size(), 0U);
+
+ // Unknown tags should not be returned in key characteristics.
+ AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
+ AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
+ EXPECT_EQ(hw_enforced.find(unknown_tag), -1);
+ EXPECT_EQ(sw_enforced.find(unknown_tag), -1);
+
+ // Encrypt without mentioning the unknown parameter.
+ auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
+ string message = "12345678901234567890123456789012";
+ string ciphertext = EncryptMessage(message, params);
+ EXPECT_EQ(message.size(), ciphertext.size());
+
+ // Decrypt including the unknown parameter.
+ auto decrypt_params = AuthorizationSetBuilder()
+ .BlockMode(BlockMode::ECB)
+ .Padding(PaddingMode::NONE)
+ .Authorization(unknown_param);
+ string plaintext = DecryptMessage(ciphertext, decrypt_params);
+ EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesWrongMode
*
* Verifies that AES encryption fails in the correct way when an unauthorized mode is specified.
*/
@@ -2690,11 +4262,8 @@
.AesEncryptionKey(128)
.Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
.Padding(PaddingMode::NONE)));
-
ASSERT_GT(key_blob_.size(), 0U);
- // Two-block message.
- string message = "12345678901234567890123456789012";
EXPECT_EQ(
ErrorCode::INCOMPATIBLE_BLOCK_MODE,
Begin(KeyPurpose::ENCRYPT,
@@ -2702,6 +4271,55 @@
}
/*
+ * EncryptionOperationsTest.AesWrongPadding
+ *
+ * Verifies that AES encryption fails in the correct way when an unauthorized padding is specified.
+ */
+TEST_P(EncryptionOperationsTest, AesWrongPadding) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
+ .Padding(PaddingMode::NONE)));
+ ASSERT_GT(key_blob_.size(), 0U);
+
+ EXPECT_EQ(
+ ErrorCode::INCOMPATIBLE_PADDING_MODE,
+ Begin(KeyPurpose::ENCRYPT,
+ AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::PKCS7)));
+}
+
+/*
+ * EncryptionOperationsTest.AesInvalidParams
+ *
+ * Verifies that AES encryption fails in the correct way when an duplicate parameters are specified.
+ */
+TEST_P(EncryptionOperationsTest, AesInvalidParams) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+ .Padding(PaddingMode::NONE)
+ .Padding(PaddingMode::PKCS7)));
+ ASSERT_GT(key_blob_.size(), 0U);
+
+ auto result = Begin(KeyPurpose::ENCRYPT, AuthorizationSetBuilder()
+ .BlockMode(BlockMode::CBC)
+ .BlockMode(BlockMode::ECB)
+ .Padding(PaddingMode::NONE));
+ EXPECT_TRUE(result == ErrorCode::INCOMPATIBLE_BLOCK_MODE ||
+ result == ErrorCode::UNSUPPORTED_BLOCK_MODE);
+
+ result = Begin(KeyPurpose::ENCRYPT, AuthorizationSetBuilder()
+ .BlockMode(BlockMode::ECB)
+ .Padding(PaddingMode::NONE)
+ .Padding(PaddingMode::PKCS7));
+ EXPECT_TRUE(result == ErrorCode::INCOMPATIBLE_PADDING_MODE ||
+ result == ErrorCode::UNSUPPORTED_PADDING_MODE);
+}
+
+/*
* EncryptionOperationsTest.AesWrongPurpose
*
* Verifies that AES encryption fails in the correct way when an unauthorized purpose is
@@ -2742,25 +4360,30 @@
}
/*
- * EncryptionOperationsTest.AesEcbNoPaddingWrongInputSize
+ * EncryptionOperationsTest.AesEcbCbcNoPaddingWrongInputSize
*
* Verifies that AES encryption fails in the correct way when provided an input that is not a
* multiple of the block size and no padding is specified.
*/
-TEST_P(EncryptionOperationsTest, AesEcbNoPaddingWrongInputSize) {
- ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
- .Authorization(TAG_NO_AUTH_REQUIRED)
- .AesEncryptionKey(128)
- .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
- .Padding(PaddingMode::NONE)));
- // Message is slightly shorter than two blocks.
- string message(16 * 2 - 1, 'a');
+TEST_P(EncryptionOperationsTest, AesEcbCbcNoPaddingWrongInputSize) {
+ for (BlockMode blockMode : {BlockMode::ECB, BlockMode::CBC}) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, blockMode)
+ .Padding(PaddingMode::NONE)));
+ // Message is slightly shorter than two blocks.
+ string message(16 * 2 - 1, 'a');
- auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
- EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params));
- string ciphertext;
- EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &ciphertext));
- EXPECT_EQ(0U, ciphertext.size());
+ auto params = AuthorizationSetBuilder().BlockMode(blockMode).Padding(PaddingMode::NONE);
+ AuthorizationSet out_params;
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &out_params));
+ string ciphertext;
+ EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &ciphertext));
+ EXPECT_EQ(0U, ciphertext.size());
+
+ CheckedDeleteKey();
+ }
}
/*
@@ -3080,7 +4703,7 @@
}
/*
- * EncryptionOperationsTest.AesCtrInvalidCallerNonce
+ * EncryptionOperationsTest.AesCbcRoundTripSuccess
*
* Verifies that keymint fails correctly when the user supplies an incorrect-size nonce.
*/
@@ -3320,6 +4943,31 @@
}
/*
+ * EncryptionOperationsTest.AesGcmDifferentAutoNonces
+ *
+ * Verifies that encrypting the same data with KeyMint generated nonces produces different outputs.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmDifferentAutoNonces) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+ string aad = "foobar";
+ string message = "123456789012345678901234567890123456";
+
+ string ciphertext1 = EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128);
+ string ciphertext2 = EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128);
+ string ciphertext3 = EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128);
+
+ ASSERT_NE(ciphertext1, ciphertext2);
+ ASSERT_NE(ciphertext1, ciphertext3);
+ ASSERT_NE(ciphertext2, ciphertext3);
+}
+
+/*
* EncryptionOperationsTest.AesGcmTooShortTag
*
* Verifies that AES GCM mode fails correctly when a too-short tag length is specified.
@@ -3547,6 +5195,9 @@
EXPECT_EQ(ErrorCode::OK, Update(message, &ciphertext));
EXPECT_EQ(ErrorCode::INVALID_TAG, UpdateAad("foo"));
+ // The failure should have already cancelled the operation.
+ EXPECT_EQ(ErrorCode::INVALID_OPERATION_HANDLE, Abort());
+
op_ = {};
}
@@ -3746,11 +5397,8 @@
.BlockMode(BlockMode::ECB)
.Authorization(TAG_NO_AUTH_REQUIRED)
.Padding(PaddingMode::NONE)));
- for (size_t i = 0; i < 32; ++i) {
- auto inParams =
- AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
- EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, inParams));
- }
+ auto inParams = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+ EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, inParams));
}
/*
@@ -3916,6 +5564,25 @@
}
/*
+ * EncryptionOperationsTest.TripleDesInvalidCallerIv
+ *
+ * Validates that keymint fails correctly when the user supplies an incorrect-size IV.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesInvalidCallerIv) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .TripleDesEncryptionKey(168)
+ .BlockMode(BlockMode::CBC)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Authorization(TAG_CALLER_NONCE)
+ .Padding(PaddingMode::NONE)));
+ auto params = AuthorizationSetBuilder()
+ .BlockMode(BlockMode::CBC)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_NONCE, AidlBuf("abcdefg"));
+ EXPECT_EQ(ErrorCode::INVALID_NONCE, Begin(KeyPurpose::ENCRYPT, params));
+}
+
+/*
* EncryptionOperationsTest.TripleDesCallerIv
*
* Validates that 3DES keys can allow caller-specified IVs, and use them correctly.
@@ -3953,7 +5620,7 @@
/*
* EncryptionOperationsTest, TripleDesCallerNonceProhibited.
*
- * Verifies that 3DES keys without TAG_CALLER_NONCE do not allow caller-specified IVS.
+ * Verifies that 3DES keys without TAG_CALLER_NONCE do not allow caller-specified IVs.
*/
TEST_P(EncryptionOperationsTest, TripleDesCallerNonceProhibited) {
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
@@ -4001,25 +5668,29 @@
}
/*
- * EncryptionOperationsTest.TripleDesCbcNoPaddingWrongInputSize
+ * EncryptionOperationsTest.TripleDesEcbCbcNoPaddingWrongInputSize
*
* Verifies that unpadded CBC operations reject inputs that are not a multiple of block size.
*/
-TEST_P(EncryptionOperationsTest, TripleDesCbcNoPaddingWrongInputSize) {
- ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
- .TripleDesEncryptionKey(168)
- .BlockMode(BlockMode::CBC)
- .Authorization(TAG_NO_AUTH_REQUIRED)
- .Padding(PaddingMode::NONE)));
- // Message is slightly shorter than two blocks.
- string message = "123456789012345";
+TEST_P(EncryptionOperationsTest, TripleDesEcbCbcNoPaddingWrongInputSize) {
+ for (BlockMode blockMode : {BlockMode::ECB, BlockMode::CBC}) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .TripleDesEncryptionKey(168)
+ .BlockMode(blockMode)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Padding(PaddingMode::NONE)));
+ // Message is slightly shorter than two blocks.
+ string message = "123456789012345";
- auto begin_params =
- AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE);
- AuthorizationSet output_params;
- EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &output_params));
- string ciphertext;
- EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, "", &ciphertext));
+ auto begin_params =
+ AuthorizationSetBuilder().BlockMode(blockMode).Padding(PaddingMode::NONE);
+ AuthorizationSet output_params;
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &output_params));
+ string ciphertext;
+ EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, "", &ciphertext));
+
+ CheckedDeleteKey();
+ }
}
/*
@@ -4427,6 +6098,19 @@
INSTANTIATE_KEYMINT_AIDL_TEST(UsageCountLimitTest);
+typedef KeyMintAidlTestBase GetHardwareInfoTest;
+
+TEST_P(GetHardwareInfoTest, GetHardwareInfo) {
+ // Retrieving hardware info should give the same result each time.
+ KeyMintHardwareInfo info;
+ ASSERT_TRUE(keyMint().getHardwareInfo(&info).isOk());
+ KeyMintHardwareInfo info2;
+ ASSERT_TRUE(keyMint().getHardwareInfo(&info2).isOk());
+ EXPECT_EQ(info, info2);
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(GetHardwareInfoTest);
+
typedef KeyMintAidlTestBase AddEntropyTest;
/*
@@ -4458,6 +6142,16 @@
EXPECT_TRUE(keyMint().addRngEntropy(AidlBuf(string(2 * 1024, 'a'))).isOk());
}
+/*
+ * AddEntropyTest.AddTooLargeEntropy
+ *
+ * Verifies that the addRngEntropy method rejects more than 2KiB of data.
+ */
+TEST_P(AddEntropyTest, AddTooLargeEntropy) {
+ ErrorCode rc = GetReturnErrorCode(keyMint().addRngEntropy(AidlBuf(string(2 * 1024 + 1, 'a'))));
+ EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, rc);
+}
+
INSTANTIATE_KEYMINT_AIDL_TEST(AddEntropyTest);
typedef KeyMintAidlTestBase KeyDeletionTest;
@@ -4569,6 +6263,28 @@
INSTANTIATE_KEYMINT_AIDL_TEST(KeyDeletionTest);
+typedef KeyMintAidlTestBase KeyUpgradeTest;
+
+/**
+ * KeyUpgradeTest.UpgradeInvalidKey
+ *
+ * This test checks that the HAL excepts invalid key blobs..
+ */
+TEST_P(KeyUpgradeTest, UpgradeInvalidKey) {
+ AidlBuf key_blob = AidlBuf("just some garbage data which is not a valid key blob");
+
+ std::vector<uint8_t> new_blob;
+ Status result = keymint_->upgradeKey(key_blob,
+ AuthorizationSetBuilder()
+ .Authorization(TAG_APPLICATION_ID, "clientid")
+ .Authorization(TAG_APPLICATION_DATA, "appdata")
+ .vector_data(),
+ &new_blob);
+ ASSERT_EQ(ErrorCode::INVALID_KEY_BLOB, GetReturnErrorCode(result));
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(KeyUpgradeTest);
+
using UpgradeKeyTest = KeyMintAidlTestBase;
/*
@@ -4637,7 +6353,7 @@
typedef KeyMintAidlTestBase TransportLimitTest;
/*
- * TransportLimitTest.FinishInput
+ * TransportLimitTest.LargeFinishInput
*
* Verifies that passing input data to finish succeeds as expected.
*/
@@ -4792,6 +6508,17 @@
INSTANTIATE_KEYMINT_AIDL_TEST(KeyAgreementTest);
+using DestroyAttestationIdsTest = KeyMintAidlTestBase;
+
+// This is a problematic test, as it can render the device under test permanently unusable.
+// Re-enable and run at your own risk.
+TEST_P(DestroyAttestationIdsTest, DISABLED_DestroyTest) {
+ auto result = DestroyAttestationIds();
+ EXPECT_TRUE(result == ErrorCode::OK || result == ErrorCode::UNIMPLEMENTED);
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(DestroyAttestationIdsTest);
+
using EarlyBootKeyTest = KeyMintAidlTestBase;
TEST_P(EarlyBootKeyTest, CreateEarlyBootKeys) {
@@ -4804,7 +6531,7 @@
CheckedDeleteKey(&ecdsaKeyData.blob);
}
-// This is a more comprenhensive test, but it can only be run on a machine which is still in early
+// This is a more comprehensive test, but it can only be run on a machine which is still in early
// boot stage, which no proper Android device is by the time we can run VTS. To use this,
// un-disable it and modify vold to remove the call to earlyBootEnded(). Running the test will end
// early boot, so you'll have to reboot between runs.
@@ -4872,7 +6599,7 @@
EXPECT_EQ(ErrorCode::OK, UseEcdsaKey(ecdsaKeyData.blob));
ErrorCode rc = GetReturnErrorCode(
- keyMint().deviceLocked(false /* passwordOnly */, {} /* verificationToken */));
+ keyMint().deviceLocked(false /* passwordOnly */, {} /* timestampToken */));
ASSERT_EQ(ErrorCode::OK, rc);
EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseAesKey(aesKeyData.blob));
EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseHmacKey(hmacKeyData.blob));
@@ -4887,16 +6614,6 @@
INSTANTIATE_KEYMINT_AIDL_TEST(UnlockedDeviceRequiredTest);
-using PerformOperationTest = KeyMintAidlTestBase;
-
-TEST_P(PerformOperationTest, RequireUnimplemented) {
- vector<uint8_t> response;
- auto result = keymint_->performOperation({} /* request */, &response);
- ASSERT_EQ(GetReturnErrorCode(result), ErrorCode::UNIMPLEMENTED);
-}
-
-INSTANTIATE_KEYMINT_AIDL_TEST(PerformOperationTest);
-
} // namespace aidl::android::hardware::security::keymint::test
int main(int argc, char** argv) {
@@ -4921,6 +6638,10 @@
} else {
std::cout << "NOT dumping attestations" << std::endl;
}
+ // TODO(drysdale): Remove this flag when available KeyMint devices comply with spec
+ if (std::string(argv[i]) == "--check_patchLevels") {
+ aidl::android::hardware::security::keymint::test::check_patchLevels = true;
+ }
}
}
return RUN_ALL_TESTS();
diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
index 9e52b20..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>
diff --git a/security/keymint/aidl/vts/performance/KeyMintBenchmark.cpp b/security/keymint/aidl/vts/performance/KeyMintBenchmark.cpp
index 6c795f5..54b6fdc 100644
--- a/security/keymint/aidl/vts/performance/KeyMintBenchmark.cpp
+++ b/security/keymint/aidl/vts/performance/KeyMintBenchmark.cpp
@@ -228,8 +228,7 @@
AuthorizationSet* out_params) {
Status result;
BeginResult out;
- result = keymint_->begin(purpose, key_blob_, in_params.vector_data(), HardwareAuthToken(),
- &out);
+ result = keymint_->begin(purpose, key_blob_, in_params.vector_data(), std::nullopt, &out);
if (result.isOk()) {
*out_params = out.params;
op_ = out.operation;
diff --git a/security/keymint/support/Android.bp b/security/keymint/support/Android.bp
index 4c4258b..718133a 100644
--- a/security/keymint/support/Android.bp
+++ b/security/keymint/support/Android.bp
@@ -57,25 +57,8 @@
"include",
],
shared_libs: [
- "libcppcose",
"libcppbor_external",
+ "libcppcose_rkp",
"libcrypto",
],
}
-
-cc_library {
- name: "libcppcose",
- vendor_available: true,
- host_supported: true,
- srcs: [
- "cppcose.cpp",
- ],
- export_include_dirs: [
- "include",
- ],
- shared_libs: [
- "libcppbor_external",
- "libcrypto",
- "liblog",
- ],
-}
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/keymint_support/keymint_tags.h b/security/keymint/support/include/keymint_support/keymint_tags.h
index ae21125..5b2a6f3 100644
--- a/security/keymint/support/include/keymint_support/keymint_tags.h
+++ b/security/keymint/support/include/keymint_support/keymint_tags.h
@@ -153,7 +153,7 @@
TAG_RESET_SINCE_ID_ROTATION_t, TAG_PURPOSE_t, TAG_ALGORITHM_t, TAG_BLOCK_MODE_t,
TAG_DIGEST_t, TAG_PADDING_t, TAG_ORIGIN_t, TAG_USER_AUTH_TYPE_t, TAG_EC_CURVE_t,
TAG_BOOT_PATCHLEVEL_t, TAG_VENDOR_PATCHLEVEL_t, TAG_TRUSTED_CONFIRMATION_REQUIRED_t,
- TAG_TRUSTED_USER_PRESENCE_REQUIRED_t>;
+ TAG_TRUSTED_USER_PRESENCE_REQUIRED_t, TAG_CERTIFICATE_SERIAL_t, TAG_CERTIFICATE_SUBJECT_t>;
template <typename TypedTagType>
struct TypedTag2ValueType;
diff --git a/security/keymint/support/include/keymint_support/keymint_utils.h b/security/keymint/support/include/keymint_support/keymint_utils.h
index 53d5b96..e1ead21 100644
--- a/security/keymint/support/include/keymint_support/keymint_utils.h
+++ b/security/keymint/support/include/keymint_support/keymint_utils.h
@@ -38,5 +38,6 @@
uint32_t getOsVersion();
uint32_t getOsPatchlevel();
+uint32_t getVendorPatchlevel();
} // namespace aidl::android::hardware::security::keymint
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/keymint/support/keymint_utils.cpp b/security/keymint/support/keymint_utils.cpp
index e73d602..2dbdfa8 100644
--- a/security/keymint/support/keymint_utils.cpp
+++ b/security/keymint/support/keymint_utils.cpp
@@ -31,10 +31,11 @@
constexpr size_t kPlatformVersionMatchCount = kSubminorVersionMatch + 1;
constexpr char kPlatformPatchlevelProp[] = "ro.build.version.security_patch";
-constexpr char kPlatformPatchlevelRegex[] = "^([0-9]{4})-([0-9]{2})-[0-9]{2}$";
+constexpr char kVendorPatchlevelProp[] = "ro.vendor.build.security_patch";
+constexpr char kPatchlevelRegex[] = "^([0-9]{4})-([0-9]{2})-[0-9]{2}$";
constexpr size_t kYearMatch = 1;
constexpr size_t kMonthMatch = 2;
-constexpr size_t kPlatformPatchlevelMatchCount = kMonthMatch + 1;
+constexpr size_t kPatchlevelMatchCount = kMonthMatch + 1;
uint32_t match_to_uint32(const char* expression, const regmatch_t& match) {
if (match.rm_so == -1) return 0;
@@ -80,15 +81,14 @@
return getOsVersion(version.c_str());
}
-uint32_t getOsPatchlevel(const char* patchlevel_str) {
+uint32_t getPatchlevel(const char* patchlevel_str) {
regex_t regex;
- if (regcomp(®ex, kPlatformPatchlevelRegex, REG_EXTENDED) != 0) {
+ if (regcomp(®ex, kPatchlevelRegex, REG_EXTENDED) != 0) {
return 0;
}
- regmatch_t matches[kPlatformPatchlevelMatchCount];
- int not_match =
- regexec(®ex, patchlevel_str, kPlatformPatchlevelMatchCount, matches, 0 /* flags */);
+ regmatch_t matches[kPatchlevelMatchCount];
+ int not_match = regexec(®ex, patchlevel_str, kPatchlevelMatchCount, matches, 0 /* flags */);
regfree(®ex);
if (not_match) {
return 0;
@@ -105,7 +105,12 @@
uint32_t getOsPatchlevel() {
std::string patchlevel = wait_and_get_property(kPlatformPatchlevelProp);
- return getOsPatchlevel(patchlevel.c_str());
+ return getPatchlevel(patchlevel.c_str());
+}
+
+uint32_t getVendorPatchlevel() {
+ std::string patchlevel = wait_and_get_property(kVendorPatchlevelProp);
+ return getPatchlevel(patchlevel.c_str());
}
} // namespace aidl::android::hardware::security::keymint
diff --git a/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl b/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl
index 71b4278..2fbd29a 100644
--- a/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl
+++ b/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl
@@ -43,7 +43,7 @@
*
* where:
*
- * ``ISecureClock.TIME_STAMP_MAC_LABEL'' is a sting constant defined in ISecureClock.aidl.
+ * ``ISecureClock.TIME_STAMP_MAC_LABEL'' is a string constant defined in ISecureClock.aidl.
*
* ``H'' is the shared HMAC key (see computeSharedHmac() in ISharedSecret).
*
diff --git a/tetheroffload/control/1.1/ITetheringOffloadCallback.hal b/tetheroffload/control/1.1/ITetheringOffloadCallback.hal
index 7a7d56d..9c74641 100644
--- a/tetheroffload/control/1.1/ITetheringOffloadCallback.hal
+++ b/tetheroffload/control/1.1/ITetheringOffloadCallback.hal
@@ -26,8 +26,8 @@
interface ITetheringOffloadCallback extends @1.0::ITetheringOffloadCallback {
/**
* Called when an asynchronous event is generated by the hardware
- * management process. Events which are common for 1.0 and 1.1 HAL
- * MUST be fired on both 1.0 and 1.1 callback.
+ * management process. Implementations that report events via this callback
+ * should not invoke onEvent of 1.0 HAL.
*/
oneway onEvent_1_1(OffloadCallbackEvent event);
};
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/DescramblerTests.cpp b/tv/tuner/1.0/vts/functional/DescramblerTests.cpp
index 2e27475..67f6bae 100644
--- a/tv/tuner/1.0/vts/functional/DescramblerTests.cpp
+++ b/tv/tuner/1.0/vts/functional/DescramblerTests.cpp
@@ -53,12 +53,15 @@
return failure();
}
- auto status = mCas->setSessionPrivateData(sessionId, hidlPvtData);
- if (status != android::hardware::cas::V1_0::Status::OK) {
- ALOGW("[vts] Failed to set session private data");
- mCas->closeSession(sessionId);
- return failure();
+ if (hidlPvtData.size() > 0) {
+ auto status = mCas->setSessionPrivateData(sessionId, hidlPvtData);
+ if (status != android::hardware::cas::V1_0::Status::OK) {
+ ALOGW("[vts] Failed to set session private data");
+ mCas->closeSession(sessionId);
+ return failure();
+ }
}
+
return success();
}
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..b39abe3 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,13 @@
}
void TunerFilterHidlTest::testTimeFilter(TimeFilterConfig filterConf) {
- if (!filterConf.supportTimeFilter) {
- 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 +74,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 +99,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 +110,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 +147,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,27 +158,36 @@
void TunerRecordHidlTest::recordSingleFilterTest(FilterConfig filterConf,
FrontendConfig frontendConf, DvrConfig dvrConf) {
- if (!frontendConf.enable) {
- return;
- }
- uint32_t feId;
uint32_t demuxId;
sp<IDemux> demux;
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+ mDvrTests.setDemux(demux);
+
+ DvrConfig dvrSourceConfig;
+ if (mLnbId || record.hasFrontendConnection) {
+ uint32_t feId;
+ mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+ ASSERT_TRUE(feId != INVALID_ID);
+ ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+ ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ if (mLnbId) {
+ ASSERT_TRUE(mFrontendTests.setLnb(*mLnbId));
+ }
+ if (frontendConf.isSoftwareFe) {
+ mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[record.dvrSoftwareFeId]);
+ }
+ ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+ mFrontendTests.setDvrTests(mDvrTests);
+ } else {
+ dvrSourceConfig = dvrMap[record.dvrSourceId];
+ ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrSourceConfig.type, dvrSourceConfig.bufferSize));
+ ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrSourceConfig.settings));
+ ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
+ }
+
uint32_t filterId;
sp<IFilter> filter;
-
- mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
- ASSERT_TRUE(feId != INVALID_ID);
- ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
- ASSERT_TRUE(mFrontendTests.setFrontendCallback());
- if (mLnbId) {
- ASSERT_TRUE(mFrontendTests.setLnb(*mLnbId));
- }
- ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
- ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
mFilterTests.setDemux(demux);
- mDvrTests.setDemux(demux);
- mFrontendTests.setDvrTests(mDvrTests);
ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrConf.type, dvrConf.bufferSize));
ASSERT_TRUE(mDvrTests.configDvrRecord(dvrConf.settings));
ASSERT_TRUE(mDvrTests.getDvrRecordMQDescriptor());
@@ -197,34 +201,61 @@
ASSERT_TRUE(mDvrTests.attachFilterToDvr(filter));
ASSERT_TRUE(mDvrTests.startDvrRecord());
ASSERT_TRUE(mFilterTests.startFilter(filterId));
- ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+
+ if (mLnbId || record.hasFrontendConnection) {
+ ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+ } else {
+ // Start DVR Source
+ mDvrTests.startPlaybackInputThread(dvrSourceConfig.playbackInputFile,
+ dvrSourceConfig.settings.playback());
+ ASSERT_TRUE(mDvrTests.startDvrPlayback());
+ }
+
mDvrTests.testRecordOutput();
mDvrTests.stopRecordThread();
- ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+
+ if (mLnbId || record.hasFrontendConnection) {
+ ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+ } else {
+ mDvrTests.stopPlaybackThread();
+ ASSERT_TRUE(mDvrTests.stopDvrPlayback());
+ }
+
ASSERT_TRUE(mFilterTests.stopFilter(filterId));
ASSERT_TRUE(mDvrTests.stopDvrRecord());
ASSERT_TRUE(mDvrTests.detachFilterToDvr(filter));
ASSERT_TRUE(mFilterTests.closeFilter(filterId));
mDvrTests.closeDvrRecord();
+
+ if (mLnbId || record.hasFrontendConnection) {
+ ASSERT_TRUE(mFrontendTests.closeFrontend());
+ } else {
+ mDvrTests.closeDvrPlayback();
+ }
+
ASSERT_TRUE(mDemuxTests.closeDemux());
- ASSERT_TRUE(mFrontendTests.closeFrontend());
}
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;
@@ -233,23 +264,34 @@
void TunerRecordHidlTest::attachSingleFilterToRecordDvrTest(FilterConfig filterConf,
FrontendConfig frontendConf,
DvrConfig dvrConf) {
- uint32_t feId;
uint32_t demuxId;
sp<IDemux> demux;
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+ mDvrTests.setDemux(demux);
+
+ DvrConfig dvrSourceConfig;
+ if (record.hasFrontendConnection) {
+ uint32_t feId;
+ mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+ ASSERT_TRUE(feId != INVALID_ID);
+ ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+ ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+ } else {
+ dvrSourceConfig = dvrMap[record.dvrSourceId];
+ ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrSourceConfig.type, dvrSourceConfig.bufferSize));
+ ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrSourceConfig.settings));
+ ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
+ }
+
uint32_t filterId;
sp<IFilter> filter;
-
- mFrontendTests.getFrontendIdByType(frontendConf.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);
- mDvrTests.setDemux(demux);
+
ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrConf.type, dvrConf.bufferSize));
ASSERT_TRUE(mDvrTests.configDvrRecord(dvrConf.settings));
ASSERT_TRUE(mDvrTests.getDvrRecordMQDescriptor());
+
ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
@@ -265,36 +307,43 @@
ASSERT_TRUE(mFilterTests.closeFilter(filterId));
mDvrTests.closeDvrRecord();
ASSERT_TRUE(mDemuxTests.closeDemux());
- ASSERT_TRUE(mFrontendTests.closeFrontend());
+
+ if (record.hasFrontendConnection) {
+ ASSERT_TRUE(mFrontendTests.closeFrontend());
+ }
}
void TunerDescramblerHidlTest::scrambledBroadcastTest(set<struct FilterConfig> mediaFilterConfs,
FrontendConfig frontendConf,
DescramblerConfig descConfig) {
- if (!frontendConf.enable) {
- return;
- }
- uint32_t feId;
uint32_t demuxId;
sp<IDemux> demux;
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+
+ DvrConfig dvrSourceConfig;
+ if (descrambling.hasFrontendConnection) {
+ uint32_t feId;
+ mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+ ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+ ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ if (frontendConf.isSoftwareFe) {
+ mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[descrambling.dvrSoftwareFeId]);
+ }
+ ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+ mFrontendTests.setDemux(demux);
+ } else {
+ dvrSourceConfig = dvrMap[descrambling.dvrSourceId];
+ mDvrTests.setDemux(demux);
+ ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrSourceConfig.type, dvrSourceConfig.bufferSize));
+ ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrSourceConfig.settings));
+ ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
+ }
+
set<uint32_t> filterIds;
uint32_t filterId;
set<struct FilterConfig>::iterator config;
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());
- ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
- ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
mFilterTests.setDemux(demux);
- mFrontendTests.setDemux(demux);
for (config = mediaFilterConfs.begin(); config != mediaFilterConfs.end(); config++) {
ASSERT_TRUE(mFilterTests.openFilterInDemux((*config).type, (*config).bufferSize));
ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
@@ -305,7 +354,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++) {
@@ -317,10 +366,26 @@
for (id = filterIds.begin(); id != filterIds.end(); id++) {
ASSERT_TRUE(mFilterTests.startFilter(*id));
}
- // tune test
- ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
- ASSERT_TRUE(filterDataOutputTest(goldenOutputFiles));
- ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+
+ if (descrambling.hasFrontendConnection) {
+ // tune test
+ ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+ } else {
+ // Start DVR Source
+ mDvrTests.startPlaybackInputThread(dvrSourceConfig.playbackInputFile,
+ dvrSourceConfig.settings.playback());
+ ASSERT_TRUE(mDvrTests.startDvrPlayback());
+ }
+
+ ASSERT_TRUE(filterDataOutputTest());
+
+ if (descrambling.hasFrontendConnection) {
+ ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+ } else {
+ mDvrTests.stopPlaybackThread();
+ ASSERT_TRUE(mDvrTests.stopDvrPlayback());
+ }
+
for (id = filterIds.begin(); id != filterIds.end(); id++) {
ASSERT_TRUE(mFilterTests.stopFilter(*id));
}
@@ -331,59 +396,73 @@
for (id = filterIds.begin(); id != filterIds.end(); id++) {
ASSERT_TRUE(mFilterTests.closeFilter(*id));
}
+
+ if (descrambling.hasFrontendConnection) {
+ ASSERT_TRUE(mFrontendTests.closeFrontend());
+ } else {
+ mDvrTests.closeDvrPlayback();
+ }
+
ASSERT_TRUE(mDemuxTests.closeDemux());
- ASSERT_TRUE(mFrontendTests.closeFrontend());
}
TEST_P(TunerFrontendHidlTest, TuneFrontend) {
description("Tune one Frontend with specific setting and check Lock event");
- mFrontendTests.tuneTest(frontendArray[defaultFrontend]);
+ if (!live.hasFrontendConnection) {
+ return;
+ }
+ 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);
+ if (!scan.hasFrontendConnection) {
+ return;
+ }
+ 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());
+ if (!scan.hasFrontendConnection) {
+ return;
+ }
+ 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) {
+ if (!lnbLive.support) {
return;
}
- ASSERT_TRUE(ids.size() > 0);
- ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
+ 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(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());
}
TEST_P(TunerDemuxHidlTest, openDemux) {
description("Open and close a Demux.");
+ if (!live.hasFrontendConnection) {
+ return;
+ }
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 +474,12 @@
TEST_P(TunerDemuxHidlTest, getAvSyncTime) {
description("Get the A/V sync time from a PCR filter.");
+ if (!live.hasFrontendConnection) {
+ return;
+ }
+ if (live.pcrFilterId.compare(emptyHardwareId) == 0) {
+ return;
+ }
uint32_t feId;
uint32_t demuxId;
sp<IDemux> demux;
@@ -403,22 +488,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));
@@ -430,8 +515,11 @@
TEST_P(TunerFilterHidlTest, StartFilterInDemux) {
description("Open and start a filter in Demux.");
+ if (!live.hasFrontendConnection) {
+ return;
+ }
// TODO use paramterized tests
- configSingleFilterInDemuxTest(filterArray[TS_VIDEO0], frontendArray[defaultFrontend]);
+ configSingleFilterInDemuxTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
}
TEST_P(TunerFilterHidlTest, SetFilterLinkage) {
@@ -448,11 +536,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));
@@ -466,81 +552,147 @@
TEST_P(TunerFilterHidlTest, testTimeFilter) {
description("Open a timer filter in Demux and set time stamp.");
+ if (!timeFilter.support) {
+ return;
+ }
// 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]);
+ if (!live.hasFrontendConnection) {
+ return;
+ }
+ 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]);
+ if (!live.hasFrontendConnection) {
+ return;
+ }
+ 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.hasFrontendConnection) {
+ return;
+ }
+ 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]);
+ if (!live.hasFrontendConnection) {
+ return;
+ }
+ 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(TunerPlaybackHidlTest, PlaybackDataFlowWithTsAudioFilterTest) {
+ description("Feed ts data from playback and configure Ts audio filter to get output");
+ if (!playback.support) {
+ return;
+ }
+ playbackSingleFilterTest(filterMap[playback.audioFilterId], dvrMap[playback.dvrId]);
+}
+
+TEST_P(TunerPlaybackHidlTest, PlaybackDataFlowWithTsVideoFilterTest) {
+ description("Feed ts data from playback and configure Ts video filter to get output");
+ if (!playback.support) {
+ return;
+ }
+ playbackSingleFilterTest(filterMap[playback.videoFilterId], 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");
- uint32_t feId;
+ if (!descrambling.support) {
+ return;
+ }
uint32_t demuxId;
sp<IDemux> demux;
- mFrontendTests.getFrontendIdByType(frontendArray[defaultFrontend].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));
+
+ if (descrambling.hasFrontendConnection) {
+ uint32_t feId;
+ mFrontendTests.getFrontendIdByType(frontendMap[descrambling.frontendId].type, feId);
+ ASSERT_TRUE(feId != INVALID_ID);
+ ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+ ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+ }
+
ASSERT_TRUE(mDescramblerTests.openDescrambler(demuxId));
ASSERT_TRUE(mDescramblerTests.closeDescrambler());
ASSERT_TRUE(mDemuxTests.closeDemux());
- ASSERT_TRUE(mFrontendTests.closeFrontend());
+
+ if (descrambling.hasFrontendConnection) {
+ ASSERT_TRUE(mFrontendTests.closeFrontend());
+ }
}
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..8328e0c 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
@@ -21,372 +21,258 @@
#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() {
+ if ((!live.hasFrontendConnection || !scan.hasFrontendConnection) && !playback.support) {
+ ALOGW("[vts config] VTS must support either a DVR source or a Frontend source.");
+ return false;
+ }
+
+ if (record.support && !record.hasFrontendConnection &&
+ record.dvrSourceId.compare(emptyHardwareId) == 0) {
+ ALOGW("[vts config] Record must support either a DVR source or a Frontend source.");
+ return false;
+ }
+
+ if (descrambling.support && !descrambling.hasFrontendConnection &&
+ descrambling.dvrSourceId.compare(emptyHardwareId) == 0) {
+ ALOGW("[vts config] Descrambling must support either a DVR source or a Frontend source.");
+ return false;
+ }
+
+ bool feIsValid = live.hasFrontendConnection
+ ? frontendMap.find(live.frontendId) != frontendMap.end()
+ : true;
+ feIsValid &= scan.hasFrontendConnection ? frontendMap.find(scan.frontendId) != frontendMap.end()
+ : true;
+ feIsValid &= record.support && record.hasFrontendConnection
+ ? frontendMap.find(record.frontendId) != frontendMap.end()
+ : true;
+ feIsValid &= (descrambling.support && descrambling.hasFrontendConnection)
+ ? frontendMap.find(descrambling.frontendId) != frontendMap.end()
+ : true;
+ feIsValid &= lnbLive.support ? frontendMap.find(lnbLive.frontendId) != frontendMap.end() : true;
+ feIsValid &=
+ lnbRecord.support ? frontendMap.find(lnbRecord.frontendId) != frontendMap.end() : true;
+
+ if (!feIsValid) {
+ ALOGW("[vts config] dynamic config fe connection is invalid.");
+ return false;
+ }
+
+ bool dvrIsValid = (live.hasFrontendConnection && 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 (record.hasFrontendConnection) {
+ if (frontendMap[record.frontendId].isSoftwareFe) {
+ dvrIsValid &= dvrMap.find(record.dvrSoftwareFeId) != dvrMap.end();
+ }
+ } else {
+ dvrIsValid &= dvrMap.find(record.dvrSourceId) != dvrMap.end();
+ }
+ dvrIsValid &= dvrMap.find(record.dvrRecordId) != dvrMap.end();
+ }
+ if (descrambling.support) {
+ if (descrambling.hasFrontendConnection) {
+ if (frontendMap[descrambling.frontendId].isSoftwareFe) {
+ dvrIsValid &= dvrMap.find(descrambling.dvrSoftwareFeId) != dvrMap.end();
+ }
+ } else {
+ dvrIsValid &= dvrMap.find(descrambling.dvrSourceId) != dvrMap.end();
+ }
+ }
+
+ if (!dvrIsValid) {
+ ALOGW("[vts config] dynamic config dvr connection is invalid.");
+ return false;
+ }
+
+ bool filterIsValid = (live.hasFrontendConnection)
+ ? filterMap.find(live.audioFilterId) != filterMap.end() &&
+ filterMap.find(live.videoFilterId) != filterMap.end()
+ : true;
+ 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..e6aba2c
--- /dev/null
+++ b/tv/tuner/config/TunerTestingConfigReader.h
@@ -0,0 +1,770 @@
+/*
+ * 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 {
+ bool hasFrontendConnection;
+ string frontendId;
+ string dvrSoftwareFeId;
+ string audioFilterId;
+ string videoFilterId;
+ string sectionFilterId;
+ string pcrFilterId;
+ /* list string of extra filters; */
+};
+
+struct ScanHardwareConnections {
+ bool hasFrontendConnection;
+ 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;
+ bool hasFrontendConnection;
+ string frontendId;
+ string dvrRecordId;
+ string dvrSoftwareFeId;
+ string recordFilterId;
+ string dvrSourceId;
+};
+
+struct DescramblingHardwareConnections {
+ bool support;
+ bool hasFrontendConnection;
+ string frontendId;
+ string dvrSoftwareFeId;
+ string audioFilterId;
+ string videoFilterId;
+ string descramblerId;
+ string dvrSourceId;
+ /* 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);
+ }
+ }
+ }
+ }
+
+ 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 dataFlow = getDataFlowConfiguration();
+ if (dataFlow.hasClearLiveBroadcast()) {
+ live.hasFrontendConnection = true;
+ } else {
+ live.hasFrontendConnection = false;
+ return;
+ }
+ auto liveConfig = *dataFlow.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 dataFlow = getDataFlowConfiguration();
+ if (dataFlow.hasScan()) {
+ scan.hasFrontendConnection = true;
+ } else {
+ scan.hasFrontendConnection = false;
+ return;
+ }
+ auto scanConfig = *dataFlow.getFirstScan();
+ scan.frontendId = scanConfig.getFrontendConnection();
+ }
+
+ static void connectDvrPlayback(DvrPlaybackHardwareConnections& playback) {
+ auto dataFlow = getDataFlowConfiguration();
+ if (dataFlow.hasDvrPlayback()) {
+ playback.support = true;
+ } else {
+ playback.support = false;
+ 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 {
+ record.support = false;
+ return;
+ }
+ auto recordConfig = *dataFlow.getFirstDvrRecord();
+ record.recordFilterId = recordConfig.getRecordFilterConnection();
+ record.dvrRecordId = recordConfig.getDvrRecordConnection();
+ if (recordConfig.hasDvrSoftwareFeConnection()) {
+ record.dvrSoftwareFeId = recordConfig.getDvrSoftwareFeConnection();
+ }
+ if (recordConfig.getHasFrontendConnection()) {
+ record.hasFrontendConnection = true;
+ record.dvrSourceId = emptyHardwareId;
+ record.frontendId = recordConfig.getFrontendConnection();
+ } else {
+ record.hasFrontendConnection = false;
+ record.dvrSourceId = recordConfig.getDvrSourceConnection();
+ }
+ }
+
+ static void connectDescrambling(DescramblingHardwareConnections& descrambling) {
+ auto dataFlow = getDataFlowConfiguration();
+ if (dataFlow.hasDescrambling()) {
+ descrambling.support = true;
+ } else {
+ descrambling.support = false;
+ return;
+ }
+ auto descConfig = *dataFlow.getFirstDescrambling();
+ descrambling.descramblerId = descConfig.getDescramblerConnection();
+ descrambling.audioFilterId = descConfig.getAudioFilterConnection();
+ descrambling.videoFilterId = descConfig.getVideoFilterConnection();
+ if (descConfig.hasDvrSoftwareFeConnection()) {
+ descrambling.dvrSoftwareFeId = descConfig.getDvrSoftwareFeConnection();
+ }
+ if (descConfig.getHasFrontendConnection()) {
+ descrambling.hasFrontendConnection = true;
+ descrambling.dvrSourceId = emptyHardwareId;
+ descrambling.frontendId = descConfig.getFrontendConnection();
+ } else {
+ descrambling.hasFrontendConnection = false;
+ descrambling.dvrSourceId = descConfig.getDvrSourceConnection();
+ }
+ }
+
+ static void connectLnbLive(LnbLiveHardwareConnections& lnbLive) {
+ auto dataFlow = getDataFlowConfiguration();
+ if (dataFlow.hasLnbLive()) {
+ lnbLive.support = true;
+ } else {
+ lnbLive.support = false;
+ 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 {
+ lnbRecord.support = false;
+ 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 {
+ timeFilter.support = false;
+ 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..abd7155
--- /dev/null
+++ b/tv/tuner/config/api/current.txt
@@ -0,0 +1,441 @@
+// 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 getDvrSourceConnection();
+ method @Nullable public String getFrontendConnection();
+ method @Nullable public boolean getHasFrontendConnection();
+ 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 setDvrSourceConnection(@Nullable String);
+ method public void setFrontendConnection(@Nullable String);
+ method public void setHasFrontendConnection(@Nullable boolean);
+ 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 getDvrSourceConnection();
+ method @Nullable public String getFrontendConnection();
+ method @Nullable public boolean getHasFrontendConnection();
+ method @Nullable public String getRecordFilterConnection();
+ method public void setDvrRecordConnection(@Nullable String);
+ method public void setDvrSoftwareFeConnection(@Nullable String);
+ method public void setDvrSourceConnection(@Nullable String);
+ method public void setFrontendConnection(@Nullable String);
+ method public void setHasFrontendConnection(@Nullable boolean);
+ 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..2624076
--- /dev/null
+++ b/tv/tuner/config/sample_tuner_vts_config.xml
@@ -0,0 +1,226 @@
+<?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 hasFrontendConnection="true"
+ 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 hasFrontendConnection="true"
+ 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..e0c3e33
--- /dev/null
+++ b/tv/tuner/config/tuner_testing_dynamic_configuration.xsd
@@ -0,0 +1,658 @@
+<?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>
+ <!-- clearLiveBroadcast is only optional when there is no physical frontend. In this
+ case, the dvrPlayback config is required. -->
+ <xs:element name="clearLiveBroadcast" 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="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>
+ <!-- scan is only optional when there is no physical frontend. In this case, the
+ dvrPlayback config is required. -->
+ <xs:element name="scan" minOccurs="0" 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>
+ <!-- If there is a software or hardware frontend connection or not. If false,
+ dvrSourceConnection config is required when testing dvrRecord. -->
+ <xs:attribute name="hasFrontendConnection" type="xs:boolean" use="required"/>
+ <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 -->
+ <!-- This DVR is only required when the frontend is using the software input -->
+ <xs:attribute name="dvrSoftwareFeConnection" type="dvrId" use="optional"/>
+ <!-- This Dvr is only required when there's no frontend(sw or hw) connection -->
+ <xs:attribute name="dvrSourceConnection" 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>
+ <!-- If there is a software or hardware frontend connection or not. If false,
+ dvrSourceConnection config is required when testing dvrRecord. -->
+ <xs:attribute name="hasFrontendConnection" type="xs:boolean" use="required"/>
+ <xs:attribute name="frontendConnection" type="frontendId" use="optional"/>
+ <xs:attribute name="dvrRecordConnection" type="dvrId" use="required"/>
+ <!-- This Dvr is only required when the frontend is using the software input -->
+ <xs:attribute name="dvrSoftwareFeConnection" type="dvrId" use="optional"/>
+ <!-- This Dvr is only required when there's no frontend(sw or hw) connection -->
+ <xs:attribute name="dvrSourceConnection" 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/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/CompositeEffect.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/CompositeEffect.aidl
index 8cb259f..7431804 100644
--- a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/CompositeEffect.aidl
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/CompositeEffect.aidl
@@ -19,6 +19,6 @@
@VintfStability
parcelable CompositeEffect {
int delayMs;
- android.hardware.vibrator.CompositePrimitive primitive;
+ android.hardware.vibrator.CompositePrimitive primitive = android.hardware.vibrator.CompositePrimitive.NOOP;
float scale;
}
diff --git a/vibrator/aidl/android/hardware/vibrator/CompositeEffect.aidl b/vibrator/aidl/android/hardware/vibrator/CompositeEffect.aidl
index 406a899..5a990c0 100644
--- a/vibrator/aidl/android/hardware/vibrator/CompositeEffect.aidl
+++ b/vibrator/aidl/android/hardware/vibrator/CompositeEffect.aidl
@@ -22,7 +22,7 @@
parcelable CompositeEffect {
/* Period of silence preceding primitive. */
int delayMs;
- CompositePrimitive primitive;
+ CompositePrimitive primitive = CompositePrimitive.NOOP;
/*
* 0.0 (inclusive) - 1.0 (inclusive),
* where 0.0 is minimum "feelable" amplitude.
diff --git a/wifi/supplicant/1.0/vts/functional/supplicant_sta_iface_hidl_test.cpp b/wifi/supplicant/1.0/vts/functional/supplicant_sta_iface_hidl_test.cpp
index e4fe52c..bdca32c 100644
--- a/wifi/supplicant/1.0/vts/functional/supplicant_sta_iface_hidl_test.cpp
+++ b/wifi/supplicant/1.0/vts/functional/supplicant_sta_iface_hidl_test.cpp
@@ -25,6 +25,7 @@
#include "supplicant_hidl_call_util.h"
#include "supplicant_hidl_test_utils.h"
+#include <cutils/properties.h>
using ::android::sp;
using ::android::hardware::hidl_array;
@@ -61,7 +62,7 @@
constexpr uint32_t kTestRadioWorkFrequency = 2412;
constexpr uint32_t kTestRadioWorkTimeout = 8;
constexpr uint32_t kTestRadioWorkId = 16;
-constexpr int8_t kTestCountryCode[] = {'U', 'S'};
+int8_t kTestCountryCode[] = {'U', 'S'};
constexpr uint8_t kTestWpsDeviceType[] = {[0 ... 7] = 0x01};
constexpr uint16_t kTestWpsConfigMethods = 0xffff;
} // namespace
@@ -454,6 +455,10 @@
* SetCountryCode.
*/
TEST_P(SupplicantStaIfaceHidlTest, SetCountryCode) {
+ std::array<char, PROPERTY_VALUE_MAX> buffer;
+ property_get("ro.boot.wificountrycode", buffer.data(), "US");
+ kTestCountryCode[0] = buffer.data()[0];
+ kTestCountryCode[1] = buffer.data()[1];
sta_iface_->setCountryCode(
kTestCountryCode, [](const SupplicantStatus& status) {
EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);