Merge "Update doc for PERF_VEHICLE_SPEED" into sc-dev
diff --git a/audio/common/all-versions/default/Android.bp b/audio/common/all-versions/default/Android.bp
index 45f0b8f..29a3d6f 100644
--- a/audio/common/all-versions/default/Android.bp
+++ b/audio/common/all-versions/default/Android.bp
@@ -114,7 +114,7 @@
],
}
-cc_library_shared {
+cc_library {
name: "android.hardware.audio.common@6.0-util",
defaults: ["android.hardware.audio.common-util_default"],
srcs: [":android.hardware.audio.common-util@2-6"],
@@ -152,6 +152,32 @@
// Note: this isn't a VTS test, but rather a unit test
// to verify correctness of conversion utilities.
cc_test {
+ name: "android.hardware.audio.common@6.0-util_tests",
+ defaults: ["android.hardware.audio.common-util_default"],
+
+ srcs: ["tests/hidlutils6_tests.cpp"],
+
+ // Use static linking to allow running in presubmit on
+ // targets that don't have HAL V6.
+ static_libs: [
+ "android.hardware.audio.common@6.0",
+ "android.hardware.audio.common@6.0-util",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ "-DMAJOR_VERSION=6",
+ "-DMINOR_VERSION=0",
+ "-include common/all-versions/VersionMacro.h",
+ ],
+
+ test_suites: ["device-tests"],
+}
+
+// Note: this isn't a VTS test, but rather a unit test
+// to verify correctness of conversion utilities.
+cc_test {
name: "android.hardware.audio.common@7.0-util_tests",
defaults: ["android.hardware.audio.common-util_default"],
diff --git a/audio/common/all-versions/default/HidlUtils.h b/audio/common/all-versions/default/HidlUtils.h
index 22b7152..dd4ca4d 100644
--- a/audio/common/all-versions/default/HidlUtils.h
+++ b/audio/common/all-versions/default/HidlUtils.h
@@ -210,6 +210,9 @@
*halDeviceType == AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "%s",
device.rSubmixAddress.c_str());
+ } else {
+ // Fall back to bus address for other device types, e.g. for microphones.
+ snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "%s", device.busAddress.c_str());
}
return NO_ERROR;
}
@@ -249,6 +252,7 @@
device->rSubmixAddress = halDeviceAddress;
return OK;
}
+ // Fall back to bus address for other device types, e.g. for microphones.
device->busAddress = halDeviceAddress;
return NO_ERROR;
}
diff --git a/audio/common/all-versions/default/TEST_MAPPING b/audio/common/all-versions/default/TEST_MAPPING
index 4316ccf..c965113 100644
--- a/audio/common/all-versions/default/TEST_MAPPING
+++ b/audio/common/all-versions/default/TEST_MAPPING
@@ -1,6 +1,9 @@
{
"presubmit": [
{
+ "name": "android.hardware.audio.common@6.0-util_tests"
+ },
+ {
"name": "android.hardware.audio.common@7.0-util_tests"
}
]
diff --git a/audio/common/all-versions/default/tests/hidlutils6_tests.cpp b/audio/common/all-versions/default/tests/hidlutils6_tests.cpp
new file mode 100644
index 0000000..3a24e75
--- /dev/null
+++ b/audio/common/all-versions/default/tests/hidlutils6_tests.cpp
@@ -0,0 +1,108 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#define LOG_TAG "HidlUtils_Test"
+#include <log/log.h>
+
+#include <HidlUtils.h>
+#include <system/audio.h>
+
+using namespace android;
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+
+// Not generated automatically because DeviceAddress contains
+// an union.
+//
+// operator== must be defined in the same namespace as the data type.
+namespace android::hardware::audio::common::CPP_VERSION {
+
+inline bool operator==(const DeviceAddress& lhs, const DeviceAddress& rhs) {
+ if (lhs.device != rhs.device) return false;
+ audio_devices_t halDeviceType = static_cast<audio_devices_t>(lhs.device);
+ if (audio_is_a2dp_out_device(halDeviceType) || audio_is_a2dp_in_device(halDeviceType)) {
+ return lhs.address.mac == rhs.address.mac;
+ } else if (halDeviceType == AUDIO_DEVICE_OUT_IP || halDeviceType == AUDIO_DEVICE_IN_IP) {
+ return lhs.address.ipv4 == rhs.address.ipv4;
+ } else if (audio_is_usb_out_device(halDeviceType) || audio_is_usb_in_device(halDeviceType)) {
+ return lhs.address.alsa == rhs.address.alsa;
+ } else if (halDeviceType == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ||
+ halDeviceType == AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
+ return lhs.rSubmixAddress == rhs.rSubmixAddress;
+ }
+ // busAddress field can be used for types other than bus, e.g. for microphones.
+ return lhs.busAddress == rhs.busAddress;
+}
+
+} // namespace android::hardware::audio::common::CPP_VERSION
+
+static void ConvertDeviceAddress(const DeviceAddress& device) {
+ audio_devices_t halDeviceType;
+ char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN] = {};
+ EXPECT_EQ(NO_ERROR, HidlUtils::deviceAddressToHal(device, &halDeviceType, halDeviceAddress));
+ DeviceAddress deviceBack;
+ EXPECT_EQ(NO_ERROR,
+ HidlUtils::deviceAddressFromHal(halDeviceType, halDeviceAddress, &deviceBack));
+ EXPECT_EQ(device, deviceBack);
+}
+
+TEST(HidlUtils6, ConvertUniqueDeviceAddress) {
+ DeviceAddress speaker;
+ speaker.device = AudioDevice::OUT_SPEAKER;
+ ConvertDeviceAddress(speaker);
+
+ DeviceAddress micWithAddress;
+ micWithAddress.device = AudioDevice::IN_BUILTIN_MIC;
+ micWithAddress.busAddress = "bottom";
+ ConvertDeviceAddress(micWithAddress);
+}
+
+TEST(HidlUtils6, ConvertA2dpDeviceAddress) {
+ DeviceAddress a2dpSpeaker;
+ a2dpSpeaker.device = AudioDevice::OUT_BLUETOOTH_A2DP_SPEAKER;
+ a2dpSpeaker.address.mac = std::array<uint8_t, 6>{1, 2, 3, 4, 5, 6};
+ ConvertDeviceAddress(a2dpSpeaker);
+}
+
+TEST(HidlUtils6, ConvertIpv4DeviceAddress) {
+ DeviceAddress ipv4;
+ ipv4.device = AudioDevice::OUT_IP;
+ ipv4.address.ipv4 = std::array<uint8_t, 4>{1, 2, 3, 4};
+ ConvertDeviceAddress(ipv4);
+}
+
+TEST(HidlUtils6, ConvertUsbDeviceAddress) {
+ DeviceAddress usbHeadset;
+ usbHeadset.device = AudioDevice::OUT_USB_HEADSET;
+ usbHeadset.address.alsa = {1, 2};
+ ConvertDeviceAddress(usbHeadset);
+}
+
+TEST(HidlUtils6, ConvertBusDeviceAddress) {
+ DeviceAddress bus;
+ bus.device = AudioDevice::OUT_BUS;
+ bus.busAddress = "bus_device";
+ ConvertDeviceAddress(bus);
+}
+
+TEST(HidlUtils6, ConvertRSubmixDeviceAddress) {
+ DeviceAddress rSubmix;
+ rSubmix.device = AudioDevice::OUT_REMOTE_SUBMIX;
+ rSubmix.rSubmixAddress = AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS;
+ ConvertDeviceAddress(rSubmix);
+}
diff --git a/audio/common/all-versions/default/tests/hidlutils_tests.cpp b/audio/common/all-versions/default/tests/hidlutils_tests.cpp
index 99d2e72..ec6bdf3 100644
--- a/audio/common/all-versions/default/tests/hidlutils_tests.cpp
+++ b/audio/common/all-versions/default/tests/hidlutils_tests.cpp
@@ -476,6 +476,11 @@
DeviceAddress speaker;
speaker.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER);
ConvertDeviceAddress(speaker);
+
+ DeviceAddress micWithAddress;
+ micWithAddress.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_BUILTIN_MIC);
+ micWithAddress.address.id("bottom");
+ ConvertDeviceAddress(micWithAddress);
}
TEST(HidlUtils, ConvertA2dpDeviceAddress) {
diff --git a/automotive/OWNERS b/automotive/OWNERS
index fb3e3d6..43c5f3e 100644
--- a/automotive/OWNERS
+++ b/automotive/OWNERS
@@ -1,6 +1,6 @@
pirozzoj@google.com
twasilczyk@google.com
-pfg@google.com
+krachuri@google.com
gurunagarajan@google.com
keunyoung@google.com
felipeal@google.com
diff --git a/biometrics/face/1.1/default/Android.bp b/biometrics/face/1.0/default/Android.bp
similarity index 84%
rename from biometrics/face/1.1/default/Android.bp
rename to biometrics/face/1.0/default/Android.bp
index 360071f..d6ff087 100644
--- a/biometrics/face/1.1/default/Android.bp
+++ b/biometrics/face/1.0/default/Android.bp
@@ -15,10 +15,10 @@
*/
cc_binary {
- name: "android.hardware.biometrics.face@1.1-service.example",
+ name: "android.hardware.biometrics.face@1.0-service.example",
defaults: ["hidl_defaults"],
vendor: true,
- init_rc: ["android.hardware.biometrics.face@1.1-service.rc"],
+ init_rc: ["android.hardware.biometrics.face@1.0-service.rc"],
vintf_fragments: ["manifest_face_default.xml"],
relative_install_path: "hw",
proprietary: true,
@@ -31,6 +31,5 @@
"libutils",
"liblog",
"android.hardware.biometrics.face@1.0",
- "android.hardware.biometrics.face@1.1",
],
}
diff --git a/biometrics/face/1.1/default/BiometricsFace.cpp b/biometrics/face/1.0/default/BiometricsFace.cpp
similarity index 81%
rename from biometrics/face/1.1/default/BiometricsFace.cpp
rename to biometrics/face/1.0/default/BiometricsFace.cpp
index 57b3a92..97dc469 100644
--- a/biometrics/face/1.1/default/BiometricsFace.cpp
+++ b/biometrics/face/1.0/default/BiometricsFace.cpp
@@ -110,20 +110,4 @@
return Status::OK;
}
-// Methods from ::android::hardware::biometrics::face::V1_1::IBiometricsFace follow.
-Return<Status> BiometricsFace::enroll_1_1(const hidl_vec<uint8_t>& /* hat */,
- uint32_t /* timeoutSec */,
- const hidl_vec<Feature>& /* disabledFeatures */,
- const hidl_handle& /* windowId */) {
- mClientCallback->onError(kDeviceId, mUserId, FaceError::UNABLE_TO_PROCESS, 0 /* vendorCode */);
- return Status::OK;
-}
-
-Return<Status> BiometricsFace::enrollRemotely(const hidl_vec<uint8_t>& /* hat */,
- uint32_t /* timeoutSec */,
- const hidl_vec<Feature>& /* disabledFeatures */) {
- mClientCallback->onError(kDeviceId, mUserId, FaceError::UNABLE_TO_PROCESS, 0 /* vendorCode */);
- return Status::OK;
-}
-
} // namespace android::hardware::biometrics::face::implementation
diff --git a/biometrics/face/1.1/default/BiometricsFace.h b/biometrics/face/1.0/default/BiometricsFace.h
similarity index 81%
rename from biometrics/face/1.1/default/BiometricsFace.h
rename to biometrics/face/1.0/default/BiometricsFace.h
index 5ce5771..1d99ed2 100644
--- a/biometrics/face/1.1/default/BiometricsFace.h
+++ b/biometrics/face/1.0/default/BiometricsFace.h
@@ -16,7 +16,7 @@
#pragma once
-#include <android/hardware/biometrics/face/1.1/IBiometricsFace.h>
+#include <android/hardware/biometrics/face/1.0/IBiometricsFace.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
#include <random>
@@ -34,7 +34,7 @@
using ::android::hardware::biometrics::face::V1_0::IBiometricsFaceClientCallback;
using ::android::hardware::biometrics::face::V1_0::Status;
-class BiometricsFace : public V1_1::IBiometricsFace {
+class BiometricsFace : public V1_0::IBiometricsFace {
public:
BiometricsFace();
@@ -71,14 +71,6 @@
Return<Status> resetLockout(const hidl_vec<uint8_t>& hat) override;
- // Methods from ::android::hardware::biometrics::face::V1_1::IBiometricsFace follow.
- Return<Status> enroll_1_1(const hidl_vec<uint8_t>& hat, uint32_t timeoutSec,
- const hidl_vec<Feature>& disabledFeatures,
- const hidl_handle& windowId) override;
-
- Return<Status> enrollRemotely(const hidl_vec<uint8_t>& hat, uint32_t timeoutSec,
- const hidl_vec<Feature>& disabledFeatures) override;
-
private:
std::mt19937 mRandom;
int32_t mUserId;
diff --git a/biometrics/face/1.1/default/android.hardware.biometrics.face@1.1-service.rc b/biometrics/face/1.0/default/android.hardware.biometrics.face@1.0-service.rc
similarity index 75%
rename from biometrics/face/1.1/default/android.hardware.biometrics.face@1.1-service.rc
rename to biometrics/face/1.0/default/android.hardware.biometrics.face@1.0-service.rc
index 687e2d8..6c7362f 100644
--- a/biometrics/face/1.1/default/android.hardware.biometrics.face@1.1-service.rc
+++ b/biometrics/face/1.0/default/android.hardware.biometrics.face@1.0-service.rc
@@ -1,4 +1,4 @@
-service vendor.face-hal-1-1-default /vendor/bin/hw/android.hardware.biometrics.face@1.1-service.example
+service vendor.face-hal-1-0-default /vendor/bin/hw/android.hardware.biometrics.face@1.0-service.example
# "class hal" causes a race condition on some devices due to files created
# in /data. As a workaround, postpone startup until later in boot once
# /data is mounted.
diff --git a/biometrics/face/1.1/default/manifest_face_default.xml b/biometrics/face/1.0/default/manifest_face_default.xml
similarity index 90%
rename from biometrics/face/1.1/default/manifest_face_default.xml
rename to biometrics/face/1.0/default/manifest_face_default.xml
index ec71d9c..380ae49 100644
--- a/biometrics/face/1.1/default/manifest_face_default.xml
+++ b/biometrics/face/1.0/default/manifest_face_default.xml
@@ -2,7 +2,7 @@
<hal format="hidl">
<name>android.hardware.biometrics.face</name>
<transport>hwbinder</transport>
- <version>1.1</version>
+ <version>1.0</version>
<interface>
<name>IBiometricsFace</name>
<instance>default</instance>
diff --git a/biometrics/face/1.1/default/service.cpp b/biometrics/face/1.0/default/service.cpp
similarity index 88%
rename from biometrics/face/1.1/default/service.cpp
rename to biometrics/face/1.0/default/service.cpp
index 344bdb9..9818c95 100644
--- a/biometrics/face/1.1/default/service.cpp
+++ b/biometrics/face/1.0/default/service.cpp
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-#define LOG_TAG "android.hardware.biometrics.face@1.1-service"
+#define LOG_TAG "android.hardware.biometrics.face@1.0-service"
#include <android/hardware/biometrics/face/1.0/types.h>
-#include <android/hardware/biometrics/face/1.1/IBiometricsFace.h>
+#include <android/hardware/biometrics/face/1.0/IBiometricsFace.h>
#include <android/log.h>
#include <hidl/HidlSupport.h>
#include <hidl/HidlTransportSupport.h>
@@ -27,7 +27,7 @@
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
using android::hardware::biometrics::face::implementation::BiometricsFace;
-using android::hardware::biometrics::face::V1_1::IBiometricsFace;
+using android::hardware::biometrics::face::V1_0::IBiometricsFace;
int main() {
ALOGI("BiometricsFace HAL is being started.");
diff --git a/biometrics/face/1.1/Android.bp b/biometrics/face/1.1/Android.bp
deleted file mode 100644
index 14a86f1..0000000
--- a/biometrics/face/1.1/Android.bp
+++ /dev/null
@@ -1,14 +0,0 @@
-// This file is autogenerated by hidl-gen -Landroidbp.
-
-hidl_interface {
- name: "android.hardware.biometrics.face@1.1",
- root: "android.hardware",
- srcs: [
- "IBiometricsFace.hal",
- ],
- interfaces: [
- "android.hardware.biometrics.face@1.0",
- "android.hidl.base@1.0",
- ],
- gen_java: true,
-}
diff --git a/biometrics/face/1.1/IBiometricsFace.hal b/biometrics/face/1.1/IBiometricsFace.hal
deleted file mode 100644
index 84e7443..0000000
--- a/biometrics/face/1.1/IBiometricsFace.hal
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.biometrics.face@1.1;
-
-import @1.0::IBiometricsFace;
-import @1.0::Status;
-import @1.0::Feature;
-
-/**
- * The HAL interface for biometric face authentication.
- */
-interface IBiometricsFace extends @1.0::IBiometricsFace {
- /**
- * Enrolls a user's face for a remote client, for example Android Auto.
- *
- * The HAL implementation is responsible for creating a secure communication
- * channel and receiving the enrollment images from a mobile device with
- * face authentication hardware.
- *
- * Note that the Hardware Authentication Token must be valid for the
- * duration of enrollment and thus should be explicitly invalidated by a
- * call to revokeChallenge() when enrollment is complete, to reduce the
- * window of opportunity to re-use the challenge and HAT. For example,
- * Settings calls generateChallenge() once to allow the user to enroll one
- * or more faces or toggle secure settings without having to re-enter the
- * PIN/pattern/password. Once the user completes the operation, Settings
- * invokes revokeChallenge() to close the transaction. If the HAT is expired,
- * the implementation must invoke onError with UNABLE_TO_PROCESS.
- *
- * Requirements for using this API:
- * - Mobile devices MUST NOT delegate enrollment to another device by calling
- * this API. This feature is intended only to allow enrollment on devices
- * where it is impossible to enroll locally on the device.
- * - The path MUST be protected by a secret key with rollback protection.
- * - Synchronizing between devices MUST be accomplished by having both
- * devices agree on a secret PIN entered by the user (similar to BT
- * pairing procedure) and use a salted version of that PIN plus other secret
- * to encrypt traffic.
- * - All communication to/from the remote device MUST be encrypted and signed
- * to prevent image injection and other man-in-the-middle type attacks.
- * - generateChallenge() and revokeChallenge() MUST be implemented on both
- * remote and local host (e.g. hash the result of the remote host with a
- * local secret before responding to the API call) and any transmission of
- * the challenge between hosts MUST be signed to prevent man-in-the-middle
- * attacks.
- * - In the event of a lost connection, the result of the last
- * generateChallenge() MUST be invalidated and the process started over.
- * - Both the remote and local host MUST honor the timeout and invalidate the
- * challenge.
- *
- * This method triggers the IBiometricsFaceClientCallback#onEnrollResult()
- * method.
- *
- * @param hat A valid Hardware Authentication Token, generated as a result
- * of a generateChallenge() challenge being wrapped by the gatekeeper
- * after a successful strong authentication request.
- * @param timeoutSec A timeout in seconds, after which this enroll
- * attempt is cancelled. Note that the framework can continue
- * enrollment by calling this again with a valid HAT. This timeout is
- * expected to be used to limit power usage if the device becomes idle
- * during enrollment. The implementation is expected to send
- * ERROR_TIMEOUT if this happens.
- * @param disabledFeatures A list of features to be disabled during
- * enrollment. Note that all features are enabled by default.
- * @return status The status of this method call.
- */
- enrollRemotely(vec<uint8_t> hat, uint32_t timeoutSec, vec<Feature> disabledFeatures)
- generates (Status status);
-
- /**
- * Enrolls a user's face.
- *
- * Note that the Hardware Authentication Token must be valid for the
- * duration of enrollment and thus should be explicitly invalidated by a
- * call to revokeChallenge() when enrollment is complete, to reduce the
- * window of opportunity to re-use the challenge and HAT. For example,
- * Settings calls generateChallenge() once to allow the user to enroll one
- * or more faces or toggle secure settings without having to re-enter the
- * PIN/pattern/password. Once the user completes the operation, Settings
- * invokes revokeChallenge() to close the transaction. If the HAT is expired,
- * the implementation must invoke onError with UNABLE_TO_PROCESS.
- *
- * This method triggers the IBiometricsFaceClientCallback#onEnrollResult()
- * method.
- *
- * @param hat A valid Hardware Authentication Token, generated as a result
- * of a generateChallenge() challenge being wrapped by the gatekeeper
- * after a successful strong authentication request.
- * @param timeoutSec A timeout in seconds, after which this enroll
- * attempt is cancelled. Note that the framework can continue
- * enrollment by calling this again with a valid HAT. This timeout is
- * expected to be used to limit power usage if the device becomes idle
- * during enrollment. The implementation is expected to send
- * ERROR_TIMEOUT if this happens.
- * @param disabledFeatures A list of features to be disabled during
- * enrollment. Note that all features are enabled by default.
- * @param windowId optional ID of a camera preview window for a
- * single-camera device. Must be null if not used.
- * @return status The status of this method call.
- */
- enroll_1_1(vec<uint8_t> hat, uint32_t timeoutSec, vec<Feature> disabledFeatures,
- handle windowId) generates (Status status);
-};
diff --git a/biometrics/face/1.1/vts/functional/Android.bp b/biometrics/face/1.1/vts/functional/Android.bp
deleted file mode 100644
index aa0b1fa..0000000
--- a/biometrics/face/1.1/vts/functional/Android.bp
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 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.
- */
-
-cc_test {
- name: "VtsHalBiometricsFaceV1_1TargetTest",
- defaults: ["VtsHalTargetTestDefaults"],
- srcs: ["VtsHalBiometricsFaceV1_1TargetTest.cpp"],
- static_libs: [
- "android.hardware.biometrics.face@1.0",
- "android.hardware.biometrics.face@1.1",
- ],
- test_suites: [
- "general-tests",
- "vts",
- ],
-}
diff --git a/biometrics/face/1.1/vts/functional/VtsHalBiometricsFaceV1_1TargetTest.cpp b/biometrics/face/1.1/vts/functional/VtsHalBiometricsFaceV1_1TargetTest.cpp
deleted file mode 100644
index 0077c8c..0000000
--- a/biometrics/face/1.1/vts/functional/VtsHalBiometricsFaceV1_1TargetTest.cpp
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#define LOG_TAG "biometrics_face_hidl_hal_test"
-
-#include <android/hardware/biometrics/face/1.0/IBiometricsFaceClientCallback.h>
-#include <android/hardware/biometrics/face/1.1/IBiometricsFace.h>
-
-#include <VtsHalHidlTargetCallbackBase.h>
-#include <android-base/logging.h>
-#include <gtest/gtest.h>
-#include <hidl/GtestPrinter.h>
-#include <hidl/ServiceManagement.h>
-
-#include <chrono>
-#include <cstdint>
-#include <random>
-
-using android::sp;
-using android::hardware::hidl_handle;
-using android::hardware::hidl_vec;
-using android::hardware::Return;
-using android::hardware::Void;
-using android::hardware::biometrics::face::V1_0::FaceAcquiredInfo;
-using android::hardware::biometrics::face::V1_0::FaceError;
-using android::hardware::biometrics::face::V1_0::IBiometricsFaceClientCallback;
-using android::hardware::biometrics::face::V1_0::OptionalUint64;
-using android::hardware::biometrics::face::V1_0::Status;
-using android::hardware::biometrics::face::V1_1::IBiometricsFace;
-
-namespace {
-
-// Arbitrary, nonexistent userId
-constexpr uint32_t kUserId = 9;
-constexpr uint32_t kTimeoutSec = 3;
-constexpr auto kTimeout = std::chrono::seconds(kTimeoutSec);
-constexpr char kFacedataDir[] = "/data/vendor_de/0/facedata";
-constexpr char kCallbackNameOnError[] = "onError";
-
-// Callback arguments that need to be captured for the tests.
-struct FaceCallbackArgs {
- // The error passed to the last onError() callback.
- FaceError error;
-
- // The userId passed to the last callback.
- int32_t userId;
-};
-
-// Test callback class for the BiometricsFace HAL.
-// The HAL will call these callback methods to notify about completed operations
-// or encountered errors.
-class FaceCallback : public ::testing::VtsHalHidlTargetCallbackBase<FaceCallbackArgs>,
- public IBiometricsFaceClientCallback {
- public:
- Return<void> onEnrollResult(uint64_t, uint32_t, int32_t, uint32_t) override { return Void(); }
-
- Return<void> onAuthenticated(uint64_t, uint32_t, int32_t, const hidl_vec<uint8_t>&) override {
- return Void();
- }
-
- Return<void> onAcquired(uint64_t, int32_t, FaceAcquiredInfo, int32_t) override {
- return Void();
- }
-
- Return<void> onError(uint64_t, int32_t userId, FaceError error, int32_t) override {
- FaceCallbackArgs args = {};
- args.error = error;
- args.userId = userId;
- NotifyFromCallback(kCallbackNameOnError, args);
- return Void();
- }
-
- Return<void> onRemoved(uint64_t, const hidl_vec<uint32_t>&, int32_t) override { return Void(); }
-
- Return<void> onEnumerate(uint64_t, const hidl_vec<uint32_t>&, int32_t) override {
- return Void();
- }
-
- Return<void> onLockoutChanged(uint64_t) override { return Void(); }
-};
-
-// Test class for the BiometricsFace HAL.
-class FaceHidlTest : public ::testing::TestWithParam<std::string> {
- public:
- void SetUp() override {
- mService = IBiometricsFace::getService(GetParam());
- ASSERT_NE(mService, nullptr);
- mCallback = new FaceCallback();
- mCallback->SetWaitTimeoutDefault(kTimeout);
- Return<void> ret1 = mService->setCallback(mCallback, [](const OptionalUint64& res) {
- ASSERT_EQ(Status::OK, res.status);
- // Makes sure the "deviceId" represented by "res.value" is not 0.
- // 0 would mean the HIDL is not available.
- ASSERT_NE(0UL, res.value);
- });
- ASSERT_TRUE(ret1.isOk());
- Return<Status> ret2 = mService->setActiveUser(kUserId, kFacedataDir);
- ASSERT_EQ(Status::OK, static_cast<Status>(ret2));
- }
-
- void TearDown() override {}
-
- sp<IBiometricsFace> mService;
- sp<FaceCallback> mCallback;
-};
-
-// enroll with an invalid (all zeroes) HAT should fail.
-TEST_P(FaceHidlTest, Enroll1_1ZeroHatTest) {
- // Filling HAT with zeros
- hidl_vec<uint8_t> token(69);
- for (size_t i = 0; i < 69; i++) {
- token[i] = 0;
- }
-
- hidl_handle windowId = nullptr;
- Return<Status> ret = mService->enroll_1_1(token, kTimeoutSec, {}, windowId);
- ASSERT_EQ(Status::OK, static_cast<Status>(ret));
-
- // onError should be called with a meaningful (nonzero) error.
- auto res = mCallback->WaitForCallback(kCallbackNameOnError);
- EXPECT_TRUE(res.no_timeout);
- EXPECT_EQ(kUserId, res.args->userId);
- EXPECT_EQ(FaceError::UNABLE_TO_PROCESS, res.args->error);
-}
-
-// enroll with an invalid HAT should fail.
-TEST_P(FaceHidlTest, Enroll1_1GarbageHatTest) {
- // Filling HAT with pseudorandom invalid data.
- // Using default seed to make the test reproducible.
- std::mt19937 gen(std::mt19937::default_seed);
- std::uniform_int_distribution<uint8_t> dist;
- hidl_vec<uint8_t> token(69);
- for (size_t i = 0; i < 69; ++i) {
- token[i] = dist(gen);
- }
-
- hidl_handle windowId = nullptr;
- Return<Status> ret = mService->enroll_1_1(token, kTimeoutSec, {}, windowId);
- ASSERT_EQ(Status::OK, static_cast<Status>(ret));
-
- // onError should be called with a meaningful (nonzero) error.
- auto res = mCallback->WaitForCallback(kCallbackNameOnError);
- EXPECT_TRUE(res.no_timeout);
- EXPECT_EQ(kUserId, res.args->userId);
- EXPECT_EQ(FaceError::UNABLE_TO_PROCESS, res.args->error);
-}
-
-// enroll with an invalid (all zeroes) HAT should fail.
-TEST_P(FaceHidlTest, EnrollRemotelyZeroHatTest) {
- // Filling HAT with zeros
- hidl_vec<uint8_t> token(69);
- for (size_t i = 0; i < 69; i++) {
- token[i] = 0;
- }
-
- Return<Status> ret = mService->enrollRemotely(token, kTimeoutSec, {});
- ASSERT_EQ(Status::OK, static_cast<Status>(ret));
-
- // onError should be called with a meaningful (nonzero) error.
- auto res = mCallback->WaitForCallback(kCallbackNameOnError);
- EXPECT_TRUE(res.no_timeout);
- EXPECT_EQ(kUserId, res.args->userId);
- EXPECT_EQ(FaceError::UNABLE_TO_PROCESS, res.args->error);
-}
-
-// enroll with an invalid HAT should fail.
-TEST_P(FaceHidlTest, EnrollRemotelyGarbageHatTest) {
- // Filling HAT with pseudorandom invalid data.
- // Using default seed to make the test reproducible.
- std::mt19937 gen(std::mt19937::default_seed);
- std::uniform_int_distribution<uint8_t> dist;
- hidl_vec<uint8_t> token(69);
- for (size_t i = 0; i < 69; ++i) {
- token[i] = dist(gen);
- }
-
- Return<Status> ret = mService->enrollRemotely(token, kTimeoutSec, {});
- ASSERT_EQ(Status::OK, static_cast<Status>(ret));
-
- // onError should be called with a meaningful (nonzero) error.
- auto res = mCallback->WaitForCallback(kCallbackNameOnError);
- EXPECT_TRUE(res.no_timeout);
- EXPECT_EQ(kUserId, res.args->userId);
- EXPECT_EQ(FaceError::UNABLE_TO_PROCESS, res.args->error);
-}
-
-} // anonymous namespace
-
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(FaceHidlTest);
-INSTANTIATE_TEST_SUITE_P(
- PerInstance, FaceHidlTest,
- testing::ValuesIn(android::hardware::getAllHalInstanceNames(IBiometricsFace::descriptor)),
- android::hardware::PrintInstanceNameToString);
diff --git a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/IFace.aidl b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/IFace.aidl
index 37345ec..bfaf90d 100644
--- a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/IFace.aidl
+++ b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/IFace.aidl
@@ -35,4 +35,5 @@
interface IFace {
android.hardware.biometrics.face.SensorProps[] getSensorProps();
android.hardware.biometrics.face.ISession createSession(in int sensorId, in int userId, in android.hardware.biometrics.face.ISessionCallback cb);
+ void reset();
}
diff --git a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/ISession.aidl b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/ISession.aidl
index b855a9e..c9165e1 100644
--- a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/ISession.aidl
+++ b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/ISession.aidl
@@ -45,4 +45,5 @@
void getAuthenticatorId(in int cookie);
void invalidateAuthenticatorId(in int cookie);
void resetLockout(in int cookie, in android.hardware.keymaster.HardwareAuthToken hat);
+ void close(in int cookie);
}
diff --git a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/SessionState.aidl b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/SessionState.aidl
index 46751d0..3792eae 100644
--- a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/SessionState.aidl
+++ b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/SessionState.aidl
@@ -34,7 +34,7 @@
@Backing(type="byte") @VintfStability
enum SessionState {
IDLING = 0,
- TERMINATED = 1,
+ CLOSED = 1,
GENERATING_CHALLENGE = 2,
REVOKING_CHALLENGE = 3,
ENROLLING = 4,
diff --git a/biometrics/face/aidl/android/hardware/biometrics/face/IFace.aidl b/biometrics/face/aidl/android/hardware/biometrics/face/IFace.aidl
index f9ed4b1..afb7c8d 100644
--- a/biometrics/face/aidl/android/hardware/biometrics/face/IFace.aidl
+++ b/biometrics/face/aidl/android/hardware/biometrics/face/IFace.aidl
@@ -35,6 +35,10 @@
* Creates a session that can be used by the framework to perform operations such as
* enroll, authenticate, etc. for the given sensorId and userId.
*
+ * Calling this method while there is an active session is considered an error. If the
+ * framework is in a bad state and for some reason cannot close its session, it should use
+ * the reset method below.
+ *
* Implementations must store user-specific state or metadata in /data/vendor_de/<user>/facedata
* as specified by the SELinux policy. The directory /data/vendor_de is managed by vold (see
* vold_prepare_subdirs.cpp). Implementations may store additional user-specific data, such as
@@ -47,4 +51,13 @@
*/
ISession createSession(in int sensorId, in int userId, in ISessionCallback cb);
+ /**
+ * Resets the HAL into a clean state, forcing it to cancel all of the pending operations, close
+ * its current session, and release all of the acquired resources.
+ *
+ * This should be used as a last resort to recover the HAL if the current session becomes
+ * unresponsive. The implementation might choose to restart the HAL process to get back into a
+ * good state.
+ */
+ void reset();
}
diff --git a/biometrics/face/aidl/android/hardware/biometrics/face/ISession.aidl b/biometrics/face/aidl/android/hardware/biometrics/face/ISession.aidl
index f540502..6f2014a 100644
--- a/biometrics/face/aidl/android/hardware/biometrics/face/ISession.aidl
+++ b/biometrics/face/aidl/android/hardware/biometrics/face/ISession.aidl
@@ -17,19 +17,20 @@
package android.hardware.biometrics.face;
import android.hardware.biometrics.common.ICancellationSignal;
-import android.hardware.biometrics.face.Feature;
import android.hardware.biometrics.face.EnrollmentType;
-import android.hardware.keymaster.HardwareAuthToken;
+import android.hardware.biometrics.face.Feature;
import android.hardware.common.NativeHandle;
+import android.hardware.keymaster.HardwareAuthToken;
-/** * A session is a collection of immutable state (sensorId, userId), mutable state (SessionState),
+/**
+ * A session is a collection of immutable state (sensorId, userId), mutable state (SessionState),
* methods available for the framework to call, and a callback (ISessionCallback) to notify the
* framework about the events and results. A session is used to establish communication between
* the framework and the HAL.
*/
@VintfStability
interface ISession {
- /**
+ /**
* generateChallenge:
*
* Begins a secure transaction request. Note that the challenge by itself is not useful. It only
@@ -134,9 +135,9 @@
* @param hat See above documentation.
* @param enrollmentType See the EnrollmentType enum.
* @param features See the Feature enum.
- * @param previewSurface A surface provided by the framework if SensorProps#halControlsPreview is
- * set to true. The HAL must send the preview frames to previewSurface if
- * it's not null.
+ * @param previewSurface A surface provided by the framework if SensorProps#halControlsPreview
+ * is set to true. The HAL must send the preview frames to previewSurface
+ * if it's not null.
* @return ICancellationSignal An object that can be used by the framework to cancel this
* operation.
*/
@@ -420,5 +421,22 @@
* @param hat HardwareAuthToken See above documentation.
*/
void resetLockout(in int cookie, in HardwareAuthToken hat);
-}
+ /*
+ * Close this session and allow the HAL to release the resources associated with this session.
+ *
+ * A session can only be closed when it's in SessionState::IDLING. Closing a session will
+ * result in a ISessionCallback#onStateChanged call with SessionState::CLOSED.
+ *
+ * If a session is unresponsive or stuck in a state other than SessionState::CLOSED,
+ * IFace#reset could be used as a last resort to terminate the session and recover the HAL
+ * from a bad state.
+ *
+ * All sessions must be explicitly closed. Calling IFace#createSession while there is an active
+ * session is considered an error.
+ *
+ * @param cookie An identifier used to track subsystem operations related to this call path. The
+ * client must guarantee that it is unique per ISession.
+ */
+ void close(in int cookie);
+}
diff --git a/biometrics/face/aidl/android/hardware/biometrics/face/SessionState.aidl b/biometrics/face/aidl/android/hardware/biometrics/face/SessionState.aidl
index 7675564..afde4eb 100644
--- a/biometrics/face/aidl/android/hardware/biometrics/face/SessionState.aidl
+++ b/biometrics/face/aidl/android/hardware/biometrics/face/SessionState.aidl
@@ -25,9 +25,9 @@
IDLING,
/**
- * The session has been terminated by the HAL.
+ * The session has been closed by the client.
*/
- TERMINATED,
+ CLOSED,
/**
* The HAL is processing the ISession#generateChallenge request.
@@ -89,4 +89,3 @@
*/
RESETTING_LOCKOUT
}
-
diff --git a/biometrics/face/aidl/default/Face.cpp b/biometrics/face/aidl/default/Face.cpp
index 773359e..2b40850 100644
--- a/biometrics/face/aidl/default/Face.cpp
+++ b/biometrics/face/aidl/default/Face.cpp
@@ -63,4 +63,8 @@
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus Face::reset() {
+ return ndk::ScopedAStatus::ok();
+}
+
} // namespace aidl::android::hardware::biometrics::face
diff --git a/biometrics/face/aidl/default/Face.h b/biometrics/face/aidl/default/Face.h
index 786b4f8..809b856 100644
--- a/biometrics/face/aidl/default/Face.h
+++ b/biometrics/face/aidl/default/Face.h
@@ -27,6 +27,8 @@
ndk::ScopedAStatus createSession(int32_t sensorId, int32_t userId,
const std::shared_ptr<ISessionCallback>& cb,
std::shared_ptr<ISession>* _aidl_return) override;
+
+ ndk::ScopedAStatus reset() override;
};
} // namespace aidl::android::hardware::biometrics::face
diff --git a/biometrics/face/aidl/default/Session.cpp b/biometrics/face/aidl/default/Session.cpp
index 63d1721..a7130e6 100644
--- a/biometrics/face/aidl/default/Session.cpp
+++ b/biometrics/face/aidl/default/Session.cpp
@@ -15,6 +15,7 @@
*/
#include <aidl/android/hardware/biometrics/common/BnCancellationSignal.h>
+#include <android-base/logging.h>
#include "Session.h"
@@ -37,6 +38,7 @@
Session::Session(std::shared_ptr<ISessionCallback> cb) : cb_(std::move(cb)) {}
ndk::ScopedAStatus Session::generateChallenge(int32_t /*cookie*/, int32_t /*timeoutSec*/) {
+ LOG(INFO) << "generateChallenge";
if (cb_) {
cb_->onStateChanged(0, SessionState::GENERATING_CHALLENGE);
cb_->onChallengeGenerated(0);
@@ -46,6 +48,7 @@
}
ndk::ScopedAStatus Session::revokeChallenge(int32_t /*cookie*/, int64_t challenge) {
+ LOG(INFO) << "revokeChallenge";
if (cb_) {
cb_->onStateChanged(0, SessionState::REVOKING_CHALLENGE);
cb_->onChallengeRevoked(challenge);
@@ -59,11 +62,13 @@
EnrollmentType /*enrollmentType*/, const std::vector<Feature>& /*features*/,
const NativeHandle& /*previewSurface*/,
std::shared_ptr<biometrics::common::ICancellationSignal>* /*return_val*/) {
+ LOG(INFO) << "enroll";
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::authenticate(int32_t /*cookie*/, int64_t /*keystoreOperationId*/,
std::shared_ptr<common::ICancellationSignal>* return_val) {
+ LOG(INFO) << "authenticate";
if (cb_) {
cb_->onStateChanged(0, SessionState::AUTHENTICATING);
}
@@ -73,10 +78,12 @@
ndk::ScopedAStatus Session::detectInteraction(
int32_t /*cookie*/, std::shared_ptr<common::ICancellationSignal>* /*return_val*/) {
+ LOG(INFO) << "detectInteraction";
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::enumerateEnrollments(int32_t /*cookie*/) {
+ LOG(INFO) << "enumerateEnrollments";
if (cb_) {
cb_->onStateChanged(0, SessionState::ENUMERATING_ENROLLMENTS);
cb_->onEnrollmentsEnumerated(std::vector<int32_t>());
@@ -87,6 +94,7 @@
ndk::ScopedAStatus Session::removeEnrollments(int32_t /*cookie*/,
const std::vector<int32_t>& /*enrollmentIds*/) {
+ LOG(INFO) << "removeEnrollments";
if (cb_) {
cb_->onStateChanged(0, SessionState::REMOVING_ENROLLMENTS);
cb_->onEnrollmentsRemoved(std::vector<int32_t>());
@@ -96,6 +104,7 @@
}
ndk::ScopedAStatus Session::getFeatures(int32_t /*cookie*/, int32_t /*enrollmentId*/) {
+ LOG(INFO) << "getFeatures";
return ndk::ScopedAStatus::ok();
}
@@ -103,10 +112,12 @@
const keymaster::HardwareAuthToken& /*hat*/,
int32_t /*enrollmentId*/, Feature /*feature*/,
bool /*enabled*/) {
+ LOG(INFO) << "setFeature";
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::getAuthenticatorId(int32_t /*cookie*/) {
+ LOG(INFO) << "getAuthenticatorId";
if (cb_) {
cb_->onStateChanged(0, SessionState::GETTING_AUTHENTICATOR_ID);
cb_->onAuthenticatorIdRetrieved(0 /* authenticatorId */);
@@ -116,11 +127,13 @@
}
ndk::ScopedAStatus Session::invalidateAuthenticatorId(int32_t /*cookie*/) {
+ LOG(INFO) << "invalidateAuthenticatorId";
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::resetLockout(int32_t /*cookie*/,
const keymaster::HardwareAuthToken& /*hat*/) {
+ LOG(INFO) << "resetLockout";
if (cb_) {
cb_->onStateChanged(0, SessionState::RESETTING_LOCKOUT);
cb_->onLockoutCleared();
@@ -129,4 +142,8 @@
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus Session::close(int32_t /*cookie*/) {
+ return ndk::ScopedAStatus::ok();
+}
+
} // namespace aidl::android::hardware::biometrics::face
diff --git a/biometrics/face/aidl/default/Session.h b/biometrics/face/aidl/default/Session.h
index 83cb064..0651726 100644
--- a/biometrics/face/aidl/default/Session.h
+++ b/biometrics/face/aidl/default/Session.h
@@ -63,6 +63,8 @@
ndk::ScopedAStatus resetLockout(int32_t cookie,
const keymaster::HardwareAuthToken& hat) override;
+ ndk::ScopedAStatus close(int32_t cookie) override;
+
private:
std::shared_ptr<ISessionCallback> cb_;
};
diff --git a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/IFingerprint.aidl b/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/IFingerprint.aidl
index c5a5422..2d44528 100644
--- a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/IFingerprint.aidl
+++ b/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/IFingerprint.aidl
@@ -35,4 +35,5 @@
interface IFingerprint {
android.hardware.biometrics.fingerprint.SensorProps[] getSensorProps();
android.hardware.biometrics.fingerprint.ISession createSession(in int sensorId, in int userId, in android.hardware.biometrics.fingerprint.ISessionCallback cb);
+ void reset();
}
diff --git a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/ISession.aidl b/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/ISession.aidl
index be0029c..b583006 100644
--- a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/ISession.aidl
+++ b/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/ISession.aidl
@@ -43,6 +43,7 @@
void getAuthenticatorId(in int cookie);
void invalidateAuthenticatorId(in int cookie);
void resetLockout(in int cookie, in android.hardware.keymaster.HardwareAuthToken hat);
+ void close(in int cookie);
void onPointerDown(in int pointerId, in int x, in int y, in float minor, in float major);
void onPointerUp(in int pointerId);
void onUiReady();
diff --git a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/SessionState.aidl b/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/SessionState.aidl
index 44323ff..05dd85b 100644
--- a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/SessionState.aidl
+++ b/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/SessionState.aidl
@@ -34,14 +34,15 @@
@Backing(type="byte") @VintfStability
enum SessionState {
IDLING = 0,
- GENERATING_CHALLENGE = 1,
- REVOKING_CHALLENGE = 2,
- ENROLLING = 3,
- AUTHENTICATING = 4,
- DETECTING_INTERACTION = 5,
- ENUMERATING_ENROLLMENTS = 6,
- REMOVING_ENROLLMENTS = 7,
- GETTING_AUTHENTICATOR_ID = 8,
- INVALIDATING_AUTHENTICATOR_ID = 9,
- RESETTING_LOCKOUT = 10,
+ CLOSED = 1,
+ GENERATING_CHALLENGE = 2,
+ REVOKING_CHALLENGE = 3,
+ ENROLLING = 4,
+ AUTHENTICATING = 5,
+ DETECTING_INTERACTION = 6,
+ ENUMERATING_ENROLLMENTS = 7,
+ REMOVING_ENROLLMENTS = 8,
+ GETTING_AUTHENTICATOR_ID = 9,
+ INVALIDATING_AUTHENTICATOR_ID = 10,
+ RESETTING_LOCKOUT = 11,
}
diff --git a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/IFingerprint.aidl b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/IFingerprint.aidl
index 3675aa4..37062ba 100644
--- a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/IFingerprint.aidl
+++ b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/IFingerprint.aidl
@@ -35,6 +35,10 @@
* Creates a session which can then be used by the framework to perform operations such as
* enroll, authenticate, etc for the given sensorId and userId.
*
+ * Calling this method while there is an active session is considered an error. If the
+ * framework is in a bad state and for some reason cannot close its session, it should use
+ * the reset method below.
+ *
* A physical sensor identified by sensorId typically supports only a single in-flight session
* at a time. As such, if a session is currently in a state other than SessionState::IDLING, the
* HAL MUST finish or cancel the current operation and return to SessionState::IDLING before the
@@ -61,4 +65,14 @@
* @return A new session
*/
ISession createSession(in int sensorId, in int userId, in ISessionCallback cb);
+
+ /**
+ * Resets the HAL into a clean state, forcing it to cancel all of the pending operations, close
+ * its current session, and release all of the acquired resources.
+ *
+ * This should be used as a last resort to recover the HAL if the current session becomes
+ * unresponsive. The implementation might choose to restart the HAL process to get back into a
+ * good state.
+ */
+ void reset();
}
diff --git a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/ISession.aidl b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/ISession.aidl
index f9c3732..ab7930d 100644
--- a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/ISession.aidl
+++ b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/ISession.aidl
@@ -366,6 +366,24 @@
*/
void resetLockout(in int cookie, in HardwareAuthToken hat);
+ /*
+ * Close this session and allow the HAL to release the resources associated with this session.
+ *
+ * A session can only be closed when it's in SessionState::IDLING. Closing a session will
+ * result in a ISessionCallback#onStateChanged call with SessionState::CLOSED.
+ *
+ * If a session is unresponsive or stuck in a state other than SessionState::CLOSED,
+ * IFingerprint#reset could be used as a last resort to terminate the session and recover the
+ * HAL from a bad state.
+ *
+ * All sessions must be explicitly closed. Calling IFingerprint#createSession while there is an
+ * active session is considered an error.
+ *
+ * @param cookie An identifier used to track subsystem operations related to this call path. The
+ * client must guarantee that it is unique per ISession.
+ */
+ void close(in int cookie);
+
/**
* Methods for notifying the under-display fingerprint sensor about external events.
*/
@@ -420,4 +438,3 @@
*/
void onUiReady();
}
-
diff --git a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/SessionState.aidl b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/SessionState.aidl
index 1de01ad..19a6ce3 100644
--- a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/SessionState.aidl
+++ b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/SessionState.aidl
@@ -25,6 +25,11 @@
IDLING,
/**
+ * The session has been closed by the client.
+ */
+ CLOSED,
+
+ /**
* The HAL is processing the ISession#generateChallenge request.
*/
GENERATING_CHALLENGE,
@@ -74,4 +79,3 @@
*/
RESETTING_LOCKOUT
}
-
diff --git a/biometrics/fingerprint/aidl/default/Android.bp b/biometrics/fingerprint/aidl/default/Android.bp
index c8cb663..24087cf 100644
--- a/biometrics/fingerprint/aidl/default/Android.bp
+++ b/biometrics/fingerprint/aidl/default/Android.bp
@@ -1,18 +1,32 @@
cc_binary {
name: "android.hardware.biometrics.fingerprint-service.example",
+ vendor: true,
relative_install_path: "hw",
init_rc: ["fingerprint-default.rc"],
vintf_fragments: ["fingerprint-default.xml"],
- vendor: true,
+ local_include_dirs: ["include"],
+ srcs: [
+ "main.cpp",
+ "Fingerprint.cpp",
+ "Session.cpp",
+ ],
shared_libs: [
"libbase",
"libbinder_ndk",
"android.hardware.biometrics.fingerprint-V1-ndk_platform",
"android.hardware.biometrics.common-V1-ndk_platform",
],
+}
+
+cc_test_host {
+ name: "android.hardware.biometrics.fingerprint.WorkerThreadTest",
+ local_include_dirs: ["include"],
srcs: [
- "main.cpp",
- "Fingerprint.cpp",
- "Session.cpp",
+ "tests/WorkerThreadTest.cpp",
+ "WorkerThread.cpp",
],
+ shared_libs: [
+ "libcutils",
+ ],
+ test_suites: ["general-tests"],
}
diff --git a/biometrics/fingerprint/aidl/default/Fingerprint.cpp b/biometrics/fingerprint/aidl/default/Fingerprint.cpp
index f27e278..6f89346 100644
--- a/biometrics/fingerprint/aidl/default/Fingerprint.cpp
+++ b/biometrics/fingerprint/aidl/default/Fingerprint.cpp
@@ -18,50 +18,49 @@
#include "Session.h"
namespace aidl::android::hardware::biometrics::fingerprint {
+namespace {
-const int kSensorId = 1;
-const common::SensorStrength kSensorStrength = common::SensorStrength::STRONG;
-const int kMaxEnrollmentsPerUser = 5;
-const FingerprintSensorType kSensorType = FingerprintSensorType::REAR;
-const bool kSupportsNavigationGestures = true;
-const std::string kHwDeviceName = "fingerprintSensor";
-const std::string kHardwareVersion = "vendor/model/revision";
-const std::string kFirmwareVersion = "1.01";
-const std::string kSerialNumber = "00000001";
+constexpr int SENSOR_ID = 1;
+constexpr common::SensorStrength SENSOR_STRENGTH = common::SensorStrength::STRONG;
+constexpr int MAX_ENROLLMENTS_PER_USER = 5;
+constexpr FingerprintSensorType SENSOR_TYPE = FingerprintSensorType::REAR;
+constexpr bool SUPPORTS_NAVIGATION_GESTURES = true;
+constexpr char HW_DEVICE_NAME[] = "fingerprintSensor";
+constexpr char HW_VERSION[] = "vendor/model/revision";
+constexpr char FW_VERSION[] = "1.01";
+constexpr char SERIAL_NUMBER[] = "00000001";
-ndk::ScopedAStatus Fingerprint::getSensorProps(std::vector<SensorProps>* return_val) {
- *return_val = std::vector<SensorProps>();
+} // namespace
- std::vector<common::HardwareInfo> hardwareInfos = std::vector<common::HardwareInfo>();
- common::HardwareInfo sensorInfo = {kHwDeviceName,
- kHardwareVersion,
- kFirmwareVersion,
- kSerialNumber
- };
- hardwareInfos.push_back(sensorInfo);
- common::CommonProps commonProps = {kSensorId,
- kSensorStrength,
- kMaxEnrollmentsPerUser,
- hardwareInfos};
- SensorLocation sensorLocation = {
- 0 /* displayId */,
- 0 /* sensorLocationX */,
- 0 /* sensorLocationY */,
- 0 /* sensorRadius */
- };
- SensorProps props = {commonProps,
- kSensorType,
- {sensorLocation},
- kSupportsNavigationGestures,
- false /* supportsDetectInteraction */};
- return_val->push_back(props);
+Fingerprint::Fingerprint() {}
+
+ndk::ScopedAStatus Fingerprint::getSensorProps(std::vector<SensorProps>* out) {
+ std::vector<common::HardwareInfo> hardwareInfos = {
+ {HW_DEVICE_NAME, HW_VERSION, FW_VERSION, SERIAL_NUMBER}};
+
+ common::CommonProps commonProps = {SENSOR_ID, SENSOR_STRENGTH, MAX_ENROLLMENTS_PER_USER,
+ hardwareInfos};
+
+ SensorLocation sensorLocation = {0 /* displayId */, 0 /* sensorLocationX */,
+ 0 /* sensorLocationY */, 0 /* sensorRadius */};
+
+ *out = {{commonProps,
+ SENSOR_TYPE,
+ {sensorLocation},
+ SUPPORTS_NAVIGATION_GESTURES,
+ false /* supportsDetectInteraction */}};
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Fingerprint::createSession(int32_t /*sensorId*/, int32_t /*userId*/,
const std::shared_ptr<ISessionCallback>& cb,
- std::shared_ptr<ISession>* return_val) {
- *return_val = SharedRefBase::make<Session>(cb);
+ std::shared_ptr<ISession>* out) {
+ *out = SharedRefBase::make<Session>(cb);
return ndk::ScopedAStatus::ok();
}
+
+ndk::ScopedAStatus Fingerprint::reset() {
+ return ndk::ScopedAStatus::ok();
+}
+
} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/Session.cpp b/biometrics/fingerprint/aidl/default/Session.cpp
index bf08203..c928df4 100644
--- a/biometrics/fingerprint/aidl/default/Session.cpp
+++ b/biometrics/fingerprint/aidl/default/Session.cpp
@@ -15,6 +15,7 @@
*/
#include <aidl/android/hardware/biometrics/common/BnCancellationSignal.h>
+#include <android-base/logging.h>
#include "Session.h"
@@ -25,79 +26,97 @@
ndk::ScopedAStatus cancel() override { return ndk::ScopedAStatus::ok(); }
};
-Session::Session(std::shared_ptr<ISessionCallback> cb) : cb_(std::move(cb)) {}
+Session::Session(std::shared_ptr<ISessionCallback> cb) : mCb(std::move(cb)) {}
ndk::ScopedAStatus Session::generateChallenge(int32_t /*cookie*/, int32_t /*timeoutSec*/) {
+ LOG(INFO) << "generateChallenge";
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::revokeChallenge(int32_t /*cookie*/, int64_t /*challenge*/) {
+ LOG(INFO) << "revokeChallenge";
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::enroll(int32_t /*cookie*/, const keymaster::HardwareAuthToken& /*hat*/,
- std::shared_ptr<common::ICancellationSignal>* /*return_val*/) {
+ std::shared_ptr<common::ICancellationSignal>* /*out*/) {
+ LOG(INFO) << "enroll";
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::authenticate(int32_t /*cookie*/, int64_t /*keystoreOperationId*/,
- std::shared_ptr<common::ICancellationSignal>* return_val) {
- if (cb_) {
- cb_->onStateChanged(0, SessionState::AUTHENTICATING);
+ std::shared_ptr<common::ICancellationSignal>* out) {
+ LOG(INFO) << "authenticate";
+ if (mCb) {
+ mCb->onStateChanged(0, SessionState::AUTHENTICATING);
}
- *return_val = SharedRefBase::make<CancellationSignal>();
+ *out = SharedRefBase::make<CancellationSignal>();
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::detectInteraction(
- int32_t /*cookie*/, std::shared_ptr<common::ICancellationSignal>* /*return_val*/) {
+ int32_t /*cookie*/, std::shared_ptr<common::ICancellationSignal>* /*out*/) {
+ LOG(INFO) << "detectInteraction";
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::enumerateEnrollments(int32_t /*cookie*/) {
- if (cb_) {
- cb_->onStateChanged(0, SessionState::ENUMERATING_ENROLLMENTS);
- cb_->onEnrollmentsEnumerated(std::vector<int32_t>());
+ LOG(INFO) << "enumerateEnrollments";
+ if (mCb) {
+ mCb->onStateChanged(0, SessionState::ENUMERATING_ENROLLMENTS);
+ mCb->onEnrollmentsEnumerated(std::vector<int32_t>());
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::removeEnrollments(int32_t /*cookie*/,
const std::vector<int32_t>& /*enrollmentIds*/) {
- if (cb_) {
- cb_->onStateChanged(0, SessionState::REMOVING_ENROLLMENTS);
- cb_->onEnrollmentsRemoved(std::vector<int32_t>());
+ LOG(INFO) << "removeEnrollments";
+ if (mCb) {
+ mCb->onStateChanged(0, SessionState::REMOVING_ENROLLMENTS);
+ mCb->onEnrollmentsRemoved(std::vector<int32_t>());
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::getAuthenticatorId(int32_t /*cookie*/) {
- if (cb_) {
- cb_->onStateChanged(0, SessionState::GETTING_AUTHENTICATOR_ID);
- cb_->onAuthenticatorIdRetrieved(0 /* authenticatorId */);
+ LOG(INFO) << "getAuthenticatorId";
+ if (mCb) {
+ mCb->onStateChanged(0, SessionState::GETTING_AUTHENTICATOR_ID);
+ mCb->onAuthenticatorIdRetrieved(0 /* authenticatorId */);
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::invalidateAuthenticatorId(int32_t /*cookie*/) {
+ LOG(INFO) << "invalidateAuthenticatorId";
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::resetLockout(int32_t /*cookie*/,
const keymaster::HardwareAuthToken& /*hat*/) {
+ LOG(INFO) << "resetLockout";
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Session::close(int32_t /*cookie*/) {
+ LOG(INFO) << "close";
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::onPointerDown(int32_t /*pointerId*/, int32_t /*x*/, int32_t /*y*/,
float /*minor*/, float /*major*/) {
+ LOG(INFO) << "onPointerDown";
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::onPointerUp(int32_t /*pointerId*/) {
+ LOG(INFO) << "onPointerUp";
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::onUiReady() {
+ LOG(INFO) << "onUiReady";
return ndk::ScopedAStatus::ok();
}
} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/WorkerThread.cpp b/biometrics/fingerprint/aidl/default/WorkerThread.cpp
new file mode 100644
index 0000000..512efb8
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/WorkerThread.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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 "WorkerThread.h"
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+// It's important that mThread is initialized after everything else because it runs a member
+// function that may use any member of this class.
+WorkerThread::WorkerThread(size_t maxQueueSize)
+ : mMaxSize(maxQueueSize),
+ mIsDestructing(false),
+ mQueue(),
+ mQueueMutex(),
+ mQueueCond(),
+ mThread(&WorkerThread::threadFunc, this) {}
+
+WorkerThread::~WorkerThread() {
+ // This is a signal for threadFunc to terminate as soon as possible, and a hint for schedule
+ // that it doesn't need to do any work.
+ mIsDestructing = true;
+ mQueueCond.notify_all();
+ mThread.join();
+}
+
+bool WorkerThread::schedule(Task&& task) {
+ if (mIsDestructing) {
+ return false;
+ }
+
+ std::unique_lock<std::mutex> lock(mQueueMutex);
+ if (mQueue.size() >= mMaxSize) {
+ return false;
+ }
+ mQueue.push_back(std::move(task));
+ lock.unlock();
+ mQueueCond.notify_one();
+ return true;
+}
+
+void WorkerThread::threadFunc() {
+ while (!mIsDestructing) {
+ std::unique_lock<std::mutex> lock(mQueueMutex);
+ mQueueCond.wait(lock, [this] { return !mQueue.empty() || mIsDestructing; });
+ if (mIsDestructing) {
+ return;
+ }
+ Task task = std::move(mQueue.front());
+ mQueue.pop_front();
+ lock.unlock();
+ task();
+ }
+}
+
+} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/Fingerprint.h b/biometrics/fingerprint/aidl/default/include/Fingerprint.h
similarity index 88%
rename from biometrics/fingerprint/aidl/default/Fingerprint.h
rename to biometrics/fingerprint/aidl/default/include/Fingerprint.h
index 4e952ba..ce1366c 100644
--- a/biometrics/fingerprint/aidl/default/Fingerprint.h
+++ b/biometrics/fingerprint/aidl/default/include/Fingerprint.h
@@ -20,13 +20,17 @@
namespace aidl::android::hardware::biometrics::fingerprint {
-class Fingerprint : public BnFingerprint {
+class Fingerprint final : public BnFingerprint {
public:
- ndk::ScopedAStatus getSensorProps(std::vector<SensorProps>* _aidl_return) override;
+ Fingerprint();
+
+ ndk::ScopedAStatus getSensorProps(std::vector<SensorProps>* out) override;
ndk::ScopedAStatus createSession(int32_t sensorId, int32_t userId,
const std::shared_ptr<ISessionCallback>& cb,
- std::shared_ptr<ISession>* _aidl_return) override;
+ std::shared_ptr<ISession>* out) override;
+
+ ndk::ScopedAStatus reset() override;
};
} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/Session.h b/biometrics/fingerprint/aidl/default/include/Session.h
similarity index 87%
rename from biometrics/fingerprint/aidl/default/Session.h
rename to biometrics/fingerprint/aidl/default/include/Session.h
index ed3ae3f..99d806b 100644
--- a/biometrics/fingerprint/aidl/default/Session.h
+++ b/biometrics/fingerprint/aidl/default/include/Session.h
@@ -33,14 +33,13 @@
ndk::ScopedAStatus revokeChallenge(int32_t cookie, int64_t challenge) override;
ndk::ScopedAStatus enroll(int32_t cookie, const keymaster::HardwareAuthToken& hat,
- std::shared_ptr<common::ICancellationSignal>* return_val) override;
+ std::shared_ptr<common::ICancellationSignal>* out) override;
- ndk::ScopedAStatus authenticate(
- int32_t cookie, int64_t keystoreOperationId,
- std::shared_ptr<common::ICancellationSignal>* return_val) override;
+ ndk::ScopedAStatus authenticate(int32_t cookie, int64_t keystoreOperationId,
+ std::shared_ptr<common::ICancellationSignal>* out) override;
ndk::ScopedAStatus detectInteraction(
- int32_t cookie, std::shared_ptr<common::ICancellationSignal>* return_val) override;
+ int32_t cookie, std::shared_ptr<common::ICancellationSignal>* out) override;
ndk::ScopedAStatus enumerateEnrollments(int32_t cookie) override;
@@ -54,6 +53,8 @@
ndk::ScopedAStatus resetLockout(int32_t cookie,
const keymaster::HardwareAuthToken& hat) override;
+ ndk::ScopedAStatus close(int32_t cookie) override;
+
ndk::ScopedAStatus onPointerDown(int32_t pointerId, int32_t x, int32_t y, float minor,
float major) override;
@@ -62,7 +63,7 @@
ndk::ScopedAStatus onUiReady() override;
private:
- std::shared_ptr<ISessionCallback> cb_;
+ std::shared_ptr<ISessionCallback> mCb;
};
} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/include/WorkerThread.h b/biometrics/fingerprint/aidl/default/include/WorkerThread.h
new file mode 100644
index 0000000..49104c8
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/include/WorkerThread.h
@@ -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.
+ */
+
+#pragma once
+
+#include <mutex>
+#include <optional>
+#include <queue>
+#include <thread>
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+using Task = std::function<void()>;
+
+// A class that encapsulates a worker thread and a task queue, and provides a convenient interface
+// for a Session to schedule its tasks for asynchronous execution.
+class WorkerThread final {
+ public:
+ // Internally creates a queue that cannot exceed maxQueueSize elements and a new thread that
+ // polls the queue for tasks until this instance is destructed.
+ explicit WorkerThread(size_t maxQueueSize);
+
+ // Unblocks the internal queue and calls join on the internal thread allowing it to gracefully
+ // exit.
+ ~WorkerThread();
+
+ // Disallow copying this class.
+ WorkerThread(const WorkerThread&) = delete;
+ WorkerThread& operator=(const WorkerThread&) = delete;
+
+ // Also disable moving this class to simplify implementation.
+ WorkerThread(WorkerThread&&) = delete;
+ WorkerThread& operator=(WorkerThread&&) = delete;
+
+ // If the internal queue is not full, pushes a task at the end of the queue and returns true.
+ // Otherwise, returns false. If the queue is busy, blocks until it becomes available.
+ bool schedule(Task&& task);
+
+ private:
+ // The function that runs on the internal thread. Sequentially runs the available tasks from
+ // the queue. If the queue is empty, waits until a new task is added. If the worker is being
+ // destructed, finishes its current task and gracefully exits.
+ void threadFunc();
+
+ // The maximum size that the queue is allowed to expand to.
+ size_t mMaxSize;
+
+ // Whether the destructor was called. If true, tells threadFunc to exit as soon as possible, and
+ // tells schedule to avoid doing any work.
+ std::atomic<bool> mIsDestructing;
+
+ // Queue that's guarded by mQueueMutex and mQueueCond.
+ std::deque<Task> mQueue;
+ std::mutex mQueueMutex;
+ std::condition_variable mQueueCond;
+
+ // The internal thread that works on the tasks from the queue.
+ std::thread mThread;
+};
+
+} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/tests/WorkerThreadTest.cpp b/biometrics/fingerprint/aidl/default/tests/WorkerThreadTest.cpp
new file mode 100644
index 0000000..ba901ad
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/tests/WorkerThreadTest.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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 <algorithm>
+#include <chrono>
+#include <future>
+#include <thread>
+
+#include <gtest/gtest.h>
+
+#include "WorkerThread.h"
+
+namespace {
+
+using aidl::android::hardware::biometrics::fingerprint::WorkerThread;
+using namespace std::chrono_literals;
+
+TEST(WorkerThreadTest, ScheduleReturnsTrueWhenQueueHasSpace) {
+ WorkerThread worker(1 /*maxQueueSize*/);
+ for (int i = 0; i < 100; ++i) {
+ EXPECT_TRUE(worker.schedule([] {}));
+ // Allow enough time for the previous task to be processed.
+ std::this_thread::sleep_for(2ms);
+ }
+}
+
+TEST(WorkerThreadTest, ScheduleReturnsFalseWhenQueueIsFull) {
+ WorkerThread worker(2 /*maxQueueSize*/);
+ // Add a long-running task.
+ worker.schedule([] { std::this_thread::sleep_for(1s); });
+
+ // Allow enough time for the worker to start working on the previous task.
+ std::this_thread::sleep_for(2ms);
+
+ // Fill the worker's queue to the maximum.
+ worker.schedule([] {});
+ worker.schedule([] {});
+
+ EXPECT_FALSE(worker.schedule([] {}));
+}
+
+TEST(WorkerThreadTest, TasksExecuteInOrder) {
+ constexpr int NUM_TASKS = 10000;
+ WorkerThread worker(NUM_TASKS);
+
+ std::vector<int> results;
+ for (int i = 0; i < NUM_TASKS; ++i) {
+ worker.schedule([&results, i] {
+ // Delay tasks differently to provoke races.
+ std::this_thread::sleep_for(std::chrono::nanoseconds(100 - i % 100));
+ // Unguarded write to results to provoke races.
+ results.push_back(i);
+ });
+ }
+
+ std::promise<void> promise;
+ auto future = promise.get_future();
+
+ // Schedule a special task to signal when all of the tasks are finished.
+ worker.schedule([&promise] { promise.set_value(); });
+ auto status = future.wait_for(1s);
+ ASSERT_EQ(status, std::future_status::ready);
+
+ ASSERT_EQ(results.size(), NUM_TASKS);
+ EXPECT_TRUE(std::is_sorted(results.begin(), results.end()));
+}
+
+TEST(WorkerThreadTest, ExecutionStopsAfterWorkerIsDestroyed) {
+ std::promise<void> promise1;
+ std::promise<void> promise2;
+ auto future1 = promise1.get_future();
+ auto future2 = promise2.get_future();
+
+ {
+ WorkerThread worker(2 /*maxQueueSize*/);
+ worker.schedule([&promise1] {
+ promise1.set_value();
+ std::this_thread::sleep_for(200ms);
+ });
+ worker.schedule([&promise2] { promise2.set_value(); });
+
+ // Make sure the first task is executing.
+ auto status1 = future1.wait_for(1s);
+ ASSERT_EQ(status1, std::future_status::ready);
+ }
+
+ // The second task should never execute.
+ auto status2 = future2.wait_for(1s);
+ EXPECT_EQ(status2, std::future_status::timeout);
+}
+
+} // namespace
diff --git a/bluetooth/audio/2.1/vts/functional/VtsHalBluetoothAudioV2_1TargetTest.cpp b/bluetooth/audio/2.1/vts/functional/VtsHalBluetoothAudioV2_1TargetTest.cpp
index 95903d1..57fa07b 100644
--- a/bluetooth/audio/2.1/vts/functional/VtsHalBluetoothAudioV2_1TargetTest.cpp
+++ b/bluetooth/audio/2.1/vts/functional/VtsHalBluetoothAudioV2_1TargetTest.cpp
@@ -1032,7 +1032,7 @@
* stopped with different PCM config
*/
TEST_P(BluetoothAudioProviderLeAudioOutputSoftwareHidlTest,
- StartAndEndLeAudioOutputSessionWithPossiblePcmConfig) {
+ DISABLED_StartAndEndLeAudioOutputSessionWithPossiblePcmConfig) {
bool is_codec_config_valid;
std::unique_ptr<DataMQ> tempDataMQ;
auto hidl_cb = [&is_codec_config_valid, &tempDataMQ](
@@ -1126,7 +1126,7 @@
* stopped with different PCM config
*/
TEST_P(BluetoothAudioProviderLeAudioInputSoftwareHidlTest,
- StartAndEndLeAudioInputSessionWithPossiblePcmConfig) {
+ DISABLED_StartAndEndLeAudioInputSessionWithPossiblePcmConfig) {
bool is_codec_config_valid;
std::unique_ptr<DataMQ> tempDataMQ;
auto hidl_cb = [&is_codec_config_valid, &tempDataMQ](
diff --git a/camera/metadata/3.6/Android.bp b/camera/metadata/3.6/Android.bp
new file mode 100644
index 0000000..d9f3fb8
--- /dev/null
+++ b/camera/metadata/3.6/Android.bp
@@ -0,0 +1,16 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.camera.metadata@3.6",
+ root: "android.hardware",
+ srcs: [
+ "types.hal",
+ ],
+ interfaces: [
+ "android.hardware.camera.metadata@3.2",
+ "android.hardware.camera.metadata@3.3",
+ "android.hardware.camera.metadata@3.4",
+ "android.hardware.camera.metadata@3.5",
+ ],
+ gen_java: true,
+}
diff --git a/camera/metadata/3.6/types.hal b/camera/metadata/3.6/types.hal
new file mode 100644
index 0000000..fb95736
--- /dev/null
+++ b/camera/metadata/3.6/types.hal
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+/*
+ * Autogenerated from camera metadata definitions in
+ * /system/media/camera/docs/metadata_definitions.xml
+ * *** DO NOT EDIT BY HAND ***
+ */
+
+package android.hardware.camera.metadata@3.6;
+
+import android.hardware.camera.metadata@3.2;
+import android.hardware.camera.metadata@3.3;
+import android.hardware.camera.metadata@3.4;
+import android.hardware.camera.metadata@3.5;
+
+// No new metadata sections added in this revision
+
+/**
+ * Main enumeration for defining camera metadata tags added in this revision
+ *
+ * <p>Partial documentation is included for each tag; for complete documentation, reference
+ * '/system/media/camera/docs/docs.html' in the corresponding Android source tree.</p>
+ */
+enum CameraMetadataTag : @3.5::CameraMetadataTag {
+ /** android.scaler.defaultSecureImageSize [static, int32[], public]
+ *
+ * <p>Default YUV/PRIVATE size to use for requesting secure image buffers.</p>
+ */
+ ANDROID_SCALER_DEFAULT_SECURE_IMAGE_SIZE = android.hardware.camera.metadata@3.5::CameraMetadataTag:ANDROID_SCALER_END_3_5,
+
+ ANDROID_SCALER_END_3_6,
+
+};
+
+/*
+ * Enumeration definitions for the various entries that need them
+ */
diff --git a/cas/1.0/vts/functional/VtsHalCasV1_0TargetTest.cpp b/cas/1.0/vts/functional/VtsHalCasV1_0TargetTest.cpp
index df0c859..a1d5930 100644
--- a/cas/1.0/vts/functional/VtsHalCasV1_0TargetTest.cpp
+++ b/cas/1.0/vts/functional/VtsHalCasV1_0TargetTest.cpp
@@ -256,12 +256,19 @@
::testing::AssertionResult MediaCasHidlTest::createCasPlugin(int32_t caSystemId) {
auto status = mService->isSystemIdSupported(caSystemId);
+ bool skipDescrambler = false;
if (!status.isOk() || !status) {
return ::testing::AssertionFailure();
}
status = mService->isDescramblerSupported(caSystemId);
if (!status.isOk() || !status) {
- return ::testing::AssertionFailure();
+ if (mIsTestDescrambler) {
+ return ::testing::AssertionFailure();
+ } else {
+ ALOGI("Skip Descrambler test since it's not required in cas@1.2.");
+ mDescramblerBase = nullptr;
+ skipDescrambler = true;
+ }
}
mCasListener = new MediaCasListener();
@@ -274,16 +281,15 @@
return ::testing::AssertionFailure();
}
+ if (skipDescrambler) {
+ return ::testing::AssertionSuccess();
+ }
+
auto descramblerStatus = mService->createDescrambler(caSystemId);
if (!descramblerStatus.isOk()) {
- if (mIsTestDescrambler) {
- return ::testing::AssertionFailure();
- } else {
- ALOGI("Skip Descrambler test since it's not required in cas@1.2.");
- return ::testing::AssertionSuccess();
- }
+ return ::testing::AssertionFailure();
}
- mIsTestDescrambler = true;
+
mDescramblerBase = descramblerStatus;
return ::testing::AssertionResult(mDescramblerBase != nullptr);
}
@@ -506,7 +512,7 @@
returnStatus = mMediaCas->setSessionPrivateData(streamSessionId, hidlPvtData);
EXPECT_TRUE(returnStatus.isOk());
EXPECT_EQ(Status::OK, returnStatus);
- if (mIsTestDescrambler) {
+ if (mDescramblerBase != nullptr) {
returnStatus = mDescramblerBase->setMediaCasSession(sessionId);
EXPECT_TRUE(returnStatus.isOk());
EXPECT_EQ(Status::OK, returnStatus);
@@ -556,7 +562,7 @@
EXPECT_TRUE(returnStatus.isOk());
EXPECT_EQ(Status::OK, returnStatus);
- if (mIsTestDescrambler) {
+ if (mDescramblerBase != nullptr) {
EXPECT_FALSE(mDescramblerBase->requiresSecureDecoderComponent("video/avc"));
sp<IDescrambler> descrambler;
@@ -606,7 +612,7 @@
EXPECT_TRUE(returnStatus.isOk());
EXPECT_EQ(Status::OK, returnStatus);
- if (mIsTestDescrambler) {
+ if (mDescramblerBase != nullptr) {
returnStatus = mDescramblerBase->setMediaCasSession(sessionId);
EXPECT_TRUE(returnStatus.isOk());
EXPECT_EQ(Status::ERROR_CAS_SESSION_NOT_OPENED, returnStatus);
@@ -672,7 +678,7 @@
EXPECT_TRUE(returnStatus.isOk());
EXPECT_EQ(Status::ERROR_CAS_UNKNOWN, returnStatus);
- if (mIsTestDescrambler) {
+ if (mDescramblerBase != nullptr) {
/*
* Test MediaDescrambler error codes
*/
@@ -720,7 +726,7 @@
std::vector<uint8_t> sessionId;
ASSERT_TRUE(openCasSession(&sessionId));
- if (mIsTestDescrambler) {
+ if (mDescramblerBase != nullptr) {
returnStatus = mDescramblerBase->setMediaCasSession(sessionId);
EXPECT_TRUE(returnStatus.isOk());
EXPECT_EQ(Status::OK, returnStatus);
@@ -732,7 +738,7 @@
EXPECT_TRUE(returnStatus.isOk());
EXPECT_EQ(Status::OK, returnStatus);
- if (mIsTestDescrambler) {
+ if (mDescramblerBase != nullptr) {
sp<IDescrambler> descrambler = IDescrambler::castFrom(mDescramblerBase);
ASSERT_NE(nullptr, descrambler.get());
diff --git a/cas/1.1/vts/functional/VtsHalCasV1_1TargetTest.cpp b/cas/1.1/vts/functional/VtsHalCasV1_1TargetTest.cpp
index 6797506..42d70cf 100644
--- a/cas/1.1/vts/functional/VtsHalCasV1_1TargetTest.cpp
+++ b/cas/1.1/vts/functional/VtsHalCasV1_1TargetTest.cpp
@@ -297,12 +297,19 @@
::testing::AssertionResult MediaCasHidlTest::createCasPlugin(int32_t caSystemId) {
auto status = mService->isSystemIdSupported(caSystemId);
+ bool skipDescrambler = false;
if (!status.isOk() || !status) {
return ::testing::AssertionFailure();
}
status = mService->isDescramblerSupported(caSystemId);
if (!status.isOk() || !status) {
- return ::testing::AssertionFailure();
+ if (mIsTestDescrambler) {
+ return ::testing::AssertionFailure();
+ } else {
+ ALOGI("Skip Descrambler test since it's not required in cas@1.2.");
+ mDescramblerBase = nullptr;
+ skipDescrambler = true;
+ }
}
mCasListener = new MediaCasListener();
@@ -315,16 +322,14 @@
return ::testing::AssertionFailure();
}
+ if (skipDescrambler) {
+ return ::testing::AssertionSuccess();
+ }
+
auto descramblerStatus = mService->createDescrambler(caSystemId);
if (!descramblerStatus.isOk()) {
- if (mIsTestDescrambler) {
- return ::testing::AssertionFailure();
- } else {
- ALOGI("Skip Descrambler test since it's not required in cas@1.2.");
- return ::testing::AssertionSuccess();
- }
+ return ::testing::AssertionFailure();
}
- mIsTestDescrambler = true;
mDescramblerBase = descramblerStatus;
return ::testing::AssertionResult(mDescramblerBase != nullptr);
@@ -481,7 +486,7 @@
EXPECT_TRUE(returnStatus.isOk());
EXPECT_EQ(Status::OK, returnStatus);
- if (mIsTestDescrambler) {
+ if (mDescramblerBase != nullptr) {
returnStatus = mDescramblerBase->setMediaCasSession(sessionId);
EXPECT_TRUE(returnStatus.isOk());
EXPECT_EQ(Status::OK, returnStatus);
@@ -533,7 +538,7 @@
EXPECT_TRUE(returnStatus.isOk());
EXPECT_EQ(Status::OK, returnStatus);
- if (mIsTestDescrambler) {
+ if (mDescramblerBase != nullptr) {
EXPECT_FALSE(mDescramblerBase->requiresSecureDecoderComponent("video/avc"));
sp<IDescrambler> descrambler;
diff --git a/cas/1.2/vts/functional/VtsHalCasV1_2TargetTest.cpp b/cas/1.2/vts/functional/VtsHalCasV1_2TargetTest.cpp
index 333dea6..0d75f5b 100644
--- a/cas/1.2/vts/functional/VtsHalCasV1_2TargetTest.cpp
+++ b/cas/1.2/vts/functional/VtsHalCasV1_2TargetTest.cpp
@@ -311,7 +311,6 @@
sp<ICas> mMediaCas;
sp<IDescramblerBase> mDescramblerBase;
sp<MediaCasListener> mCasListener;
- bool mIsTestDescrambler = false;
typedef struct _OobInputTestParams {
const SubSample* subSamples;
uint32_t numSubSamples;
@@ -336,12 +335,15 @@
::testing::AssertionResult MediaCasHidlTest::createCasPlugin(int32_t caSystemId) {
auto status = mService->isSystemIdSupported(caSystemId);
+ bool skipDescrambler = false;
if (!status.isOk() || !status) {
return ::testing::AssertionFailure();
}
status = mService->isDescramblerSupported(caSystemId);
if (!status.isOk() || !status) {
- return ::testing::AssertionFailure();
+ ALOGI("Skip Descrambler test since it's not required in cas@1.2.");
+ mDescramblerBase = nullptr;
+ skipDescrambler = true;
}
mCasListener = new MediaCasListener();
@@ -354,12 +356,14 @@
return ::testing::AssertionFailure();
}
- auto descramblerStatus = mService->createDescrambler(caSystemId);
- if (!descramblerStatus.isOk()) {
- ALOGI("Skip Descrambler test since it's not required in cas@1.2.");
+ if (skipDescrambler) {
return ::testing::AssertionSuccess();
}
- mIsTestDescrambler = true;
+
+ auto descramblerStatus = mService->createDescrambler(caSystemId);
+ if (!descramblerStatus.isOk()) {
+ return ::testing::AssertionFailure();
+ }
mDescramblerBase = descramblerStatus;
return ::testing::AssertionResult(mDescramblerBase != nullptr);
@@ -516,7 +520,7 @@
EXPECT_TRUE(returnStatus.isOk());
EXPECT_EQ(Status::OK, returnStatus);
- if (mIsTestDescrambler) {
+ if (mDescramblerBase != nullptr) {
returnStatus = mDescramblerBase->setMediaCasSession(sessionId);
EXPECT_TRUE(returnStatus.isOk());
EXPECT_EQ(Status::OK, returnStatus);
@@ -571,7 +575,7 @@
EXPECT_TRUE(returnStatus.isOk());
EXPECT_EQ(Status::OK, returnStatus);
- if (mIsTestDescrambler) {
+ if (mDescramblerBase != nullptr) {
EXPECT_FALSE(mDescramblerBase->requiresSecureDecoderComponent("video/avc"));
sp<IDescrambler> descrambler;
diff --git a/compatibility_matrices/compatibility_matrix.5.xml b/compatibility_matrices/compatibility_matrix.5.xml
index e772b6f..96a3692 100644
--- a/compatibility_matrices/compatibility_matrix.5.xml
+++ b/compatibility_matrices/compatibility_matrix.5.xml
@@ -86,7 +86,7 @@
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.biometrics.face</name>
- <version>1.0-1</version>
+ <version>1.0</version>
<interface>
<name>IBiometricsFace</name>
<instance>default</instance>
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 613ba11..6562f22 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -95,7 +95,7 @@
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.biometrics.face</name>
- <version>1.0-1</version>
+ <version>1.0</version>
<interface>
<name>IBiometricsFace</name>
<instance>default</instance>
@@ -189,7 +189,7 @@
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.contexthub</name>
- <version>1.0-2</version>
+ <version>1.2</version>
<interface>
<name>IContexthub</name>
<instance>default</instance>
@@ -343,6 +343,13 @@
</interface>
</hal>
<hal format="aidl" optional="true">
+ <name>android.hardware.security.keymint</name>
+ <interface>
+ <name>IRemotelyProvisionedComponent</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="aidl" optional="true">
<name>android.hardware.light</name>
<version>1</version>
<interface>
@@ -554,7 +561,7 @@
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.tv.cec</name>
- <version>1.0</version>
+ <version>1.0-1</version>
<interface>
<name>IHdmiCec</name>
<instance>default</instance>
@@ -578,7 +585,7 @@
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.usb</name>
- <version>1.0-2</version>
+ <version>1.0-3</version>
<interface>
<name>IUsb</name>
<instance>default</instance>
diff --git a/contexthub/1.0/vts/functional/VtsHalContexthubV1_0TargetTest.cpp b/contexthub/1.0/vts/functional/VtsHalContexthubV1_0TargetTest.cpp
index 8a90173..356ad97 100644
--- a/contexthub/1.0/vts/functional/VtsHalContexthubV1_0TargetTest.cpp
+++ b/contexthub/1.0/vts/functional/VtsHalContexthubV1_0TargetTest.cpp
@@ -52,40 +52,17 @@
using ::android::hardware::contexthub::vts_utils::ContexthubHidlTestBase;
using ::android::hardware::contexthub::vts_utils::getHalAndHubIdList;
using ::android::hardware::contexthub::vts_utils::getHubsSync;
+using ::android::hardware::contexthub::vts_utils::kNonExistentAppId;
+using ::android::hardware::contexthub::vts_utils::waitForCallback;
namespace {
-// App ID with vendor "GoogT" (Google Testing), app identifier 0x555555. This
-// app ID is reserved and must never appear in the list of loaded apps.
-constexpr uint64_t kNonExistentAppId = 0x476f6f6754555555;
-
const std::vector<std::tuple<std::string, std::string>> kTestParameters =
getHalAndHubIdList<IContexthub>();
class ContexthubHidlTest : public ContexthubHidlTestBase<IContexthub> {};
-// Wait for a callback to occur (signaled by the given future) up to the
-// provided timeout. If the future is invalid or the callback does not come
-// within the given time, returns false.
-template <class ReturnType>
-bool waitForCallback(std::future<ReturnType> future, ReturnType* result,
- std::chrono::milliseconds timeout = std::chrono::seconds(5)) {
- auto expiration = std::chrono::system_clock::now() + timeout;
-
- EXPECT_NE(result, nullptr);
- EXPECT_TRUE(future.valid());
- if (result != nullptr && future.valid()) {
- std::future_status status = future.wait_until(expiration);
- EXPECT_NE(status, std::future_status::timeout) << "Timed out waiting for callback";
-
- if (status == std::future_status::ready) {
- *result = future.get();
- return true;
- }
- }
-
- return false;
-}
+class ContexthubCallbackV1_0 : public ContexthubCallbackBase<IContexthubCallback> {};
// Ensures that the metadata reported in getHubs() is sane
TEST_P(ContexthubHidlTest, TestGetHubs) {
@@ -110,7 +87,7 @@
TEST_P(ContexthubHidlTest, TestRegisterCallback) {
ALOGD("TestRegisterCallback called, hubId %" PRIu32, getHubId());
- ASSERT_OK(registerCallback(new ContexthubCallbackBase()));
+ ASSERT_OK(registerCallback(new ContexthubCallbackV1_0()));
}
TEST_P(ContexthubHidlTest, TestRegisterNullCallback) {
@@ -119,7 +96,7 @@
}
// Helper callback that puts the async appInfo callback data into a promise
-class QueryAppsCallback : public ContexthubCallbackBase {
+class QueryAppsCallback : public ContexthubCallbackV1_0 {
public:
virtual Return<void> handleAppsInfo(const hidl_vec<HubAppInfo>& appInfo) override {
ALOGD("Got app info callback with %zu apps", appInfo.size());
@@ -150,7 +127,7 @@
// Helper callback that puts the TransactionResult for the expectedTxnId into a
// promise
-class TxnResultCallback : public ContexthubCallbackBase {
+class TxnResultCallback : public ContexthubCallbackV1_0 {
public:
virtual Return<void> handleTxnResult(uint32_t txnId, TransactionResult result) override {
ALOGD("Got transaction result callback for txnId %" PRIu32 " (expecting %" PRIu32
diff --git a/contexthub/1.1/vts/functional/VtsHalContexthubV1_1TargetTest.cpp b/contexthub/1.1/vts/functional/VtsHalContexthubV1_1TargetTest.cpp
index 5f1dad9..acf4be0 100644
--- a/contexthub/1.1/vts/functional/VtsHalContexthubV1_1TargetTest.cpp
+++ b/contexthub/1.1/vts/functional/VtsHalContexthubV1_1TargetTest.cpp
@@ -31,6 +31,7 @@
#include <cinttypes>
+using ::android::hardware::contexthub::V1_0::IContexthubCallback;
using ::android::hardware::contexthub::V1_1::IContexthub;
using ::android::hardware::contexthub::V1_1::Setting;
using ::android::hardware::contexthub::V1_1::SettingValue;
@@ -45,10 +46,12 @@
class ContexthubHidlTest : public ContexthubHidlTestBase<IContexthub> {};
+class ContexthubCallbackV1_0 : public ContexthubCallbackBase<IContexthubCallback> {};
+
TEST_P(ContexthubHidlTest, TestOnSettingChanged) {
// In VTS, we only test that sending the values doesn't cause things to blow up - other test
// suites verify the expected E2E behavior in CHRE
- ASSERT_OK(registerCallback(new ContexthubCallbackBase()));
+ ASSERT_OK(registerCallback(new ContexthubCallbackV1_0()));
hubApi->onSettingChanged(Setting::LOCATION, SettingValue::DISABLED);
hubApi->onSettingChanged(Setting::LOCATION, SettingValue::ENABLED);
ASSERT_OK(registerCallback(nullptr));
diff --git a/contexthub/1.2/IContexthub.hal b/contexthub/1.2/IContexthub.hal
index 3488b74..4bb9361 100644
--- a/contexthub/1.2/IContexthub.hal
+++ b/contexthub/1.2/IContexthub.hal
@@ -16,6 +16,7 @@
package android.hardware.contexthub@1.2;
+import @1.0::ContextHub;
import @1.0::Result;
import @1.1::IContexthub;
import @1.1::SettingValue;
@@ -23,6 +24,17 @@
interface IContexthub extends @1.1::IContexthub {
/**
+ * Enumerate all available context hubs on the system.
+ *
+ * @return hubs list of hubs on this system.
+ * @return supportedPermissions list of Android permissions all hubs
+ * support for nanoapps to enforce host
+ * endpoints are granted in order to
+ * communicate with them.
+ */
+ getHubs_1_2() generates (vec<ContextHub> hubs, vec<string> supportedPermissions);
+
+ /**
* Register a callback for the HAL implementation to send asynchronous
* messages to the service from a context hub. There can be a maximum of
* one callback registered with the HAL. A call to this function when a
diff --git a/contexthub/1.2/IContexthubCallback.hal b/contexthub/1.2/IContexthubCallback.hal
index 0236160..1a40512 100644
--- a/contexthub/1.2/IContexthubCallback.hal
+++ b/contexthub/1.2/IContexthubCallback.hal
@@ -24,10 +24,18 @@
* implementation to allow the HAL to send asynchronous messages back
* to the service and registered clients of the ContextHub service.
*
- * @param msg message that should be delivered to host app clients
- *
+ * @param msg message that should be delivered to host app
+ * clients
+ * @param msgContentPerms list of Android permissions that cover the
+ * contents of the message being sent from the app.
+ * This is different from the permissions stored
+ * inside of ContextHubMsg in that these must be a
+ * subset of those permissions and are meant to
+ * assist in properly attributing the message
+ * contents when delivering to a ContextHub service
+ * client.
*/
- handleClientMsg_1_2(ContextHubMsg msg);
+ handleClientMsg_1_2(ContextHubMsg msg, vec<string> msgContentPerms);
/**
* This callback is passed by the Contexthub service to the HAL
diff --git a/contexthub/1.2/default/Contexthub.cpp b/contexthub/1.2/default/Contexthub.cpp
index db0c5bc..601eccd 100644
--- a/contexthub/1.2/default/Contexthub.cpp
+++ b/contexthub/1.2/default/Contexthub.cpp
@@ -23,10 +23,36 @@
namespace V1_2 {
namespace implementation {
+using ::android::hardware::hidl_string;
using ::android::hardware::contexthub::V1_0::Result;
using ::android::hardware::contexthub::V1_X::implementation::IContextHubCallbackWrapperV1_0;
using ::android::hardware::contexthub::V1_X::implementation::IContextHubCallbackWrapperV1_2;
+Return<void> Contexthub::getHubs_1_2(getHubs_1_2_cb _hidl_cb) {
+ ::android::hardware::contexthub::V1_0::ContextHub hub = {};
+ hub.name = "Mock Context Hub";
+ hub.vendor = "AOSP";
+ hub.toolchain = "n/a";
+ hub.platformVersion = 1;
+ hub.toolchainVersion = 1;
+ hub.hubId = kMockHubId;
+ hub.peakMips = 1;
+ hub.peakPowerDrawMw = 1;
+ hub.maxSupportedMsgLen = 4096;
+ hub.chrePlatformId = UINT64_C(0x476f6f6754000000);
+ hub.chreApiMajorVersion = 1;
+ hub.chreApiMinorVersion = 4;
+
+ // Report a single mock hub
+ std::vector<::android::hardware::contexthub::V1_0::ContextHub> hubs;
+ hubs.push_back(hub);
+
+ std::vector<hidl_string> hubPermissionList;
+
+ _hidl_cb(hubs, hubPermissionList);
+ return Void();
+}
+
Return<Result> Contexthub::registerCallback(uint32_t hubId,
const sp<V1_0::IContexthubCallback>& cb) {
if (hubId == kMockHubId) {
diff --git a/contexthub/1.2/default/Contexthub.h b/contexthub/1.2/default/Contexthub.h
index 8b89824..32b862d 100644
--- a/contexthub/1.2/default/Contexthub.h
+++ b/contexthub/1.2/default/Contexthub.h
@@ -35,6 +35,7 @@
using Result = ::android::hardware::contexthub::V1_0::Result;
using SettingValue = ::android::hardware::contexthub::V1_1::SettingValue;
using SettingV1_1 = ::android::hardware::contexthub::V1_1::Setting;
+ using getHubs_1_2_cb = ::android::hardware::contexthub::V1_2::IContexthub::getHubs_1_2_cb;
public:
// Methods from V1_0::IContexthub
@@ -47,6 +48,8 @@
Return<void> onSettingChanged(SettingV1_1 setting, SettingValue newValue) override;
// Methods from V1_2::IContexthub
+ Return<void> getHubs_1_2(getHubs_1_2_cb _hidl_cb) override;
+
Return<void> onSettingChanged_1_2(Setting setting, SettingValue newValue) override;
Return<Result> registerCallback_1_2(uint32_t hubId,
diff --git a/contexthub/1.2/vts/functional/VtsHalContexthubV1_2TargetTest.cpp b/contexthub/1.2/vts/functional/VtsHalContexthubV1_2TargetTest.cpp
index 782edae..c50d43c 100644
--- a/contexthub/1.2/vts/functional/VtsHalContexthubV1_2TargetTest.cpp
+++ b/contexthub/1.2/vts/functional/VtsHalContexthubV1_2TargetTest.cpp
@@ -32,45 +32,202 @@
#include <cinttypes>
+using ::android::sp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::contexthub::V1_0::ContextHub;
+using ::android::hardware::contexthub::V1_0::Result;
+using ::android::hardware::contexthub::V1_0::TransactionResult;
using ::android::hardware::contexthub::V1_1::SettingValue;
+using ::android::hardware::contexthub::V1_2::ContextHubMsg;
+using ::android::hardware::contexthub::V1_2::HubAppInfo;
using ::android::hardware::contexthub::V1_2::IContexthub;
+using ::android::hardware::contexthub::V1_2::IContexthubCallback;
using ::android::hardware::contexthub::V1_2::Setting;
+using ::android::hardware::contexthub::vts_utils::asBaseType;
using ::android::hardware::contexthub::vts_utils::ContexthubCallbackBase;
using ::android::hardware::contexthub::vts_utils::ContexthubHidlTestBase;
using ::android::hardware::contexthub::vts_utils::getHalAndHubIdList;
+using ::android::hardware::contexthub::vts_utils::kNonExistentAppId;
+using ::android::hardware::contexthub::vts_utils::waitForCallback;
namespace {
const std::vector<std::tuple<std::string, std::string>> kTestParameters =
getHalAndHubIdList<IContexthub>();
-class ContexthubHidlTest : public ContexthubHidlTestBase<IContexthub> {};
+class ContexthubCallbackV1_2 : public ContexthubCallbackBase<IContexthubCallback> {
+ public:
+ virtual Return<void> handleClientMsg_1_2(
+ const ContextHubMsg& /*msg*/,
+ const hidl_vec<hidl_string>& /*msgContentPerms*/) override {
+ ALOGD("Got client message callback");
+ return Void();
+ }
+
+ virtual Return<void> handleAppsInfo_1_2(const hidl_vec<HubAppInfo>& /*appInfo*/) override {
+ ALOGD("Got app info callback");
+ return Void();
+ }
+};
+
+class ContexthubHidlTest : public ContexthubHidlTestBase<IContexthub> {
+ public:
+ Result registerCallback_1_2(sp<IContexthubCallback> cb) {
+ return hubApi->registerCallback_1_2(getHubId(), cb);
+ }
+};
+
+// Ensures that the metadata reported in getHubs_1_2() is valid
+TEST_P(ContexthubHidlTest, TestGetHubs_1_2) {
+ hidl_vec<ContextHub> hubList;
+ hubApi->getHubs_1_2(
+ [&hubList](const hidl_vec<ContextHub>& hubs,
+ const hidl_vec<hidl_string>& /*hubPermissions*/) { hubList = hubs; });
+
+ ALOGD("System reports %zu hubs", hubList.size());
+
+ for (const ContextHub& hub : hubList) {
+ ALOGD("Checking hub ID %" PRIu32, hub.hubId);
+
+ EXPECT_FALSE(hub.name.empty());
+ EXPECT_FALSE(hub.vendor.empty());
+ EXPECT_FALSE(hub.toolchain.empty());
+ EXPECT_GT(hub.peakMips, 0);
+ EXPECT_GE(hub.stoppedPowerDrawMw, 0);
+ EXPECT_GE(hub.sleepPowerDrawMw, 0);
+ EXPECT_GT(hub.peakPowerDrawMw, 0);
+
+ // Minimum 128 byte MTU as required by CHRE API v1.0
+ EXPECT_GE(hub.maxSupportedMsgLen, UINT32_C(128));
+ }
+}
+
+TEST_P(ContexthubHidlTest, TestRegisterCallback) {
+ ALOGD("TestRegisterCallback called, hubId %" PRIu32, getHubId());
+ ASSERT_OK(registerCallback_1_2(new ContexthubCallbackV1_2()));
+}
+
+TEST_P(ContexthubHidlTest, TestRegisterNullCallback) {
+ ALOGD("TestRegisterNullCallback called, hubId %" PRIu32, getHubId());
+ ASSERT_OK(registerCallback_1_2(nullptr));
+}
// In VTS, we only test that sending the values doesn't cause things to blow up - other test
// suites verify the expected E2E behavior in CHRE
TEST_P(ContexthubHidlTest, TestOnWifiSettingChanged) {
- ASSERT_OK(registerCallback(new ContexthubCallbackBase()));
+ ASSERT_OK(registerCallback_1_2(new ContexthubCallbackV1_2()));
hubApi->onSettingChanged_1_2(Setting::WIFI_AVAILABLE, SettingValue::DISABLED);
hubApi->onSettingChanged_1_2(Setting::WIFI_AVAILABLE, SettingValue::ENABLED);
- ASSERT_OK(registerCallback(nullptr));
+ ASSERT_OK(registerCallback_1_2(nullptr));
}
TEST_P(ContexthubHidlTest, TestOnAirplaneModeSettingChanged) {
- ASSERT_OK(registerCallback(new ContexthubCallbackBase()));
+ ASSERT_OK(registerCallback_1_2(new ContexthubCallbackV1_2()));
hubApi->onSettingChanged_1_2(Setting::AIRPLANE_MODE, SettingValue::DISABLED);
hubApi->onSettingChanged_1_2(Setting::AIRPLANE_MODE, SettingValue::ENABLED);
- ASSERT_OK(registerCallback(nullptr));
+ ASSERT_OK(registerCallback_1_2(nullptr));
}
TEST_P(ContexthubHidlTest, TestOnGlobalMicDisableSettingChanged) {
- ASSERT_OK(registerCallback(new ContexthubCallbackBase()));
+ ASSERT_OK(registerCallback_1_2(new ContexthubCallbackV1_2()));
hubApi->onSettingChanged_1_2(Setting::GLOBAL_MIC_DISABLE, SettingValue::DISABLED);
hubApi->onSettingChanged_1_2(Setting::GLOBAL_MIC_DISABLE, SettingValue::ENABLED);
- ASSERT_OK(registerCallback(nullptr));
+ ASSERT_OK(registerCallback_1_2(nullptr));
+}
+
+// Helper callback that puts the async appInfo callback data into a promise
+class QueryAppsCallback : public ContexthubCallbackV1_2 {
+ public:
+ virtual Return<void> handleAppsInfo_1_2(const hidl_vec<HubAppInfo>& appInfo) override {
+ ALOGD("Got app info callback with %zu apps", appInfo.size());
+ promise.set_value(appInfo);
+ return Void();
+ }
+
+ std::promise<hidl_vec<HubAppInfo>> promise;
+};
+
+// Calls queryApps() and checks the returned metadata
+TEST_P(ContexthubHidlTest, TestQueryApps) {
+ hidl_vec<hidl_string> hubPerms;
+ hubApi->getHubs_1_2([&hubPerms](const hidl_vec<ContextHub>& /*hubs*/,
+ const hidl_vec<hidl_string>& hubPermissions) {
+ hubPerms = hubPermissions;
+ });
+
+ ALOGD("TestQueryApps called, hubId %u", getHubId());
+ sp<QueryAppsCallback> cb = new QueryAppsCallback();
+ ASSERT_OK(registerCallback_1_2(cb));
+
+ Result result = hubApi->queryApps(getHubId());
+ ASSERT_OK(result);
+
+ ALOGD("Waiting for app info callback");
+ hidl_vec<HubAppInfo> appList;
+ ASSERT_TRUE(waitForCallback(cb->promise.get_future(), &appList));
+ for (const HubAppInfo& appInfo : appList) {
+ EXPECT_NE(appInfo.info_1_0.appId, UINT64_C(0));
+ EXPECT_NE(appInfo.info_1_0.appId, kNonExistentAppId);
+ for (std::string permission : appInfo.permissions) {
+ ASSERT_TRUE(hubPerms.contains(permission));
+ }
+ }
+}
+
+// Helper callback that puts the TransactionResult for the expectedTxnId into a
+// promise
+class TxnResultCallback : public ContexthubCallbackV1_2 {
+ public:
+ virtual Return<void> handleTxnResult(uint32_t txnId, TransactionResult result) override {
+ ALOGD("Got transaction result callback for txnId %" PRIu32 " (expecting %" PRIu32
+ ") with result %" PRId32,
+ txnId, expectedTxnId, result);
+ if (txnId == expectedTxnId) {
+ promise.set_value(result);
+ }
+ return Void();
+ }
+
+ uint32_t expectedTxnId = 0;
+ std::promise<TransactionResult> promise;
+};
+
+// Parameterized fixture that sets the callback to TxnResultCallback
+class ContexthubTxnTest : public ContexthubHidlTest {
+ public:
+ virtual void SetUp() override {
+ ContexthubHidlTest::SetUp();
+ ASSERT_OK(registerCallback_1_2(cb));
+ }
+
+ sp<TxnResultCallback> cb = new TxnResultCallback();
+};
+
+TEST_P(ContexthubTxnTest, TestSendMessageToNonExistentNanoApp) {
+ ContextHubMsg msg;
+ msg.msg_1_0.appName = kNonExistentAppId;
+ msg.msg_1_0.msgType = 1;
+ msg.msg_1_0.msg.resize(4);
+ std::fill(msg.msg_1_0.msg.begin(), msg.msg_1_0.msg.end(), 0);
+
+ ALOGD("Sending message to non-existent nanoapp");
+ Result result = hubApi->sendMessageToHub_1_2(getHubId(), msg);
+ if (result != Result::OK && result != Result::BAD_PARAMS &&
+ result != Result::TRANSACTION_FAILED) {
+ FAIL() << "Got result " << asBaseType(result) << ", expected OK, BAD_PARAMS"
+ << ", or TRANSACTION_FAILED";
+ }
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ContexthubHidlTest);
INSTANTIATE_TEST_SUITE_P(HubIdSpecificTests, ContexthubHidlTest, testing::ValuesIn(kTestParameters),
android::hardware::PrintInstanceTupleNameToString<>);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ContexthubTxnTest);
+INSTANTIATE_TEST_SUITE_P(HubIdSpecificTests, ContexthubTxnTest, testing::ValuesIn(kTestParameters),
+ android::hardware::PrintInstanceTupleNameToString<>);
+
} // anonymous namespace
diff --git a/contexthub/common/default/1.X/utils/IContextHubCallbackWrapper.h b/contexthub/common/default/1.X/utils/IContextHubCallbackWrapper.h
index df78438..d9459b7 100644
--- a/contexthub/common/default/1.X/utils/IContextHubCallbackWrapper.h
+++ b/contexthub/common/default/1.X/utils/IContextHubCallbackWrapper.h
@@ -54,7 +54,8 @@
*/
class IContextHubCallbackWrapperBase : public VirtualLightRefBase {
public:
- virtual Return<void> handleClientMsg(V1_2::ContextHubMsg msg) = 0;
+ virtual Return<void> handleClientMsg(V1_2::ContextHubMsg msg,
+ hidl_vec<hidl_string> msgContentPerms) = 0;
virtual Return<void> handleTxnResult(uint32_t txnId, V1_0::TransactionResult result) = 0;
@@ -63,6 +64,11 @@
virtual Return<void> handleAppAbort(uint64_t appId, uint32_t abortCode) = 0;
virtual Return<void> handleAppsInfo(hidl_vec<V1_2::HubAppInfo> appInfo) = 0;
+
+ virtual Return<bool> linkToDeath(const sp<hidl_death_recipient>& recipient,
+ uint64_t cookie) = 0;
+
+ virtual Return<bool> unlinkToDeath(const sp<hidl_death_recipient>& recipient) = 0;
};
template <typename T>
@@ -70,7 +76,8 @@
public:
ContextHubCallbackWrapper(sp<T> callback) : mCallback(callback){};
- virtual Return<void> handleClientMsg(V1_2::ContextHubMsg msg) override {
+ virtual Return<void> handleClientMsg(V1_2::ContextHubMsg msg,
+ hidl_vec<hidl_string> /* msgContentPerms */) override {
return mCallback->handleClientMsg(convertToOldMsg(msg));
}
@@ -90,6 +97,14 @@
return mCallback->handleAppsInfo(convertToOldAppInfo(appInfo));
}
+ Return<bool> linkToDeath(const sp<hidl_death_recipient>& recipient, uint64_t cookie) override {
+ return mCallback->linkToDeath(recipient, cookie);
+ }
+
+ Return<bool> unlinkToDeath(const sp<hidl_death_recipient>& recipient) override {
+ return mCallback->unlinkToDeath(recipient);
+ }
+
protected:
sp<T> mCallback;
};
@@ -105,8 +120,9 @@
IContextHubCallbackWrapperV1_2(sp<V1_2::IContexthubCallback> callback)
: ContextHubCallbackWrapper(callback){};
- Return<void> handleClientMsg(V1_2::ContextHubMsg msg) override {
- return mCallback->handleClientMsg_1_2(msg);
+ Return<void> handleClientMsg(V1_2::ContextHubMsg msg,
+ hidl_vec<hidl_string> msgContentPerms) override {
+ return mCallback->handleClientMsg_1_2(msg, msgContentPerms);
}
Return<void> handleAppsInfo(hidl_vec<V1_2::HubAppInfo> appInfo) override {
diff --git a/contexthub/common/vts/ContexthubCallbackBase.h b/contexthub/common/vts/ContexthubCallbackBase.h
index 124a116..24d6c52 100644
--- a/contexthub/common/vts/ContexthubCallbackBase.h
+++ b/contexthub/common/vts/ContexthubCallbackBase.h
@@ -27,7 +27,8 @@
// Base callback implementation that just logs all callbacks by default, but
// records a failure if
-class ContexthubCallbackBase : public V1_0::IContexthubCallback {
+template <class CallbackType>
+class ContexthubCallbackBase : public CallbackType {
public:
virtual Return<void> handleClientMsg(const V1_0::ContextHubMsg& /*msg*/) override {
ALOGD("Got client message callback");
diff --git a/contexthub/common/vts/VtsHalContexthubUtils.h b/contexthub/common/vts/VtsHalContexthubUtils.h
index 8f9b694..dff1865 100644
--- a/contexthub/common/vts/VtsHalContexthubUtils.h
+++ b/contexthub/common/vts/VtsHalContexthubUtils.h
@@ -30,6 +30,10 @@
namespace contexthub {
namespace vts_utils {
+// App ID with vendor "GoogT" (Google Testing), app identifier 0x555555. This
+// app ID is reserved and must never appear in the list of loaded apps.
+constexpr uint64_t kNonExistentAppId = 0x476f6f6754555555;
+
#define ASSERT_OK(result) ASSERT_EQ(result, ::android::hardware::contexthub::V1_0::Result::OK)
#define EXPECT_OK(result) EXPECT_EQ(result, ::android::hardware::contexthub::V1_0::Result::OK)
@@ -64,6 +68,29 @@
return parameters;
}
+// Wait for a callback to occur (signaled by the given future) up to the
+// provided timeout. If the future is invalid or the callback does not come
+// within the given time, returns false.
+template <class ReturnType>
+bool waitForCallback(std::future<ReturnType> future, ReturnType* result,
+ std::chrono::milliseconds timeout = std::chrono::seconds(5)) {
+ auto expiration = std::chrono::system_clock::now() + timeout;
+
+ EXPECT_NE(result, nullptr);
+ EXPECT_TRUE(future.valid());
+ if (result != nullptr && future.valid()) {
+ std::future_status status = future.wait_until(expiration);
+ EXPECT_NE(status, std::future_status::timeout) << "Timed out waiting for callback";
+
+ if (status == std::future_status::ready) {
+ *result = future.get();
+ return true;
+ }
+ }
+
+ return false;
+}
+
} // namespace vts_utils
} // namespace contexthub
} // namespace hardware
diff --git a/current.txt b/current.txt
index fb9b056..5e9a34c 100644
--- a/current.txt
+++ b/current.txt
@@ -779,8 +779,9 @@
6017b4f2481feb0fffceae81c62bc372c898998b2d8fe69fbd39859d3a315e5e android.hardware.keymaster@4.0::IKeymasterDevice
dabe23dde7c9e3ad65c61def7392f186d7efe7f4216f9b6f9cf0863745b1a9f4 android.hardware.keymaster@4.1::IKeymasterDevice
cd84ab19c590e0e73dd2307b591a3093ee18147ef95e6d5418644463a6620076 android.hardware.neuralnetworks@1.2::IDevice
-9625e85f56515ad2cf87b6a1847906db669f746ea4ab02cd3d4ca25abc9b0109 android.hardware.neuralnetworks@1.2::types
-9e758e208d14f7256e0885d6d8ad0b61121b21d8c313864f981727ae55bffd16 android.hardware.neuralnetworks@1.3::types
+f729ee6a5f136b25d79ea6895d24700fce413df555baaecf2c39e4440d15d043 android.hardware.neuralnetworks@1.0::types
+c6ae443608502339aec4256feef48e7b2d36f7477ca5361cc95cd27a8ed9c612 android.hardware.neuralnetworks@1.2::types
+9fe5a4093043c2b5da4e9491aed1646c388a5d3059b8fd77d5b6a9807e6d3a3e android.hardware.neuralnetworks@1.3::types
e8c86c69c438da8d1549856c1bb3e2d1b8da52722f8235ff49a30f2cce91742c android.hardware.soundtrigger@2.1::ISoundTriggerHwCallback
b9fbb6e2e061ed0960939d48b785e9700210add1f13ed32ecd688d0f1ca20ef7 android.hardware.renderscript@1.0::types
0f53d70e1eadf8d987766db4bf6ae2048004682168f4cab118da576787def3fa android.hardware.radio@1.0::types
diff --git a/drm/1.4/types.hal b/drm/1.4/types.hal
index 706c3aa..17eba8a 100644
--- a/drm/1.4/types.hal
+++ b/drm/1.4/types.hal
@@ -19,11 +19,14 @@
import @1.2::Status;
enum LogPriority : uint32_t {
- ERROR,
- WARN,
- INFO,
- DEBUG,
- VERBOSE
+ UNKNOWN,
+ DEFAULT,
+ VERBOSE,
+ DEBUG,
+ INFO,
+ WARN,
+ ERROR,
+ FATAL,
};
/**
@@ -37,15 +40,100 @@
};
enum Status : @1.2::Status {
-
+ /**
+ * queueSecureInput buffer called with 0 subsamples.
+ */
+ CANNOT_DECRYPT_ZERO_SUBSAMPLES,
+ /**
+ * An error happened within the crypto library used by the drm plugin.
+ */
+ CRYPTO_LIBRARY_ERROR,
/**
* Non-specific error reported by the device OEM subsystem.
*/
GENERAL_OEM_ERROR,
-
/**
* Unexpected internal failure in the drm/crypto plugin.
*/
GENERAL_PLUGIN_ERROR,
-
+ /**
+ * The init data parameter passed to getKeyRequest is empty or invalid.
+ */
+ INIT_DATA_INVALID,
+ /**
+ * Either the key was not loaded from the license before attempting the
+ * operation, or the key ID parameter provided by the app is incorrect.
+ */
+ KEY_NOT_LOADED,
+ /**
+ * The license response was empty, fields are missing or otherwise unable
+ * to be parsed.
+ */
+ LICENSE_PARSE_ERROR,
+ /**
+ * The operation (e.g. to renew or persist a license) is prohibited by the
+ * license policy.
+ */
+ LICENSE_POLICY_ERROR,
+ /**
+ * Failed to generate a release request because a field in the stored
+ * license is empty or malformed.
+ */
+ LICENSE_RELEASE_ERROR,
+ /**
+ * The license server detected an error in the license request.
+ */
+ LICENSE_REQUEST_REJECTED,
+ /**
+ * Failed to restore an offline license because a field is empty or
+ * malformed.
+ */
+ LICENSE_RESTORE_ERROR,
+ /**
+ * License is in an invalid state for the attempted operation.
+ */
+ LICENSE_STATE_ERROR,
+ /**
+ * Certificate is malformed or is of the wrong type.
+ */
+ MALFORMED_CERTIFICATE,
+ /**
+ * Failure in the media framework.
+ */
+ MEDIA_FRAMEWORK_ERROR,
+ /**
+ * Certificate has not been set.
+ */
+ MISSING_CERTIFICATE,
+ /**
+ * There was an error loading the provisioned certificate.
+ */
+ PROVISIONING_CERTIFICATE_ERROR,
+ /**
+ * Required steps where not performed before provisioning was attempted.
+ */
+ PROVISIONING_CONFIGURATION_ERROR,
+ /**
+ * The provisioning response was empty, fields are missing or otherwise
+ * unable to be parsed.
+ */
+ PROVISIONING_PARSE_ERROR,
+ /**
+ * Provisioning failed in a way that is likely to succeed on a subsequent
+ * attempt.
+ */
+ RETRYABLE_PROVISIONING_ERROR,
+ /**
+ * Failed to generate a secure stop request because a field in the stored
+ * license is empty or malformed.
+ */
+ SECURE_STOP_RELEASE_ERROR,
+ /**
+ * The plugin was unable to read data from the filesystem.
+ */
+ STORAGE_READ_FAILURE,
+ /**
+ * The plugin was unable to write data to the filesystem.
+ */
+ STORAGE_WRITE_FAILURE,
};
diff --git a/drm/1.4/vts/OWNERS b/drm/1.4/vts/OWNERS
new file mode 100644
index 0000000..3a0672e
--- /dev/null
+++ b/drm/1.4/vts/OWNERS
@@ -0,0 +1,9 @@
+conglin@google.com
+edwinwong@google.com
+fredgc@google.com
+jtinker@google.com
+juce@google.com
+kylealexander@google.com
+rfrias@google.com
+robertshih@google.com
+sigquit@google.com
diff --git a/drm/1.4/vts/functional/Android.bp b/drm/1.4/vts/functional/Android.bp
new file mode 100644
index 0000000..80b1dd1
--- /dev/null
+++ b/drm/1.4/vts/functional/Android.bp
@@ -0,0 +1,95 @@
+//
+// 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.
+//
+
+cc_library_static {
+ name: "android.hardware.drm@1.4-vts",
+ defaults: ["VtsHalTargetTestDefaults"],
+ local_include_dirs: [
+ "include",
+ ],
+ srcs: [
+ "drm_hal_test.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.drm@1.0",
+ "android.hardware.drm@1.1",
+ "android.hardware.drm@1.2",
+ "android.hardware.drm@1.3",
+ "android.hardware.drm@1.4",
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "libhidlmemory",
+ "libnativehelper",
+ ],
+ static_libs: [
+ "android.hardware.drm@1.0-helper",
+ "android.hardware.drm@1.2-vts",
+ "libdrmvtshelper",
+ ],
+ export_static_lib_headers: [
+ "android.hardware.drm@1.2-vts",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+}
+
+cc_test {
+ name: "VtsHalDrmV1_4TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ include_dirs: ["hardware/interfaces/drm/1.0/vts/functional"],
+ srcs: [
+ "drm_hal_test_main.cpp",
+ ],
+ whole_static_libs: [
+ "android.hardware.drm@1.4-vts",
+ ],
+ shared_libs: [
+ "android.hardware.drm@1.0",
+ "android.hardware.drm@1.1",
+ "android.hardware.drm@1.2",
+ "android.hardware.drm@1.3",
+ "android.hardware.drm@1.4",
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "libcrypto",
+ "libhidlmemory",
+ "libnativehelper",
+ ],
+ static_libs: [
+ "android.hardware.drm@1.0-helper",
+ "android.hardware.drm@1.2-vts",
+ "libdrmvtshelper",
+ ],
+ arch: {
+ arm: {
+ data: [":libvtswidevine-arm-prebuilts"],
+ },
+ arm64: {
+ data: [":libvtswidevine-arm64-prebuilts"],
+ },
+ x86: {
+ data: [":libvtswidevine-x86-prebuilts"],
+ },
+ x86_64: {
+ data: [":libvtswidevine-x86_64-prebuilts"],
+ },
+ },
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
diff --git a/drm/1.4/vts/functional/AndroidTest.xml b/drm/1.4/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..b18da49
--- /dev/null
+++ b/drm/1.4/vts/functional/AndroidTest.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 VtsHalDrmV1_4TargetTest.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+ <option name="not-shardable" value="true" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.WifiPreparer" >
+ <option name="verify-only" value="true" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push-file" key="VtsHalDrmV1_4TargetTest" value="/data/local/tmp/VtsHalDrmV1_4TargetTest" />
+ <option name="push-file" key="libvtswidevine64.so" value="/data/local/tmp/64/lib/libvtswidevine.so" />
+ <option name="push-file" key="libvtswidevine32.so" value="/data/local/tmp/32/lib/libvtswidevine.so" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="VtsHalDrmV1_4TargetTest" />
+ </test>
+</configuration>
diff --git a/drm/1.4/vts/functional/drm_hal_test.cpp b/drm/1.4/vts/functional/drm_hal_test.cpp
new file mode 100644
index 0000000..f9fa0bd
--- /dev/null
+++ b/drm/1.4/vts/functional/drm_hal_test.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "drm_hal_test@1.4"
+
+#include "android/hardware/drm/1.4/vts/drm_hal_test.h"
+
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_4 {
+namespace vts {
+
+const char* const DrmHalTest::kVideoMp4 = "video/mp4";
+const char* const DrmHalTest::kAudioMp4 = "audio/mp4";
+const uint32_t DrmHalTest::kSecLevelDefault = DrmHalTest::kSecLevelMax + 1;
+
+sp<drm::V1_4::IDrmPlugin> DrmHalTest::DrmPluginV1_4() const {
+ sp<drm::V1_4::IDrmPlugin> plugin(drm::V1_4::IDrmPlugin::castFrom(drmPlugin));
+ EXPECT_NE(nullptr, plugin.get());
+ return plugin;
+}
+
+sp<V1_0::ICryptoPlugin> DrmHalTest::CryptoPlugin(const SessionId& sid) {
+ sp<V1_0::ICryptoPlugin> crypto;
+ auto res = cryptoFactory->createPlugin(
+ getUUID(), sid,
+ [&](V1_0::Status status, const sp<V1_0::ICryptoPlugin>& plugin) {
+ EXPECT_EQ(V1_0::Status::OK, status);
+ EXPECT_NE(nullptr, plugin.get());
+ crypto = plugin;
+ });
+ EXPECT_OK(res);
+ return crypto;
+}
+
+SessionId DrmHalTest::OpenSession(uint32_t level = kSecLevelDefault) {
+ V1_0::Status err;
+ SessionId sessionId;
+ bool attemptedProvision = false;
+
+ V1_0::IDrmPlugin::openSession_cb cb = [&](
+ V1_0::Status status,
+ const hidl_vec<unsigned char> &id) {
+ err = status;
+ sessionId = id;
+ };
+
+ while (true) {
+ Return<void> res;
+ if (level > kSecLevelMax) {
+ res = drmPlugin->openSession(cb);
+ } else if (level >= kSecLevelMin) {
+ auto securityLevel = static_cast<SecurityLevel>(level);
+ res = drmPlugin->openSession_1_1(securityLevel, cb);
+ }
+ EXPECT_OK(res);
+ if (V1_0::Status::ERROR_DRM_NOT_PROVISIONED == err
+ && !attemptedProvision) {
+ // provision once if necessary
+ provision();
+ attemptedProvision = true;
+ continue;
+ } else if (V1_0::Status::ERROR_DRM_CANNOT_HANDLE == err) {
+ // must be able to handle default level
+ EXPECT_NE(kSecLevelDefault, level);
+ sessionId = {};
+ } else {
+ EXPECT_EQ(V1_0::Status::OK, err);
+ EXPECT_NE(sessionId.size(), 0u);
+ }
+ break;
+ }
+
+ return sessionId;
+}
+
+TEST_P(DrmHalTest, RequiresSecureDecoder) {
+ for (uint32_t level : {kSecLevelMin, kSecLevelMax, kSecLevelDefault}) {
+ for (auto mime : {kVideoMp4, kAudioMp4}) {
+ auto sid = OpenSession(level);
+ if (sid.size() == 0u) {
+ continue;
+ }
+ auto drm = DrmPluginV1_4();
+ sp<V1_0::ICryptoPlugin> crypto(CryptoPlugin(sid));
+ if (drm == nullptr || crypto == nullptr) {
+ continue;
+ }
+ bool r1 = crypto->requiresSecureDecoderComponent(mime);
+ bool r2;
+ if (level == kSecLevelDefault) {
+ r2 = drm->requiresSecureDecoderDefault(mime);
+ } else {
+ auto sL = static_cast<SecurityLevel>(level);
+ r2 = drm->requiresSecureDecoder(mime, sL);
+ }
+ EXPECT_EQ(r1, r2);
+ closeSession(sid);
+ }
+ }
+}
+
+TEST_P(DrmHalTest, SetPlaybackId) {
+ auto testInfo = ::testing::UnitTest::GetInstance()->current_test_info();
+ auto testName = testInfo->name();
+ const hidl_string& pbId{testName};
+ auto sid = OpenSession();
+ auto drm = DrmPluginV1_4();
+ if (drm == nullptr) {
+ return;
+ }
+ V1_0::Status err = drm->setPlaybackId(sid, pbId);
+ EXPECT_EQ(V1_0::Status::OK, err);
+ closeSession(sid);
+
+ // search for playback id among metric attributes/values
+ bool foundPbId = false;
+ auto res = drmPlugin->getMetrics([&](
+ V1_0::Status status,
+ hidl_vec<V1_1::DrmMetricGroup> metricGroups) {
+ EXPECT_EQ(V1_0::Status::OK, status);
+ for (const auto& group : metricGroups) {
+ for (const auto& metric : group.metrics) {
+ for (const auto& value : metric.values) {
+ if (value.stringValue == pbId) {
+ foundPbId = true;
+ break;
+ }
+ }
+ for (const auto& attr : metric.attributes) {
+ if (attr.stringValue == pbId) {
+ foundPbId = true;
+ break;
+ }
+ }
+ }
+ }
+ });
+ EXPECT_OK(res);
+ EXPECT_TRUE(foundPbId);
+}
+
+TEST_P(DrmHalTest, GetLogMessages) {
+ auto drm = DrmPluginV1_4();
+ auto sid = OpenSession();
+ auto crypto_1_0 = CryptoPlugin(sid);
+ sp<V1_4::ICryptoPlugin> crypto(V1_4::ICryptoPlugin::castFrom(crypto_1_0));
+
+ hidl_vec<uint8_t> initData;
+ hidl_string mime{"text/plain"};
+ V1_0::KeyedVector optionalParameters;
+ auto res = drmPlugin->getKeyRequest_1_2(
+ sid, initData, mime, V1_0::KeyType::STREAMING,
+ optionalParameters, [&](V1_2::Status status, const hidl_vec<uint8_t>&,
+ V1_1::KeyRequestType, const hidl_string&) {
+ EXPECT_NE(V1_2::Status::OK, status);
+ });
+ EXPECT_OK(res);
+
+ V1_4::IDrmPlugin::getLogMessages_cb cb = [&](
+ V1_4::Status status,
+ hidl_vec<V1_4::LogMessage> logs) {
+ EXPECT_EQ(V1_4::Status::OK, status);
+ EXPECT_NE(0, logs.size());
+ for (auto log: logs) {
+ ALOGI("priority=[%u] message='%s'", log.priority, log.message.c_str());
+ }
+ };
+
+ auto res2 = drm->getLogMessages(cb);
+ EXPECT_OK(res2);
+
+ auto res3 = crypto->getLogMessages(cb);
+ EXPECT_OK(res3);
+
+ closeSession(sid);
+}
+
+} // namespace vts
+} // namespace V1_4
+} // namespace drm
+} // namespace hardware
+} // namespace android
diff --git a/drm/1.4/vts/functional/drm_hal_test_main.cpp b/drm/1.4/vts/functional/drm_hal_test_main.cpp
new file mode 100644
index 0000000..65d1b76
--- /dev/null
+++ b/drm/1.4/vts/functional/drm_hal_test_main.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+/**
+ * Instantiate the set of test cases for each vendor module
+ */
+
+#define LOG_TAG "drm_hal_test@1.4"
+
+#include <android/hardware/drm/1.4/ICryptoFactory.h>
+#include <android/hardware/drm/1.4/IDrmFactory.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/ServiceManagement.h>
+#include <log/log.h>
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "android/hardware/drm/1.4/vts/drm_hal_test.h"
+
+using drm_vts::DrmHalTestParam;
+using drm_vts::PrintParamInstanceToString;
+
+using android::hardware::drm::V1_4::vts::DrmHalTest;
+
+static const std::vector<DrmHalTestParam> kAllInstances = [] {
+ using ::android::hardware::hidl_array;
+ using ::android::hardware::hidl_vec;
+ using ::android::hardware::drm::V1_4::ICryptoFactory;
+ using ::android::hardware::drm::V1_4::IDrmFactory;
+
+ std::vector<std::string> drmInstances =
+ android::hardware::getAllHalInstanceNames(IDrmFactory::descriptor);
+ std::vector<std::string> cryptoInstances =
+ android::hardware::getAllHalInstanceNames(ICryptoFactory::descriptor);
+ std::set<std::string> allInstances;
+ allInstances.insert(drmInstances.begin(), drmInstances.end());
+ allInstances.insert(cryptoInstances.begin(), cryptoInstances.end());
+
+ std::vector<DrmHalTestParam> firstInstanceUuidCombos;
+ for (const auto &instance : allInstances) {
+ auto drmFactory = IDrmFactory::getService(instance);
+ if (drmFactory == nullptr) {
+ continue;
+ }
+ drmFactory->getSupportedCryptoSchemes(
+ [&](const hidl_vec<hidl_array<uint8_t, 16>>& schemes) {
+ if (schemes.size() > 0) {
+ firstInstanceUuidCombos.push_back(DrmHalTestParam(instance, schemes[0]));
+ }
+ });
+ }
+ return firstInstanceUuidCombos;
+}();
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DrmHalTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalTest,
+ testing::ValuesIn(kAllInstances),
+ PrintParamInstanceToString);
+
+int main(int argc, char** argv) {
+#if defined(__LP64__)
+ const char* kModulePath = "/data/local/tmp/64/lib";
+#else
+ const char* kModulePath = "/data/local/tmp/32/lib";
+#endif
+ DrmHalTest::gVendorModules
+ = new drm_vts::VendorModules(kModulePath);
+ if (DrmHalTest::gVendorModules->getPathList().size() == 0) {
+ std::cerr << "WARNING: No vendor modules found in " << kModulePath <<
+ ", all vendor tests will be skipped" << std::endl;
+ }
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ ALOGI("Test result = %d", status);
+ return status;
+}
diff --git a/drm/1.4/vts/functional/include/android/hardware/drm/1.4/vts/drm_hal_test.h b/drm/1.4/vts/functional/include/android/hardware/drm/1.4/vts/drm_hal_test.h
new file mode 100644
index 0000000..ed49a61
--- /dev/null
+++ b/drm/1.4/vts/functional/include/android/hardware/drm/1.4/vts/drm_hal_test.h
@@ -0,0 +1,83 @@
+/*
+ * 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 DRM_HAL_TEST_V1_4_H
+#define DRM_HAL_TEST_V1_4_H
+
+#include <android/hardware/drm/1.0/IDrmPlugin.h>
+#include <android/hardware/drm/1.3/IDrmFactory.h>
+#include <android/hardware/drm/1.4/ICryptoFactory.h>
+#include <android/hardware/drm/1.4/ICryptoPlugin.h>
+#include <android/hardware/drm/1.4/IDrmFactory.h>
+#include <android/hardware/drm/1.4/IDrmPlugin.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/ServiceManagement.h>
+#include <log/log.h>
+
+#include <algorithm>
+#include <cstdint>
+#include <iterator>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "drm_hal_vendor_module_api.h"
+#include "drm_vts_helper.h"
+#include "vendor_modules.h"
+#include "VtsHalHidlTargetCallbackBase.h"
+
+#include "android/hardware/drm/1.2/vts/drm_hal_common.h"
+
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_4 {
+namespace vts {
+
+namespace drm = ::android::hardware::drm;
+using android::hardware::hidl_array;
+using android::hardware::hidl_string;
+using V1_0::SessionId;
+using V1_1::SecurityLevel;
+
+using drm_vts::DrmHalTestParam;
+
+class DrmHalTest : public drm::V1_2::vts::DrmHalTest {
+public:
+ using drm::V1_2::vts::DrmHalTest::DrmHalTest;
+ static const char* const kVideoMp4;
+ static const char* const kAudioMp4;
+ static const uint32_t kSecLevelMin = static_cast<uint32_t>(SecurityLevel::SW_SECURE_CRYPTO);
+ static const uint32_t kSecLevelMax = static_cast<uint32_t>(SecurityLevel::HW_SECURE_ALL);
+ static const uint32_t kSecLevelDefault;
+
+protected:
+ sp<V1_4::IDrmPlugin> DrmPluginV1_4() const;
+ sp<V1_0::ICryptoPlugin> CryptoPlugin(const SessionId& sid);
+ SessionId OpenSession(uint32_t level);
+
+private:
+ void DoProvisioning();
+};
+
+} // namespace vts
+} // namespace V1_4
+} // namespace drm
+} // namespace hardware
+} // namespace android
+
+#endif // DRM_HAL_TEST_V1_4_H
diff --git a/health/utils/libhealth2impl/Health.cpp b/health/utils/libhealth2impl/Health.cpp
index f4684ae..035b36f 100644
--- a/health/utils/libhealth2impl/Health.cpp
+++ b/health/utils/libhealth2impl/Health.cpp
@@ -80,14 +80,14 @@
Return<Result> Health::update() {
Result result = Result::UNKNOWN;
- getHealthInfo_2_1([&](auto res, const auto& /* health_info */) {
+ getHealthInfo_2_1([&](auto res, const auto& health_info) {
result = res;
if (res != Result::SUCCESS) {
LOG(ERROR) << "Cannot call getHealthInfo_2_1: " << toString(res);
return;
}
- battery_monitor_.logValues();
+ BatteryMonitor::logValues(health_info, *healthd_config_);
});
return result;
}
diff --git a/identity/support/src/IdentityCredentialSupport.cpp b/identity/support/src/IdentityCredentialSupport.cpp
index 38348ac..6418028 100644
--- a/identity/support/src/IdentityCredentialSupport.cpp
+++ b/identity/support/src/IdentityCredentialSupport.cpp
@@ -833,9 +833,16 @@
optional<vector<vector<uint8_t>>> createAttestation(
const EVP_PKEY* key, const vector<uint8_t>& applicationId, const vector<uint8_t>& challenge,
uint64_t activeTimeMilliSeconds, uint64_t expireTimeMilliSeconds, bool isTestCredential) {
+ // Pretend to be implemented in a trusted environment just so we can pass
+ // the VTS tests. Of course, this is a pretend-only game since hopefully no
+ // relying party is ever going to trust our batch key and those keys above
+ // it.
+ ::keymaster::PureSoftKeymasterContext context(::keymaster::KmVersion::KEYMASTER_4_1,
+ KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT);
+
keymaster_error_t error;
::keymaster::CertificateChain attestation_chain =
- ::keymaster::getAttestationChain(KM_ALGORITHM_EC, &error);
+ context.GetAttestationChain(KM_ALGORITHM_EC, &error);
if (KM_ERROR_OK != error) {
LOG(ERROR) << "Error getting attestation chain " << error;
return {};
@@ -855,12 +862,6 @@
}
expireTimeMilliSeconds = bcNotAfter * 1000;
}
- const keymaster_key_blob_t* attestation_signing_key =
- ::keymaster::getAttestationKey(KM_ALGORITHM_EC, nullptr);
- if (attestation_signing_key == nullptr) {
- LOG(ERROR) << "Error getting attestation key";
- return {};
- }
::keymaster::X509_NAME_Ptr subjectName;
if (KM_ERROR_OK !=
@@ -874,8 +875,11 @@
i2d_X509_NAME(subjectName.get(), &subjectPtr);
+ uint64_t nowMilliSeconds = time(nullptr) * 1000;
::keymaster::AuthorizationSet auth_set(
::keymaster::AuthorizationSetBuilder()
+ .Authorization(::keymaster::TAG_CERTIFICATE_NOT_BEFORE, nowMilliSeconds)
+ .Authorization(::keymaster::TAG_CERTIFICATE_NOT_AFTER, expireTimeMilliSeconds)
.Authorization(::keymaster::TAG_ATTESTATION_CHALLENGE, challenge.data(),
challenge.size())
.Authorization(::keymaster::TAG_ACTIVE_DATETIME, activeTimeMilliSeconds)
@@ -914,19 +918,11 @@
}
::keymaster::AuthorizationSet hwEnforced(hwEnforcedBuilder);
- // Pretend to be implemented in a trusted environment just so we can pass
- // the VTS tests. Of course, this is a pretend-only game since hopefully no
- // relying party is ever going to trust our batch key and those keys above
- // it.
- ::keymaster::PureSoftKeymasterContext context(::keymaster::KmVersion::KEYMASTER_4_1,
- KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT);
-
- ::keymaster::CertificateChain cert_chain_out = generate_attestation_from_EVP(
- key, swEnforced, hwEnforced, auth_set, context, move(attestation_chain),
- *attestation_signing_key, &error);
+ ::keymaster::CertificateChain cert_chain_out = generate_attestation(
+ key, swEnforced, hwEnforced, auth_set, {} /* attest_key */, context, &error);
if (KM_ERROR_OK != error) {
- LOG(ERROR) << "Error generate attestation from EVP key" << error;
+ LOG(ERROR) << "Error generating attestation from EVP key: " << error;
return {};
}
diff --git a/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/DeviceInfo.aidl b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/DeviceInfo.aidl
index 00abff9..3e25c56 100644
--- a/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/DeviceInfo.aidl
+++ b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/DeviceInfo.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/IMemtrack.aidl b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/IMemtrack.aidl
index 844a1bb..2e2b68e 100644
--- a/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/IMemtrack.aidl
+++ b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/IMemtrack.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/MemtrackRecord.aidl b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/MemtrackRecord.aidl
index 09ecefc..0e15ce3 100644
--- a/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/MemtrackRecord.aidl
+++ b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/MemtrackRecord.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/MemtrackType.aidl b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/MemtrackType.aidl
index 7f3f939..b19869e 100644
--- a/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/MemtrackType.aidl
+++ b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/MemtrackType.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
@@ -23,5 +38,4 @@
GRAPHICS = 2,
MULTIMEDIA = 3,
CAMERA = 4,
- NUM_TYPES = 5,
}
diff --git a/memtrack/aidl/android/hardware/memtrack/MemtrackType.aidl b/memtrack/aidl/android/hardware/memtrack/MemtrackType.aidl
index 715c6bf..5db735a 100644
--- a/memtrack/aidl/android/hardware/memtrack/MemtrackType.aidl
+++ b/memtrack/aidl/android/hardware/memtrack/MemtrackType.aidl
@@ -27,5 +27,4 @@
GRAPHICS = 2,
MULTIMEDIA = 3,
CAMERA = 4,
- NUM_TYPES,
}
diff --git a/memtrack/aidl/default/Memtrack.cpp b/memtrack/aidl/default/Memtrack.cpp
index 000b25c..49a6582 100644
--- a/memtrack/aidl/default/Memtrack.cpp
+++ b/memtrack/aidl/default/Memtrack.cpp
@@ -26,7 +26,8 @@
if (pid < 0) {
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
}
- if (type < MemtrackType::OTHER || type >= MemtrackType::NUM_TYPES) {
+ if (type != MemtrackType::OTHER && type != MemtrackType::GL && type != MemtrackType::GRAPHICS &&
+ type != MemtrackType::MULTIMEDIA && type != MemtrackType::CAMERA) {
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
}
_aidl_return->clear();
diff --git a/memtrack/aidl/vts/Android.bp b/memtrack/aidl/vts/Android.bp
index df87db8..eff2a56 100644
--- a/memtrack/aidl/vts/Android.bp
+++ b/memtrack/aidl/vts/Android.bp
@@ -13,6 +13,6 @@
"android.hardware.memtrack-V1-ndk_platform",
],
test_suites: [
- "vts-core",
+ "vts",
],
}
diff --git a/memtrack/aidl/vts/VtsHalMemtrackTargetTest.cpp b/memtrack/aidl/vts/VtsHalMemtrackTargetTest.cpp
index d5f4612..8905f50 100644
--- a/memtrack/aidl/vts/VtsHalMemtrackTargetTest.cpp
+++ b/memtrack/aidl/vts/VtsHalMemtrackTargetTest.cpp
@@ -46,17 +46,19 @@
TEST_P(MemtrackAidlTest, GetMemoryInvalidPid) {
int pid = -1;
- MemtrackType type = MemtrackType::OTHER;
- std::vector<MemtrackRecord> records;
- auto status = memtrack_->getMemory(pid, type, &records);
+ for (MemtrackType type : ndk::enum_range<MemtrackType>()) {
+ std::vector<MemtrackRecord> records;
- EXPECT_EQ(status.getExceptionCode(), EX_ILLEGAL_ARGUMENT);
+ auto status = memtrack_->getMemory(pid, type, &records);
+
+ EXPECT_EQ(status.getExceptionCode(), EX_ILLEGAL_ARGUMENT);
+ }
}
TEST_P(MemtrackAidlTest, GetMemoryInvalidType) {
int pid = 1;
- MemtrackType type = MemtrackType::NUM_TYPES;
+ MemtrackType type = static_cast<MemtrackType>(-1);
std::vector<MemtrackRecord> records;
auto status = memtrack_->getMemory(pid, type, &records);
@@ -66,12 +68,13 @@
TEST_P(MemtrackAidlTest, GetMemory) {
int pid = 1;
- MemtrackType type = MemtrackType::OTHER;
- std::vector<MemtrackRecord> records;
+ for (MemtrackType type : ndk::enum_range<MemtrackType>()) {
+ std::vector<MemtrackRecord> records;
- auto status = memtrack_->getMemory(pid, type, &records);
+ auto status = memtrack_->getMemory(pid, type, &records);
- EXPECT_TRUE(status.isOk());
+ EXPECT_TRUE(status.isOk());
+ }
}
TEST_P(MemtrackAidlTest, GetGpuDeviceInfo) {
@@ -87,7 +90,7 @@
->getRuntimeInfo(RuntimeInfo::FetchFlag::CPU_VERSION)
->kernelVersion();
EXPECT_LT(kernel_version, min_kernel_version)
- << "Devices with 5.10 or later kernels must implement getGpuDeviceInfo()";
+ << "Devices with 5.4 or later kernels must implement getGpuDeviceInfo()";
return;
}
diff --git a/neuralnetworks/1.0/types.hal b/neuralnetworks/1.0/types.hal
index 620eefb..5bfadd3 100644
--- a/neuralnetworks/1.0/types.hal
+++ b/neuralnetworks/1.0/types.hal
@@ -308,8 +308,9 @@
* Outputs:
* * 0: The output 4-D tensor, of shape
* [batches, out_height, out_width, depth_out].
- * For output tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
- * the following condition must be satisfied: output_scale > input_scale * filter_scale
+ * For output tensor of
+ * {@link OperandType::TENSOR_QUANT8_ASYMM}, the following condition must
+ * be satisfied: output_scale > input_scale * filter_scale
*/
CONV_2D = 3,
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 f2cbe93..8329303 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
@@ -41,7 +41,7 @@
Burst(PrivateConstructorTag tag, nn::SharedPreparedModel preparedModel);
- OptionalCacheHold cacheMemory(const nn::Memory& memory) const override;
+ OptionalCacheHold cacheMemory(const nn::SharedMemory& memory) const override;
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
const nn::Request& request, nn::MeasureTiming measure) const override;
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Conversions.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Conversions.h
index d3d933b..5d4bdbc 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Conversions.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Conversions.h
@@ -36,7 +36,7 @@
GeneralResult<Operation> unvalidatedConvert(const hal::V1_0::Operation& operation);
GeneralResult<Model::OperandValues> unvalidatedConvert(
const hardware::hidl_vec<uint8_t>& operandValues);
-GeneralResult<Memory> unvalidatedConvert(const hardware::hidl_memory& memory);
+GeneralResult<SharedMemory> unvalidatedConvert(const hardware::hidl_memory& memory);
GeneralResult<Model> unvalidatedConvert(const hal::V1_0::Model& model);
GeneralResult<Request::Argument> unvalidatedConvert(
const hal::V1_0::RequestArgument& requestArgument);
@@ -65,7 +65,7 @@
nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation);
nn::GeneralResult<hidl_vec<uint8_t>> unvalidatedConvert(
const nn::Model::OperandValues& operandValues);
-nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Memory& memory);
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::SharedMemory& memory);
nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model);
nn::GeneralResult<RequestArgument> unvalidatedConvert(const nn::Request::Argument& requestArgument);
nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Request::MemoryPool& memoryPool);
diff --git a/neuralnetworks/1.0/utils/src/Burst.cpp b/neuralnetworks/1.0/utils/src/Burst.cpp
index 384bd9b..971ad08 100644
--- a/neuralnetworks/1.0/utils/src/Burst.cpp
+++ b/neuralnetworks/1.0/utils/src/Burst.cpp
@@ -43,7 +43,7 @@
CHECK(kPreparedModel != nullptr);
}
-Burst::OptionalCacheHold Burst::cacheMemory(const nn::Memory& /*memory*/) const {
+Burst::OptionalCacheHold Burst::cacheMemory(const nn::SharedMemory& /*memory*/) const {
return nullptr;
}
diff --git a/neuralnetworks/1.0/utils/src/Conversions.cpp b/neuralnetworks/1.0/utils/src/Conversions.cpp
index fde7346..7a099cf 100644
--- a/neuralnetworks/1.0/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.0/utils/src/Conversions.cpp
@@ -153,8 +153,8 @@
return Model::OperandValues(operandValues.data(), operandValues.size());
}
-GeneralResult<Memory> unvalidatedConvert(const hidl_memory& memory) {
- return createSharedMemoryFromHidlMemory(memory);
+GeneralResult<SharedMemory> unvalidatedConvert(const hidl_memory& memory) {
+ return hal::utils::createSharedMemoryFromHidlMemory(memory);
}
GeneralResult<Model> unvalidatedConvert(const hal::V1_0::Model& model) {
@@ -346,9 +346,8 @@
return hidl_vec<uint8_t>(operandValues.data(), operandValues.data() + operandValues.size());
}
-nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Memory& memory) {
- return hidl_memory(memory.name, NN_TRY(hal::utils::hidlHandleFromSharedHandle(memory.handle)),
- memory.size);
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::SharedMemory& memory) {
+ return hal::utils::createHidlMemoryFromSharedMemory(memory);
}
nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) {
@@ -392,7 +391,7 @@
}
nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Request::MemoryPool& memoryPool) {
- return unvalidatedConvert(std::get<nn::Memory>(memoryPool));
+ return unvalidatedConvert(std::get<nn::SharedMemory>(memoryPool));
}
nn::GeneralResult<Request> unvalidatedConvert(const nn::Request& request) {
diff --git a/neuralnetworks/1.1/utils/src/Conversions.cpp b/neuralnetworks/1.1/utils/src/Conversions.cpp
index b47f25a..07bf7bc 100644
--- a/neuralnetworks/1.1/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.1/utils/src/Conversions.cpp
@@ -175,7 +175,7 @@
return V1_0::utils::unvalidatedConvert(operandValues);
}
-nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Memory& memory) {
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::SharedMemory& memory) {
return V1_0::utils::unvalidatedConvert(memory);
}
diff --git a/neuralnetworks/1.2/types.hal b/neuralnetworks/1.2/types.hal
index 7441a54..7cec49e 100644
--- a/neuralnetworks/1.2/types.hal
+++ b/neuralnetworks/1.2/types.hal
@@ -314,7 +314,8 @@
* tensors. The output shape is [D0, D1, ..., sum(Daxis(i)), ..., Dm].
* Since HAL version 1.2, for a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor,
* the scale and zeroPoint values can be different from
- * input tensors. Before HAL version 1.2 they have to be the same as for the input tensors.
+ * input tensors. Before HAL version 1.2 they have to be the same as for the
+ * input tensors.
*/
CONCATENATION = @1.1::OperationType:CONCATENATION,
@@ -460,8 +461,9 @@
* Outputs:
* * 0: The output 4-D tensor, of shape
* [batches, out_height, out_width, depth_out].
- * Before HAL version 1.2, for output tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
- * the following condition must be satisfied: output_scale > input_scale * filter_scale
+ * Before HAL version 1.2, for output tensor of
+ * {@link OperandType::TENSOR_QUANT8_ASYMM}, the following condition must
+ * be satisfied: output_scale > input_scale * filter_scale
*/
CONV_2D = @1.1::OperationType:CONV_2D,
diff --git a/neuralnetworks/1.2/utils/src/Conversions.cpp b/neuralnetworks/1.2/utils/src/Conversions.cpp
index 062f6f7..7ae483e 100644
--- a/neuralnetworks/1.2/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.2/utils/src/Conversions.cpp
@@ -304,7 +304,11 @@
}
GeneralResult<SharedHandle> unvalidatedConvert(const hidl_handle& hidlHandle) {
- return hal::utils::sharedHandleFromNativeHandle(hidlHandle.getNativeHandle());
+ if (hidlHandle.getNativeHandle() == nullptr) {
+ return nullptr;
+ }
+ auto handle = NN_TRY(hal::utils::sharedHandleFromNativeHandle(hidlHandle.getNativeHandle()));
+ return std::make_shared<const Handle>(std::move(handle));
}
GeneralResult<DeviceType> convert(const hal::V1_2::DeviceType& deviceType) {
@@ -365,7 +369,7 @@
return V1_0::utils::unvalidatedConvert(operandValues);
}
-nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Memory& memory) {
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::SharedMemory& memory) {
return V1_0::utils::unvalidatedConvert(memory);
}
@@ -588,7 +592,10 @@
}
nn::GeneralResult<hidl_handle> unvalidatedConvert(const nn::SharedHandle& handle) {
- return hal::utils::hidlHandleFromSharedHandle(handle);
+ if (handle == nullptr) {
+ return {};
+ }
+ return hal::utils::hidlHandleFromSharedHandle(*handle);
}
nn::GeneralResult<DeviceType> convert(const nn::DeviceType& deviceType) {
diff --git a/neuralnetworks/1.3/types.hal b/neuralnetworks/1.3/types.hal
index 5f5ee03..51837fe 100644
--- a/neuralnetworks/1.3/types.hal
+++ b/neuralnetworks/1.3/types.hal
@@ -263,7 +263,8 @@
* tensors. The output shape is [D0, D1, ..., sum(Daxis(i)), ..., Dm].
* Since HAL version 1.2, for a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor,
* the scale and zeroPoint values can be different from
- * input tensors. Before HAL version 1.2 they have to be the same as for the input tensors.
+ * input tensors. Before HAL version 1.2 they have to be the same as for the
+ * input tensors.
* For a {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
* the scale and zeroPoint values can be different from input tensors.
*/
@@ -312,7 +313,8 @@
* * * {@link OperandType::TENSOR_INT32} for bias (with scale set to
* * * input.scale * filter.scale).
*
- * * Quantized signed with filter symmetric per channel quantization (since HAL version 1.3):
+ * * Quantized signed with filter symmetric per channel quantization
+ * (since HAL version 1.3):
* * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} for input, and output.
* * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} for filter.
* * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
@@ -425,8 +427,9 @@
* Outputs:
* * 0: The output 4-D tensor, of shape
* [batches, out_height, out_width, depth_out].
- * Before HAL version 1.2, for output tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
- * the following condition must be satisfied: output_scale > input_scale * filter_scale
+ * Before HAL version 1.2, for output tensor of
+ * {@link OperandType::TENSOR_QUANT8_ASYMM}, the following condition must
+ * be satisfied: output_scale > input_scale * filter_scale
*/
CONV_2D = @1.2::OperationType:CONV_2D,
@@ -477,7 +480,8 @@
* * * {@link OperandType::TENSOR_INT32} for bias (with scale set to
* * * input.scale * filter.scale).
*
- * * Quantized signed with filter symmetric per channel quantization (since HAL version 1.3):
+ * * Quantized signed with filter symmetric per channel quantization
+ * (since HAL version 1.3):
* * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} for input, and output.
* * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} for filter.
* * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
@@ -3354,7 +3358,8 @@
* * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
* * * each value scaling is separate and equal to input.scale * filter.scales[channel]).
*
- * * Quantized signed with filter symmetric per channel quantization (since HAL version 1.3):
+ * * Quantized signed with filter symmetric per channel quantization
+ * (since HAL version 1.3):
* * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} for input, and output.
* * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} for filter.
* * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
@@ -4615,7 +4620,8 @@
* * * {@link OperandType::TENSOR_INT32} for bias (with scale set to
* * * input.scale * filter.scale).
*
- * * Quantized signed with filter symmetric per channel quantization (since HAL version 1.3):
+ * * Quantized signed with filter symmetric per channel quantization
+ * (since HAL version 1.3):
* * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} for input, and output.
* * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} for filter.
* * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Buffer.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Buffer.h
index fda79c8..69e87f7 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Buffer.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Buffer.h
@@ -42,8 +42,8 @@
nn::Request::MemoryDomainToken getToken() const override;
- nn::GeneralResult<void> copyTo(const nn::Memory& dst) const override;
- nn::GeneralResult<void> copyFrom(const nn::Memory& src,
+ nn::GeneralResult<void> copyTo(const nn::SharedMemory& dst) const override;
+ nn::GeneralResult<void> copyFrom(const nn::SharedMemory& src,
const nn::Dimensions& dimensions) const override;
private:
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h
index 74a6534..8e1cdb8 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h
@@ -59,7 +59,7 @@
GeneralResult<ErrorStatus> convert(const hal::V1_3::ErrorStatus& errorStatus);
GeneralResult<SharedHandle> convert(const hardware::hidl_handle& handle);
-GeneralResult<Memory> convert(const hardware::hidl_memory& memory);
+GeneralResult<SharedMemory> convert(const hardware::hidl_memory& memory);
GeneralResult<std::vector<BufferRole>> convert(
const hardware::hidl_vec<hal::V1_3::BufferRole>& bufferRoles);
@@ -100,7 +100,7 @@
nn::GeneralResult<ErrorStatus> convert(const nn::ErrorStatus& errorStatus);
nn::GeneralResult<hidl_handle> convert(const nn::SharedHandle& handle);
-nn::GeneralResult<hidl_memory> convert(const nn::Memory& memory);
+nn::GeneralResult<hidl_memory> convert(const nn::SharedMemory& memory);
nn::GeneralResult<hidl_vec<BufferRole>> convert(const std::vector<nn::BufferRole>& bufferRoles);
nn::GeneralResult<V1_0::DeviceStatus> convert(const nn::DeviceStatus& deviceStatus);
diff --git a/neuralnetworks/1.3/utils/src/Buffer.cpp b/neuralnetworks/1.3/utils/src/Buffer.cpp
index 614033e..ada5265 100644
--- a/neuralnetworks/1.3/utils/src/Buffer.cpp
+++ b/neuralnetworks/1.3/utils/src/Buffer.cpp
@@ -61,7 +61,7 @@
return kToken;
}
-nn::GeneralResult<void> Buffer::copyTo(const nn::Memory& dst) const {
+nn::GeneralResult<void> Buffer::copyTo(const nn::SharedMemory& dst) const {
const auto hidlDst = NN_TRY(convert(dst));
const auto ret = kBuffer->copyTo(hidlDst);
@@ -71,7 +71,7 @@
return {};
}
-nn::GeneralResult<void> Buffer::copyFrom(const nn::Memory& src,
+nn::GeneralResult<void> Buffer::copyFrom(const nn::SharedMemory& src,
const nn::Dimensions& dimensions) const {
const auto hidlSrc = NN_TRY(convert(src));
const auto hidlDimensions = hidl_vec<uint32_t>(dimensions);
diff --git a/neuralnetworks/1.3/utils/src/Conversions.cpp b/neuralnetworks/1.3/utils/src/Conversions.cpp
index 8b7db2b..6e74a62 100644
--- a/neuralnetworks/1.3/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.3/utils/src/Conversions.cpp
@@ -261,7 +261,7 @@
using Discriminator = hal::V1_3::Request::MemoryPool::hidl_discriminator;
switch (memoryPool.getDiscriminator()) {
case Discriminator::hidlMemory:
- return createSharedMemoryFromHidlMemory(memoryPool.hidlMemory());
+ return hal::utils::createSharedMemoryFromHidlMemory(memoryPool.hidlMemory());
case Discriminator::token:
return static_cast<Request::MemoryDomainToken>(memoryPool.token());
}
@@ -352,7 +352,7 @@
return validatedConvert(handle);
}
-GeneralResult<Memory> convert(const hardware::hidl_memory& memory) {
+GeneralResult<SharedMemory> convert(const hardware::hidl_memory& memory) {
return validatedConvert(memory);
}
@@ -386,7 +386,7 @@
return V1_2::utils::unvalidatedConvert(handle);
}
-nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Memory& memory) {
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::SharedMemory& memory) {
return V1_0::utils::unvalidatedConvert(memory);
}
@@ -424,7 +424,7 @@
return unvalidatedConvertVec(arguments);
}
-nn::GeneralResult<Request::MemoryPool> makeMemoryPool(const nn::Memory& memory) {
+nn::GeneralResult<Request::MemoryPool> makeMemoryPool(const nn::SharedMemory& memory) {
Request::MemoryPool ret;
ret.hidlMemory(NN_TRY(unvalidatedConvert(memory)));
return ret;
@@ -677,7 +677,7 @@
return validatedConvert(handle);
}
-nn::GeneralResult<hidl_memory> convert(const nn::Memory& memory) {
+nn::GeneralResult<hidl_memory> convert(const nn::SharedMemory& memory) {
return validatedConvert(memory);
}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferDesc.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferDesc.aidl
index 2074a2a..71b7758 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferDesc.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferDesc.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferRole.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferRole.aidl
index 97f748b..c2d636c 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferRole.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferRole.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Capabilities.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Capabilities.aidl
index 31afafc..01cc753 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Capabilities.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Capabilities.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DataLocation.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DataLocation.aidl
index 5b03ba0..074cc09 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DataLocation.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DataLocation.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DeviceBuffer.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DeviceBuffer.aidl
index 9cff6db..7bc8aa7 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DeviceBuffer.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DeviceBuffer.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DeviceType.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DeviceType.aidl
index dd4dae7..1abacc8 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DeviceType.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DeviceType.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ErrorStatus.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ErrorStatus.aidl
index ba18c38..873c584 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ErrorStatus.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ErrorStatus.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionPreference.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionPreference.aidl
index cccae54..c4badc0 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionPreference.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionPreference.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionResult.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionResult.aidl
index c17ddb9..b99bb31 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionResult.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionResult.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Extension.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Extension.aidl
index 9eb8896..a7ae942 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Extension.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Extension.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExtensionNameAndPrefix.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExtensionNameAndPrefix.aidl
index a271a63..4c25538 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExtensionNameAndPrefix.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExtensionNameAndPrefix.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.aidl
index d1c3f09..b32b217 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/FusedActivationFunc.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/FusedActivationFunc.aidl
index ddd3c2a..2fee136 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/FusedActivationFunc.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/FusedActivationFunc.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBuffer.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBuffer.aidl
index a297a6b..2860692 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBuffer.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBuffer.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/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 38fda16..4c5fd2f 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
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IFencedExecutionCallback.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IFencedExecutionCallback.aidl
index a7cf906..abe67b8 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IFencedExecutionCallback.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IFencedExecutionCallback.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/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 8767712..3ca1550 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
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModelCallback.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModelCallback.aidl
index d1ae2eb..8eaaab6 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModelCallback.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModelCallback.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModelParcel.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModelParcel.aidl
index 048251a..8388fda 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModelParcel.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModelParcel.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Memory.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Memory.aidl
index aa735c0..3b2f240 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Memory.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Memory.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Model.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Model.aidl
index 944bd7f..9d12e58 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Model.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Model.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/NumberOfCacheFiles.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/NumberOfCacheFiles.aidl
index ca5f917..c1e87da 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/NumberOfCacheFiles.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/NumberOfCacheFiles.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/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 6615b9b..bb78caa 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
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandExtraParams.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandExtraParams.aidl
index 20317c7..3f6d93b 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandExtraParams.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandExtraParams.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandLifeTime.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandLifeTime.aidl
index 1082f9e..d581ced 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandLifeTime.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandLifeTime.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/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 9232b4c..87fd3a6 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
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandType.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandType.aidl
index bd95fab..186c13d 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandType.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandType.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/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 383eba4..fec83a8 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
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperationType.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperationType.aidl
index f786829..ad42b02 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperationType.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperationType.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OutputShape.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OutputShape.aidl
index 1300c49..09a43f7 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OutputShape.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OutputShape.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/PerformanceInfo.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/PerformanceInfo.aidl
index b5dc179..178946c 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/PerformanceInfo.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/PerformanceInfo.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Priority.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Priority.aidl
index 980bee3..d9b77fa 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Priority.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Priority.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Request.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Request.aidl
index 6f77066..599b3f4 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Request.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Request.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/RequestArgument.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/RequestArgument.aidl
index c9560ef..91b9aa7 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/RequestArgument.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/RequestArgument.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/RequestMemoryPool.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/RequestMemoryPool.aidl
index 123e4b0..3813b51 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/RequestMemoryPool.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/RequestMemoryPool.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Subgraph.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Subgraph.aidl
index 771d15a..dec976f 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Subgraph.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Subgraph.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/SymmPerChannelQuantParams.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/SymmPerChannelQuantParams.aidl
index 2282feb..66fdfe7 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/SymmPerChannelQuantParams.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/SymmPerChannelQuantParams.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/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 b08d34a..d0de34a 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
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferDesc.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferDesc.aidl
index 1b92ebc..bec7e86 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferDesc.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferDesc.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
/**
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferRole.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferRole.aidl
index 7877bc0..0d7f678 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferRole.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferRole.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
/**
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Capabilities.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Capabilities.aidl
index 5ce78ee..3802f1f 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/Capabilities.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Capabilities.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
import android.hardware.neuralnetworks.OperandPerformance;
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/DataLocation.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/DataLocation.aidl
index 57e3f4a..f6b5e0d 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/DataLocation.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/DataLocation.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
/**
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/DeviceBuffer.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/DeviceBuffer.aidl
index d51e1b2..07930a6 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/DeviceBuffer.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/DeviceBuffer.aidl
@@ -14,15 +14,15 @@
* limitations under the License.
*/
- package android.hardware.neuralnetworks;
+package android.hardware.neuralnetworks;
import android.hardware.neuralnetworks.IBuffer;
/**
* A type that is used to represent a driver allocated buffer and token that corresponds to it.
*/
- @VintfStability
- parcelable DeviceBuffer {
+@VintfStability
+parcelable DeviceBuffer {
/**
* An IBuffer object used to interact with the device allocated buffer.
*/
@@ -33,4 +33,4 @@
* with the tokens of other IBuffer objects that are currently alive in the same driver service.
*/
int token;
- }
\ No newline at end of file
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/DeviceType.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/DeviceType.aidl
index 8399d50..815be64 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/DeviceType.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/DeviceType.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
/**
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/ErrorStatus.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/ErrorStatus.aidl
index 860f86a..c2752d9 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/ErrorStatus.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/ErrorStatus.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
/**
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/ExecutionPreference.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/ExecutionPreference.aidl
index 901cb38..a3e3ce3 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/ExecutionPreference.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/ExecutionPreference.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
/**
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/ExecutionResult.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/ExecutionResult.aidl
index 403fe09..1f88207 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/ExecutionResult.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/ExecutionResult.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
import android.hardware.neuralnetworks.ErrorStatus;
@@ -44,4 +43,3 @@
*/
Timing timing;
}
-
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Extension.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Extension.aidl
index 159e3c1..20109bd 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/Extension.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Extension.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
import android.hardware.neuralnetworks.ExtensionOperandTypeInformation;
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/ExtensionNameAndPrefix.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/ExtensionNameAndPrefix.aidl
index 76074bf..29be93f 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/ExtensionNameAndPrefix.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/ExtensionNameAndPrefix.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
/**
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.aidl
index d7f93c1..b8e3449 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
/**
@@ -35,4 +34,3 @@
*/
int byteSize;
}
-
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/FusedActivationFunc.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/FusedActivationFunc.aidl
index 40f1053..861b6f0 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/FusedActivationFunc.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/FusedActivationFunc.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
/**
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/IBuffer.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IBuffer.aidl
index eb3dec6..2915a22 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/IBuffer.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IBuffer.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
import android.hardware.neuralnetworks.Memory;
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/IDevice.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IDevice.aidl
index 0c4954c..e17e0cd 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/IDevice.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IDevice.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
import android.hardware.neuralnetworks.BufferDesc;
@@ -114,7 +113,7 @@
* - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
*/
DeviceBuffer allocate(in BufferDesc desc, in IPreparedModelParcel[] preparedModels,
- in BufferRole[] inputRoles, in BufferRole[] outputRoles);
+ in BufferRole[] inputRoles, in BufferRole[] outputRoles);
/**
* Gets the capabilities of a driver.
@@ -345,8 +344,9 @@
* - 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 ParcelFileDescriptor[] dataCache,
- in byte[] token, in IPreparedModelCallback callback);
+ in long deadline, in ParcelFileDescriptor[] modelCache,
+ in ParcelFileDescriptor[] dataCache, in byte[] token,
+ in IPreparedModelCallback callback);
/**
* Creates a prepared model from cache files for execution.
@@ -427,5 +427,6 @@
* - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
*/
void prepareModelFromCache(in long deadline, in ParcelFileDescriptor[] modelCache,
- in ParcelFileDescriptor[] dataCache, in byte[] token, in IPreparedModelCallback callback);
+ in ParcelFileDescriptor[] dataCache, in byte[] token,
+ in IPreparedModelCallback callback);
}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/IFencedExecutionCallback.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IFencedExecutionCallback.aidl
index 47e5916..cb6db11 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/IFencedExecutionCallback.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IFencedExecutionCallback.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
import android.hardware.neuralnetworks.ErrorStatus;
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModel.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModel.aidl
index c1b2992..2414a4a 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModel.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModel.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
import android.hardware.common.NativeHandle;
@@ -95,7 +94,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 deadline, in long loopTimeoutDuration);
/**
* Launch a fenced asynchronous execution on a prepared model.
@@ -168,6 +167,6 @@
* - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
*/
IFencedExecutionCallback executeFenced(in Request request, in ParcelFileDescriptor[] waitFor,
- in boolean measureTiming, in long deadline, in long loopTimeoutDuration, in long duration,
- out @nullable ParcelFileDescriptor syncFence);
+ in boolean measureTiming, in long deadline, in long loopTimeoutDuration,
+ in long duration, out @nullable ParcelFileDescriptor syncFence);
}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModelCallback.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModelCallback.aidl
index adb4218..29cca6d 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModelCallback.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModelCallback.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
import android.hardware.neuralnetworks.ErrorStatus;
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModelParcel.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModelParcel.aidl
index f198c3f..878b0ad 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModelParcel.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModelParcel.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
import android.hardware.neuralnetworks.IPreparedModel;
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Memory.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Memory.aidl
index 8ecb067..870f0ae 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/Memory.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Memory.aidl
@@ -16,7 +16,6 @@
package android.hardware.neuralnetworks;
import android.hardware.common.NativeHandle;
-
import android.os.ParcelFileDescriptor;
/**
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Model.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Model.aidl
index 3bb7318..2f11dec 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/Model.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Model.aidl
@@ -14,12 +14,11 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
import android.hardware.neuralnetworks.ExtensionNameAndPrefix;
-import android.hardware.neuralnetworks.Subgraph;
import android.hardware.neuralnetworks.Memory;
+import android.hardware.neuralnetworks.Subgraph;
/**
* A Neural Network Model.
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Operand.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Operand.aidl
index 243a89d..4d2260f 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/Operand.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Operand.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
import android.hardware.neuralnetworks.DataLocation;
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandExtraParams.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandExtraParams.aidl
index b0112ae..229754a 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandExtraParams.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandExtraParams.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
import android.hardware.neuralnetworks.SymmPerChannelQuantParams;
@@ -37,4 +36,4 @@
* The format is up to individual extensions.
*/
byte[] extension;
-}
\ No newline at end of file
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandLifeTime.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandLifeTime.aidl
index 63d1971..1d18149 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandLifeTime.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandLifeTime.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
/**
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandPerformance.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandPerformance.aidl
index 9a8c2cc..7fd86f9 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandPerformance.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandPerformance.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
import android.hardware.neuralnetworks.OperandType;
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandType.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandType.aidl
index 9274b6f..12edc0f 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandType.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandType.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
/**
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Operation.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Operation.aidl
index acfb4b7..0c6032f 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/Operation.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Operation.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
import android.hardware.neuralnetworks.OperationType;
@@ -43,4 +42,3 @@
*/
int[] outputs;
}
-
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl
index fd9da67..3f49154 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
/**
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/OutputShape.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/OutputShape.aidl
index d206a25..f90a613 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/OutputShape.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/OutputShape.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
/**
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/PerformanceInfo.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/PerformanceInfo.aidl
index 6ee29c2..6915c67 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/PerformanceInfo.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/PerformanceInfo.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
/**
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Priority.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Priority.aidl
index fe87598..7dbf8e9 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/Priority.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Priority.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
/**
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Request.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Request.aidl
index 396ff30..dc138ba 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/Request.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Request.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
import android.hardware.neuralnetworks.RequestArgument;
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/RequestArgument.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/RequestArgument.aidl
index e615fa6..8dc9252 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/RequestArgument.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/RequestArgument.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
import android.hardware.neuralnetworks.DataLocation;
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/RequestMemoryPool.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/RequestMemoryPool.aidl
index 166746d..faca2fe 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/RequestMemoryPool.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/RequestMemoryPool.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
import android.hardware.neuralnetworks.Memory;
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Subgraph.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Subgraph.aidl
index 0a76285..2e9c450 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/Subgraph.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Subgraph.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
import android.hardware.neuralnetworks.Operand;
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/SymmPerChannelQuantParams.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/SymmPerChannelQuantParams.aidl
index 8ae41a4..eb47df0 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/SymmPerChannelQuantParams.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/SymmPerChannelQuantParams.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
/**
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Timing.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Timing.aidl
index b04f74e..8130e08 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/Timing.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Timing.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.hardware.neuralnetworks;
/**
diff --git a/neuralnetworks/aidl/utils/Android.bp b/neuralnetworks/aidl/utils/Android.bp
index 56017da..147d401 100644
--- a/neuralnetworks/aidl/utils/Android.bp
+++ b/neuralnetworks/aidl/utils/Android.bp
@@ -21,12 +21,14 @@
local_include_dirs: ["include/nnapi/hal/aidl/"],
export_include_dirs: ["include"],
static_libs: [
+ "libarect",
"neuralnetworks_types",
"neuralnetworks_utils_hal_common",
],
shared_libs: [
- "libhidlbase",
"android.hardware.neuralnetworks-V1-ndk_platform",
"libbinder_ndk",
+ "libhidlbase",
+ "libnativewindow",
],
}
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h
index 35de5be..1b2f69c 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h
@@ -79,7 +79,7 @@
GeneralResult<Model::Subgraph> unvalidatedConvert(const aidl_hal::Subgraph& subgraph);
GeneralResult<OutputShape> unvalidatedConvert(const aidl_hal::OutputShape& outputShape);
GeneralResult<MeasureTiming> unvalidatedConvert(bool measureTiming);
-GeneralResult<Memory> unvalidatedConvert(const aidl_hal::Memory& memory);
+GeneralResult<SharedMemory> unvalidatedConvert(const aidl_hal::Memory& memory);
GeneralResult<Timing> unvalidatedConvert(const aidl_hal::Timing& timing);
GeneralResult<BufferDesc> unvalidatedConvert(const aidl_hal::BufferDesc& bufferDesc);
GeneralResult<BufferRole> unvalidatedConvert(const aidl_hal::BufferRole& bufferRole);
@@ -99,7 +99,7 @@
GeneralResult<ExecutionPreference> convert(
const aidl_hal::ExecutionPreference& executionPreference);
-GeneralResult<Memory> convert(const aidl_hal::Memory& memory);
+GeneralResult<SharedMemory> convert(const aidl_hal::Memory& memory);
GeneralResult<Model> convert(const aidl_hal::Model& model);
GeneralResult<Operand> convert(const aidl_hal::Operand& operand);
GeneralResult<OperandType> convert(const aidl_hal::OperandType& operandType);
@@ -108,7 +108,7 @@
GeneralResult<Request> convert(const aidl_hal::Request& request);
GeneralResult<std::vector<Operation>> convert(const std::vector<aidl_hal::Operation>& outputShapes);
-GeneralResult<std::vector<Memory>> convert(const std::vector<aidl_hal::Memory>& memories);
+GeneralResult<std::vector<SharedMemory>> convert(const std::vector<aidl_hal::Memory>& memories);
GeneralResult<std::vector<uint32_t>> toUnsigned(const std::vector<int32_t>& vec);
@@ -118,11 +118,11 @@
namespace nn = ::android::nn;
-nn::GeneralResult<Memory> unvalidatedConvert(const nn::Memory& memory);
+nn::GeneralResult<Memory> unvalidatedConvert(const nn::SharedMemory& memory);
nn::GeneralResult<OutputShape> unvalidatedConvert(const nn::OutputShape& outputShape);
nn::GeneralResult<ErrorStatus> unvalidatedConvert(const nn::ErrorStatus& errorStatus);
-nn::GeneralResult<Memory> convert(const nn::Memory& memory);
+nn::GeneralResult<Memory> convert(const nn::SharedMemory& memory);
nn::GeneralResult<ErrorStatus> convert(const nn::ErrorStatus& errorStatus);
nn::GeneralResult<std::vector<OutputShape>> convert(
const std::vector<nn::OutputShape>& outputShapes);
diff --git a/neuralnetworks/aidl/utils/src/Conversions.cpp b/neuralnetworks/aidl/utils/src/Conversions.cpp
index 0e93b02..db3504b 100644
--- a/neuralnetworks/aidl/utils/src/Conversions.cpp
+++ b/neuralnetworks/aidl/utils/src/Conversions.cpp
@@ -18,6 +18,8 @@
#include <aidl/android/hardware/common/NativeHandle.h>
#include <android-base/logging.h>
+#include <android/hardware_buffer.h>
+#include <cutils/native_handle.h>
#include <nnapi/OperandTypes.h>
#include <nnapi/OperationTypes.h>
#include <nnapi/Result.h>
@@ -27,6 +29,7 @@
#include <nnapi/Validation.h>
#include <nnapi/hal/CommonUtils.h>
#include <nnapi/hal/HandleError.h>
+#include <vndk/hardware_buffer.h>
#include <algorithm>
#include <chrono>
@@ -53,6 +56,8 @@
namespace android::nn {
namespace {
+using ::aidl::android::hardware::common::NativeHandle;
+
constexpr auto validOperandType(nn::OperandType operandType) {
switch (operandType) {
case nn::OperandType::FLOAT32:
@@ -125,6 +130,61 @@
return canonical;
}
+GeneralResult<Handle> unvalidatedConvertHelper(const NativeHandle& aidlNativeHandle) {
+ std::vector<base::unique_fd> fds;
+ fds.reserve(aidlNativeHandle.fds.size());
+ for (const auto& fd : aidlNativeHandle.fds) {
+ const int dupFd = dup(fd.get());
+ if (dupFd == -1) {
+ // TODO(b/120417090): is ANEURALNETWORKS_UNEXPECTED_NULL the correct error to return
+ // here?
+ return NN_ERROR() << "Failed to dup the fd";
+ }
+ fds.emplace_back(dupFd);
+ }
+
+ return Handle{.fds = std::move(fds), .ints = aidlNativeHandle.ints};
+}
+
+struct NativeHandleDeleter {
+ void operator()(native_handle_t* handle) const {
+ if (handle) {
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+ }
+};
+
+using UniqueNativeHandle = std::unique_ptr<native_handle_t, NativeHandleDeleter>;
+
+static nn::GeneralResult<UniqueNativeHandle> nativeHandleFromAidlHandle(
+ const NativeHandle& handle) {
+ std::vector<base::unique_fd> fds;
+ fds.reserve(handle.fds.size());
+ for (const auto& fd : handle.fds) {
+ const int dupFd = dup(fd.get());
+ if (dupFd == -1) {
+ return NN_ERROR() << "Failed to dup the fd";
+ }
+ fds.emplace_back(dupFd);
+ }
+
+ constexpr size_t kIntMax = std::numeric_limits<int>::max();
+ CHECK_LE(handle.fds.size(), kIntMax);
+ CHECK_LE(handle.ints.size(), kIntMax);
+ native_handle_t* nativeHandle = native_handle_create(static_cast<int>(handle.fds.size()),
+ static_cast<int>(handle.ints.size()));
+ if (nativeHandle == nullptr) {
+ return NN_ERROR() << "Failed to create native_handle";
+ }
+ for (size_t i = 0; i < fds.size(); ++i) {
+ nativeHandle->data[i] = fds[i].release();
+ }
+ std::copy(handle.ints.begin(), handle.ints.end(), &nativeHandle->data[nativeHandle->numFds]);
+
+ return UniqueNativeHandle(nativeHandle);
+}
+
} // anonymous namespace
GeneralResult<OperandType> unvalidatedConvert(const aidl_hal::OperandType& operandType) {
@@ -316,13 +376,67 @@
return measureTiming ? MeasureTiming::YES : MeasureTiming::NO;
}
-GeneralResult<Memory> unvalidatedConvert(const aidl_hal::Memory& memory) {
+static uint32_t roundUpToMultiple(uint32_t value, uint32_t multiple) {
+ return (value + multiple - 1) / multiple * multiple;
+}
+
+GeneralResult<SharedMemory> unvalidatedConvert(const aidl_hal::Memory& memory) {
VERIFY_NON_NEGATIVE(memory.size) << "Memory size must not be negative";
- return Memory{
- .handle = NN_TRY(unvalidatedConvert(memory.handle)),
+ if (memory.size > std::numeric_limits<uint32_t>::max()) {
+ return NN_ERROR() << "Memory: size must be <= std::numeric_limits<size_t>::max()";
+ }
+
+ if (memory.name != "hardware_buffer_blob") {
+ return std::make_shared<const Memory>(Memory{
+ .handle = NN_TRY(unvalidatedConvertHelper(memory.handle)),
+ .size = static_cast<uint32_t>(memory.size),
+ .name = memory.name,
+ });
+ }
+
+ const auto size = static_cast<uint32_t>(memory.size);
+ const auto format = AHARDWAREBUFFER_FORMAT_BLOB;
+ const auto usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
+ const uint32_t width = size;
+ const uint32_t height = 1; // height is always 1 for BLOB mode AHardwareBuffer.
+ const uint32_t layers = 1; // layers is always 1 for BLOB mode AHardwareBuffer.
+
+ const UniqueNativeHandle handle = NN_TRY(nativeHandleFromAidlHandle(memory.handle));
+ const native_handle_t* nativeHandle = handle.get();
+
+ // AHardwareBuffer_createFromHandle() might fail because an allocator
+ // expects a specific stride value. In that case, we try to guess it by
+ // aligning the width to small powers of 2.
+ // TODO(b/174120849): Avoid stride assumptions.
+ AHardwareBuffer* hardwareBuffer = nullptr;
+ status_t status = UNKNOWN_ERROR;
+ for (uint32_t alignment : {1, 4, 32, 64, 128, 2, 8, 16}) {
+ const uint32_t stride = roundUpToMultiple(width, alignment);
+ AHardwareBuffer_Desc desc{
+ .width = width,
+ .height = height,
+ .layers = layers,
+ .format = format,
+ .usage = usage,
+ .stride = stride,
+ };
+ status = AHardwareBuffer_createFromHandle(&desc, nativeHandle,
+ AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE,
+ &hardwareBuffer);
+ if (status == NO_ERROR) {
+ break;
+ }
+ }
+ if (status != NO_ERROR) {
+ return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
+ << "Can't create AHardwareBuffer from handle. Error: " << status;
+ }
+
+ return std::make_shared<const Memory>(Memory{
+ .handle = HardwareBufferHandle(hardwareBuffer, /*takeOwnership=*/true),
.size = static_cast<uint32_t>(memory.size),
.name = memory.name,
- };
+ });
}
GeneralResult<Model::OperandValues> unvalidatedConvert(const std::vector<uint8_t>& operandValues) {
@@ -397,24 +511,8 @@
return static_cast<ExecutionPreference>(executionPreference);
}
-GeneralResult<SharedHandle> unvalidatedConvert(
- const ::aidl::android::hardware::common::NativeHandle& aidlNativeHandle) {
- std::vector<base::unique_fd> fds;
- fds.reserve(aidlNativeHandle.fds.size());
- for (const auto& fd : aidlNativeHandle.fds) {
- int dupFd = dup(fd.get());
- if (dupFd == -1) {
- // TODO(b/120417090): is ANEURALNETWORKS_UNEXPECTED_NULL the correct error to return
- // here?
- return NN_ERROR() << "Failed to dup the fd";
- }
- fds.emplace_back(dupFd);
- }
-
- return std::make_shared<const Handle>(Handle{
- .fds = std::move(fds),
- .ints = aidlNativeHandle.ints,
- });
+GeneralResult<SharedHandle> unvalidatedConvert(const NativeHandle& aidlNativeHandle) {
+ return std::make_shared<const Handle>(NN_TRY(unvalidatedConvertHelper(aidlNativeHandle)));
}
GeneralResult<ExecutionPreference> convert(
@@ -422,7 +520,7 @@
return validatedConvert(executionPreference);
}
-GeneralResult<Memory> convert(const aidl_hal::Memory& operand) {
+GeneralResult<SharedMemory> convert(const aidl_hal::Memory& operand) {
return validatedConvert(operand);
}
@@ -454,7 +552,7 @@
return unvalidatedConvert(operations);
}
-GeneralResult<std::vector<Memory>> convert(const std::vector<aidl_hal::Memory>& memories) {
+GeneralResult<std::vector<SharedMemory>> convert(const std::vector<aidl_hal::Memory>& memories) {
return validatedConvert(memories);
}
@@ -507,13 +605,11 @@
return halObject;
}
-} // namespace
-
-nn::GeneralResult<common::NativeHandle> unvalidatedConvert(const nn::SharedHandle& sharedHandle) {
+nn::GeneralResult<common::NativeHandle> unvalidatedConvert(const nn::Handle& handle) {
common::NativeHandle aidlNativeHandle;
- aidlNativeHandle.fds.reserve(sharedHandle->fds.size());
- for (const auto& fd : sharedHandle->fds) {
- int dupFd = dup(fd.get());
+ aidlNativeHandle.fds.reserve(handle.fds.size());
+ for (const auto& fd : handle.fds) {
+ const int dupFd = dup(fd.get());
if (dupFd == -1) {
// TODO(b/120417090): is ANEURALNETWORKS_UNEXPECTED_NULL the correct error to return
// here?
@@ -521,18 +617,71 @@
}
aidlNativeHandle.fds.emplace_back(dupFd);
}
- aidlNativeHandle.ints = sharedHandle->ints;
+ aidlNativeHandle.ints = handle.ints;
return aidlNativeHandle;
}
-nn::GeneralResult<Memory> unvalidatedConvert(const nn::Memory& memory) {
- if (memory.size > std::numeric_limits<int64_t>::max()) {
+static nn::GeneralResult<common::NativeHandle> aidlHandleFromNativeHandle(
+ const native_handle_t& handle) {
+ common::NativeHandle aidlNativeHandle;
+
+ aidlNativeHandle.fds.reserve(handle.numFds);
+ for (int i = 0; i < handle.numFds; ++i) {
+ const int dupFd = dup(handle.data[i]);
+ if (dupFd == -1) {
+ return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Failed to dup the fd";
+ }
+ aidlNativeHandle.fds.emplace_back(dupFd);
+ }
+
+ aidlNativeHandle.ints = std::vector<int>(&handle.data[handle.numFds],
+ &handle.data[handle.numFds + handle.numInts]);
+
+ return aidlNativeHandle;
+}
+
+} // namespace
+
+nn::GeneralResult<common::NativeHandle> unvalidatedConvert(const nn::SharedHandle& sharedHandle) {
+ CHECK(sharedHandle != nullptr);
+ return unvalidatedConvert(*sharedHandle);
+}
+
+nn::GeneralResult<Memory> unvalidatedConvert(const nn::SharedMemory& memory) {
+ CHECK(memory != nullptr);
+ if (memory->size > std::numeric_limits<int64_t>::max()) {
return NN_ERROR() << "Memory size doesn't fit into int64_t.";
}
+ if (const auto* handle = std::get_if<nn::Handle>(&memory->handle)) {
+ return Memory{
+ .handle = NN_TRY(unvalidatedConvert(*handle)),
+ .size = static_cast<int64_t>(memory->size),
+ .name = memory->name,
+ };
+ }
+
+ const auto* ahwb = std::get<nn::HardwareBufferHandle>(memory->handle).get();
+ AHardwareBuffer_Desc bufferDesc;
+ AHardwareBuffer_describe(ahwb, &bufferDesc);
+
+ if (bufferDesc.format == AHARDWAREBUFFER_FORMAT_BLOB) {
+ CHECK_EQ(memory->size, bufferDesc.width);
+ CHECK_EQ(memory->name, "hardware_buffer_blob");
+ } else {
+ CHECK_EQ(memory->size, 0u);
+ CHECK_EQ(memory->name, "hardware_buffer");
+ }
+
+ const native_handle_t* nativeHandle = AHardwareBuffer_getNativeHandle(ahwb);
+ if (nativeHandle == nullptr) {
+ return NN_ERROR() << "unvalidatedConvert failed because AHardwareBuffer_getNativeHandle "
+ "returned nullptr";
+ }
+
return Memory{
- .handle = NN_TRY(unvalidatedConvert(memory.handle)),
- .size = static_cast<int64_t>(memory.size),
- .name = memory.name,
+ .handle = NN_TRY(aidlHandleFromNativeHandle(*nativeHandle)),
+ .size = static_cast<int64_t>(memory->size),
+ .name = memory->name,
};
}
@@ -558,7 +707,7 @@
.isSufficient = outputShape.isSufficient};
}
-nn::GeneralResult<Memory> convert(const nn::Memory& memory) {
+nn::GeneralResult<Memory> convert(const nn::SharedMemory& memory) {
return validatedConvert(memory);
}
diff --git a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
index 86d5f3f..4beb828 100644
--- a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
@@ -266,7 +266,7 @@
copyTestBuffers(constCopies, operandValues.data());
// Shared memory.
- std::vector<nn::Memory> pools = {};
+ std::vector<nn::SharedMemory> pools = {};
if (constRefSize > 0) {
const auto pool = nn::createSharedMemory(constRefSize).value();
pools.push_back(pool);
diff --git a/neuralnetworks/aidl/vts/functional/Utils.cpp b/neuralnetworks/aidl/vts/functional/Utils.cpp
index 14a496a..3c7f5f7 100644
--- a/neuralnetworks/aidl/vts/functional/Utils.cpp
+++ b/neuralnetworks/aidl/vts/functional/Utils.cpp
@@ -135,7 +135,8 @@
ASSERT_EQ(AHardwareBuffer_allocate(&desc, &mAhwb), 0);
ASSERT_NE(mAhwb, nullptr);
- const auto sharedMemory = nn::createSharedMemoryFromAHWB(*mAhwb).value();
+ const auto sharedMemory =
+ nn::createSharedMemoryFromAHWB(mAhwb, /*takeOwnership=*/false).value();
mMapping = nn::map(sharedMemory).value();
mPtr = static_cast<uint8_t*>(std::get<void*>(mMapping.pointer));
CHECK_NE(mPtr, nullptr);
diff --git a/neuralnetworks/utils/common/Android.bp b/neuralnetworks/utils/common/Android.bp
index 6c491ae..50295f1 100644
--- a/neuralnetworks/utils/common/Android.bp
+++ b/neuralnetworks/utils/common/Android.bp
@@ -22,10 +22,12 @@
export_include_dirs: ["include"],
cflags: ["-Wthread-safety"],
static_libs: [
+ "libarect",
"neuralnetworks_types",
],
shared_libs: [
"libhidlbase",
+ "libnativewindow",
],
}
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
index fef9d9c..547f203 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
@@ -74,10 +74,12 @@
std::vector<uint32_t> countNumberOfConsumers(size_t numberOfOperands,
const std::vector<nn::Operation>& operations);
-nn::GeneralResult<nn::Memory> createSharedMemoryFromHidlMemory(const hidl_memory& memory);
+nn::GeneralResult<hidl_memory> createHidlMemoryFromSharedMemory(const nn::SharedMemory& memory);
+nn::GeneralResult<nn::SharedMemory> createSharedMemoryFromHidlMemory(const hidl_memory& memory);
-nn::GeneralResult<hidl_handle> hidlHandleFromSharedHandle(const nn::SharedHandle& handle);
-nn::GeneralResult<nn::SharedHandle> sharedHandleFromNativeHandle(const native_handle_t* handle);
+nn::GeneralResult<hidl_handle> hidlHandleFromSharedHandle(const nn::Handle& handle);
+nn::GeneralResult<nn::Handle> sharedHandleFromNativeHandle(const native_handle_t* handle);
+
nn::GeneralResult<hidl_vec<hidl_handle>> convertSyncFences(
const std::vector<nn::SyncFence>& fences);
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h b/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h
index 95a20a8..209b663 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_HANDLE_ERROR_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_HANDLE_ERROR_H
+
#include <android/hidl/base/1.0/IBase.h>
#include <hidl/HidlSupport.h>
#include <nnapi/Result.h>
@@ -50,7 +53,8 @@
})
template <typename Type>
-nn::GeneralResult<Type> makeGeneralFailure(nn::Result<Type> result, nn::ErrorStatus status) {
+nn::GeneralResult<Type> makeGeneralFailure(
+ nn::Result<Type> result, nn::ErrorStatus status = nn::ErrorStatus::GENERAL_FAILURE) {
if (!result.has_value()) {
return nn::error(status) << std::move(result).error();
}
@@ -75,7 +79,8 @@
}
template <typename Type>
-nn::ExecutionResult<Type> makeExecutionFailure(nn::Result<Type> result, nn::ErrorStatus status) {
+nn::ExecutionResult<Type> makeExecutionFailure(
+ nn::Result<Type> result, nn::ErrorStatus status = nn::ErrorStatus::GENERAL_FAILURE) {
return makeExecutionFailure(makeGeneralFailure(result, status));
}
@@ -86,4 +91,6 @@
} else \
return NN_ERROR(canonical)
-} // namespace android::hardware::neuralnetworks::utils
\ No newline at end of file
+} // namespace android::hardware::neuralnetworks::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_HANDLE_ERROR_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidBuffer.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBuffer.h
index 8c04b88..0e98c2e 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/InvalidBuffer.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBuffer.h
@@ -31,9 +31,9 @@
public:
nn::Request::MemoryDomainToken getToken() const override;
- nn::GeneralResult<void> copyTo(const nn::Memory& dst) const override;
+ nn::GeneralResult<void> copyTo(const nn::SharedMemory& dst) const override;
- nn::GeneralResult<void> copyFrom(const nn::Memory& src,
+ nn::GeneralResult<void> copyFrom(const nn::SharedMemory& src,
const nn::Dimensions& dimensions) const override;
};
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h
index 83e60b6..996858c 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h
@@ -29,7 +29,7 @@
class InvalidBurst final : public nn::IBurst {
public:
- OptionalCacheHold cacheMemory(const nn::Memory& memory) const override;
+ OptionalCacheHold cacheMemory(const nn::SharedMemory& memory) const override;
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
const nn::Request& request, nn::MeasureTiming measure) const override;
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientBuffer.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBuffer.h
index d2c2469..c8ca6f2 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/ResilientBuffer.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBuffer.h
@@ -46,9 +46,9 @@
nn::Request::MemoryDomainToken getToken() const override;
- nn::GeneralResult<void> copyTo(const nn::Memory& dst) const override;
+ nn::GeneralResult<void> copyTo(const nn::SharedMemory& dst) const override;
- nn::GeneralResult<void> copyFrom(const nn::Memory& src,
+ nn::GeneralResult<void> copyFrom(const nn::SharedMemory& src,
const nn::Dimensions& dimensions) const override;
private:
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h
index 0df287f..3b87330 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h
@@ -44,7 +44,7 @@
nn::SharedBurst getBurst() const;
nn::GeneralResult<nn::SharedBurst> recover(const nn::IBurst* failingBurst) const;
- OptionalCacheHold cacheMemory(const nn::Memory& memory) const override;
+ OptionalCacheHold cacheMemory(const nn::SharedMemory& memory) const override;
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
const nn::Request& request, nn::MeasureTiming measure) const override;
diff --git a/neuralnetworks/utils/common/src/CommonUtils.cpp b/neuralnetworks/utils/common/src/CommonUtils.cpp
index c04c8df..7a5035f 100644
--- a/neuralnetworks/utils/common/src/CommonUtils.cpp
+++ b/neuralnetworks/utils/common/src/CommonUtils.cpp
@@ -20,11 +20,14 @@
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
+#include <android/hardware_buffer.h>
+#include <hidl/HidlSupport.h>
#include <nnapi/Result.h>
#include <nnapi/SharedMemory.h>
#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
#include <nnapi/Validation.h>
+#include <vndk/hardware_buffer.h>
#include <algorithm>
#include <any>
@@ -203,13 +206,13 @@
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::Memory>(maybeRequestInShared->pools.back())) {
+ !std::holds_alternative<nn::SharedMemory>(maybeRequestInShared->pools.back())) {
return {};
}
const auto& requestInShared = *maybeRequestInShared;
// Map the memory.
- const auto& outputMemory = std::get<nn::Memory>(requestInShared.pools.back());
+ 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);
@@ -248,44 +251,128 @@
return nn::countNumberOfConsumers(numberOfOperands, operations);
}
-nn::GeneralResult<hidl_handle> hidlHandleFromSharedHandle(const nn::SharedHandle& handle) {
- if (handle == nullptr) {
- return {};
+nn::GeneralResult<hidl_memory> createHidlMemoryFromSharedMemory(const nn::SharedMemory& memory) {
+ if (memory == nullptr) {
+ return NN_ERROR() << "Memory must be non-empty";
+ }
+ if (const auto* handle = std::get_if<nn::Handle>(&memory->handle)) {
+ return hidl_memory(memory->name, NN_TRY(hidlHandleFromSharedHandle(*handle)), memory->size);
}
+ const auto* ahwb = std::get<nn::HardwareBufferHandle>(memory->handle).get();
+ AHardwareBuffer_Desc bufferDesc;
+ AHardwareBuffer_describe(ahwb, &bufferDesc);
+
+ if (bufferDesc.format == AHARDWAREBUFFER_FORMAT_BLOB) {
+ CHECK_EQ(memory->size, bufferDesc.width);
+ CHECK_EQ(memory->name, "hardware_buffer_blob");
+ } else {
+ CHECK_EQ(memory->size, 0u);
+ CHECK_EQ(memory->name, "hardware_buffer");
+ }
+
+ const native_handle_t* nativeHandle = AHardwareBuffer_getNativeHandle(ahwb);
+ const hidl_handle hidlHandle(nativeHandle);
+ hidl_handle handle(hidlHandle);
+
+ return hidl_memory(memory->name, std::move(handle), memory->size);
+}
+
+static uint32_t roundUpToMultiple(uint32_t value, uint32_t multiple) {
+ return (value + multiple - 1) / multiple * multiple;
+}
+
+nn::GeneralResult<nn::SharedMemory> createSharedMemoryFromHidlMemory(const hidl_memory& memory) {
+ CHECK_LE(memory.size(), std::numeric_limits<uint32_t>::max());
+
+ if (memory.name() != "hardware_buffer_blob") {
+ return std::make_shared<const nn::Memory>(nn::Memory{
+ .handle = NN_TRY(sharedHandleFromNativeHandle(memory.handle())),
+ .size = static_cast<uint32_t>(memory.size()),
+ .name = memory.name(),
+ });
+ }
+
+ const auto size = memory.size();
+ const auto format = AHARDWAREBUFFER_FORMAT_BLOB;
+ const auto usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
+ const uint32_t width = size;
+ const uint32_t height = 1; // height is always 1 for BLOB mode AHardwareBuffer.
+ const uint32_t layers = 1; // layers is always 1 for BLOB mode AHardwareBuffer.
+
+ // AHardwareBuffer_createFromHandle() might fail because an allocator
+ // expects a specific stride value. In that case, we try to guess it by
+ // aligning the width to small powers of 2.
+ // TODO(b/174120849): Avoid stride assumptions.
+ AHardwareBuffer* hardwareBuffer = nullptr;
+ status_t status = UNKNOWN_ERROR;
+ for (uint32_t alignment : {1, 4, 32, 64, 128, 2, 8, 16}) {
+ const uint32_t stride = roundUpToMultiple(width, alignment);
+ AHardwareBuffer_Desc desc{
+ .width = width,
+ .height = height,
+ .layers = layers,
+ .format = format,
+ .usage = usage,
+ .stride = stride,
+ };
+ status = AHardwareBuffer_createFromHandle(&desc, memory.handle(),
+ AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE,
+ &hardwareBuffer);
+ if (status == NO_ERROR) {
+ break;
+ }
+ }
+ if (status != NO_ERROR) {
+ return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+ << "Can't create AHardwareBuffer from handle. Error: " << status;
+ }
+
+ return std::make_shared<const nn::Memory>(nn::Memory{
+ .handle = nn::HardwareBufferHandle(hardwareBuffer, /*takeOwnership=*/true),
+ .size = static_cast<uint32_t>(memory.size()),
+ .name = memory.name(),
+ });
+}
+
+nn::GeneralResult<hidl_handle> hidlHandleFromSharedHandle(const nn::Handle& handle) {
std::vector<base::unique_fd> fds;
- fds.reserve(handle->fds.size());
- for (const auto& fd : handle->fds) {
- int dupFd = dup(fd);
+ fds.reserve(handle.fds.size());
+ for (const auto& fd : handle.fds) {
+ const int dupFd = dup(fd);
if (dupFd == -1) {
return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Failed to dup the fd";
}
fds.emplace_back(dupFd);
}
- native_handle_t* nativeHandle = native_handle_create(handle->fds.size(), handle->ints.size());
+ constexpr size_t kIntMax = std::numeric_limits<int>::max();
+ CHECK_LE(handle.fds.size(), kIntMax);
+ CHECK_LE(handle.ints.size(), kIntMax);
+ native_handle_t* nativeHandle = native_handle_create(static_cast<int>(handle.fds.size()),
+ static_cast<int>(handle.ints.size()));
if (nativeHandle == nullptr) {
return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Failed to create native_handle";
}
for (size_t i = 0; i < fds.size(); ++i) {
nativeHandle->data[i] = fds[i].release();
}
- std::copy(handle->ints.begin(), handle->ints.end(), &nativeHandle->data[nativeHandle->numFds]);
+ std::copy(handle.ints.begin(), handle.ints.end(), &nativeHandle->data[nativeHandle->numFds]);
hidl_handle hidlHandle;
hidlHandle.setTo(nativeHandle, /*shouldOwn=*/true);
return hidlHandle;
}
-nn::GeneralResult<nn::SharedHandle> sharedHandleFromNativeHandle(const native_handle_t* handle) {
+nn::GeneralResult<nn::Handle> sharedHandleFromNativeHandle(const native_handle_t* handle) {
if (handle == nullptr) {
- return nullptr;
+ return NN_ERROR() << "sharedHandleFromNativeHandle failed because handle is nullptr";
}
std::vector<base::unique_fd> fds;
fds.reserve(handle->numFds);
for (int i = 0; i < handle->numFds; ++i) {
- int dupFd = dup(handle->data[i]);
+ const int dupFd = dup(handle->data[i]);
if (dupFd == -1) {
return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Failed to dup the fd";
}
@@ -295,18 +382,18 @@
std::vector<int> ints(&handle->data[handle->numFds],
&handle->data[handle->numFds + handle->numInts]);
- return std::make_shared<const nn::Handle>(nn::Handle{
- .fds = std::move(fds),
- .ints = std::move(ints),
- });
+ return nn::Handle{.fds = std::move(fds), .ints = std::move(ints)};
}
nn::GeneralResult<hidl_vec<hidl_handle>> convertSyncFences(
const std::vector<nn::SyncFence>& syncFences) {
hidl_vec<hidl_handle> handles(syncFences.size());
for (size_t i = 0; i < syncFences.size(); ++i) {
- handles[i] =
- NN_TRY(hal::utils::hidlHandleFromSharedHandle(syncFences[i].getSharedHandle()));
+ const auto& handle = syncFences[i].getSharedHandle();
+ if (handle == nullptr) {
+ return NN_ERROR() << "convertSyncFences failed because sync fence is empty";
+ }
+ handles[i] = NN_TRY(hidlHandleFromSharedHandle(*handle));
}
return handles;
}
diff --git a/neuralnetworks/utils/common/src/InvalidBuffer.cpp b/neuralnetworks/utils/common/src/InvalidBuffer.cpp
index c6f75d7..e73001d 100644
--- a/neuralnetworks/utils/common/src/InvalidBuffer.cpp
+++ b/neuralnetworks/utils/common/src/InvalidBuffer.cpp
@@ -30,11 +30,11 @@
return nn::Request::MemoryDomainToken{};
}
-nn::GeneralResult<void> InvalidBuffer::copyTo(const nn::Memory& /*dst*/) const {
+nn::GeneralResult<void> InvalidBuffer::copyTo(const nn::SharedMemory& /*dst*/) const {
return NN_ERROR() << "InvalidBuffer";
}
-nn::GeneralResult<void> InvalidBuffer::copyFrom(const nn::Memory& /*src*/,
+nn::GeneralResult<void> InvalidBuffer::copyFrom(const nn::SharedMemory& /*src*/,
const nn::Dimensions& /*dimensions*/) const {
return NN_ERROR() << "InvalidBuffer";
}
diff --git a/neuralnetworks/utils/common/src/InvalidBurst.cpp b/neuralnetworks/utils/common/src/InvalidBurst.cpp
index 4ca6603..81ca18d 100644
--- a/neuralnetworks/utils/common/src/InvalidBurst.cpp
+++ b/neuralnetworks/utils/common/src/InvalidBurst.cpp
@@ -26,7 +26,8 @@
namespace android::hardware::neuralnetworks::utils {
-InvalidBurst::OptionalCacheHold InvalidBurst::cacheMemory(const nn::Memory& /*memory*/) const {
+InvalidBurst::OptionalCacheHold InvalidBurst::cacheMemory(
+ const nn::SharedMemory& /*memory*/) const {
return nullptr;
}
diff --git a/neuralnetworks/utils/common/src/ResilientBuffer.cpp b/neuralnetworks/utils/common/src/ResilientBuffer.cpp
index 47abbe2..1904375 100644
--- a/neuralnetworks/utils/common/src/ResilientBuffer.cpp
+++ b/neuralnetworks/utils/common/src/ResilientBuffer.cpp
@@ -99,12 +99,12 @@
return getBuffer()->getToken();
}
-nn::GeneralResult<void> ResilientBuffer::copyTo(const nn::Memory& dst) const {
+nn::GeneralResult<void> ResilientBuffer::copyTo(const nn::SharedMemory& dst) const {
const auto fn = [&dst](const nn::IBuffer& buffer) { return buffer.copyTo(dst); };
return protect(*this, fn);
}
-nn::GeneralResult<void> ResilientBuffer::copyFrom(const nn::Memory& src,
+nn::GeneralResult<void> ResilientBuffer::copyFrom(const nn::SharedMemory& src,
const nn::Dimensions& dimensions) const {
const auto fn = [&src, &dimensions](const nn::IBuffer& buffer) {
return buffer.copyFrom(src, dimensions);
diff --git a/neuralnetworks/utils/common/src/ResilientBurst.cpp b/neuralnetworks/utils/common/src/ResilientBurst.cpp
index 0d3cb33..5ca868b 100644
--- a/neuralnetworks/utils/common/src/ResilientBurst.cpp
+++ b/neuralnetworks/utils/common/src/ResilientBurst.cpp
@@ -94,7 +94,8 @@
return mBurst;
}
-ResilientBurst::OptionalCacheHold ResilientBurst::cacheMemory(const nn::Memory& memory) const {
+ResilientBurst::OptionalCacheHold ResilientBurst::cacheMemory(
+ const nn::SharedMemory& memory) const {
return getBurst()->cacheMemory(memory);
}
diff --git a/neuralnetworks/utils/common/test/MockBuffer.h b/neuralnetworks/utils/common/test/MockBuffer.h
index c5405fb..59d5700 100644
--- a/neuralnetworks/utils/common/test/MockBuffer.h
+++ b/neuralnetworks/utils/common/test/MockBuffer.h
@@ -27,9 +27,9 @@
class MockBuffer final : public IBuffer {
public:
MOCK_METHOD(Request::MemoryDomainToken, getToken, (), (const, override));
- MOCK_METHOD(GeneralResult<void>, copyTo, (const Memory& dst), (const, override));
- MOCK_METHOD(GeneralResult<void>, copyFrom, (const Memory& src, const Dimensions& dimensions),
- (const, override));
+ MOCK_METHOD(GeneralResult<void>, copyTo, (const SharedMemory& dst), (const, override));
+ MOCK_METHOD(GeneralResult<void>, copyFrom,
+ (const SharedMemory& src, const Dimensions& dimensions), (const, override));
};
} // namespace android::nn
diff --git a/neuralnetworks/utils/common/test/ResilientBufferTest.cpp b/neuralnetworks/utils/common/test/ResilientBufferTest.cpp
index deb9b7c..7afd020 100644
--- a/neuralnetworks/utils/common/test/ResilientBufferTest.cpp
+++ b/neuralnetworks/utils/common/test/ResilientBufferTest.cpp
@@ -15,9 +15,11 @@
*/
#include <gmock/gmock.h>
+#include <nnapi/SharedMemory.h>
#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
#include <nnapi/hal/ResilientBuffer.h>
+#include <memory>
#include <tuple>
#include <utility>
#include "MockBuffer.h"
@@ -113,7 +115,8 @@
EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(Return(kNoError));
// run test
- const auto result = buffer->copyTo({});
+ const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+ const auto result = buffer->copyTo(memory);
// verify result
ASSERT_TRUE(result.has_value())
@@ -126,7 +129,8 @@
EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(kReturnGeneralFailure);
// run test
- const auto result = buffer->copyTo({});
+ const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+ const auto result = buffer->copyTo(memory);
// verify result
ASSERT_FALSE(result.has_value());
@@ -140,7 +144,8 @@
EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
// run test
- const auto result = buffer->copyTo({});
+ const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+ const auto result = buffer->copyTo(memory);
// verify result
ASSERT_FALSE(result.has_value());
@@ -156,7 +161,8 @@
EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(Return(recoveredMockBuffer));
// run test
- const auto result = buffer->copyTo({});
+ const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+ const auto result = buffer->copyTo(memory);
// verify result
ASSERT_TRUE(result.has_value())
@@ -169,7 +175,8 @@
EXPECT_CALL(*mockBuffer, copyFrom(_, _)).Times(1).WillOnce(Return(kNoError));
// run test
- const auto result = buffer->copyFrom({}, {});
+ const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+ const auto result = buffer->copyFrom(memory, {});
// verify result
ASSERT_TRUE(result.has_value())
@@ -182,7 +189,8 @@
EXPECT_CALL(*mockBuffer, copyFrom(_, _)).Times(1).WillOnce(kReturnGeneralFailure);
// run test
- const auto result = buffer->copyFrom({}, {});
+ const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+ const auto result = buffer->copyFrom(memory, {});
// verify result
ASSERT_FALSE(result.has_value());
@@ -196,7 +204,8 @@
EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
// run test
- const auto result = buffer->copyFrom({}, {});
+ const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+ const auto result = buffer->copyFrom(memory, {});
// verify result
ASSERT_FALSE(result.has_value());
@@ -212,7 +221,8 @@
EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(Return(recoveredMockBuffer));
// run test
- const auto result = buffer->copyFrom({}, {});
+ const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+ const auto result = buffer->copyFrom(memory, {});
// verify result
ASSERT_TRUE(result.has_value())
diff --git a/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/EnergyConsumer.aidl b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/EnergyConsumer.aidl
index c8d7645..cd9239e 100644
--- a/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/EnergyConsumer.aidl
+++ b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/EnergyConsumer.aidl
@@ -35,6 +35,6 @@
parcelable EnergyConsumer {
int id;
int ordinal;
- android.hardware.power.stats.EnergyConsumerType type;
+ android.hardware.power.stats.EnergyConsumerType type = android.hardware.power.stats.EnergyConsumerType.OTHER;
@utf8InCpp String name;
}
diff --git a/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/EnergyConsumerType.aidl b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/EnergyConsumerType.aidl
index 7b05d2f..ce3e1f5 100644
--- a/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/EnergyConsumerType.aidl
+++ b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/EnergyConsumerType.aidl
@@ -34,6 +34,10 @@
@VintfStability
enum EnergyConsumerType {
OTHER = 0,
- CPU_CLUSTER = 1,
- DISPLAY = 2,
+ BLUETOOTH = 1,
+ CPU_CLUSTER = 2,
+ DISPLAY = 3,
+ GNSS = 4,
+ MOBILE_RADIO = 5,
+ WIFI = 6,
}
diff --git a/power/stats/aidl/android/hardware/power/stats/EnergyConsumer.aidl b/power/stats/aidl/android/hardware/power/stats/EnergyConsumer.aidl
index 2ff1279..ec616f2 100644
--- a/power/stats/aidl/android/hardware/power/stats/EnergyConsumer.aidl
+++ b/power/stats/aidl/android/hardware/power/stats/EnergyConsumer.aidl
@@ -32,10 +32,10 @@
int ordinal;
/* Type of this EnergyConsumer */
- EnergyConsumerType type;
+ EnergyConsumerType type = EnergyConsumerType.OTHER;
/**
* Unique name of this EnergyConsumer. Vendor/device specific. Opaque to framework
*/
@utf8InCpp String name;
-}
\ No newline at end of file
+}
diff --git a/power/stats/aidl/android/hardware/power/stats/EnergyConsumerType.aidl b/power/stats/aidl/android/hardware/power/stats/EnergyConsumerType.aidl
index 7fd2348..d871ced 100644
--- a/power/stats/aidl/android/hardware/power/stats/EnergyConsumerType.aidl
+++ b/power/stats/aidl/android/hardware/power/stats/EnergyConsumerType.aidl
@@ -20,6 +20,10 @@
@VintfStability
enum EnergyConsumerType {
OTHER,
+ BLUETOOTH,
CPU_CLUSTER,
DISPLAY,
-}
\ No newline at end of file
+ GNSS,
+ MOBILE_RADIO,
+ WIFI,
+}
diff --git a/radio/1.6/IRadio.hal b/radio/1.6/IRadio.hal
index 32f8b0b..714be47 100644
--- a/radio/1.6/IRadio.hal
+++ b/radio/1.6/IRadio.hal
@@ -465,7 +465,7 @@
* cell information isn't known then the appropriate unknown value will be returned.
* This does not cause or change the rate of unsolicited cellInfoList().
*
- * This is identitcal to getCellInfoList in V1.0, but it requests updated version of CellInfo.
+ * This is identical to getCellInfoList in V1.0, but it requests updated version of CellInfo.
*
* @param serial Serial number of request.
*
@@ -521,4 +521,20 @@
* Response function is IRadioResponse.getSlicingConfigResponse()
*/
oneway getSlicingConfig(int32_t serial);
+
+ /**
+ * Provide Carrier specific information to the modem that must be used to
+ * encrypt the IMSI and IMPI. Sent by the framework during boot, carrier
+ * switch and everytime the framework receives a new certificate.
+ *
+ * @param serial Serial number of request.
+ * @param imsiEncryptionInfo ImsiEncryptionInfo as defined in types.hal.
+ *
+ * Response callback is
+ * IRadioResponse.setCarrierInfoForImsiEncryptionResponse()
+ *
+ * Note this API is the same as the 1.1 version except using the 1.6 ImsiEncryptionInfo
+ * as the input param.
+ */
+ oneway setCarrierInfoForImsiEncryption_1_6(int32_t serial, @1.6::ImsiEncryptionInfo imsiEncryptionInfo);
};
diff --git a/radio/1.6/IRadioResponse.hal b/radio/1.6/IRadioResponse.hal
index 6ad5cf2..56ce809 100644
--- a/radio/1.6/IRadioResponse.hal
+++ b/radio/1.6/IRadioResponse.hal
@@ -19,6 +19,7 @@
import @1.0::SendSmsResult;
import @1.4::RadioAccessFamily;
import @1.5::IRadioResponse;
+import @1.5::RadioAccessSpecifier;
import @1.6::Call;
import @1.6::CellInfo;
import @1.6::RegStateResult;
@@ -344,6 +345,7 @@
/**
* @param info Response info struct containing response type, serial no. and error
+ * @param specifiers List of RadioAccessSpecifiers that are scanned.
*
* Valid errors returned:
* RadioError:NONE
@@ -351,7 +353,8 @@
* RadioError:INTERNAL_ERR
* RadioError:INVALID_ARGUMENTS
*/
- oneway getSystemSelectionChannelsResponse(RadioResponseInfo info);
+ oneway getSystemSelectionChannelsResponse(
+ RadioResponseInfo info, vec<RadioAccessSpecifier> specifiers);
/**
* This is identical to getCellInfoListResponse_1_5 but uses an updated version of CellInfo.
diff --git a/radio/1.6/types.hal b/radio/1.6/types.hal
index 6c23650..5d363c9 100644
--- a/radio/1.6/types.hal
+++ b/radio/1.6/types.hal
@@ -27,6 +27,7 @@
import @1.1::GeranBands;
import @1.1::ScanStatus;
import @1.1::UtranBands;
+import @1.1::ImsiEncryptionInfo;
import @1.2::Call;
import @1.2::CellInfoCdma;
import @1.2::CellConnectionStatus;
@@ -1115,3 +1116,20 @@
MODE_2 = 2,
MODE_3 = 3,
};
+
+/**
+ * Public key type from carrier certificate.
+ */
+enum PublicKeyType : int32_t {
+ EPDG = 1, // Key type to be used for ePDG
+ WLAN = 2, // Key type to be used for WLAN
+};
+
+/**
+ * Carrier specific Information sent by the carrier,
+ * which will be used to encrypt the IMSI and IMPI.
+ */
+struct ImsiEncryptionInfo {
+ @1.1::ImsiEncryptionInfo base;
+ PublicKeyType keyType; // Public key type
+};
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 07b8ccb..fb50990 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
@@ -676,3 +676,29 @@
EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
EXPECT_EQ(::android::hardware::radio::V1_6::RadioError::NONE, radioRsp_v1_6->rspInfo.error);
}
+
+/*
+ * Test IRadio.setCarrierInfoForImsiEncryption_1_6() for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, setCarrierInfoForImsiEncryption_1_6) {
+ serial = GetRandomSerialNumber();
+ ::android::hardware::radio::V1_6::ImsiEncryptionInfo imsiInfo;
+ imsiInfo.base.mcc = "310";
+ imsiInfo.base.mnc = "004";
+ imsiInfo.base.carrierKey = (std::vector<uint8_t>){1, 2, 3, 4, 5, 6};
+ imsiInfo.base.keyIdentifier = "Test";
+ imsiInfo.base.expirationTime = 20180101;
+ imsiInfo.keyType = PublicKeyType::EPDG;
+
+ radio_v1_6->setCarrierInfoForImsiEncryption_1_6(serial, imsiInfo);
+ 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 (cardStatus.base.base.base.cardState == CardState::ABSENT) {
+ ASSERT_TRUE(CheckAnyOfErrors(
+ radioRsp_v1_6->rspInfo.error,
+ {::android::hardware::radio::V1_6::RadioError::NONE,
+ ::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
+ }
+}
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 f32e312..f610f2a 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
@@ -805,7 +805,8 @@
const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
Return<void> getSystemSelectionChannelsResponse(
- const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
+ const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+ const hidl_vec<::android::hardware::radio::V1_5::RadioAccessSpecifier>& specifier);
Return<void> getSignalStrengthResponse_1_6(
const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
diff --git a/radio/1.6/vts/functional/radio_response.cpp b/radio/1.6/vts/functional/radio_response.cpp
index fad3f12..027e9ac 100644
--- a/radio/1.6/vts/functional/radio_response.cpp
+++ b/radio/1.6/vts/functional/radio_response.cpp
@@ -1190,7 +1190,8 @@
}
Return<void> RadioResponse_v1_6::getSystemSelectionChannelsResponse(
- const ::android::hardware::radio::V1_6::RadioResponseInfo& info) {
+ const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+ const hidl_vec<::android::hardware::radio::V1_5::RadioAccessSpecifier>& /*specifier*/) {
rspInfo = info;
parent_v1_6.notify(info.serial);
return Void();
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/AttestationKey.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/AttestationKey.aidl
new file mode 100644
index 0000000..893b016
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/AttestationKey.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
+// 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;
+@RustDerive(Clone=true, Eq=true, Hash=true, Ord=true, PartialEq=true, PartialOrd=true) @VintfStability
+parcelable AttestationKey {
+ byte[] keyBlob;
+ android.hardware.security.keymint.KeyParameter[] attestKeyParams;
+ byte[] issuerSubjectName;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl
index a35b46c..3faba48 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl
@@ -113,6 +113,8 @@
UNSUPPORTED_MGF_DIGEST = -79,
MISSING_NOT_BEFORE = -80,
MISSING_NOT_AFTER = -81,
+ MISSING_ISSUER_SUBJECT = -82,
+ INVALID_ISSUER_SUBJECT = -83,
UNIMPLEMENTED = -100,
VERSION_MISMATCH = -101,
UNKNOWN_ERROR = -1000,
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
index 132135b..d3c6910 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
@@ -35,13 +35,15 @@
interface IKeyMintDevice {
android.hardware.security.keymint.KeyMintHardwareInfo getHardwareInfo();
void addRngEntropy(in byte[] data);
- android.hardware.security.keymint.KeyCreationResult generateKey(in android.hardware.security.keymint.KeyParameter[] keyParams);
- android.hardware.security.keymint.KeyCreationResult importKey(in android.hardware.security.keymint.KeyParameter[] keyParams, in android.hardware.security.keymint.KeyFormat keyFormat, in byte[] keyData);
+ android.hardware.security.keymint.KeyCreationResult generateKey(in android.hardware.security.keymint.KeyParameter[] keyParams, in @nullable android.hardware.security.keymint.AttestationKey attestationKey);
+ android.hardware.security.keymint.KeyCreationResult importKey(in android.hardware.security.keymint.KeyParameter[] keyParams, in android.hardware.security.keymint.KeyFormat keyFormat, in byte[] keyData, in @nullable android.hardware.security.keymint.AttestationKey attestationKey);
android.hardware.security.keymint.KeyCreationResult importWrappedKey(in byte[] wrappedKeyData, in byte[] wrappingKeyBlob, in byte[] maskingKey, in android.hardware.security.keymint.KeyParameter[] unwrappingParams, in long passwordSid, in long biometricSid);
- byte[] upgradeKey(in byte[] inKeyBlobToUpgrade, in android.hardware.security.keymint.KeyParameter[] inUpgradeParams);
- void deleteKey(in byte[] inKeyBlob);
+ byte[] upgradeKey(in byte[] keyBlobToUpgrade, in android.hardware.security.keymint.KeyParameter[] upgradeParams);
+ void deleteKey(in byte[] keyBlob);
void deleteAllKeys();
void destroyAttestationIds();
- android.hardware.security.keymint.BeginResult begin(in android.hardware.security.keymint.KeyPurpose inPurpose, in byte[] inKeyBlob, in android.hardware.security.keymint.KeyParameter[] inParams, in android.hardware.security.keymint.HardwareAuthToken inAuthToken);
+ 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);
+ void deviceLocked(in boolean passwordOnly, in @nullable android.hardware.security.secureclock.TimeStampToken timestampToken);
+ void earlyBootEnded();
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
new file mode 100644
index 0000000..a864c3c
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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;
+@VintfStability
+interface IRemotelyProvisionedComponent {
+ byte[] generateEcdsaP256KeyPair(in boolean testMode, out android.hardware.security.keymint.MacedPublicKey macedPublicKey);
+ void generateCertificateRequest(in boolean testMode, in android.hardware.security.keymint.MacedPublicKey[] keysToSign, in byte[] endpointEncryptionCertChain, in byte[] challenge, out byte[] keysToSignMac, out android.hardware.security.keymint.ProtectedData protectedData);
+ const int STATUS_FAILED = 1;
+ const int STATUS_INVALID_MAC = 2;
+ const int STATUS_PRODUCTION_KEY_IN_TEST_REQUEST = 3;
+ const int STATUS_TEST_KEY_IN_PRODUCTION_REQUEST = 4;
+ const int STATUS_INVALID_EEK = 5;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyMintHardwareInfo.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
index 93966ea..d06312a 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
@@ -37,4 +37,5 @@
android.hardware.security.keymint.SecurityLevel securityLevel;
@utf8InCpp String keyMintName;
@utf8InCpp String keyMintAuthorName;
+ boolean timestampTokenRequired;
}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyPurpose.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyPurpose.aidl
index c1e92af..61bb7e4 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyPurpose.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyPurpose.aidl
@@ -39,4 +39,5 @@
VERIFY = 3,
WRAP_KEY = 5,
AGREE_KEY = 6,
+ ATTEST_KEY = 7,
}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/MacedPublicKey.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/MacedPublicKey.aidl
new file mode 100644
index 0000000..b4caeed
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/MacedPublicKey.aidl
@@ -0,0 +1,37 @@
+/*
+ * 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;
+@VintfStability
+parcelable MacedPublicKey {
+ byte[] macedKey;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ProtectedData.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ProtectedData.aidl
new file mode 100644
index 0000000..46f602f
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ProtectedData.aidl
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
+// 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;
+@VintfStability
+parcelable ProtectedData {
+ byte[] protectedData;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/AttestationKey.aidl b/security/keymint/aidl/android/hardware/security/keymint/AttestationKey.aidl
new file mode 100644
index 0000000..8167ceb
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/AttestationKey.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+import android.hardware.security.keymint.KeyParameter;
+
+/**
+ * Contains a key blob with Tag::ATTEST_KEY that can be used to sign an attestation certificate,
+ * and the DER-encoded X.501 Subject Name that will be placed in the Issuer field of the attestation
+ * certificate.
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable AttestationKey {
+ byte[] keyBlob;
+ KeyParameter[] attestKeyParams;
+ byte[] issuerSubjectName;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl b/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl
index 35e3827..5765130 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl
@@ -103,6 +103,8 @@
UNSUPPORTED_MGF_DIGEST = -79,
MISSING_NOT_BEFORE = -80,
MISSING_NOT_AFTER = -81,
+ MISSING_ISSUER_SUBJECT = -82,
+ INVALID_ISSUER_SUBJECT = -83,
UNIMPLEMENTED = -100,
VERSION_MISMATCH = -101,
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
index 0120a30..13e98af 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -16,16 +16,18 @@
package android.hardware.security.keymint;
+import android.hardware.security.keymint.AttestationKey;
import android.hardware.security.keymint.BeginResult;
import android.hardware.security.keymint.ByteArray;
import android.hardware.security.keymint.HardwareAuthToken;
import android.hardware.security.keymint.IKeyMintOperation;
import android.hardware.security.keymint.KeyCreationResult;
import android.hardware.security.keymint.KeyFormat;
-import android.hardware.security.keymint.KeyParameter;
import android.hardware.security.keymint.KeyMintHardwareInfo;
+import android.hardware.security.keymint.KeyParameter;
import android.hardware.security.keymint.KeyPurpose;
import android.hardware.security.keymint.SecurityLevel;
+import android.hardware.security.secureclock.TimeStampToken;
/**
* KeyMint device definition.
@@ -314,9 +316,18 @@
* provided in params. See above for detailed specifications of which tags are required
* for which types of keys.
*
+ * @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
+ * but `attestationKey` is non-null, the IKeyMintDevice must return
+ * ErrorCode::INVALID_ARGUMENT. If the provided AttestationKey does not contain a key
+ * blob containing an asymmetric key with KeyPurpose::ATTEST_KEY, the IKeyMintDevice must
+ * return ErrorCode::INVALID_PURPOSE. If the provided AttestationKey has an empty issuer
+ * subject name, the IKeyMintDevice must return ErrorCode::INVALID_ARGUMENT.
+ *
* @return The result of key creation. See KeyCreationResult.aidl.
*/
- KeyCreationResult generateKey(in KeyParameter[] keyParams);
+ KeyCreationResult generateKey(
+ in KeyParameter[] keyParams, in @nullable AttestationKey attestationKey);
/**
* Imports key material into an IKeyMintDevice. Key definition parameters and return values
@@ -344,10 +355,18 @@
*
* @param inKeyData 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
+ * but `attestationKey` is non-null, the IKeyMintDevice must return
+ * ErrorCode::INVALID_ARGUMENT. If the provided AttestationKey does not contain a key
+ * blob containing an asymmetric key with KeyPurpose::ATTEST_KEY, the IKeyMintDevice must
+ * return ErrorCode::INVALID_PURPOSE. If the provided AttestationKey has an empty issuer
+ * subject name, the IKeyMintDevice must return ErrorCode::INVALID_ARGUMENT.
+ *
* @return The result of key creation. See KeyCreationResult.aidl.
*/
KeyCreationResult importKey(in KeyParameter[] keyParams, in KeyFormat keyFormat,
- in byte[] keyData);
+ in byte[] keyData, in @nullable AttestationKey attestationKey);
/**
* Securely imports a key, or key pair, returning a key blob and a description of the imported
@@ -429,12 +448,9 @@
*
* @return The result of key creation. See KeyCreationResult.aidl.
*/
- KeyCreationResult importWrappedKey(in byte[] wrappedKeyData,
- in byte[] wrappingKeyBlob,
- in byte[] maskingKey,
- in KeyParameter[] unwrappingParams,
- in long passwordSid,
- in long biometricSid);
+ KeyCreationResult importWrappedKey(in byte[] wrappedKeyData, in byte[] wrappingKeyBlob,
+ in byte[] maskingKey, in KeyParameter[] unwrappingParams, in long passwordSid,
+ in long biometricSid);
/**
* Upgrades an old key blob. Keys can become "old" in two ways: IKeyMintDevice can be
@@ -469,7 +485,7 @@
* @return A new key blob that references the same key as keyBlobToUpgrade, but is in the new
* format, or has the new version data.
*/
- byte[] upgradeKey(in byte[] inKeyBlobToUpgrade, in KeyParameter[] inUpgradeParams);
+ byte[] upgradeKey(in byte[] keyBlobToUpgrade, in KeyParameter[] upgradeParams);
/**
* Deletes the key, or key pair, associated with the key blob. Calling this function on
@@ -479,7 +495,7 @@
*
* @param inKeyBlob The opaque descriptor returned by generateKey() or importKey();
*/
- void deleteKey(in byte[] inKeyBlob);
+ void deleteKey(in byte[] keyBlob);
/**
* Deletes all keys in the hardware keystore. Used when keystore is reset completely. After
@@ -705,8 +721,44 @@
* from operations that generate an IV or nonce, and IKeyMintOperation object pointer
* which is used to perform update(), finish() or abort() operations.
*/
- BeginResult begin(in KeyPurpose inPurpose,
- in byte[] inKeyBlob,
- in KeyParameter[] inParams,
- in HardwareAuthToken inAuthToken);
+ BeginResult begin(in KeyPurpose purpose, in byte[] keyBlob, in KeyParameter[] params,
+ in HardwareAuthToken authToken);
+
+ /**
+ * Called by client to notify the IKeyMintDevice that the device is now locked, and keys with
+ * the UNLOCKED_DEVICE_REQUIRED tag should no longer be usable. When this function is called,
+ * the IKeyMintDevice should note the current timestamp, and attempts to use
+ * UNLOCKED_DEVICE_REQUIRED keys must be rejected with Error::DEVICE_LOCKED until an
+ * authentication token with a later timestamp is presented. If the `passwordOnly' argument is
+ * set to true the sufficiently-recent authentication token must indicate that the user
+ * authenticated with a password, not a biometric.
+ *
+ * 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
+ * IKeyMintDevice perspective, any user unlock unlocks all UNLOCKED_DEVICE_REQUIRED keys.
+ * Keystore will continue enforcing the per-user device locking.
+ *
+ * @param passwordOnly specifies whether the device must be unlocked with a password, rather
+ * than a biometric, before UNLOCKED_DEVICE_REQUIRED keys can be used.
+ *
+ * @param timestampToken is used by StrongBox implementations of IKeyMintDevice. It
+ * provides the StrongBox IKeyMintDevice with a fresh, MACed timestamp which it can use as the
+ * device-lock time, for future comparison against auth tokens when operations using
+ * UNLOCKED_DEVICE_REQUIRED keys are attempted. Unless the auth token timestamp is newer than
+ * the timestamp in the timestampToken, the device is still considered to be locked.
+ * Crucially, if a StrongBox IKeyMintDevice receives a deviceLocked() call with a timestampToken
+ * timestamp that is less than the timestamp in the last deviceLocked() call, it must ignore the
+ * new timestamp. TEE IKeyMintDevice implementations will receive an empty timestampToken (zero
+ * values and empty vectors) and should use their own clock as the device-lock time.
+ */
+ void deviceLocked(in boolean passwordOnly, in @nullable TimeStampToken timestampToken);
+
+ /**
+ * Called by client to notify the IKeyMintDevice that the device has left the early boot
+ * state, and that keys with the EARLY_BOOT_ONLY tag may no longer be used. All attempts to use
+ * an EARLY_BOOT_ONLY key after this method is called must fail with Error::INVALID_KEY_BLOB.
+ */
+ void earlyBootEnded();
}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl b/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
new file mode 100644
index 0000000..1b09e9d
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
@@ -0,0 +1,262 @@
+/*
+ * 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;
+
+import android.hardware.security.keymint.MacedPublicKey;
+import android.hardware.security.keymint.ProtectedData;
+
+/**
+ * An IRemotelyProvisionedComponent is a secure-side component for which certificates can be
+ * remotely provisioned. It provides an interface for generating asymmetric key pairs and then
+ * creating a CertificateRequest that contains the generated public keys, plus other information to
+ * authenticate the request origin. The CertificateRequest can be sent to a server, which can
+ * validate the request and create certificates.
+ *
+ * This interface does not provide any way to use the generated and certified key pairs. It's
+ * intended to be implemented by a HAL service that does other things with keys (e.g. Keymint).
+ *
+ * The root of trust for secure provisioning is something called the "Boot Certificate Chain", or
+ * BCC. The BCC is a chain of public key certificates, represented as COSE_Sign1 objects containing
+ * COSE_Key representations of the public keys. The "root" of the BCC is a self-signed certificate
+ * for a device-unique public key, denoted DK_pub. All public keys in the BCC are device-unique. The
+ * public key from each certificate in the chain is used to sign the next certificate in the
+ * chain. The final, "leaf" certificate contains a public key, denoted KM_pub, whose corresponding
+ * private key, denoted KM_priv, is available for use by the IRemotelyProvisionedComponent.
+ *
+ * BCC Design
+ * ==========
+ *
+ * The BCC is designed to mirror the boot stages of a device, and to prove the content and integrity
+ * of each firmware image. In a proper BCC, each boot stage hashes its own private key with the code
+ * and any relevant configuration parameters of the next stage to produce a key pair for the next
+ * stage. Each stage also uses its own private key to sign the public key of the next stage,
+ * including in the certificate the hash of the next firmware stage, then loads the next stage,
+ * passing the private key and certificate to it in a manner that does not leak the private key to
+ * later boot stages. The BCC root key pair is generated by immutable code (e.g. ROM), from a
+ * device-unique secret. After the device-unique secret is used, it must be made unavailable to any
+ * later boot stage.
+ *
+ * In this way, booting the device incrementally builds a certificate chain that (a) identifies and
+ * validates the integrity of every stage and (b) contains a set of public keys that correspond to
+ * private keys, one known to each stage. Any stage can compute the secrets of all later stages
+ * (given the necessary input), but no stage can compute the secret of any preceding stage. Updating
+ * the firmware or configuration of any stage changes the key pair of that stage, and of all
+ * subsequent stages, and no attacker who compromised the previous version of the updated firmware
+ * can know or predict the post-update key pairs.
+ *
+ * The first BCC certificate is special because its contained public key, DK_pub, will never change,
+ * making it a permanent, device-unique identifier. Although the remaining keys in the BCC are also
+ * device-unique, they are not necessarily permanent, since they can change when the device software
+ * is updated.
+ *
+ * When the provisioning server receives a message signed by KM_priv and containing a BCC that
+ * chains from DK_pub to KM_pub, it can be certain that (barring vulnerabilities in some boot
+ * stage), the CertificateRequest came from the device associated with DK_pub, running the specific
+ * software identified by the certificates in the BCC. If the server has some mechanism for knowing
+ * which the DK_pub values of "valid" devices, it can determine whether signing certificates is
+ * appropriate.
+ *
+ * Degenerate BCCs
+ * ===============
+ *
+ * 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
+ * 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.
+ *
+ * BCC Privacy
+ * ===========
+ *
+ * Because the BCC constitutes an unspoofable, device-unique identifier, special care is taken to
+ * prevent its availability to entities who may wish to track devices. Two precautions are taken:
+ *
+ * 1. The BCC is never exported from the IRemotelyProvisionedComponent except in encrypted
+ * form. The portion of the CertificateRequest that contains the BCC is encrypted using an
+ * Endpoint Encryption Key (EEK). The EEK is provided in the form of a certificate chain whose
+ * root must be pre-provisioned into the secure area (hardcoding the roots into the secure area
+ * firmware image is a recommended approach). Multiple roots may be provisioned. If the provided
+ * EEK does not chain back to this already-known root, the IRemotelyProvisionedComponent must
+ * reject it.
+ *
+ * 2. Precaution 1 above ensures that only an entity with a valid EEK private key can decrypt the
+ * BCC. To make it feasible to build a provisioning server which cannot use the BCC to track
+ * devices, the CertificateRequest is structured so that the server can be partitioned into two
+ * components. The "decrypter" decrypts the BCC, verifies DK_pub and the device's right to
+ * receive provisioned certificates, but does not see the public keys to be signed or the
+ * resulting certificates. The "certifier" gets informed of the results of the decrypter's
+ * validation and sees the public keys to be signed and resulting certificates, but does not see
+ * the BCC.
+ *
+ * Test Mode
+ * =========
+ *
+ * The IRemotelyProvisionedComponent supports a test mode, allowing the generation of test key pairs
+ * and test CertificateRequests. Test keys/requests are annotated as such, and the BCC used for test
+ * CertificateRequests must contain freshly-generated keys, not the real BCC key pairs.
+ */
+@VintfStability
+interface IRemotelyProvisionedComponent {
+ const int STATUS_FAILED = 1;
+ const int STATUS_INVALID_MAC = 2;
+ const int STATUS_PRODUCTION_KEY_IN_TEST_REQUEST = 3;
+ const int STATUS_TEST_KEY_IN_PRODUCTION_REQUEST = 4;
+ const int STATUS_INVALID_EEK = 5;
+
+ /**
+ * 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.
+ *
+ * @param in boolean testMode indicates whether the generated key is for testing only. Test keys
+ * are marked (see the definition of PublicKey in the MacedPublicKey structure) to
+ * prevent them from being confused with production keys.
+ *
+ * @param out MacedPublicKey macedPublicKey contains the public key of the generated key pair,
+ * MACed so that generateCertificateRequest can easily verify, without the
+ * privateKeyHandle, that the contained public key is for remote certification.
+ *
+ * @return data representing a handle to the private key. The format is implementation-defined,
+ * but note that specific services may define a required format.
+ */
+ byte[] generateEcdsaP256KeyPair(in boolean testMode, out MacedPublicKey macedPublicKey);
+
+ /**
+ * generateCertificateRequest creates a certificate request to be sent to the provisioning
+ * server.
+ *
+ * @param in boolean testMode indicates whether the generated certificate request is for testing
+ * only.
+ *
+ * @param in MacedPublicKey[] keysToSign contains the set of keys to certify. The
+ * IRemotelyProvisionedComponent must validate the MACs on each key. If any entry in the
+ * array lacks a valid MAC, the method must return STATUS_INVALID_MAC.
+ *
+ * If testMode is true, the keysToCertify array must contain only keys flagged as test
+ * keys. Otherwise, the method must return STATUS_PRODUCTION_KEY_IN_TEST_REQUEST.
+ *
+ * If testMode is false, the keysToCertify array must not contain any keys flagged as
+ * test keys. Otherwise, the method must return STATUS_TEST_KEY_IN_PRODUCTION_REQUEST.
+ *
+ * @param in endpointEncryptionKey contains an X25519 public key which will be used to encrypt
+ * the BCC. For flexibility, this is represented as a certificate chain, represented as a
+ * CBOR array of COSE_Sign1 objects, ordered from root to leaf. The leaf contains the
+ * X25519 encryption key, each other element is an Ed25519 key signing the next in the
+ * chain. The root is self-signed.
+ *
+ * EekChain = [ + SignedSignatureKey, SignedEek ]
+ *
+ * SignedSignatureKey = [ // COSE_Sign1
+ * protected: bstr .cbor {
+ * 1 : -8, // Algorithm : EdDSA
+ * },
+ * unprotected: bstr .size 0
+ * payload: bstr .cbor SignatureKey,
+ * signature: bstr PureEd25519(.cbor SignatureKeySignatureInput)
+ * ]
+ *
+ * SignatureKey = { // COSE_Key
+ * 1 : 1, // Key type : Octet Key Pair
+ * 3 : -8, // Algorithm : EdDSA
+ * -1 : 6, // Curve : Ed25519
+ * -2 : bstr // Ed25519 public key
+ * }
+ *
+ * SignatureKeySignatureInput = [
+ * context: "Signature1",
+ * body_protected: bstr .cbor {
+ * 1 : -8, // Algorithm : EdDSA
+ * },
+ * external_aad: bstr .size 0,
+ * payload: bstr .cbor SignatureKey
+ * ]
+ *
+ * SignedEek = [ // COSE_Sign1
+ * protected: bstr .cbor {
+ * 1 : -8, // Algorithm : EdDSA
+ * },
+ * unprotected: bstr .size 0
+ * payload: bstr .cbor Eek,
+ * signature: bstr PureEd25519(.cbor EekSignatureInput)
+ * ]
+ *
+ * Eek = { // COSE_Key
+ * 1 : 1, // Key type : Octet Key Pair
+ * 2 : bstr // KID : EEK ID
+ * 3 : -25, // Algorithm : ECDH-ES + HKDF-256
+ * -1 : 4, // Curve : X25519
+ * -2 : bstr // Ed25519 public key
+ * }
+ *
+ * EekSignatureInput = [
+ * context: "Signature1",
+ * body_protected: bstr .cbor {
+ * 1 : -8, // Algorithm : EdDSA
+ * },
+ * external_aad: bstr .size 0,
+ * payload: bstr .cbor Eek
+ * ]
+ *
+ * If the contents of endpointEncryptionKey do not match the SignedEek structure above,
+ * the method must return STATUS_INVALID_EEK.
+ *
+ * If testMode is true, the method must ignore the length and content of the signatures
+ * in the chain, which implies that it must not attempt to validate the signature.
+ *
+ * If testMode is false, the method must validate the chain signatures, and must verify
+ * that the public key in the root certifictate is in its pre-configured set of
+ * authorized EEK root keys. If the public key is not in the database, or if signature
+ * verification fails, the method must return STATUS_INVALID_EEK.
+ *
+ * @param in challenge contains a byte string from the provisioning server that must be signed
+ * by the secure area. See the description of the 'signature' output parameter for
+ * details.
+ *
+ * @param out keysToSignMac contains the MAC of KeysToSign in the CertificateRequest
+ * structure. Specifically, it contains:
+ *
+ * HMAC-256(EK_mac, .cbor KeysToMacStructure)
+ *
+ * Where EK_mac is an ephemeral MAC key, found in ProtectedData (see below). The MACed
+ * data is the "tag" field of a COSE_Mac0 structure like:
+ *
+ * MacedKeys = [ // COSE_Mac0
+ * protected : bstr .cbor {
+ * 1 : 5, // Algorithm : HMAC-256
+ * },
+ * unprotected : bstr .size 0,
+ * // Payload is PublicKeys from keysToSign argument, in provided order.
+ * payload: bstr .cbor [ * PublicKey ],
+ * tag: bstr
+ * ]
+ *
+ * KeysToMacStructure = [
+ * context : "MAC0",
+ * protected : bstr .cbor { 1 : 5 }, // Algorithm : HMAC-256
+ * external_aad : bstr .size 0,
+ * // Payload is PublicKeys from keysToSign argument, in provided order.
+ * payload : bstr .cbor [ * PublicKey ]
+ * ]
+ *
+ * @param out ProtectedData contains the encrypted BCC and the ephemeral MAC key used to
+ * authenticate the keysToSign (see keysToSignMac output argument).
+ */
+ void generateCertificateRequest(in boolean testMode, in MacedPublicKey[] keysToSign,
+ in byte[] endpointEncryptionCertChain, in byte[] challenge, out byte[] keysToSignMac,
+ out ProtectedData protectedData);
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
index 1a107ba..2fcaf4c 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
@@ -45,4 +45,11 @@
* same author.
*/
@utf8InCpp String keyMintAuthorName;
+
+ /* The timestampTokenRequired is a boolean flag, which when true reflects that IKeyMintDevice
+ * instance will expect a valid TimeStampToken with various operations. This will typically
+ * required by the StrongBox implementations that generally don't have secure clock hardware to
+ * generate timestamp tokens.
+ */
+ boolean timestampTokenRequired;
}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
index 68c1740..978a027 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
@@ -16,12 +16,11 @@
package android.hardware.security.keymint;
-
/**
* Possible purposes of a key (or pair).
*/
@VintfStability
-@Backing(type = "int")
+@Backing(type="int")
enum KeyPurpose {
/* Usable with RSA, EC and AES keys. */
ENCRYPT = 0,
@@ -42,5 +41,7 @@
/* Key Agreement, usable with EC keys. */
AGREE_KEY = 6,
- /* TODO(seleneh) add ATTEST_KEY and their corresponding codes and tests later*/
+ /* Usable as an attestation signing key. Keys with this purpose must not have any other
+ * purpose. */
+ ATTEST_KEY = 7,
}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/MacedPublicKey.aidl b/security/keymint/aidl/android/hardware/security/keymint/MacedPublicKey.aidl
new file mode 100644
index 0000000..da85a50
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/MacedPublicKey.aidl
@@ -0,0 +1,57 @@
+/*
+ * 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;
+
+/**
+ * MacedPublicKey contains a CBOR-encoded public key, MACed by an IRemotelyProvisionedComponent, to
+ * prove that the key pair was generated by that component.
+ */
+@VintfStability
+parcelable MacedPublicKey {
+ /**
+ * key is a COSE_Mac0 structure containing the new public key. It's MACed by a key available
+ * only to the secure environment, as proof that the public key was generated by that
+ * environment. In CDDL, assuming the contained key is an Ed25519 public key:
+ *
+ * MacedPublicKey = [ // COSE_Mac0
+ * protected: bstr .cbor { 1 : 5}, // Algorithm : HMAC-256
+ * unprotected: bstr .size 0,
+ * payload : bstr .cbor PublicKey,
+ * tag : bstr HMAC-256(K_mac, MAC_structure)
+ * ]
+ *
+ * PublicKey = { // COSE_Key
+ * 1 : 1, // Key type : octet key pair
+ * 3 : -8 // Algorithm : EdDSA
+ * -1 : 6, // Curve : Ed25519
+ * -2 : bstr // X coordinate, little-endian
+ * ? -70000 : nil // Presence indicates this is a test key. If set, K_mac is
+ * // all zeros.
+ * },
+ *
+ * MAC_structure = [
+ * context : "MAC0",
+ * protected : bstr .cbor { 1 : 5 },
+ * external_aad : bstr .size 0,
+ * payload : bstr .cbor PublicKey
+ * ]
+ *
+ * if a non-Ed25519 public key were contained, the contents of the PublicKey map would change a
+ * little; see RFC 8152 for details.
+ */
+ byte[] macedKey;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl b/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl
new file mode 100644
index 0000000..1ec3bf0
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl
@@ -0,0 +1,169 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * ProtectedData contains the encrypted BCC and the ephemeral MAC key used to
+ * authenticate the keysToSign (see keysToSignMac output argument).
+ */
+@VintfStability
+parcelable ProtectedData {
+ /**
+ * ProtectedData is a COSE_Encrypt structure, specified by the following CDDL
+ *
+ * ProtectedData = [ // COSE_Encrypt
+ * protected: bstr .cbor {
+ * 1 : 3 // Algorithm : AES-GCM 256
+ * },
+ * unprotected: {
+ * 5 : bstr .size 12 // IV
+ * },
+ * ciphertext: bstr, // AES-GCM-128(K, .cbor ProtectedDataPayload)
+ * recipients : [
+ * [ // COSE_Recipient
+ * protected : bstr .cbor {
+ * 1 : -25 // Algorithm : ECDH-ES + HKDF-256
+ * },
+ * unprotected : {
+ * -1 : { // COSE_Key
+ * 1 : 1, // Key type : Octet Key Pair
+ * -1 : 4, // Curve : X25519
+ * -2 : bstr // Sender X25519 public key
+ * }
+ * 4 : bstr, // KID : EEK ID
+ * },
+ * ciphertext : nil
+ * ]
+ * ]
+ * ]
+ *
+ * K = HKDF-256(ECDH(EEK_pub, Ephemeral_priv), Context)
+ *
+ * Context = [ // COSE_KDF_Context
+ * AlgorithmID : 3 // AES-GCM 256
+ * PartyUInfo : [
+ * identity : bstr "client"
+ * nonce : bstr .size 0,
+ * other : bstr // Ephemeral pubkey
+ * ],
+ * PartyVInfo : [
+ * identity : bstr "server",
+ * nonce : bstr .size 0,
+ * other : bstr // EEK pubkey
+ * ],
+ * SuppPubInfo : [
+ * 128, // Output key length
+ * protected : bstr .size 0
+ * ]
+ * ]
+ *
+ * ProtectedDataPayload [
+ * SignedMac,
+ * Bcc,
+ * ]
+ *
+ * SignedMac = [ // COSE_Sign1
+ * bstr .cbor { // Protected params
+ * 1 : -8, // Algorithm : EdDSA
+ * },
+ * bstr .size 0, // Unprotected params
+ * bstr .size 32, // MAC key
+ * bstr PureEd25519(DK_priv, .cbor SignedMac_structure)
+ * ]
+ *
+ * SignedMac_structure = [
+ * "Signature1",
+ * bstr .cbor { // Protected params
+ * 1 : -8, // Algorithm : EdDSA
+ * },
+ * bstr .cbor SignedMacAad
+ * bstr .size 32 // MAC key
+ * ]
+ *
+ * SignedMacAad = [
+ * challenge : bstr,
+ * DeviceInfo
+ * ]
+ *
+ * Bcc = [
+ * PubKey, // DK_pub
+ * + BccEntry, // Root -> leaf (KM_pub)
+ * ]
+ *
+ * BccPayload = { // CWT
+ * 1 : tstr, // Issuer
+ * 2 : tstr, // Subject
+ * // See the Open Profile for DICE for details on these fields.
+ * ? -4670545 : bstr, // Code Hash
+ * ? -4670546 : bstr, // Code Descriptor
+ * ? -4670547 : bstr, // Configuration Hash
+ * ? -4670548 : bstr .cbor { // Configuration Descriptor
+ * ? -70002 : tstr, // Component name
+ * ? -70003 : int, // Firmware version
+ * ? -70004 : null, // Resettable
+ * },
+ * ? -4670549 : bstr, // Authority Hash
+ * ? -4670550 : bstr, // Authority Descriptor
+ * ? -4670551 : bstr, // Mode
+ * -4670552 : bstr .cbor PubKey // Subject Public Key
+ * -4670553 : bstr // Key Usage
+ * }
+ *
+ * BccEntry = [ // COSE_Sign1
+ * protected: bstr .cbor {
+ * 1 : -8, // Algorithm : EdDSA
+ * },
+ * unprotected: bstr .size 0,
+ * payload: bstr .cbor BccPayload,
+ * // First entry in the chain is signed by DK_pub, the others are each signed by their
+ * // immediate predecessor. See RFC 8032 for signature representation.
+ * signature: bstr .cbor PureEd25519(SigningKey, bstr .cbor BccEntryInput)
+ * ]
+ *
+ * PubKey = { // COSE_Key
+ * 1 : 1, // Key type : octet key pair
+ * 3 : -8, // Algorithm : EdDSA
+ * 4 : 2, // Ops: Verify
+ * -1 : 6, // Curve : Ed25519
+ * -2 : bstr // X coordinate, little-endian
+ * }
+ *
+ * BccEntryInput = [
+ * context: "Signature1",
+ * protected: bstr .cbor {
+ * 1 : -8, // Algorithm : EdDSA
+ * },
+ * external_aad: bstr .size 0,
+ * payload: bstr .cbor BccPayload
+ * ]
+ *
+ * DeviceInfo = {
+ * ? "brand" : tstr,
+ * ? "manufacturer" : tstr,
+ * ? "product" : tstr,
+ * ? "model" : tstr,
+ * ? "board" : tstr,
+ * ? "vb_state" : "green" / "yellow" / "orange",
+ * ? "bootloader_state" : "locked" / "unlocked",
+ * ? "os_version" : tstr,
+ * ? "system_patch_level" : uint, // YYYYMMDD
+ * ? "boot_patch_level" : uint, // YYYYMMDD
+ * ? "vendor_patch_level" : uint, // YYYYMMDD
+ * }
+ */
+ byte[] protectedData;
+}
diff --git a/security/keymint/aidl/default/Android.bp b/security/keymint/aidl/default/Android.bp
index b2758ad..e160548 100644
--- a/security/keymint/aidl/default/Android.bp
+++ b/security/keymint/aidl/default/Android.bp
@@ -2,7 +2,11 @@
name: "android.hardware.security.keymint-service",
relative_install_path: "hw",
init_rc: ["android.hardware.security.keymint-service.rc"],
- vintf_fragments: ["android.hardware.security.keymint-service.xml"],
+ vintf_fragments: [
+ "android.hardware.security.keymint-service.xml",
+ "android.hardware.security.sharedsecret-service.xml",
+ "android.hardware.security.secureclock-service.xml",
+ ],
vendor: true,
cflags: [
"-Wall",
@@ -10,17 +14,45 @@
],
shared_libs: [
"android.hardware.security.keymint-V1-ndk_platform",
+ "android.hardware.security.sharedsecret-V1-ndk_platform",
+ "android.hardware.security.secureclock-V1-ndk_platform",
"libbase",
"libbinder_ndk",
- "libcppbor",
+ "libcppbor_external",
"libcrypto",
"libkeymaster_portable",
"libkeymint",
"liblog",
"libpuresoftkeymasterdevice",
+ "libremote_provisioner",
"libutils",
],
srcs: [
"service.cpp",
],
}
+
+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
new file mode 100644
index 0000000..f2651fb
--- /dev/null
+++ b/security/keymint/aidl/default/RemotelyProvisionedComponent.cpp
@@ -0,0 +1,430 @@
+/*
+ * 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 {
+
+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);
+ }
+
+ 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)->asBstr();
+ 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_SIGN), 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, bytevec* keysToSignMac,
+ ProtectedData* protectedData) {
+ 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();
+ }
+
+ auto signedMac = constructCoseSign1(devicePrivKey /* Signing key */, //
+ ephemeralMacKey /* Payload */,
+ cppbor::Array() /* AAD */
+ .add(challenge)
+ .add(createDeviceInfo())
+ .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;
+}
+
+bytevec RemotelyProvisionedComponent::createDeviceInfo() const {
+ return cppbor::Map().encode();
+}
+
+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 */,
+ std::vector<uint8_t>(0x05) /* Big endian order */)
+ .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
new file mode 100644
index 0000000..e8d2343
--- /dev/null
+++ b/security/keymint/aidl/default/RemotelyProvisionedComponent.h
@@ -0,0 +1,57 @@
+/*
+ * 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,
+ std::vector<uint8_t>* keysToSignMac,
+ ProtectedData* protectedData) 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::vector<uint8_t> 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/android.hardware.security.keymint-service.xml b/security/keymint/aidl/default/android.hardware.security.keymint-service.xml
index 73d15a8..4aa05ef 100644
--- a/security/keymint/aidl/default/android.hardware.security.keymint-service.xml
+++ b/security/keymint/aidl/default/android.hardware.security.keymint-service.xml
@@ -3,4 +3,8 @@
<name>android.hardware.security.keymint</name>
<fqname>IKeyMintDevice/default</fqname>
</hal>
+ <hal format="aidl">
+ <name>android.hardware.security.keymint</name>
+ <fqname>IRemotelyProvisionedComponent/default</fqname>
+ </hal>
</manifest>
diff --git a/security/keymint/aidl/default/android.hardware.security.secureclock-service.xml b/security/keymint/aidl/default/android.hardware.security.secureclock-service.xml
new file mode 100644
index 0000000..c0ff775
--- /dev/null
+++ b/security/keymint/aidl/default/android.hardware.security.secureclock-service.xml
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.security.secureclock</name>
+ <fqname>ISecureClock/default</fqname>
+ </hal>
+</manifest>
diff --git a/security/keymint/aidl/default/android.hardware.security.sharedsecret-service.xml b/security/keymint/aidl/default/android.hardware.security.sharedsecret-service.xml
new file mode 100644
index 0000000..d37981f
--- /dev/null
+++ b/security/keymint/aidl/default/android.hardware.security.sharedsecret-service.xml
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.security.sharedsecret</name>
+ <fqname>ISharedSecret/default</fqname>
+ </hal>
+</manifest>
diff --git a/security/keymint/aidl/default/service.cpp b/security/keymint/aidl/default/service.cpp
index a710535..bcebbaf 100644
--- a/security/keymint/aidl/default/service.cpp
+++ b/security/keymint/aidl/default/service.cpp
@@ -21,25 +21,42 @@
#include <android/binder_process.h>
#include <AndroidKeyMintDevice.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::SecurityLevel;
+using aidl::android::hardware::security::secureclock::AndroidSecureClock;
+using aidl::android::hardware::security::sharedsecret::AndroidSharedSecret;
+
+template <typename T, class... Args>
+std::shared_ptr<T> addService(Args&&... args) {
+ std::shared_ptr<T> ser = ndk::SharedRefBase::make<T>(std::forward<Args>(args)...);
+ auto instanceName = std::string(T::descriptor) + "/default";
+ LOG(INFO) << "adding keymint service instance: " << instanceName;
+ binder_status_t status =
+ AServiceManager_addService(ser->asBinder().get(), instanceName.c_str());
+ CHECK(status == STATUS_OK);
+ return ser;
+}
int main() {
// Zero threads seems like a useless pool, but below we'll join this thread to it, increasing
// the pool size to 1.
ABinderProcess_setThreadPoolMaxThreadCount(0);
+ // Add Keymint Service
std::shared_ptr<AndroidKeyMintDevice> keyMint =
- ndk::SharedRefBase::make<AndroidKeyMintDevice>(SecurityLevel::SOFTWARE);
-
- keymaster::SoftKeymasterLogger logger;
- const auto instanceName = std::string(AndroidKeyMintDevice::descriptor) + "/default";
- LOG(INFO) << "instance: " << instanceName;
- binder_status_t status =
- AServiceManager_addService(keyMint->asBinder().get(), instanceName.c_str());
- CHECK(status == STATUS_OK);
-
+ addService<AndroidKeyMintDevice>(SecurityLevel::SOFTWARE);
+ // Add Secure Clock Service
+ addService<AndroidSecureClock>(keyMint);
+ // Add Shared Secret Service
+ addService<AndroidSharedSecret>(keyMint);
+ // Add Remotely Provisioned Component Service
+ addService<RemotelyProvisionedComponent>(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 f4ba9e7..24fe616 100644
--- a/security/keymint/aidl/vts/functional/Android.bp
+++ b/security/keymint/aidl/vts/functional/Android.bp
@@ -21,6 +21,7 @@
"use_libaidlvintf_gtest_helper_static",
],
srcs: [
+ "AttestKeyTest.cpp",
"KeyMintTest.cpp",
],
shared_libs: [
@@ -62,6 +63,36 @@
static_libs: [
"android.hardware.security.keymint-V1-ndk_platform",
"android.hardware.security.secureclock-V1-ndk_platform",
- "libcppbor",
+ "libcppbor_external",
+ ],
+}
+
+cc_test {
+ name: "VtsHalRemotelyProvisionedComponentTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: [
+ "VtsRemotelyProvisionedComponentTests.cpp",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ "libcppbor_external",
+ "libcrypto",
+ "libkeymaster_portable",
+ "libpuresoftkeymasterdevice",
+ ],
+ static_libs: [
+ "android.hardware.security.keymint-V1-ndk_platform",
+ "libcppcose",
+ "libgmock_ndk",
+ "libremote_provisioner",
+ "libkeymint",
+ "libkeymint_remote_prov_support",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
],
}
diff --git a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
new file mode 100644
index 0000000..7e7a466
--- /dev/null
+++ b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
@@ -0,0 +1,235 @@
+/*
+ * 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 <keymint_support/key_param_output.h>
+#include <keymint_support/openssl_utils.h>
+
+#include "KeyMintAidlTestBase.h"
+
+namespace aidl::android::hardware::security::keymint::test {
+
+namespace {
+
+vector<uint8_t> make_name_from_str(const string& name) {
+ X509_NAME_Ptr x509_name(X509_NAME_new());
+ EXPECT_TRUE(x509_name.get() != nullptr);
+ if (!x509_name) return {};
+
+ EXPECT_EQ(1, X509_NAME_add_entry_by_txt(x509_name.get(), //
+ "CN", //
+ MBSTRING_ASC,
+ reinterpret_cast<const uint8_t*>(name.c_str()),
+ -1, // len
+ -1, // loc
+ 0 /* set */));
+
+ int len = i2d_X509_NAME(x509_name.get(), nullptr /* only return length */);
+ EXPECT_GT(len, 0);
+
+ vector<uint8_t> retval(len);
+ uint8_t* p = retval.data();
+ i2d_X509_NAME(x509_name.get(), &p);
+
+ return retval;
+}
+
+bool IsSelfSigned(const vector<Certificate>& chain) {
+ if (chain.size() != 1) return false;
+ return ChainSignaturesAreValid(chain);
+}
+
+} // namespace
+
+using AttestKeyTest = KeyMintAidlTestBase;
+
+TEST_P(AttestKeyTest, AllRsaSizes) {
+ for (auto size : ValidKeySizes(Algorithm::RSA)) {
+ /*
+ * Create attestaton 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 key
+ */
+ 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::OK,
+ GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(2048, 65537)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AttestationChallenge("foo")
+ .AttestationApplicationId("bar")
+ .SetDefaultValidity(),
+ attest_key, &attested_key_blob, &attested_key_characteristics,
+ &attested_key_cert_chain));
+
+ CheckedDeleteKey(&attested_key_blob);
+
+ 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(),
+ 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.
+ if (attest_key_cert_chain.size() > 0) {
+ attested_key_cert_chain.push_back(attest_key_cert_chain[0]);
+ }
+ EXPECT_TRUE(ChainSignaturesAreValid(attested_key_cert_chain));
+
+ /*
+ * Use attestation key to sign EC key
+ */
+ EXPECT_EQ(ErrorCode::OK,
+ GenerateKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(EcCurve::P_256)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AttestationChallenge("foo")
+ .AttestationApplicationId("bar")
+ .SetDefaultValidity(),
+ attest_key, &attested_key_blob, &attested_key_characteristics,
+ &attested_key_cert_chain));
+
+ CheckedDeleteKey(&attested_key_blob);
+ CheckedDeleteKey(&attest_key.keyBlob);
+
+ hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
+ sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+ EXPECT_TRUE(verify_attestation_record("foo", "bar", 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.
+ if (attest_key_cert_chain.size() > 0) {
+ attested_key_cert_chain.push_back(attest_key_cert_chain[0]);
+ }
+ EXPECT_TRUE(ChainSignaturesAreValid(attested_key_cert_chain));
+
+ // Bail early if anything failed.
+ if (HasFailure()) return;
+ }
+}
+
+TEST_P(AttestKeyTest, AllEcCurves) {
+ for (auto curve : ValidCurves()) {
+ /*
+ * Create attestaton key.
+ */
+ AttestationKey attest_key;
+ vector<KeyCharacteristics> attest_key_characteristics;
+ vector<Certificate> attest_key_cert_chain;
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(curve)
+ .AttestKey()
+ .SetDefaultValidity(),
+ {} /* attestation siging 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 curve " << curve;
+
+ /*
+ * Use attestation key to sign RSA key
+ */
+ 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::OK,
+ GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(2048, 65537)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AttestationChallenge("foo")
+ .AttestationApplicationId("bar")
+ .SetDefaultValidity(),
+ attest_key, &attested_key_blob, &attested_key_characteristics,
+ &attested_key_cert_chain));
+
+ CheckedDeleteKey(&attested_key_blob);
+
+ 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(),
+ 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.
+ if (attest_key_cert_chain.size() > 0) {
+ attested_key_cert_chain.push_back(attest_key_cert_chain[0]);
+ }
+ EXPECT_TRUE(ChainSignaturesAreValid(attested_key_cert_chain));
+
+ /*
+ * Use attestation key to sign EC key
+ */
+ EXPECT_EQ(ErrorCode::OK,
+ GenerateKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(EcCurve::P_256)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AttestationChallenge("foo")
+ .AttestationApplicationId("bar")
+ .SetDefaultValidity(),
+ attest_key, &attested_key_blob, &attested_key_characteristics,
+ &attested_key_cert_chain));
+
+ CheckedDeleteKey(&attested_key_blob);
+ CheckedDeleteKey(&attest_key.keyBlob);
+
+ hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
+ sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+ EXPECT_TRUE(verify_attestation_record("foo", "bar", 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.
+ if (attest_key_cert_chain.size() > 0) {
+ attested_key_cert_chain.push_back(attest_key_cert_chain[0]);
+ }
+ EXPECT_TRUE(ChainSignaturesAreValid(attested_key_cert_chain));
+
+ // Bail early if anything failed.
+ if (HasFailure()) return;
+ }
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(AttestKeyTest);
+
+} // 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 6555157..d61a081 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -22,15 +22,23 @@
#include <android-base/logging.h>
#include <android/binder_manager.h>
+#include <cutils/properties.h>
+#include <openssl/mem.h>
+#include <keymint_support/attestation_record.h>
#include <keymint_support/key_param_output.h>
#include <keymint_support/keymint_utils.h>
+#include <keymint_support/openssl_utils.h>
namespace aidl::android::hardware::security::keymint {
using namespace std::literals::chrono_literals;
using std::endl;
using std::optional;
+using std::unique_ptr;
+using ::testing::AssertionFailure;
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
::std::ostream& operator<<(::std::ostream& os, const AuthorizationSet& set) {
if (set.size() == 0)
@@ -45,7 +53,7 @@
namespace test {
namespace {
-
+typedef KeyMintAidlTestBase::KeyData KeyData;
// Predicate for testing basic characteristics validity in generation or import.
bool KeyCharacteristicsBasicallyValid(SecurityLevel secLevel,
const vector<KeyCharacteristics>& key_characteristics) {
@@ -73,8 +81,67 @@
return true;
}
+// Extract attestation record from cert. Returned object is still part of cert; don't free it
+// separately.
+ASN1_OCTET_STRING* get_attestation_record(X509* certificate) {
+ ASN1_OBJECT_Ptr oid(OBJ_txt2obj(kAttestionRecordOid, 1 /* dotted string format */));
+ EXPECT_TRUE(!!oid.get());
+ if (!oid.get()) return nullptr;
+
+ int location = X509_get_ext_by_OBJ(certificate, oid.get(), -1 /* search from beginning */);
+ EXPECT_NE(-1, location) << "Attestation extension not found in certificate";
+ if (location == -1) return nullptr;
+
+ X509_EXTENSION* attest_rec_ext = X509_get_ext(certificate, location);
+ EXPECT_TRUE(!!attest_rec_ext)
+ << "Found attestation extension but couldn't retrieve it? Probably a BoringSSL bug.";
+ if (!attest_rec_ext) return nullptr;
+
+ ASN1_OCTET_STRING* attest_rec = X509_EXTENSION_get_data(attest_rec_ext);
+ EXPECT_TRUE(!!attest_rec) << "Attestation extension contained no data";
+ return attest_rec;
+}
+
+bool avb_verification_enabled() {
+ char value[PROPERTY_VALUE_MAX];
+ return property_get("ro.boot.vbmeta.device_state", value, "") != 0;
+}
+
+char nibble2hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+// 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,
+ Tag::INCLUDE_UNIQUE_ID,
+};
+
+AuthorizationSet filtered_tags(const AuthorizationSet& set) {
+ AuthorizationSet filtered;
+ std::remove_copy_if(
+ set.begin(), set.end(), std::back_inserter(filtered), [](const auto& entry) -> bool {
+ return std::find(kTagsToFilter.begin(), kTagsToFilter.end(), entry.tag) !=
+ kTagsToFilter.end();
+ });
+ return filtered;
+}
+
+string x509NameToStr(X509_NAME* name) {
+ char* s = X509_NAME_oneline(name, nullptr, 0);
+ string retval(s);
+ OPENSSL_free(s);
+ return retval;
+}
+
} // namespace
+bool KeyMintAidlTestBase::arm_deleteAllKeys = false;
+bool KeyMintAidlTestBase::dump_Attestations = false;
+
ErrorCode KeyMintAidlTestBase::GetReturnErrorCode(const Status& result) {
if (result.isOk()) return ErrorCode::OK;
@@ -110,48 +177,48 @@
}
ErrorCode KeyMintAidlTestBase::GenerateKey(const AuthorizationSet& key_desc,
+ const optional<AttestationKey>& attest_key,
vector<uint8_t>* key_blob,
- vector<KeyCharacteristics>* key_characteristics) {
+ vector<KeyCharacteristics>* key_characteristics,
+ vector<Certificate>* cert_chain) {
EXPECT_NE(key_blob, nullptr) << "Key blob pointer must not be null. Test bug";
EXPECT_NE(key_characteristics, nullptr)
<< "Previous characteristics not deleted before generating key. Test bug.";
- // Aidl does not clear these output parameters if the function returns
- // error. This is different from hal where output parameter is always
- // cleared due to hal returning void. So now we need to do our own clearing
- // of the output variables prior to calling keyMint aidl libraries.
- key_blob->clear();
- key_characteristics->clear();
- cert_chain_.clear();
-
KeyCreationResult creationResult;
- Status result = keymint_->generateKey(key_desc.vector_data(), &creationResult);
-
+ Status result = keymint_->generateKey(key_desc.vector_data(), attest_key, &creationResult);
if (result.isOk()) {
EXPECT_PRED2(KeyCharacteristicsBasicallyValid, SecLevel(),
creationResult.keyCharacteristics);
EXPECT_GT(creationResult.keyBlob.size(), 0);
*key_blob = std::move(creationResult.keyBlob);
*key_characteristics = std::move(creationResult.keyCharacteristics);
- cert_chain_ = std::move(creationResult.certificateChain);
+ *cert_chain = std::move(creationResult.certificateChain);
auto algorithm = key_desc.GetTagValue(TAG_ALGORITHM);
EXPECT_TRUE(algorithm);
if (algorithm &&
(algorithm.value() == Algorithm::RSA || algorithm.value() == Algorithm::EC)) {
- EXPECT_GE(cert_chain_.size(), 1);
- if (key_desc.Contains(TAG_ATTESTATION_CHALLENGE)) EXPECT_GT(cert_chain_.size(), 1);
+ EXPECT_GE(cert_chain->size(), 1);
+ if (key_desc.Contains(TAG_ATTESTATION_CHALLENGE)) {
+ if (attest_key) {
+ EXPECT_EQ(cert_chain->size(), 1);
+ } else {
+ EXPECT_GT(cert_chain->size(), 1);
+ }
+ }
} else {
// For symmetric keys there should be no certificates.
- EXPECT_EQ(cert_chain_.size(), 0);
+ EXPECT_EQ(cert_chain->size(), 0);
}
}
return GetReturnErrorCode(result);
}
-ErrorCode KeyMintAidlTestBase::GenerateKey(const AuthorizationSet& key_desc) {
- return GenerateKey(key_desc, &key_blob_, &key_characteristics_);
+ErrorCode KeyMintAidlTestBase::GenerateKey(const AuthorizationSet& key_desc,
+ const optional<AttestationKey>& attest_key) {
+ return GenerateKey(key_desc, attest_key, &key_blob_, &key_characteristics_, &cert_chain_);
}
ErrorCode KeyMintAidlTestBase::ImportKey(const AuthorizationSet& key_desc, KeyFormat format,
@@ -166,7 +233,7 @@
KeyCreationResult creationResult;
result = keymint_->importKey(key_desc.vector_data(), format,
vector<uint8_t>(key_material.begin(), key_material.end()),
- &creationResult);
+ {} /* attestationSigningKeyBlob */, &creationResult);
if (result.isOk()) {
EXPECT_PRED2(KeyCharacteristicsBasicallyValid, SecLevel(),
@@ -461,6 +528,34 @@
}
}
+auto KeyMintAidlTestBase::ProcessMessage(const vector<uint8_t>& key_blob, KeyPurpose operation,
+ const string& message, const AuthorizationSet& in_params)
+ -> std::tuple<ErrorCode, string, AuthorizationSet /* out_params */> {
+ AuthorizationSet begin_out_params;
+ ErrorCode result = Begin(operation, key_blob, in_params, &begin_out_params);
+ AuthorizationSet out_params(std::move(begin_out_params));
+ if (result != ErrorCode::OK) {
+ return {result, {}, out_params};
+ }
+
+ string output;
+ int32_t consumed = 0;
+ AuthorizationSet update_params;
+ AuthorizationSet update_out_params;
+ result = Update(update_params, message, &update_out_params, &output, &consumed);
+ out_params.push_back(update_out_params);
+ if (result != ErrorCode::OK) {
+ return {result, output, out_params};
+ }
+
+ string unused;
+ AuthorizationSet finish_params;
+ AuthorizationSet finish_out_params;
+ result = Finish(finish_params, message.substr(consumed), unused, &finish_out_params, &output);
+ out_params.push_back(finish_out_params);
+ return {result, output, out_params};
+}
+
string KeyMintAidlTestBase::ProcessMessage(const vector<uint8_t>& key_blob, KeyPurpose operation,
const string& message, const AuthorizationSet& in_params,
AuthorizationSet* out_params) {
@@ -859,6 +954,269 @@
return authList;
}
+ErrorCode KeyMintAidlTestBase::UseAesKey(const vector<uint8_t>& aesKeyBlob) {
+ auto [result, ciphertext, out_params] = ProcessMessage(
+ aesKeyBlob, KeyPurpose::ENCRYPT, "1234567890123456",
+ AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE));
+ return result;
+}
+
+ErrorCode KeyMintAidlTestBase::UseHmacKey(const vector<uint8_t>& hmacKeyBlob) {
+ auto [result, mac, out_params] = ProcessMessage(
+ hmacKeyBlob, KeyPurpose::SIGN, "1234567890123456",
+ AuthorizationSetBuilder().Authorization(TAG_MAC_LENGTH, 128).Digest(Digest::SHA_2_256));
+ return result;
+}
+
+ErrorCode KeyMintAidlTestBase::UseRsaKey(const vector<uint8_t>& rsaKeyBlob) {
+ std::string message(2048 / 8, 'a');
+ auto [result, signature, out_params] = ProcessMessage(
+ rsaKeyBlob, KeyPurpose::SIGN, message,
+ AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
+ return result;
+}
+
+ErrorCode KeyMintAidlTestBase::UseEcdsaKey(const vector<uint8_t>& ecdsaKeyBlob) {
+ auto [result, signature, out_params] =
+ ProcessMessage(ecdsaKeyBlob, KeyPurpose::SIGN, "a",
+ AuthorizationSetBuilder().Digest(Digest::SHA_2_256));
+ return result;
+}
+
+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) {
+ X509_Ptr cert(parse_cert_blob(attestation_cert));
+ EXPECT_TRUE(!!cert.get());
+ if (!cert.get()) return false;
+
+ ASN1_OCTET_STRING* attest_rec = get_attestation_record(cert.get());
+ EXPECT_TRUE(!!attest_rec);
+ if (!attest_rec) return false;
+
+ AuthorizationSet att_sw_enforced;
+ AuthorizationSet att_hw_enforced;
+ uint32_t att_attestation_version;
+ uint32_t att_keymaster_version;
+ SecurityLevel att_attestation_security_level;
+ SecurityLevel att_keymaster_security_level;
+ vector<uint8_t> att_challenge;
+ vector<uint8_t> att_unique_id;
+ vector<uint8_t> att_app_id;
+
+ auto error = parse_attestation_record(attest_rec->data, //
+ attest_rec->length, //
+ &att_attestation_version, //
+ &att_attestation_security_level, //
+ &att_keymaster_version, //
+ &att_keymaster_security_level, //
+ &att_challenge, //
+ &att_sw_enforced, //
+ &att_hw_enforced, //
+ &att_unique_id);
+ EXPECT_EQ(ErrorCode::OK, error);
+ if (error != ErrorCode::OK) return false;
+
+ EXPECT_GE(att_attestation_version, 3U);
+
+ expected_sw_enforced.push_back(TAG_ATTESTATION_APPLICATION_ID,
+ vector<uint8_t>(app_id.begin(), app_id.end()));
+
+ EXPECT_GE(att_keymaster_version, 4U);
+ EXPECT_EQ(security_level, att_keymaster_security_level);
+ EXPECT_EQ(security_level, att_attestation_security_level);
+
+ EXPECT_EQ(challenge.length(), att_challenge.size());
+ EXPECT_EQ(0, memcmp(challenge.data(), att_challenge.data(), challenge.length()));
+
+ char property_value[PROPERTY_VALUE_MAX] = {};
+ // TODO(b/136282179): When running under VTS-on-GSI the TEE-backed
+ // keymaster implementation will report YYYYMM dates instead of YYYYMMDD
+ // for the BOOT_PATCH_LEVEL.
+ if (avb_verification_enabled()) {
+ for (int i = 0; i < att_hw_enforced.size(); i++) {
+ if (att_hw_enforced[i].tag == TAG_BOOT_PATCHLEVEL ||
+ att_hw_enforced[i].tag == TAG_VENDOR_PATCHLEVEL) {
+ std::string date =
+ std::to_string(att_hw_enforced[i].value.get<KeyParameterValue::dateTime>());
+ // strptime seems to require delimiters, but the tag value will
+ // be YYYYMMDD
+ date.insert(6, "-");
+ date.insert(4, "-");
+ EXPECT_EQ(date.size(), 10);
+ struct tm time;
+ strptime(date.c_str(), "%Y-%m-%d", &time);
+
+ // Day of the month (0-31)
+ EXPECT_GE(time.tm_mday, 0);
+ EXPECT_LT(time.tm_mday, 32);
+ // Months since Jan (0-11)
+ EXPECT_GE(time.tm_mon, 0);
+ EXPECT_LT(time.tm_mon, 12);
+ // Years since 1900
+ EXPECT_GT(time.tm_year, 110);
+ EXPECT_LT(time.tm_year, 200);
+ }
+ }
+ }
+
+ // Check to make sure boolean values are properly encoded. Presence of a boolean tag
+ // indicates true. A provided boolean tag that can be pulled back out of the certificate
+ // indicates correct encoding. No need to check if it's in both lists, since the
+ // AuthorizationSet compare below will handle mismatches of tags.
+ if (security_level == SecurityLevel::SOFTWARE) {
+ EXPECT_TRUE(expected_sw_enforced.Contains(TAG_NO_AUTH_REQUIRED));
+ } else {
+ EXPECT_TRUE(expected_hw_enforced.Contains(TAG_NO_AUTH_REQUIRED));
+ }
+
+ // Alternatively this checks the opposite - a false boolean tag (one that isn't provided in
+ // the authorization list during key generation) isn't being attested to in the certificate.
+ EXPECT_FALSE(expected_sw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));
+ EXPECT_FALSE(att_sw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));
+ EXPECT_FALSE(expected_hw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));
+ EXPECT_FALSE(att_hw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));
+
+ if (att_hw_enforced.Contains(TAG_ALGORITHM, Algorithm::EC)) {
+ // For ECDSA keys, either an EC_CURVE or a KEY_SIZE can be specified, but one must be.
+ EXPECT_TRUE(att_hw_enforced.Contains(TAG_EC_CURVE) ||
+ att_hw_enforced.Contains(TAG_KEY_SIZE));
+ }
+
+ // Test root of trust elements
+ vector<uint8_t> verified_boot_key;
+ VerifiedBoot verified_boot_state;
+ bool device_locked;
+ vector<uint8_t> verified_boot_hash;
+ error = parse_root_of_trust(attest_rec->data, attest_rec->length, &verified_boot_key,
+ &verified_boot_state, &device_locked, &verified_boot_hash);
+ EXPECT_EQ(ErrorCode::OK, error);
+
+ if (avb_verification_enabled()) {
+ EXPECT_NE(property_get("ro.boot.vbmeta.digest", property_value, ""), 0);
+ string prop_string(property_value);
+ EXPECT_EQ(prop_string.size(), 64);
+ EXPECT_EQ(prop_string, bin2hex(verified_boot_hash));
+
+ EXPECT_NE(property_get("ro.boot.vbmeta.device_state", property_value, ""), 0);
+ if (!strcmp(property_value, "unlocked")) {
+ EXPECT_FALSE(device_locked);
+ } else {
+ EXPECT_TRUE(device_locked);
+ }
+
+ // Check that the device is locked if not debuggable, e.g., user build
+ // images in CTS. For VTS, debuggable images are used to allow adb root
+ // and the device is unlocked.
+ if (!property_get_bool("ro.debuggable", false)) {
+ EXPECT_TRUE(device_locked);
+ } else {
+ EXPECT_FALSE(device_locked);
+ }
+ }
+
+ // Verified boot key should be all 0's if the boot state is not verified or self signed
+ std::string empty_boot_key(32, '\0');
+ std::string verified_boot_key_str((const char*)verified_boot_key.data(),
+ verified_boot_key.size());
+ EXPECT_NE(property_get("ro.boot.verifiedbootstate", property_value, ""), 0);
+ if (!strcmp(property_value, "green")) {
+ EXPECT_EQ(verified_boot_state, VerifiedBoot::VERIFIED);
+ EXPECT_NE(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
+ verified_boot_key.size()));
+ } else if (!strcmp(property_value, "yellow")) {
+ EXPECT_EQ(verified_boot_state, VerifiedBoot::SELF_SIGNED);
+ EXPECT_NE(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
+ verified_boot_key.size()));
+ } else if (!strcmp(property_value, "orange")) {
+ EXPECT_EQ(verified_boot_state, VerifiedBoot::UNVERIFIED);
+ EXPECT_EQ(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
+ verified_boot_key.size()));
+ } else if (!strcmp(property_value, "red")) {
+ EXPECT_EQ(verified_boot_state, VerifiedBoot::FAILED);
+ } else {
+ EXPECT_EQ(verified_boot_state, VerifiedBoot::UNVERIFIED);
+ EXPECT_NE(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
+ verified_boot_key.size()));
+ }
+
+ att_sw_enforced.Sort();
+ expected_sw_enforced.Sort();
+ auto a = filtered_tags(expected_sw_enforced);
+ auto b = filtered_tags(att_sw_enforced);
+ EXPECT_EQ(a, b);
+
+ att_hw_enforced.Sort();
+ expected_hw_enforced.Sort();
+ EXPECT_EQ(filtered_tags(expected_hw_enforced), filtered_tags(att_hw_enforced));
+
+ return true;
+}
+
+string bin2hex(const vector<uint8_t>& data) {
+ string retval;
+ retval.reserve(data.size() * 2 + 1);
+ for (uint8_t byte : data) {
+ retval.push_back(nibble2hex[0x0F & (byte >> 4)]);
+ retval.push_back(nibble2hex[0x0F & byte]);
+ }
+ return retval;
+}
+
+AssertionResult ChainSignaturesAreValid(const vector<Certificate>& chain) {
+ std::stringstream cert_data;
+
+ for (size_t i = 0; i < chain.size(); ++i) {
+ cert_data << bin2hex(chain[i].encodedCertificate) << std::endl;
+
+ X509_Ptr key_cert(parse_cert_blob(chain[i].encodedCertificate));
+ X509_Ptr signing_cert;
+ if (i < chain.size() - 1) {
+ signing_cert = parse_cert_blob(chain[i + 1].encodedCertificate);
+ } else {
+ signing_cert = parse_cert_blob(chain[i].encodedCertificate);
+ }
+ if (!key_cert.get() || !signing_cert.get()) return AssertionFailure() << cert_data.str();
+
+ EVP_PKEY_Ptr signing_pubkey(X509_get_pubkey(signing_cert.get()));
+ if (!signing_pubkey.get()) return AssertionFailure() << cert_data.str();
+
+ if (!X509_verify(key_cert.get(), signing_pubkey.get())) {
+ return AssertionFailure()
+ << "Verification of certificate " << i << " failed "
+ << "OpenSSL error string: " << ERR_error_string(ERR_get_error(), NULL) << '\n'
+ << cert_data.str();
+ }
+
+ 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();
+ }
+ }
+ }
+
+ if (KeyMintAidlTestBase::dump_Attestations) std::cout << cert_data.str();
+ return AssertionSuccess();
+}
+
+X509_Ptr parse_cert_blob(const vector<uint8_t>& blob) {
+ const uint8_t* p = blob.data();
+ return X509_Ptr(d2i_X509(nullptr /* allocate new */, &p, blob.size()));
+}
+
} // namespace test
} // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
index 780971d..452d2b6 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -21,20 +21,27 @@
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <gtest/gtest.h>
+#include <openssl/x509.h>
#include <aidl/android/hardware/security/keymint/ErrorCode.h>
#include <aidl/android/hardware/security/keymint/IKeyMintDevice.h>
#include <keymint_support/authorization_set.h>
+#include <keymint_support/openssl_utils.h>
namespace aidl::android::hardware::security::keymint {
::std::ostream& operator<<(::std::ostream& os, const AuthorizationSet& set);
+inline bool operator==(const keymint::AuthorizationSet& a, const keymint::AuthorizationSet& b) {
+ return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin());
+}
+
namespace test {
using ::android::sp;
using Status = ::ndk::ScopedAStatus;
+using ::std::optional;
using ::std::shared_ptr;
using ::std::string;
using ::std::vector;
@@ -43,6 +50,14 @@
class KeyMintAidlTestBase : public ::testing::TestWithParam<string> {
public:
+ struct KeyData {
+ vector<uint8_t> blob;
+ vector<KeyCharacteristics> characteristics;
+ };
+
+ static bool arm_deleteAllKeys;
+ static bool dump_Attestations;
+
void SetUp() override;
void TearDown() override {
if (key_blob_.size()) {
@@ -57,10 +72,18 @@
uint32_t os_patch_level() { return os_patch_level_; }
ErrorCode GetReturnErrorCode(const Status& result);
- ErrorCode GenerateKey(const AuthorizationSet& key_desc, vector<uint8_t>* key_blob,
- vector<KeyCharacteristics>* key_characteristics);
- ErrorCode GenerateKey(const AuthorizationSet& key_desc);
+ ErrorCode GenerateKey(const AuthorizationSet& key_desc, vector<uint8_t>* key_blob,
+ vector<KeyCharacteristics>* key_characteristics) {
+ return GenerateKey(key_desc, std::nullopt /* attest_key */, key_blob, key_characteristics,
+ &cert_chain_);
+ }
+ ErrorCode GenerateKey(const AuthorizationSet& key_desc,
+ const optional<AttestationKey>& attest_key, vector<uint8_t>* key_blob,
+ vector<KeyCharacteristics>* key_characteristics,
+ vector<Certificate>* cert_chain);
+ ErrorCode GenerateKey(const AuthorizationSet& key_desc,
+ const optional<AttestationKey>& attest_key = std::nullopt);
ErrorCode ImportKey(const AuthorizationSet& key_desc, KeyFormat format,
const string& key_material, vector<uint8_t>* key_blob,
@@ -106,7 +129,9 @@
string ProcessMessage(const vector<uint8_t>& key_blob, KeyPurpose operation,
const string& message, const AuthorizationSet& in_params,
AuthorizationSet* out_params);
-
+ std::tuple<ErrorCode, std::string /* processedMessage */, AuthorizationSet /* out_params */>
+ ProcessMessage(const vector<uint8_t>& key_blob, KeyPurpose operation,
+ const std::string& message, const AuthorizationSet& in_params);
string SignMessage(const vector<uint8_t>& key_blob, const string& message,
const AuthorizationSet& params);
string SignMessage(const string& message, const AuthorizationSet& params);
@@ -149,6 +174,56 @@
std::pair<ErrorCode, vector<uint8_t>> UpgradeKey(const vector<uint8_t>& key_blob);
+ template <typename TagType>
+ std::tuple<KeyData /* aesKey */, KeyData /* hmacKey */, KeyData /* rsaKey */,
+ KeyData /* ecdsaKey */>
+ CreateTestKeys(TagType tagToTest, ErrorCode expectedReturn) {
+ /* AES */
+ KeyData aesKeyData;
+ ErrorCode errorCode = GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(tagToTest)
+ .BlockMode(BlockMode::ECB)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_NO_AUTH_REQUIRED),
+ &aesKeyData.blob, &aesKeyData.characteristics);
+ EXPECT_EQ(expectedReturn, errorCode);
+
+ /* HMAC */
+ KeyData hmacKeyData;
+ errorCode = GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Authorization(tagToTest)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)
+ .Authorization(TAG_NO_AUTH_REQUIRED),
+ &hmacKeyData.blob, &hmacKeyData.characteristics);
+ EXPECT_EQ(expectedReturn, errorCode);
+
+ /* RSA */
+ KeyData rsaKeyData;
+ errorCode = GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(2048, 65537)
+ .Authorization(tagToTest)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .SetDefaultValidity(),
+ &rsaKeyData.blob, &rsaKeyData.characteristics);
+ EXPECT_EQ(expectedReturn, errorCode);
+
+ /* ECDSA */
+ KeyData ecdsaKeyData;
+ errorCode = GenerateKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(256)
+ .Authorization(tagToTest)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .SetDefaultValidity(),
+ &ecdsaKeyData.blob, &ecdsaKeyData.characteristics);
+ EXPECT_EQ(expectedReturn, errorCode);
+ return {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData};
+ }
bool IsSecure() const { return securityLevel_ != SecurityLevel::SOFTWARE; }
SecurityLevel SecLevel() const { return securityLevel_; }
@@ -182,6 +257,10 @@
const vector<KeyCharacteristics>& key_characteristics);
AuthorizationSet SwEnforcedAuthorizations(
const vector<KeyCharacteristics>& key_characteristics);
+ ErrorCode UseAesKey(const vector<uint8_t>& aesKeyBlob);
+ ErrorCode UseHmacKey(const vector<uint8_t>& hmacKeyBlob);
+ ErrorCode UseRsaKey(const vector<uint8_t>& rsaKeyBlob);
+ ErrorCode UseEcdsaKey(const vector<uint8_t>& ecdsaKeyBlob);
private:
std::shared_ptr<IKeyMintDevice> keymint_;
@@ -194,6 +273,16 @@
long challenge_;
};
+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);
+::testing::AssertionResult ChainSignaturesAreValid(const vector<Certificate>& chain);
+
#define INSTANTIATE_KEYMINT_AIDL_TEST(name) \
INSTANTIATE_TEST_SUITE_P(PerInstance, name, \
testing::ValuesIn(KeyMintAidlTestBase::build_params()), \
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index 88122ce..71aae90 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "keymint_5_test"
+#define LOG_TAG "keymint_1_test"
#include <cutils/log.h>
#include <signal.h>
@@ -23,34 +23,21 @@
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/mem.h>
-#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <cutils/properties.h>
#include <aidl/android/hardware/security/keymint/KeyFormat.h>
-#include <keymint_support/attestation_record.h>
#include <keymint_support/key_param_output.h>
#include <keymint_support/openssl_utils.h>
#include "KeyMintAidlTestBase.h"
-static bool arm_deleteAllKeys = false;
-static bool dump_Attestations = false;
-
using aidl::android::hardware::security::keymint::AuthorizationSet;
using aidl::android::hardware::security::keymint::KeyCharacteristics;
using aidl::android::hardware::security::keymint::KeyFormat;
-namespace aidl::android::hardware::security::keymint {
-
-bool operator==(const keymint::AuthorizationSet& a, const keymint::AuthorizationSet& b) {
- return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin());
-}
-
-} // namespace aidl::android::hardware::security::keymint
-
namespace std {
using namespace aidl::android::hardware::security::keymint;
@@ -78,7 +65,8 @@
namespace {
template <TagType tag_type, Tag tag, typename ValueT>
-bool contains(vector<KeyParameter>& set, TypedTag<tag_type, tag> ttag, ValueT expected_value) {
+bool contains(const vector<KeyParameter>& set, TypedTag<tag_type, tag> ttag,
+ ValueT expected_value) {
auto it = std::find_if(set.begin(), set.end(), [&](const KeyParameter& param) {
if (auto p = authorizationValue(ttag, param)) {
return *p == expected_value;
@@ -89,7 +77,7 @@
}
template <TagType tag_type, Tag tag>
-bool contains(vector<KeyParameter>& set, TypedTag<tag_type, tag>) {
+bool contains(const vector<KeyParameter>& set, TypedTag<tag_type, tag>) {
auto it = std::find_if(set.begin(), set.end(),
[&](const KeyParameter& param) { return param.tag == tag; });
return (it != set.end());
@@ -182,281 +170,6 @@
void operator()(RSA* p) { RSA_free(p); }
};
-char nibble2hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
-
-string bin2hex(const vector<uint8_t>& data) {
- string retval;
- retval.reserve(data.size() * 2 + 1);
- for (uint8_t byte : data) {
- retval.push_back(nibble2hex[0x0F & (byte >> 4)]);
- retval.push_back(nibble2hex[0x0F & byte]);
- }
- return retval;
-}
-
-X509* parse_cert_blob(const vector<uint8_t>& blob) {
- const uint8_t* p = blob.data();
- return d2i_X509(nullptr, &p, blob.size());
-}
-
-bool verify_chain(const vector<Certificate>& chain) {
- for (size_t i = 0; i < chain.size(); ++i) {
- X509_Ptr key_cert(parse_cert_blob(chain[i].encodedCertificate));
- X509_Ptr signing_cert;
- if (i < chain.size() - 1) {
- signing_cert.reset(parse_cert_blob(chain[i + 1].encodedCertificate));
- } else {
- signing_cert.reset(parse_cert_blob(chain[i].encodedCertificate));
- }
- EXPECT_TRUE(!!key_cert.get() && !!signing_cert.get());
- if (!key_cert.get() || !signing_cert.get()) return false;
-
- EVP_PKEY_Ptr signing_pubkey(X509_get_pubkey(signing_cert.get()));
- EXPECT_TRUE(!!signing_pubkey.get());
- if (!signing_pubkey.get()) return false;
-
- EXPECT_EQ(1, X509_verify(key_cert.get(), signing_pubkey.get()))
- << "Verification of certificate " << i << " failed "
- << "OpenSSL error string: " << ERR_error_string(ERR_get_error(), NULL);
-
- char* cert_issuer = //
- X509_NAME_oneline(X509_get_issuer_name(key_cert.get()), nullptr, 0);
- char* signer_subj =
- X509_NAME_oneline(X509_get_subject_name(signing_cert.get()), nullptr, 0);
- EXPECT_STREQ(cert_issuer, signer_subj) << "Cert " << i << " has wrong issuer.";
- if (i == 0) {
- char* cert_sub = X509_NAME_oneline(X509_get_subject_name(key_cert.get()), nullptr, 0);
- EXPECT_STREQ("/CN=Android Keystore Key", cert_sub)
- << "Cert " << i << " has wrong subject.";
- OPENSSL_free(cert_sub);
- }
-
- OPENSSL_free(cert_issuer);
- OPENSSL_free(signer_subj);
-
- if (dump_Attestations) std::cout << bin2hex(chain[i].encodedCertificate) << std::endl;
- }
-
- return true;
-}
-
-// Extract attestation record from cert. Returned object is still part of cert; don't free it
-// separately.
-ASN1_OCTET_STRING* get_attestation_record(X509* certificate) {
- ASN1_OBJECT_Ptr oid(OBJ_txt2obj(kAttestionRecordOid, 1 /* dotted string format */));
- EXPECT_TRUE(!!oid.get());
- if (!oid.get()) return nullptr;
-
- int location = X509_get_ext_by_OBJ(certificate, oid.get(), -1 /* search from beginning */);
- EXPECT_NE(-1, location) << "Attestation extension not found in certificate";
- if (location == -1) return nullptr;
-
- X509_EXTENSION* attest_rec_ext = X509_get_ext(certificate, location);
- EXPECT_TRUE(!!attest_rec_ext)
- << "Found attestation extension but couldn't retrieve it? Probably a BoringSSL bug.";
- if (!attest_rec_ext) return nullptr;
-
- ASN1_OCTET_STRING* attest_rec = X509_EXTENSION_get_data(attest_rec_ext);
- EXPECT_TRUE(!!attest_rec) << "Attestation extension contained no data";
- return attest_rec;
-}
-
-bool tag_in_list(const KeyParameter& entry) {
- // 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 tag_list = {
- Tag::BLOB_USAGE_REQUIREMENTS, //
- Tag::CREATION_DATETIME, //
- Tag::EC_CURVE,
- Tag::HARDWARE_TYPE,
- Tag::INCLUDE_UNIQUE_ID,
- };
- return std::find(tag_list.begin(), tag_list.end(), entry.tag) != tag_list.end();
-}
-
-AuthorizationSet filtered_tags(const AuthorizationSet& set) {
- AuthorizationSet filtered;
- std::remove_copy_if(set.begin(), set.end(), std::back_inserter(filtered), tag_in_list);
- return filtered;
-}
-
-bool avb_verification_enabled() {
- char value[PROPERTY_VALUE_MAX];
- return property_get("ro.boot.vbmeta.device_state", value, "") != 0;
-}
-
-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) {
- X509_Ptr cert(parse_cert_blob(attestation_cert));
- EXPECT_TRUE(!!cert.get());
- if (!cert.get()) return false;
-
- ASN1_OCTET_STRING* attest_rec = get_attestation_record(cert.get());
- EXPECT_TRUE(!!attest_rec);
- if (!attest_rec) return false;
-
- AuthorizationSet att_sw_enforced;
- AuthorizationSet att_hw_enforced;
- uint32_t att_attestation_version;
- uint32_t att_keymaster_version;
- SecurityLevel att_attestation_security_level;
- SecurityLevel att_keymaster_security_level;
- vector<uint8_t> att_challenge;
- vector<uint8_t> att_unique_id;
- vector<uint8_t> att_app_id;
-
- auto error = parse_attestation_record(attest_rec->data, //
- attest_rec->length, //
- &att_attestation_version, //
- &att_attestation_security_level, //
- &att_keymaster_version, //
- &att_keymaster_security_level, //
- &att_challenge, //
- &att_sw_enforced, //
- &att_hw_enforced, //
- &att_unique_id);
- EXPECT_EQ(ErrorCode::OK, error);
- if (error != ErrorCode::OK) return false;
-
- EXPECT_GE(att_attestation_version, 3U);
-
- expected_sw_enforced.push_back(TAG_ATTESTATION_APPLICATION_ID,
- vector<uint8_t>(app_id.begin(), app_id.end()));
-
- EXPECT_GE(att_keymaster_version, 4U);
- EXPECT_EQ(security_level, att_keymaster_security_level);
- EXPECT_EQ(security_level, att_attestation_security_level);
-
- EXPECT_EQ(challenge.length(), att_challenge.size());
- EXPECT_EQ(0, memcmp(challenge.data(), att_challenge.data(), challenge.length()));
-
- char property_value[PROPERTY_VALUE_MAX] = {};
- // TODO(b/136282179): When running under VTS-on-GSI the TEE-backed
- // keymaster implementation will report YYYYMM dates instead of YYYYMMDD
- // for the BOOT_PATCH_LEVEL.
- if (avb_verification_enabled()) {
- for (int i = 0; i < att_hw_enforced.size(); i++) {
- if (att_hw_enforced[i].tag == TAG_BOOT_PATCHLEVEL ||
- att_hw_enforced[i].tag == TAG_VENDOR_PATCHLEVEL) {
- std::string date =
- std::to_string(att_hw_enforced[i].value.get<KeyParameterValue::dateTime>());
- // strptime seems to require delimiters, but the tag value will
- // be YYYYMMDD
- date.insert(6, "-");
- date.insert(4, "-");
- EXPECT_EQ(date.size(), 10);
- struct tm time;
- strptime(date.c_str(), "%Y-%m-%d", &time);
-
- // Day of the month (0-31)
- EXPECT_GE(time.tm_mday, 0);
- EXPECT_LT(time.tm_mday, 32);
- // Months since Jan (0-11)
- EXPECT_GE(time.tm_mon, 0);
- EXPECT_LT(time.tm_mon, 12);
- // Years since 1900
- EXPECT_GT(time.tm_year, 110);
- EXPECT_LT(time.tm_year, 200);
- }
- }
- }
-
- // Check to make sure boolean values are properly encoded. Presence of a boolean tag indicates
- // true. A provided boolean tag that can be pulled back out of the certificate indicates correct
- // encoding. No need to check if it's in both lists, since the AuthorizationSet compare below
- // will handle mismatches of tags.
- if (security_level == SecurityLevel::SOFTWARE) {
- EXPECT_TRUE(expected_sw_enforced.Contains(TAG_NO_AUTH_REQUIRED));
- } else {
- EXPECT_TRUE(expected_hw_enforced.Contains(TAG_NO_AUTH_REQUIRED));
- }
-
- // Alternatively this checks the opposite - a false boolean tag (one that isn't provided in
- // the authorization list during key generation) isn't being attested to in the certificate.
- EXPECT_FALSE(expected_sw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));
- EXPECT_FALSE(att_sw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));
- EXPECT_FALSE(expected_hw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));
- EXPECT_FALSE(att_hw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));
-
- if (att_hw_enforced.Contains(TAG_ALGORITHM, Algorithm::EC)) {
- // For ECDSA keys, either an EC_CURVE or a KEY_SIZE can be specified, but one must be.
- EXPECT_TRUE(att_hw_enforced.Contains(TAG_EC_CURVE) ||
- att_hw_enforced.Contains(TAG_KEY_SIZE));
- }
-
- // Test root of trust elements
- vector<uint8_t> verified_boot_key;
- VerifiedBoot verified_boot_state;
- bool device_locked;
- vector<uint8_t> verified_boot_hash;
- error = parse_root_of_trust(attest_rec->data, attest_rec->length, &verified_boot_key,
- &verified_boot_state, &device_locked, &verified_boot_hash);
- EXPECT_EQ(ErrorCode::OK, error);
-
- if (avb_verification_enabled()) {
- EXPECT_NE(property_get("ro.boot.vbmeta.digest", property_value, ""), 0);
- string prop_string(property_value);
- EXPECT_EQ(prop_string.size(), 64);
- EXPECT_EQ(prop_string, bin2hex(verified_boot_hash));
-
- EXPECT_NE(property_get("ro.boot.vbmeta.device_state", property_value, ""), 0);
- if (!strcmp(property_value, "unlocked")) {
- EXPECT_FALSE(device_locked);
- } else {
- EXPECT_TRUE(device_locked);
- }
-
- // Check that the device is locked if not debuggable, e.g., user build
- // images in CTS. For VTS, debuggable images are used to allow adb root
- // and the device is unlocked.
- if (!property_get_bool("ro.debuggable", false)) {
- EXPECT_TRUE(device_locked);
- } else {
- EXPECT_FALSE(device_locked);
- }
- }
-
- // Verified boot key should be all 0's if the boot state is not verified or self signed
- std::string empty_boot_key(32, '\0');
- std::string verified_boot_key_str((const char*)verified_boot_key.data(),
- verified_boot_key.size());
- EXPECT_NE(property_get("ro.boot.verifiedbootstate", property_value, ""), 0);
- if (!strcmp(property_value, "green")) {
- EXPECT_EQ(verified_boot_state, VerifiedBoot::VERIFIED);
- EXPECT_NE(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
- verified_boot_key.size()));
- } else if (!strcmp(property_value, "yellow")) {
- EXPECT_EQ(verified_boot_state, VerifiedBoot::SELF_SIGNED);
- EXPECT_NE(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
- verified_boot_key.size()));
- } else if (!strcmp(property_value, "orange")) {
- EXPECT_EQ(verified_boot_state, VerifiedBoot::UNVERIFIED);
- EXPECT_EQ(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
- verified_boot_key.size()));
- } else if (!strcmp(property_value, "red")) {
- EXPECT_EQ(verified_boot_state, VerifiedBoot::FAILED);
- } else {
- EXPECT_EQ(verified_boot_state, VerifiedBoot::UNVERIFIED);
- EXPECT_NE(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
- verified_boot_key.size()));
- }
-
- att_sw_enforced.Sort();
- expected_sw_enforced.Sort();
- EXPECT_EQ(filtered_tags(expected_sw_enforced), filtered_tags(att_sw_enforced));
-
- att_hw_enforced.Sort();
- expected_hw_enforced.Sort();
- EXPECT_EQ(filtered_tags(expected_hw_enforced), filtered_tags(att_hw_enforced));
-
- return true;
-}
-
std::string make_string(const uint8_t* data, size_t length) {
return std::string(reinterpret_cast<const char*>(data), length);
}
@@ -595,7 +308,7 @@
<< "Key size " << key_size << "missing";
EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
- EXPECT_TRUE(verify_chain(cert_chain_));
+ EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
ASSERT_GT(cert_chain_.size(), 0);
AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
@@ -691,7 +404,7 @@
<< "key usage count limit " << 1U << " missing";
// Check the usage count limit tag also appears in the attestation.
- EXPECT_TRUE(verify_chain(cert_chain_));
+ EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
ASSERT_GT(cert_chain_.size(), 0);
AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
@@ -4596,6 +4309,57 @@
}
}
+/*
+ * UsageCountLimitTest.TestSingleUseKeyAndRollbackResistance
+ *
+ * Verifies that when rollback resistance is supported by the KeyMint implementation with
+ * the secure hardware, the single use key with usage count limit tag = 1 must also be enforced
+ * in hardware.
+ */
+TEST_P(UsageCountLimitTest, TestSingleUseKeyAndRollbackResistance) {
+ if (SecLevel() == SecurityLevel::STRONGBOX) return;
+
+ auto error = GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(2048, 65537)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Authorization(TAG_ROLLBACK_RESISTANCE)
+ .SetDefaultValidity());
+ ASSERT_TRUE(error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE || error == ErrorCode::OK);
+
+ if (error == ErrorCode::OK) {
+ // Rollback resistance is supported by KeyMint, verify it is enforced in hardware.
+ AuthorizationSet hardwareEnforced(SecLevelAuthorizations());
+ ASSERT_TRUE(hardwareEnforced.Contains(TAG_ROLLBACK_RESISTANCE));
+ ASSERT_EQ(ErrorCode::OK, DeleteKey());
+
+ // The KeyMint should also enforce single use key in hardware when it supports rollback
+ // resistance.
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaSigningKey(1024, 65537)
+ .NoDigestOrPadding()
+ .Authorization(TAG_USAGE_COUNT_LIMIT, 1)
+ .SetDefaultValidity()));
+
+ // Check the usage count limit tag appears in the hardware authorizations.
+ AuthorizationSet hardware_auths = HwEnforcedAuthorizations(key_characteristics_);
+ EXPECT_TRUE(hardware_auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U))
+ << "key usage count limit " << 1U << " missing";
+
+ string message = "1234567890123456";
+ auto params = AuthorizationSetBuilder().NoDigestOrPadding();
+
+ // First usage of RSA key should work.
+ SignMessage(message, params);
+
+ // Usage count limit tag is enforced by hardware. After using the key, the key blob
+ // must be invalidated from secure storage (such as RPMB partition).
+ EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB, Begin(KeyPurpose::SIGN, params));
+ }
+}
+
INSTANTIATE_KEYMINT_AIDL_TEST(UsageCountLimitTest);
typedef KeyMintAidlTestBase AddEntropyTest;
@@ -4645,7 +4409,8 @@
.Digest(Digest::NONE)
.Padding(PaddingMode::NONE)
.Authorization(TAG_NO_AUTH_REQUIRED)
- .Authorization(TAG_ROLLBACK_RESISTANCE));
+ .Authorization(TAG_ROLLBACK_RESISTANCE)
+ .SetDefaultValidity());
ASSERT_TRUE(error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE || error == ErrorCode::OK);
// Delete must work if rollback protection is implemented
@@ -4678,7 +4443,8 @@
.Digest(Digest::NONE)
.Padding(PaddingMode::NONE)
.Authorization(TAG_NO_AUTH_REQUIRED)
- .Authorization(TAG_ROLLBACK_RESISTANCE));
+ .Authorization(TAG_ROLLBACK_RESISTANCE)
+ .SetDefaultValidity());
ASSERT_TRUE(error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE || error == ErrorCode::OK);
// Delete must work if rollback protection is implemented
@@ -4961,17 +4727,122 @@
INSTANTIATE_KEYMINT_AIDL_TEST(KeyAgreementTest);
+typedef KeyMintAidlTestBase EarlyBootKeyTest;
+
+TEST_P(EarlyBootKeyTest, CreateEarlyBootKeys) {
+ auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] =
+ CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::OK);
+
+ CheckedDeleteKey(&aesKeyData.blob);
+ CheckedDeleteKey(&hmacKeyData.blob);
+ CheckedDeleteKey(&rsaKeyData.blob);
+ CheckedDeleteKey(&ecdsaKeyData.blob);
+}
+
+// This is a more comprenhensive 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.
+TEST_P(EarlyBootKeyTest, DISABLED_FullTest) {
+ auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] =
+ CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::OK);
+ // TAG_EARLY_BOOT_ONLY should be in hw-enforced.
+ EXPECT_TRUE(HwEnforcedAuthorizations(aesKeyData.characteristics).Contains(TAG_EARLY_BOOT_ONLY));
+ EXPECT_TRUE(
+ HwEnforcedAuthorizations(hmacKeyData.characteristics).Contains(TAG_EARLY_BOOT_ONLY));
+ EXPECT_TRUE(HwEnforcedAuthorizations(rsaKeyData.characteristics).Contains(TAG_EARLY_BOOT_ONLY));
+ EXPECT_TRUE(
+ HwEnforcedAuthorizations(ecdsaKeyData.characteristics).Contains(TAG_EARLY_BOOT_ONLY));
+
+ // Should be able to use keys, since early boot has not ended
+ EXPECT_EQ(ErrorCode::OK, UseAesKey(aesKeyData.blob));
+ EXPECT_EQ(ErrorCode::OK, UseHmacKey(hmacKeyData.blob));
+ EXPECT_EQ(ErrorCode::OK, UseRsaKey(rsaKeyData.blob));
+ EXPECT_EQ(ErrorCode::OK, UseEcdsaKey(ecdsaKeyData.blob));
+
+ // End early boot
+ ErrorCode earlyBootResult = GetReturnErrorCode(keyMint().earlyBootEnded());
+ EXPECT_EQ(earlyBootResult, ErrorCode::OK);
+
+ // Should not be able to use already-created keys.
+ EXPECT_EQ(ErrorCode::EARLY_BOOT_ENDED, UseAesKey(aesKeyData.blob));
+ EXPECT_EQ(ErrorCode::EARLY_BOOT_ENDED, UseHmacKey(hmacKeyData.blob));
+ EXPECT_EQ(ErrorCode::EARLY_BOOT_ENDED, UseRsaKey(rsaKeyData.blob));
+ EXPECT_EQ(ErrorCode::EARLY_BOOT_ENDED, UseEcdsaKey(ecdsaKeyData.blob));
+
+ CheckedDeleteKey(&aesKeyData.blob);
+ CheckedDeleteKey(&hmacKeyData.blob);
+ CheckedDeleteKey(&rsaKeyData.blob);
+ CheckedDeleteKey(&ecdsaKeyData.blob);
+
+ // Should not be able to create new keys
+ std::tie(aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData) =
+ CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::EARLY_BOOT_ENDED);
+
+ CheckedDeleteKey(&aesKeyData.blob);
+ CheckedDeleteKey(&hmacKeyData.blob);
+ CheckedDeleteKey(&rsaKeyData.blob);
+ CheckedDeleteKey(&ecdsaKeyData.blob);
+}
+INSTANTIATE_KEYMINT_AIDL_TEST(EarlyBootKeyTest);
+
+typedef KeyMintAidlTestBase UnlockedDeviceRequiredTest;
+
+// This may be a problematic test. It can't be run repeatedly without unlocking the device in
+// between runs... and on most test devices there are no enrolled credentials so it can't be
+// unlocked at all, meaning the only way to get the test to pass again on a properly-functioning
+// device is to reboot it. For that reason, this is disabled by default. It can be used as part of
+// a manual test process, which includes unlocking between runs, which is why it's included here.
+// Well, that and the fact that it's the only test we can do without also making calls into the
+// Gatekeeper HAL. We haven't written any cross-HAL tests, and don't know what all of the
+// implications might be, so that may or may not be a solution.
+TEST_P(UnlockedDeviceRequiredTest, DISABLED_KeysBecomeUnusable) {
+ auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] =
+ CreateTestKeys(TAG_UNLOCKED_DEVICE_REQUIRED, ErrorCode::OK);
+
+ EXPECT_EQ(ErrorCode::OK, UseAesKey(aesKeyData.blob));
+ EXPECT_EQ(ErrorCode::OK, UseHmacKey(hmacKeyData.blob));
+ EXPECT_EQ(ErrorCode::OK, UseRsaKey(rsaKeyData.blob));
+ EXPECT_EQ(ErrorCode::OK, UseEcdsaKey(ecdsaKeyData.blob));
+
+ ErrorCode rc = GetReturnErrorCode(
+ keyMint().deviceLocked(false /* passwordOnly */, {} /* verificationToken */));
+ ASSERT_EQ(ErrorCode::OK, rc);
+ EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseAesKey(aesKeyData.blob));
+ EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseHmacKey(hmacKeyData.blob));
+ EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseRsaKey(rsaKeyData.blob));
+ EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseEcdsaKey(ecdsaKeyData.blob));
+
+ CheckedDeleteKey(&aesKeyData.blob);
+ CheckedDeleteKey(&hmacKeyData.blob);
+ CheckedDeleteKey(&rsaKeyData.blob);
+ CheckedDeleteKey(&ecdsaKeyData.blob);
+}
+INSTANTIATE_KEYMINT_AIDL_TEST(UnlockedDeviceRequiredTest);
+
} // namespace aidl::android::hardware::security::keymint::test
int main(int argc, char** argv) {
+ std::cout << "Testing ";
+ auto halInstances =
+ aidl::android::hardware::security::keymint::test::KeyMintAidlTestBase::build_params();
+ std::cout << "HAL instances:\n";
+ for (auto& entry : halInstances) {
+ std::cout << " " << entry << '\n';
+ }
+
::testing::InitGoogleTest(&argc, argv);
for (int i = 1; i < argc; ++i) {
if (argv[i][0] == '-') {
if (std::string(argv[i]) == "--arm_deleteAllKeys") {
- arm_deleteAllKeys = true;
+ aidl::android::hardware::security::keymint::test::KeyMintAidlTestBase::
+ arm_deleteAllKeys = true;
}
if (std::string(argv[i]) == "--dump_attestations") {
- dump_Attestations = true;
+ aidl::android::hardware::security::keymint::test::KeyMintAidlTestBase::
+ dump_Attestations = true;
+ } else {
+ std::cout << "NOT dumping attestations" << std::endl;
}
}
}
diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
new file mode 100644
index 0000000..45f9df6
--- /dev/null
+++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
@@ -0,0 +1,432 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "VtsRemotelyProvisionableComponentTests"
+
+#include <RemotelyProvisionedComponent.h>
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.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 <gtest/gtest.h>
+#include <keymaster/keymaster_configuration.h>
+#include <remote_prov/remote_prov_utils.h>
+
+namespace aidl::android::hardware::security::keymint::test {
+
+using ::std::string;
+using ::std::vector;
+
+namespace {
+
+#define INSTANTIATE_REM_PROV_AIDL_TEST(name) \
+ INSTANTIATE_TEST_SUITE_P( \
+ PerInstance, name, \
+ testing::ValuesIn(VtsRemotelyProvisionedComponentTests::build_params()), \
+ ::android::PrintInstanceNameToString)
+
+using bytevec = std::vector<uint8_t>;
+using testing::MatchesRegex;
+using namespace remote_prov;
+using namespace keymaster;
+
+bytevec string_to_bytevec(const char* s) {
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(s);
+ return bytevec(p, p + strlen(s));
+}
+
+} // namespace
+
+class VtsRemotelyProvisionedComponentTests : public testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ if (AServiceManager_isDeclared(GetParam().c_str())) {
+ ::ndk::SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
+ provisionable_ = IRemotelyProvisionedComponent::fromBinder(binder);
+ }
+ ASSERT_NE(provisionable_, nullptr);
+ }
+
+ static vector<string> build_params() {
+ auto params = ::android::getAidlHalInstanceNames(IRemotelyProvisionedComponent::descriptor);
+ return params;
+ }
+
+ protected:
+ std::shared_ptr<IRemotelyProvisionedComponent> provisionable_;
+};
+
+using GenerateKeyTests = VtsRemotelyProvisionedComponentTests;
+
+INSTANTIATE_REM_PROV_AIDL_TEST(GenerateKeyTests);
+
+/**
+ * Generate and validate a production-mode key. MAC tag can't be verified.
+ */
+TEST_P(GenerateKeyTests, DISABLED_generateEcdsaP256Key_prodMode) {
+ MacedPublicKey macedPubKey;
+ bytevec privateKeyBlob;
+ bool testMode = false;
+ auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
+ ASSERT_TRUE(status.isOk());
+
+ auto [coseMac0, _, mac0ParseErr] = cppbor::parse(macedPubKey.macedKey);
+ ASSERT_TRUE(coseMac0) << "COSE Mac0 parse failed " << mac0ParseErr;
+
+ ASSERT_NE(coseMac0->asArray(), nullptr);
+ ASSERT_EQ(coseMac0->asArray()->size(), kCoseMac0EntryCount);
+
+ auto protParms = coseMac0->asArray()->get(kCoseMac0ProtectedParams)->asBstr();
+ ASSERT_NE(protParms, nullptr);
+ ASSERT_EQ(cppbor::prettyPrint(protParms->value()), "{\n 1 : 5,\n}");
+
+ auto unprotParms = coseMac0->asArray()->get(kCoseMac0UnprotectedParams)->asBstr();
+ ASSERT_NE(unprotParms, nullptr);
+ ASSERT_EQ(unprotParms->value().size(), 0);
+
+ auto payload = coseMac0->asArray()->get(kCoseMac0Payload)->asBstr();
+ ASSERT_NE(payload, nullptr);
+ auto [parsedPayload, __, payloadParseErr] = cppbor::parse(payload->value());
+ ASSERT_TRUE(parsedPayload) << "Key parse failed: " << payloadParseErr;
+ EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
+ MatchesRegex("{\n"
+ " 1 : 2,\n"
+ " 3 : -7,\n"
+ " -1 : 1,\n"
+ // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a sequence of
+ // 32 hexadecimal bytes, enclosed in braces and separated by commas.
+ // In this case, some Ed25519 public key.
+ " -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"
+ " -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"
+ "}"));
+
+ auto coseMac0Tag = coseMac0->asArray()->get(kCoseMac0Tag)->asBstr();
+ ASSERT_TRUE(coseMac0Tag);
+ auto extractedTag = coseMac0Tag->value();
+ EXPECT_EQ(extractedTag.size(), 32U);
+
+ // Compare with tag generated with kTestMacKey. Shouldn't match.
+ auto testTag = cppcose::generateCoseMac0Mac(remote_prov::kTestMacKey, {} /* external_aad */,
+ payload->value());
+ ASSERT_TRUE(testTag) << "Tag calculation failed: " << testTag.message();
+
+ EXPECT_NE(*testTag, extractedTag);
+}
+
+/**
+ * Generate and validate a test-mode key.
+ */
+TEST_P(GenerateKeyTests, DISABLED_generateEcdsaP256Key_testMode) {
+ MacedPublicKey macedPubKey;
+ bytevec privateKeyBlob;
+ bool testMode = true;
+ auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
+ ASSERT_TRUE(status.isOk());
+
+ auto [coseMac0, _, mac0ParseErr] = cppbor::parse(macedPubKey.macedKey);
+ ASSERT_TRUE(coseMac0) << "COSE Mac0 parse failed " << mac0ParseErr;
+
+ ASSERT_NE(coseMac0->asArray(), nullptr);
+ ASSERT_EQ(coseMac0->asArray()->size(), kCoseMac0EntryCount);
+
+ auto protParms = coseMac0->asArray()->get(kCoseMac0ProtectedParams)->asBstr();
+ ASSERT_NE(protParms, nullptr);
+ ASSERT_EQ(cppbor::prettyPrint(protParms->value()), "{\n 1 : 5,\n}");
+
+ auto unprotParms = coseMac0->asArray()->get(kCoseMac0UnprotectedParams)->asBstr();
+ ASSERT_NE(unprotParms, nullptr);
+ ASSERT_EQ(unprotParms->value().size(), 0);
+
+ auto payload = coseMac0->asArray()->get(kCoseMac0Payload)->asBstr();
+ ASSERT_NE(payload, nullptr);
+ auto [parsedPayload, __, payloadParseErr] = cppbor::parse(payload->value());
+ ASSERT_TRUE(parsedPayload) << "Key parse failed: " << payloadParseErr;
+ EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
+ MatchesRegex("{\n"
+ " 1 : 2,\n"
+ " 3 : -7,\n"
+ " -1 : 1,\n"
+ // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a sequence of
+ // 32 hexadecimal bytes, enclosed in braces and separated by commas.
+ // In this case, some Ed25519 public key.
+ " -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"
+ " -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"
+ " -70000 : null,\n"
+ "}"));
+
+ auto coseMac0Tag = coseMac0->asArray()->get(kCoseMac0Tag)->asBstr();
+ ASSERT_TRUE(coseMac0);
+ auto extractedTag = coseMac0Tag->value();
+ EXPECT_EQ(extractedTag.size(), 32U);
+
+ // Compare with tag generated with kTestMacKey. Should match.
+ auto testTag = cppcose::generateCoseMac0Mac(remote_prov::kTestMacKey, {} /* external_aad */,
+ payload->value());
+ ASSERT_TRUE(testTag) << testTag.message();
+
+ EXPECT_EQ(*testTag, extractedTag);
+}
+
+class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests {
+ protected:
+ CertificateRequestTest() : eekId_(string_to_bytevec("eekid")) {
+ auto chain = generateEekChain(3, eekId_);
+ EXPECT_TRUE(chain) << chain.message();
+ if (chain) eekChain_ = chain.moveValue();
+ }
+
+ void generateKeys(bool testMode, size_t numKeys) {
+ keysToSign_ = std::vector<MacedPublicKey>(numKeys);
+ cborKeysToSign_ = cppbor::Array();
+
+ for (auto& key : keysToSign_) {
+ bytevec privateKeyBlob;
+ auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &key, &privateKeyBlob);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ auto [parsedMacedKey, _, parseErr] = cppbor::parse(key.macedKey);
+ ASSERT_TRUE(parsedMacedKey) << "Failed parsing MACed key: " << parseErr;
+ ASSERT_TRUE(parsedMacedKey->asArray()) << "COSE_Mac0 not an array?";
+ ASSERT_EQ(parsedMacedKey->asArray()->size(), kCoseMac0EntryCount);
+
+ auto& payload = parsedMacedKey->asArray()->get(kCoseMac0Payload);
+ ASSERT_TRUE(payload);
+ ASSERT_TRUE(payload->asBstr());
+
+ cborKeysToSign_.add(cppbor::EncodedItem(payload->asBstr()->value()));
+ }
+ }
+
+ bytevec eekId_;
+ EekChain eekChain_;
+ std::vector<MacedPublicKey> keysToSign_;
+ cppbor::Array cborKeysToSign_;
+};
+
+/**
+ * Generate an empty certificate request in test mode, and decrypt and verify the structure and
+ * content.
+ */
+TEST_P(CertificateRequestTest, DISABLED_EmptyRequest_testMode) {
+ bool testMode = true;
+ bytevec keysToSignMac;
+ ProtectedData protectedData;
+ auto challenge = randomBytes(32);
+ auto status = provisionable_->generateCertificateRequest(testMode, {} /* keysToSign */,
+ eekChain_.chain, challenge,
+ &keysToSignMac, &protectedData);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ auto [parsedProtectedData, _, protDataErrMsg] = cppbor::parse(protectedData.protectedData);
+ ASSERT_TRUE(parsedProtectedData) << protDataErrMsg;
+ ASSERT_TRUE(parsedProtectedData->asArray());
+ ASSERT_EQ(parsedProtectedData->asArray()->size(), kCoseEncryptEntryCount);
+
+ auto senderPubkey = getSenderPubKeyFromCoseEncrypt(parsedProtectedData);
+ ASSERT_TRUE(senderPubkey) << senderPubkey.message();
+ EXPECT_EQ(senderPubkey->second, eekId_);
+
+ auto sessionKey = x25519_HKDF_DeriveKey(eekChain_.last_pubkey, eekChain_.last_privkey,
+ senderPubkey->first, false /* senderIsA */);
+ ASSERT_TRUE(sessionKey) << sessionKey.message();
+
+ auto protectedDataPayload =
+ decryptCoseEncrypt(*sessionKey, parsedProtectedData.get(), bytevec{} /* aad */);
+ ASSERT_TRUE(protectedDataPayload) << protectedDataPayload.message();
+
+ auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(*protectedDataPayload);
+ ASSERT_TRUE(parsedPayload) << "Failed to parse payload: " << payloadErrMsg;
+ ASSERT_TRUE(parsedPayload->asArray());
+ EXPECT_EQ(parsedPayload->asArray()->size(), 2U);
+
+ auto& signedMac = parsedPayload->asArray()->get(0);
+ auto& bcc = parsedPayload->asArray()->get(1);
+ ASSERT_TRUE(signedMac && signedMac->asArray());
+ ASSERT_TRUE(bcc && bcc->asArray());
+
+ // BCC is [ pubkey, + BccEntry]
+ auto bccContents = validateBcc(bcc->asArray());
+ ASSERT_TRUE(bccContents) << "\n" << bccContents.message() << "\n" << prettyPrint(bcc.get());
+ ASSERT_GT(bccContents->size(), 0U);
+
+ auto& signingKey = bccContents->back().pubKey;
+ auto macKey = verifyAndParseCoseSign1(testMode, signedMac->asArray(), signingKey,
+ cppbor::Array() // DeviceInfo
+ .add(challenge) //
+ .add(cppbor::Map())
+ .encode());
+ ASSERT_TRUE(macKey) << macKey.message();
+
+ auto coseMac0 = cppbor::Array()
+ .add(cppbor::Map() // protected
+ .add(ALGORITHM, HMAC_256)
+ .canonicalize()
+ .encode())
+ .add(cppbor::Bstr()) // unprotected
+ .add(cppbor::Array().encode()) // payload (keysToSign)
+ .add(std::move(keysToSignMac)); // tag
+
+ auto macPayload = verifyAndParseCoseMac0(&coseMac0, *macKey);
+ ASSERT_TRUE(macPayload) << macPayload.message();
+}
+
+/**
+ * Generate an empty certificate request in prod mode. Generation will fail because we don't have a
+ * valid GEEK.
+ *
+ * TODO(swillden): Get a valid GEEK and use it so the generation can succeed, though we won't be
+ * able to decrypt.
+ */
+TEST_P(CertificateRequestTest, DISABLED_EmptyRequest_prodMode) {
+ bool testMode = false;
+ bytevec keysToSignMac;
+ ProtectedData protectedData;
+ auto challenge = randomBytes(32);
+ auto status = provisionable_->generateCertificateRequest(testMode, {} /* keysToSign */,
+ eekChain_.chain, challenge,
+ &keysToSignMac, &protectedData);
+ ASSERT_FALSE(status.isOk());
+ ASSERT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
+}
+
+/**
+ * Generate a non-empty certificate request in test mode. Decrypt, parse and validate the contents.
+ */
+TEST_P(CertificateRequestTest, DISABLED_NonEmptyRequest_testMode) {
+ bool testMode = true;
+ generateKeys(testMode, 4 /* numKeys */);
+
+ bytevec keysToSignMac;
+ ProtectedData protectedData;
+ auto challenge = randomBytes(32);
+ auto status = provisionable_->generateCertificateRequest(
+ testMode, keysToSign_, eekChain_.chain, challenge, &keysToSignMac, &protectedData);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ auto [parsedProtectedData, _, protDataErrMsg] = cppbor::parse(protectedData.protectedData);
+ ASSERT_TRUE(parsedProtectedData) << protDataErrMsg;
+ ASSERT_TRUE(parsedProtectedData->asArray());
+ ASSERT_EQ(parsedProtectedData->asArray()->size(), kCoseEncryptEntryCount);
+
+ auto senderPubkey = getSenderPubKeyFromCoseEncrypt(parsedProtectedData);
+ ASSERT_TRUE(senderPubkey) << senderPubkey.message();
+ EXPECT_EQ(senderPubkey->second, eekId_);
+
+ auto sessionKey = x25519_HKDF_DeriveKey(eekChain_.last_pubkey, eekChain_.last_privkey,
+ senderPubkey->first, false /* senderIsA */);
+ ASSERT_TRUE(sessionKey) << sessionKey.message();
+
+ auto protectedDataPayload =
+ decryptCoseEncrypt(*sessionKey, parsedProtectedData.get(), bytevec{} /* aad */);
+ ASSERT_TRUE(protectedDataPayload) << protectedDataPayload.message();
+
+ auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(*protectedDataPayload);
+ ASSERT_TRUE(parsedPayload) << "Failed to parse payload: " << payloadErrMsg;
+ ASSERT_TRUE(parsedPayload->asArray());
+ EXPECT_EQ(parsedPayload->asArray()->size(), 2U);
+
+ auto& signedMac = parsedPayload->asArray()->get(0);
+ auto& bcc = parsedPayload->asArray()->get(1);
+ ASSERT_TRUE(signedMac && signedMac->asArray());
+ ASSERT_TRUE(bcc);
+
+ auto bccContents = validateBcc(bcc->asArray());
+ ASSERT_TRUE(bccContents) << "\n" << prettyPrint(bcc.get());
+ ASSERT_GT(bccContents->size(), 0U);
+
+ auto& signingKey = bccContents->back().pubKey;
+ auto macKey = verifyAndParseCoseSign1(testMode, signedMac->asArray(), signingKey,
+ cppbor::Array() // DeviceInfo
+ .add(challenge) //
+ .add(cppbor::Array())
+ .encode());
+ ASSERT_TRUE(macKey) << macKey.message();
+
+ auto coseMac0 = cppbor::Array()
+ .add(cppbor::Map() // protected
+ .add(ALGORITHM, HMAC_256)
+ .canonicalize()
+ .encode())
+ .add(cppbor::Bstr()) // unprotected
+ .add(cborKeysToSign_.encode()) // payload
+ .add(std::move(keysToSignMac)); // tag
+
+ auto macPayload = verifyAndParseCoseMac0(&coseMac0, *macKey);
+ ASSERT_TRUE(macPayload) << macPayload.message();
+}
+
+/**
+ * Generate a non-empty certificate request in prod mode. Must fail because we don't have a valid
+ * GEEK.
+ *
+ * TODO(swillden): Get a valid GEEK and use it so the generation can succeed, though we won't be
+ * able to decrypt.
+ */
+TEST_P(CertificateRequestTest, DISABLED_NonEmptyRequest_prodMode) {
+ bool testMode = false;
+ generateKeys(testMode, 4 /* numKeys */);
+
+ bytevec keysToSignMac;
+ ProtectedData protectedData;
+ auto challenge = randomBytes(32);
+ auto status = provisionable_->generateCertificateRequest(
+ testMode, keysToSign_, eekChain_.chain, challenge, &keysToSignMac, &protectedData);
+ ASSERT_FALSE(status.isOk());
+ ASSERT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
+}
+
+/**
+ * Generate a non-empty certificate request in test mode, with prod keys. Must fail with
+ * STATUS_PRODUCTION_KEY_IN_TEST_REQUEST.
+ */
+TEST_P(CertificateRequestTest, DISABLED_NonEmptyRequest_prodKeyInTestCert) {
+ generateKeys(false /* testMode */, 2 /* numKeys */);
+
+ bytevec keysToSignMac;
+ ProtectedData protectedData;
+ auto challenge = randomBytes(32);
+ auto status = provisionable_->generateCertificateRequest(true /* testMode */, keysToSign_,
+ eekChain_.chain, challenge,
+ &keysToSignMac, &protectedData);
+ ASSERT_FALSE(status.isOk());
+ ASSERT_EQ(status.getServiceSpecificError(),
+ BnRemotelyProvisionedComponent::STATUS_PRODUCTION_KEY_IN_TEST_REQUEST);
+}
+
+/**
+ * Generate a non-empty certificate request in prod mode, with test keys. Must fail with
+ * STATUS_TEST_KEY_IN_PRODUCTION_REQUEST.
+ */
+TEST_P(CertificateRequestTest, DISABLED_NonEmptyRequest_testKeyInProdCert) {
+ generateKeys(true /* testMode */, 2 /* numKeys */);
+
+ bytevec keysToSignMac;
+ ProtectedData protectedData;
+ auto status = provisionable_->generateCertificateRequest(
+ false /* testMode */, keysToSign_, eekChain_.chain, randomBytes(32) /* challenge */,
+ &keysToSignMac, &protectedData);
+ ASSERT_FALSE(status.isOk());
+ ASSERT_EQ(status.getServiceSpecificError(),
+ BnRemotelyProvisionedComponent::STATUS_TEST_KEY_IN_PRODUCTION_REQUEST);
+}
+
+INSTANTIATE_REM_PROV_AIDL_TEST(CertificateRequestTest);
+
+} // namespace aidl::android::hardware::security::keymint::test
diff --git a/security/keymint/aidl/vts/performance/Android.bp b/security/keymint/aidl/vts/performance/Android.bp
new file mode 100644
index 0000000..03240c3
--- /dev/null
+++ b/security/keymint/aidl/vts/performance/Android.bp
@@ -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.
+//
+
+cc_benchmark {
+ name: "VtsAidlKeyMintBenchmarkTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: [
+ "KeyMintBenchmark.cpp",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ "libcrypto",
+ "libkeymint",
+ "libkeymint_support",
+ ],
+ static_libs: [
+ "android.hardware.security.keymint-V1-ndk_platform",
+ "android.hardware.security.secureclock-V1-ndk_platform",
+ "libcppbor_external",
+ "libchrome",
+ ],
+}
diff --git a/security/keymint/aidl/vts/performance/KeyMintBenchmark.cpp b/security/keymint/aidl/vts/performance/KeyMintBenchmark.cpp
new file mode 100644
index 0000000..f87ca78
--- /dev/null
+++ b/security/keymint/aidl/vts/performance/KeyMintBenchmark.cpp
@@ -0,0 +1,714 @@
+/*
+ * 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_benchmark"
+
+#include <base/command_line.h>
+#include <benchmark/benchmark.h>
+#include <iostream>
+
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <aidl/android/hardware/security/keymint/IKeyMintDevice.h>
+#include <android/binder_manager.h>
+#include <binder/IServiceManager.h>
+#include <keymint_support/authorization_set.h>
+
+#define SMALL_MESSAGE_SIZE 64
+#define MEDIUM_MESSAGE_SIZE 1024
+#define LARGE_MESSAGE_SIZE 131072
+
+namespace aidl::android::hardware::security::keymint::test {
+
+::std::ostream& operator<<(::std::ostream& os, const keymint::AuthorizationSet& set);
+
+using ::android::sp;
+using Status = ::ndk::ScopedAStatus;
+using ::std::optional;
+using ::std::shared_ptr;
+using ::std::string;
+using ::std::vector;
+
+class KeyMintBenchmarkTest {
+ public:
+ KeyMintBenchmarkTest() {
+ message_cache_.push_back(string(SMALL_MESSAGE_SIZE, 'x'));
+ message_cache_.push_back(string(MEDIUM_MESSAGE_SIZE, 'x'));
+ message_cache_.push_back(string(LARGE_MESSAGE_SIZE, 'x'));
+ }
+
+ static KeyMintBenchmarkTest* newInstance(const char* instanceName) {
+ if (AServiceManager_isDeclared(instanceName)) {
+ ::ndk::SpAIBinder binder(AServiceManager_waitForService(instanceName));
+ KeyMintBenchmarkTest* test = new KeyMintBenchmarkTest();
+ test->InitializeKeyMint(IKeyMintDevice::fromBinder(binder));
+ return test;
+ } else {
+ return nullptr;
+ }
+ }
+
+ int getError() { return static_cast<int>(error_); }
+
+ const string& GenerateMessage(int size) {
+ for (const string& message : message_cache_) {
+ if (message.size() == size) {
+ return message;
+ }
+ }
+ string message = string(size, 'x');
+ message_cache_.push_back(message);
+ return std::move(message);
+ }
+
+ optional<BlockMode> getBlockMode(string transform) {
+ if (transform.find("/ECB") != string::npos) {
+ return BlockMode::ECB;
+ } else if (transform.find("/CBC") != string::npos) {
+ return BlockMode::CBC;
+ } else if (transform.find("/CTR") != string::npos) {
+ return BlockMode::CTR;
+ } else if (transform.find("/GCM") != string::npos) {
+ return BlockMode::GCM;
+ }
+ return {};
+ }
+
+ PaddingMode getPadding(string transform, bool sign) {
+ if (transform.find("/PKCS7") != string::npos) {
+ return PaddingMode::PKCS7;
+ } else if (transform.find("/PSS") != string::npos) {
+ return PaddingMode::RSA_PSS;
+ } else if (transform.find("/OAEP") != string::npos) {
+ return PaddingMode::RSA_OAEP;
+ } else if (transform.find("/PKCS1") != string::npos) {
+ return sign ? PaddingMode::RSA_PKCS1_1_5_SIGN : PaddingMode::RSA_PKCS1_1_5_ENCRYPT;
+ } else if (sign && transform.find("RSA") != string::npos) {
+ // RSA defaults to PKCS1 for sign
+ return PaddingMode::RSA_PKCS1_1_5_SIGN;
+ }
+ return PaddingMode::NONE;
+ }
+
+ optional<Algorithm> getAlgorithm(string transform) {
+ if (transform.find("AES") != string::npos) {
+ return Algorithm::AES;
+ } else if (transform.find("Hmac") != string::npos) {
+ return Algorithm::HMAC;
+ } else if (transform.find("DESede") != string::npos) {
+ return Algorithm::TRIPLE_DES;
+ } else if (transform.find("RSA") != string::npos) {
+ return Algorithm::RSA;
+ } else if (transform.find("EC") != string::npos) {
+ return Algorithm::EC;
+ }
+ std::cerr << "Can't find algorithm for " << transform << std::endl;
+ return {};
+ }
+
+ Digest getDigest(string transform) {
+ if (transform.find("MD5") != string::npos) {
+ return Digest::MD5;
+ } else if (transform.find("SHA1") != string::npos ||
+ transform.find("SHA-1") != string::npos) {
+ return Digest::SHA1;
+ } else if (transform.find("SHA224") != string::npos) {
+ return Digest::SHA_2_224;
+ } else if (transform.find("SHA256") != string::npos) {
+ return Digest::SHA_2_256;
+ } else if (transform.find("SHA384") != string::npos) {
+ return Digest::SHA_2_384;
+ } else if (transform.find("SHA512") != string::npos) {
+ return Digest::SHA_2_512;
+ } else if (transform.find("RSA") != string::npos &&
+ transform.find("OAEP") != string::npos) {
+ return Digest::SHA1;
+ } else if (transform.find("Hmac") != string::npos) {
+ return Digest::SHA_2_256;
+ }
+ return Digest::NONE;
+ }
+
+ bool GenerateKey(string transform, int keySize, bool sign = false) {
+ if (transform == key_transform_) {
+ return true;
+ } else if (key_transform_ != "") {
+ // Deleting old key first
+ key_transform_ = "";
+ if (DeleteKey() != ErrorCode::OK) {
+ return false;
+ }
+ }
+ std::optional<Algorithm> algorithm = getAlgorithm(transform);
+ if (!algorithm) {
+ std::cerr << "Error: invalid algorithm " << transform << std::endl;
+ return false;
+ }
+ key_transform_ = transform;
+ AuthorizationSetBuilder authSet = AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Authorization(TAG_PURPOSE, KeyPurpose::ENCRYPT)
+ .Authorization(TAG_PURPOSE, KeyPurpose::DECRYPT)
+ .Authorization(TAG_PURPOSE, KeyPurpose::SIGN)
+ .Authorization(TAG_PURPOSE, KeyPurpose::VERIFY)
+ .Authorization(TAG_KEY_SIZE, keySize)
+ .Authorization(TAG_ALGORITHM, algorithm.value())
+ .Digest(getDigest(transform))
+ .Padding(getPadding(transform, sign));
+ std::optional<BlockMode> blockMode = getBlockMode(transform);
+ if (blockMode) {
+ authSet.BlockMode(blockMode.value());
+ if (blockMode == BlockMode::GCM) {
+ authSet.Authorization(TAG_MIN_MAC_LENGTH, 128);
+ }
+ }
+ if (algorithm == Algorithm::HMAC) {
+ authSet.Authorization(TAG_MIN_MAC_LENGTH, 128);
+ }
+ if (algorithm == Algorithm::RSA) {
+ authSet.Authorization(TAG_RSA_PUBLIC_EXPONENT, 65537U);
+ authSet.SetDefaultValidity();
+ }
+ if (algorithm == Algorithm::EC) {
+ authSet.SetDefaultValidity();
+ }
+ error_ = GenerateKey(authSet);
+ return error_ == ErrorCode::OK;
+ }
+
+ AuthorizationSet getOperationParams(string transform, bool sign = false) {
+ AuthorizationSetBuilder builder = AuthorizationSetBuilder()
+ .Padding(getPadding(transform, sign))
+ .Digest(getDigest(transform));
+ std::optional<BlockMode> blockMode = getBlockMode(transform);
+ if (sign && (transform.find("Hmac") != string::npos)) {
+ builder.Authorization(TAG_MAC_LENGTH, 128);
+ }
+ if (blockMode) {
+ builder.BlockMode(*blockMode);
+ if (blockMode == BlockMode::GCM) {
+ builder.Authorization(TAG_MAC_LENGTH, 128);
+ }
+ }
+ return std::move(builder);
+ }
+
+ optional<string> Process(const string& message, const AuthorizationSet& /*in_params*/,
+ AuthorizationSet* out_params, const string& signature = "") {
+ static const int HIDL_BUFFER_LIMIT = 1 << 14; // 16KB
+ ErrorCode result;
+
+ // Update
+ AuthorizationSet update_params;
+ AuthorizationSet update_out_params;
+ string output;
+ string aidl_output;
+ int32_t input_consumed = 0;
+ int32_t aidl_input_consumed = 0;
+ while (message.length() - input_consumed > 0) {
+ result = Update(update_params, message.substr(input_consumed, HIDL_BUFFER_LIMIT),
+ &update_out_params, &aidl_output, &aidl_input_consumed);
+ if (result != ErrorCode::OK) {
+ error_ = result;
+ return {};
+ }
+ output.append(aidl_output);
+ input_consumed += aidl_input_consumed;
+ aidl_output.clear();
+ }
+
+ // Finish
+ AuthorizationSet finish_params;
+ AuthorizationSet finish_out_params;
+ result = Finish(finish_params, message.substr(input_consumed), signature,
+ &finish_out_params, &aidl_output);
+ if (result != ErrorCode::OK) {
+ error_ = result;
+ return {};
+ }
+ output.append(aidl_output);
+ out_params->push_back(finish_out_params);
+ return output;
+ }
+
+ ErrorCode DeleteKey() {
+ Status result = keymint_->deleteKey(key_blob_);
+ key_blob_ = vector<uint8_t>();
+ return GetReturnErrorCode(result);
+ }
+
+ ErrorCode Begin(KeyPurpose purpose, const AuthorizationSet& in_params,
+ AuthorizationSet* out_params) {
+ Status result;
+ BeginResult out;
+ result = keymint_->begin(purpose, key_blob_, in_params.vector_data(), HardwareAuthToken(),
+ &out);
+ if (result.isOk()) {
+ *out_params = out.params;
+ op_ = out.operation;
+ }
+ return GetReturnErrorCode(result);
+ }
+
+ SecurityLevel securityLevel_;
+ string name_;
+
+ private:
+ ErrorCode GenerateKey(const AuthorizationSet& key_desc,
+ const optional<AttestationKey>& attest_key = std::nullopt) {
+ key_blob_.clear();
+ KeyCreationResult creationResult;
+ Status result = keymint_->generateKey(key_desc.vector_data(), attest_key, &creationResult);
+ if (result.isOk()) {
+ key_blob_ = std::move(creationResult.keyBlob);
+ creationResult.keyCharacteristics.clear();
+ creationResult.certificateChain.clear();
+ }
+ return GetReturnErrorCode(result);
+ }
+
+ void InitializeKeyMint(std::shared_ptr<IKeyMintDevice> keyMint) {
+ if (!keyMint) {
+ std::cerr << "Trying initialize nullptr in InitializeKeyMint" << std::endl;
+ return;
+ }
+ keymint_ = std::move(keyMint);
+ KeyMintHardwareInfo info;
+ Status result = keymint_->getHardwareInfo(&info);
+ if (!result.isOk()) {
+ std::cerr << "InitializeKeyMint: getHardwareInfo failed with "
+ << result.getServiceSpecificError() << std::endl;
+ }
+ securityLevel_ = info.securityLevel;
+ name_.assign(info.keyMintName.begin(), info.keyMintName.end());
+ }
+
+ ErrorCode Finish(const AuthorizationSet& in_params, const string& input,
+ const string& signature, AuthorizationSet* out_params, string* output) {
+ Status result;
+ if (!op_) {
+ std::cerr << "Finish: Operation is nullptr" << std::endl;
+ return ErrorCode::UNEXPECTED_NULL_POINTER;
+ }
+ KeyParameterArray key_params;
+ key_params.params = in_params.vector_data();
+
+ KeyParameterArray in_keyParams;
+ in_keyParams.params = in_params.vector_data();
+
+ std::optional<KeyParameterArray> out_keyParams;
+ std::optional<vector<uint8_t>> o_put;
+
+ vector<uint8_t> oPut;
+ result = op_->finish(in_keyParams, vector<uint8_t>(input.begin(), input.end()),
+ vector<uint8_t>(signature.begin(), signature.end()), {}, {},
+ &out_keyParams, &oPut);
+
+ if (result.isOk()) {
+ if (out_keyParams) {
+ out_params->push_back(AuthorizationSet(out_keyParams->params));
+ }
+ output->append(oPut.begin(), oPut.end());
+ }
+ op_.reset();
+ return GetReturnErrorCode(result);
+ }
+
+ ErrorCode Update(const AuthorizationSet& in_params, const string& input,
+ AuthorizationSet* out_params, string* output, int32_t* input_consumed) {
+ Status result;
+ if (!op_) {
+ std::cerr << "Update: Operation is nullptr" << std::endl;
+ return ErrorCode::UNEXPECTED_NULL_POINTER;
+ }
+
+ KeyParameterArray key_params;
+ key_params.params = in_params.vector_data();
+
+ KeyParameterArray in_keyParams;
+ in_keyParams.params = in_params.vector_data();
+
+ std::optional<KeyParameterArray> out_keyParams;
+ std::optional<ByteArray> o_put;
+ result = op_->update(in_keyParams, vector<uint8_t>(input.begin(), input.end()), {}, {},
+ &out_keyParams, &o_put, input_consumed);
+
+ if (result.isOk()) {
+ if (o_put) {
+ output->append(o_put->data.begin(), o_put->data.end());
+ }
+
+ if (out_keyParams) {
+ out_params->push_back(AuthorizationSet(out_keyParams->params));
+ }
+ }
+
+ return GetReturnErrorCode(result);
+ }
+
+ ErrorCode GetReturnErrorCode(const Status& result) {
+ error_ = static_cast<ErrorCode>(result.getServiceSpecificError());
+ if (result.isOk()) return ErrorCode::OK;
+
+ if (result.getExceptionCode() == EX_SERVICE_SPECIFIC) {
+ return static_cast<ErrorCode>(result.getServiceSpecificError());
+ }
+
+ return ErrorCode::UNKNOWN_ERROR;
+ }
+
+ std::shared_ptr<IKeyMintOperation> op_;
+ vector<Certificate> cert_chain_;
+ vector<uint8_t> key_blob_;
+ vector<KeyCharacteristics> key_characteristics_;
+ std::shared_ptr<IKeyMintDevice> keymint_;
+ std::vector<string> message_cache_;
+ std::string key_transform_;
+ ErrorCode error_;
+};
+
+KeyMintBenchmarkTest* keymintTest;
+
+static void settings(benchmark::internal::Benchmark* benchmark) {
+ benchmark->Unit(benchmark::kMillisecond);
+}
+
+static void addDefaultLabel(benchmark::State& state) {
+ std::string secLevel;
+ switch (keymintTest->securityLevel_) {
+ case SecurityLevel::STRONGBOX:
+ secLevel = "STRONGBOX";
+ break;
+ case SecurityLevel::SOFTWARE:
+ secLevel = "SOFTWARE";
+ break;
+ case SecurityLevel::TRUSTED_ENVIRONMENT:
+ secLevel = "TEE";
+ break;
+ case SecurityLevel::KEYSTORE:
+ secLevel = "KEYSTORE";
+ break;
+ }
+ state.SetLabel("hardware_name:" + keymintTest->name_ + " sec_level:" + secLevel);
+}
+
+// clang-format off
+#define BENCHMARK_KM(func, transform, keySize) \
+ BENCHMARK_CAPTURE(func, transform/keySize, #transform "/" #keySize, keySize)->Apply(settings);
+#define BENCHMARK_KM_MSG(func, transform, keySize, msgSize) \
+ BENCHMARK_CAPTURE(func, transform/keySize/msgSize, #transform "/" #keySize "/" #msgSize, \
+ keySize, msgSize) \
+ ->Apply(settings);
+
+#define BENCHMARK_KM_ALL_MSGS(func, transform, keySize) \
+ BENCHMARK_KM_MSG(func, transform, keySize, SMALL_MESSAGE_SIZE) \
+ BENCHMARK_KM_MSG(func, transform, keySize, MEDIUM_MESSAGE_SIZE) \
+ BENCHMARK_KM_MSG(func, transform, keySize, LARGE_MESSAGE_SIZE)
+
+#define BENCHMARK_KM_CIPHER(transform, keySize, msgSize) \
+ BENCHMARK_KM_MSG(encrypt, transform, keySize, msgSize) \
+ BENCHMARK_KM_MSG(decrypt, transform, keySize, msgSize)
+
+#define BENCHMARK_KM_CIPHER_ALL_MSGS(transform, keySize) \
+ BENCHMARK_KM_ALL_MSGS(encrypt, transform, keySize) \
+ BENCHMARK_KM_ALL_MSGS(decrypt, transform, keySize)
+
+#define BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, keySize) \
+ BENCHMARK_KM_ALL_MSGS(sign, transform, keySize) \
+ BENCHMARK_KM_ALL_MSGS(verify, transform, keySize)
+// clang-format on
+
+/*
+ * ============= KeyGen TESTS ==================
+ */
+static void keygen(benchmark::State& state, string transform, int keySize) {
+ addDefaultLabel(state);
+ for (auto _ : state) {
+ if (!keymintTest->GenerateKey(transform, keySize)) {
+ state.SkipWithError(
+ ("Key generation error, " + std::to_string(keymintTest->getError())).c_str());
+ }
+ state.PauseTiming();
+
+ keymintTest->DeleteKey();
+ state.ResumeTiming();
+ }
+}
+
+BENCHMARK_KM(keygen, AES, 128);
+BENCHMARK_KM(keygen, AES, 256);
+
+BENCHMARK_KM(keygen, RSA, 2048);
+BENCHMARK_KM(keygen, RSA, 3072);
+BENCHMARK_KM(keygen, RSA, 4096);
+
+BENCHMARK_KM(keygen, EC, 224);
+BENCHMARK_KM(keygen, EC, 256);
+BENCHMARK_KM(keygen, EC, 384);
+BENCHMARK_KM(keygen, EC, 521);
+
+BENCHMARK_KM(keygen, DESede, 168);
+
+BENCHMARK_KM(keygen, Hmac, 64);
+BENCHMARK_KM(keygen, Hmac, 128);
+BENCHMARK_KM(keygen, Hmac, 256);
+BENCHMARK_KM(keygen, Hmac, 512);
+
+/*
+ * ============= SIGNATURE TESTS ==================
+ */
+
+static void sign(benchmark::State& state, string transform, int keySize, int msgSize) {
+ addDefaultLabel(state);
+ if (!keymintTest->GenerateKey(transform, keySize, true)) {
+ state.SkipWithError(
+ ("Key generation error, " + std::to_string(keymintTest->getError())).c_str());
+ return;
+ }
+
+ auto in_params = keymintTest->getOperationParams(transform, true);
+ AuthorizationSet out_params;
+ string message = keymintTest->GenerateMessage(msgSize);
+
+ for (auto _ : state) {
+ state.PauseTiming();
+ ErrorCode error = keymintTest->Begin(KeyPurpose::SIGN, in_params, &out_params);
+ if (error != ErrorCode::OK) {
+ state.SkipWithError(
+ ("Error beginning sign, " + std::to_string(keymintTest->getError())).c_str());
+ return;
+ }
+ state.ResumeTiming();
+ out_params.Clear();
+ if (!keymintTest->Process(message, in_params, &out_params)) {
+ state.SkipWithError(("Sign error, " + std::to_string(keymintTest->getError())).c_str());
+ break;
+ }
+ }
+}
+
+static void verify(benchmark::State& state, string transform, int keySize, int msgSize) {
+ addDefaultLabel(state);
+ if (!keymintTest->GenerateKey(transform, keySize, true)) {
+ state.SkipWithError(
+ ("Key generation error, " + std::to_string(keymintTest->getError())).c_str());
+ return;
+ }
+ AuthorizationSet out_params;
+ auto in_params = keymintTest->getOperationParams(transform, true);
+ string message = keymintTest->GenerateMessage(msgSize);
+ ErrorCode error = keymintTest->Begin(KeyPurpose::SIGN, in_params, &out_params);
+ if (error != ErrorCode::OK) {
+ state.SkipWithError(
+ ("Error beginning sign, " + std::to_string(keymintTest->getError())).c_str());
+ return;
+ }
+ std::optional<string> signature = keymintTest->Process(message, in_params, &out_params);
+ if (!signature) {
+ state.SkipWithError(("Sign error, " + std::to_string(keymintTest->getError())).c_str());
+ return;
+ }
+ out_params.Clear();
+ if (transform.find("Hmac") != string::npos) {
+ in_params = keymintTest->getOperationParams(transform, false);
+ }
+ for (auto _ : state) {
+ state.PauseTiming();
+ error = keymintTest->Begin(KeyPurpose::VERIFY, in_params, &out_params);
+ if (error != ErrorCode::OK) {
+ state.SkipWithError(
+ ("Verify begin error, " + std::to_string(keymintTest->getError())).c_str());
+ return;
+ }
+ state.ResumeTiming();
+ if (!keymintTest->Process(message, in_params, &out_params, *signature)) {
+ state.SkipWithError(
+ ("Verify error, " + std::to_string(keymintTest->getError())).c_str());
+ break;
+ }
+ }
+}
+
+// clang-format off
+#define BENCHMARK_KM_SIGNATURE_ALL_HMAC_KEYS(transform) \
+ BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 64) \
+ BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 128) \
+ BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 256) \
+ BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 512)
+
+BENCHMARK_KM_SIGNATURE_ALL_HMAC_KEYS(HmacSHA1)
+BENCHMARK_KM_SIGNATURE_ALL_HMAC_KEYS(HmacSHA256)
+BENCHMARK_KM_SIGNATURE_ALL_HMAC_KEYS(HmacSHA224)
+BENCHMARK_KM_SIGNATURE_ALL_HMAC_KEYS(HmacSHA256)
+BENCHMARK_KM_SIGNATURE_ALL_HMAC_KEYS(HmacSHA384)
+BENCHMARK_KM_SIGNATURE_ALL_HMAC_KEYS(HmacSHA512)
+
+#define BENCHMARK_KM_SIGNATURE_ALL_ECDSA_KEYS(transform) \
+ BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 224) \
+ BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 256) \
+ BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 384) \
+ BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 521)
+
+BENCHMARK_KM_SIGNATURE_ALL_ECDSA_KEYS(NONEwithECDSA);
+BENCHMARK_KM_SIGNATURE_ALL_ECDSA_KEYS(SHA1withECDSA);
+BENCHMARK_KM_SIGNATURE_ALL_ECDSA_KEYS(SHA224withECDSA);
+BENCHMARK_KM_SIGNATURE_ALL_ECDSA_KEYS(SHA256withECDSA);
+BENCHMARK_KM_SIGNATURE_ALL_ECDSA_KEYS(SHA384withECDSA);
+BENCHMARK_KM_SIGNATURE_ALL_ECDSA_KEYS(SHA512withECDSA);
+
+#define BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(transform) \
+ BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 2048) \
+ BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 3072) \
+ BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 4096)
+
+BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(MD5withRSA);
+BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA1withRSA);
+BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA224withRSA);
+BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA384withRSA);
+BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA512withRSA);
+
+BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(MD5withRSA/PSS);
+BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA1withRSA/PSS);
+BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA224withRSA/PSS);
+BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA384withRSA/PSS);
+BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA512withRSA/PSS);
+// clang-format on
+
+/*
+ * ============= CIPHER TESTS ==================
+ */
+
+static void encrypt(benchmark::State& state, string transform, int keySize, int msgSize) {
+ addDefaultLabel(state);
+ if (!keymintTest->GenerateKey(transform, keySize)) {
+ state.SkipWithError(
+ ("Key generation error, " + std::to_string(keymintTest->getError())).c_str());
+ return;
+ }
+ auto in_params = keymintTest->getOperationParams(transform);
+ AuthorizationSet out_params;
+ string message = keymintTest->GenerateMessage(msgSize);
+
+ for (auto _ : state) {
+ state.PauseTiming();
+ auto error = keymintTest->Begin(KeyPurpose::ENCRYPT, in_params, &out_params);
+ if (error != ErrorCode::OK) {
+ state.SkipWithError(
+ ("Encryption begin error, " + std::to_string(keymintTest->getError())).c_str());
+ return;
+ }
+ out_params.Clear();
+ state.ResumeTiming();
+ if (!keymintTest->Process(message, in_params, &out_params)) {
+ state.SkipWithError(
+ ("Encryption error, " + std::to_string(keymintTest->getError())).c_str());
+ break;
+ }
+ }
+}
+
+static void decrypt(benchmark::State& state, string transform, int keySize, int msgSize) {
+ addDefaultLabel(state);
+ if (!keymintTest->GenerateKey(transform, keySize)) {
+ state.SkipWithError(
+ ("Key generation error, " + std::to_string(keymintTest->getError())).c_str());
+ return;
+ }
+ AuthorizationSet out_params;
+ AuthorizationSet in_params = keymintTest->getOperationParams(transform);
+ string message = keymintTest->GenerateMessage(msgSize);
+ auto error = keymintTest->Begin(KeyPurpose::ENCRYPT, in_params, &out_params);
+ if (error != ErrorCode::OK) {
+ state.SkipWithError(
+ ("Encryption begin error, " + std::to_string(keymintTest->getError())).c_str());
+ return;
+ }
+ auto encryptedMessage = keymintTest->Process(message, in_params, &out_params);
+ if (!encryptedMessage) {
+ state.SkipWithError(
+ ("Encryption error, " + std::to_string(keymintTest->getError())).c_str());
+ return;
+ }
+ in_params.push_back(out_params);
+ out_params.Clear();
+ for (auto _ : state) {
+ state.PauseTiming();
+ error = keymintTest->Begin(KeyPurpose::DECRYPT, in_params, &out_params);
+ if (error != ErrorCode::OK) {
+ state.SkipWithError(
+ ("Decryption begin error, " + std::to_string(keymintTest->getError())).c_str());
+ return;
+ }
+ state.ResumeTiming();
+ if (!keymintTest->Process(*encryptedMessage, in_params, &out_params)) {
+ state.SkipWithError(
+ ("Decryption error, " + std::to_string(keymintTest->getError())).c_str());
+ break;
+ }
+ }
+}
+
+// clang-format off
+// AES
+#define BENCHMARK_KM_CIPHER_ALL_AES_KEYS(transform) \
+ BENCHMARK_KM_CIPHER_ALL_MSGS(transform, 128) \
+ BENCHMARK_KM_CIPHER_ALL_MSGS(transform, 256)
+
+BENCHMARK_KM_CIPHER_ALL_AES_KEYS(AES/CBC/NoPadding);
+BENCHMARK_KM_CIPHER_ALL_AES_KEYS(AES/CBC/PKCS7Padding);
+BENCHMARK_KM_CIPHER_ALL_AES_KEYS(AES/CTR/NoPadding);
+BENCHMARK_KM_CIPHER_ALL_AES_KEYS(AES/ECB/NoPadding);
+BENCHMARK_KM_CIPHER_ALL_AES_KEYS(AES/ECB/PKCS7Padding);
+BENCHMARK_KM_CIPHER_ALL_AES_KEYS(AES/GCM/NoPadding);
+
+// Triple DES
+BENCHMARK_KM_CIPHER_ALL_MSGS(DESede/CBC/NoPadding, 168);
+BENCHMARK_KM_CIPHER_ALL_MSGS(DESede/CBC/PKCS7Padding, 168);
+BENCHMARK_KM_CIPHER_ALL_MSGS(DESede/ECB/NoPadding, 168);
+BENCHMARK_KM_CIPHER_ALL_MSGS(DESede/ECB/PKCS7Padding, 168);
+
+#define BENCHMARK_KM_CIPHER_ALL_RSA_KEYS(transform, msgSize) \
+ BENCHMARK_KM_CIPHER(transform, 2048, msgSize) \
+ BENCHMARK_KM_CIPHER(transform, 3072, msgSize) \
+ BENCHMARK_KM_CIPHER(transform, 4096, msgSize)
+
+BENCHMARK_KM_CIPHER_ALL_RSA_KEYS(RSA/ECB/NoPadding, SMALL_MESSAGE_SIZE);
+BENCHMARK_KM_CIPHER_ALL_RSA_KEYS(RSA/ECB/PKCS1Padding, SMALL_MESSAGE_SIZE);
+BENCHMARK_KM_CIPHER_ALL_RSA_KEYS(RSA/ECB/OAEPPadding, SMALL_MESSAGE_SIZE);
+
+// clang-format on
+} // namespace aidl::android::hardware::security::keymint::test
+
+int main(int argc, char** argv) {
+ ::benchmark::Initialize(&argc, argv);
+ base::CommandLine::Init(argc, argv);
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ auto service_name = command_line->GetSwitchValueASCII("service_name");
+ if (service_name.empty()) {
+ service_name =
+ std::string(
+ aidl::android::hardware::security::keymint::IKeyMintDevice::descriptor) +
+ "/default";
+ }
+ std::cerr << service_name << std::endl;
+ aidl::android::hardware::security::keymint::test::keymintTest =
+ aidl::android::hardware::security::keymint::test::KeyMintBenchmarkTest::newInstance(
+ service_name.c_str());
+ if (!aidl::android::hardware::security::keymint::test::keymintTest) {
+ return 1;
+ }
+ ::benchmark::RunSpecifiedBenchmarks();
+}
diff --git a/security/keymint/aidl/vts/performance/README b/security/keymint/aidl/vts/performance/README
new file mode 100644
index 0000000..1221ad8
--- /dev/null
+++ b/security/keymint/aidl/vts/performance/README
@@ -0,0 +1,28 @@
+# KeyMint Benchmark
+
+The KeyMint Benchmark is a standalone tool for measuring the performance of
+ KeyMint implementations.
+
+## Building
+
+Build:
+`m VtsAidlKeyMintBenchmarkTest`
+
+Transfer to device/emulator:
+`adb sync data`
+
+The benchmark executable will be located at
+ `data/benchmarktest/VtsAidlKeyMintBenchmarkTest` on the device.
+
+## Usage
+
+KeyMint Benchmark is built on [Google microbenchmark
+library](https://github.com/google/benchmark). All of the commandline arguments
+provided by the microbenchmark library are valid, such as
+`--benchmark_filter=<regex>` or `benchmark_out_format={json|console|csv}`.
+In addition to the command line arguments provided by microbenchmark,
+`--service_name=<service_name>` is provided to allow specification of the KeyMint
+fully qualified service name, e.g. specify
+`--service_name=android.hardware.security.keymint.IKeyMintDevice/default` to
+benchmark default implementation of KeyMint.
+
diff --git a/security/keymint/support/Android.bp b/security/keymint/support/Android.bp
index fde6b57..0255874 100644
--- a/security/keymint/support/Android.bp
+++ b/security/keymint/support/Android.bp
@@ -37,3 +37,40 @@
"libutils",
],
}
+
+cc_library {
+ name: "libkeymint_remote_prov_support",
+ vendor_available: true,
+ srcs: [
+ "remote_prov_utils.cpp",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+ shared_libs: [
+ "libcppcose",
+ "libcppbor_external",
+ "libcrypto",
+ ],
+}
+
+cc_library {
+ name: "libcppcose",
+ vendor_available: true,
+ srcs: [
+ "cppcose.cpp",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ "libcppbor_external",
+ "libcrypto",
+ "liblog",
+ ],
+ static_libs: [
+ // TODO(swillden): Remove keymint NDK
+ "android.hardware.security.keymint-V1-ndk_platform",
+ ],
+}
diff --git a/security/keymint/support/authorization_set.cpp b/security/keymint/support/authorization_set.cpp
index 8d42571..25eace3 100644
--- a/security/keymint/support/authorization_set.cpp
+++ b/security/keymint/support/authorization_set.cpp
@@ -191,6 +191,10 @@
return Authorization(TAG_PURPOSE, KeyPurpose::DECRYPT);
}
+AuthorizationSetBuilder& AuthorizationSetBuilder::AttestKey() {
+ return Authorization(TAG_PURPOSE, KeyPurpose::ATTEST_KEY);
+}
+
AuthorizationSetBuilder& AuthorizationSetBuilder::NoDigestOrPadding() {
Authorization(TAG_DIGEST, Digest::NONE);
return Authorization(TAG_PADDING, PaddingMode::NONE);
diff --git a/security/keymint/support/cppcose.cpp b/security/keymint/support/cppcose.cpp
new file mode 100644
index 0000000..c626ade
--- /dev/null
+++ b/security/keymint/support/cppcose.cpp
@@ -0,0 +1,467 @@
+/*
+ * 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::Bstr() /* 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)->asBstr();
+ 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)->asBstr();
+ 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(bytevec{} /* 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::Bstr* unprotectedParams = coseSign1->get(kCoseSign1UnprotectedParams)->asBstr();
+ 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
new file mode 100644
index 0000000..a936bfd
--- /dev/null
+++ b/security/keymint/support/include/cppcose/cppcose.h
@@ -0,0 +1,288 @@
+/*
+ * 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/authorization_set.h b/security/keymint/support/include/keymint_support/authorization_set.h
index 6d36794..ca51b08 100644
--- a/security/keymint/support/include/keymint_support/authorization_set.h
+++ b/security/keymint/support/include/keymint_support/authorization_set.h
@@ -288,6 +288,7 @@
AuthorizationSetBuilder& SigningKey();
AuthorizationSetBuilder& EncryptionKey();
+ AuthorizationSetBuilder& AttestKey();
AuthorizationSetBuilder& NoDigestOrPadding();
diff --git a/security/keymint/support/include/keymint_support/openssl_utils.h b/security/keymint/support/include/keymint_support/openssl_utils.h
index c3bc60b..a0212aa 100644
--- a/security/keymint/support/include/keymint_support/openssl_utils.h
+++ b/security/keymint/support/include/keymint_support/openssl_utils.h
@@ -34,13 +34,14 @@
typedef std::unique_ptr<type, UniquePtrDeleter<type, type##_free>> type##_Ptr;
MAKE_OPENSSL_PTR_TYPE(ASN1_OBJECT)
-MAKE_OPENSSL_PTR_TYPE(EC_KEY)
+MAKE_OPENSSL_PTR_TYPE(BN_CTX)
MAKE_OPENSSL_PTR_TYPE(EC_GROUP)
+MAKE_OPENSSL_PTR_TYPE(EC_KEY)
MAKE_OPENSSL_PTR_TYPE(EVP_PKEY)
MAKE_OPENSSL_PTR_TYPE(EVP_PKEY_CTX)
MAKE_OPENSSL_PTR_TYPE(RSA)
MAKE_OPENSSL_PTR_TYPE(X509)
-MAKE_OPENSSL_PTR_TYPE(BN_CTX)
+MAKE_OPENSSL_PTR_TYPE(X509_NAME)
typedef std::unique_ptr<BIGNUM, UniquePtrDeleter<BIGNUM, BN_free>> BIGNUM_Ptr;
diff --git a/security/keymint/support/include/remote_prov/remote_prov_utils.h b/security/keymint/support/include/remote_prov/remote_prov_utils.h
new file mode 100644
index 0000000..5e205a2
--- /dev/null
+++ b/security/keymint/support/include/remote_prov/remote_prov_utils.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <cppcose/cppcose.h>
+
+namespace aidl::android::hardware::security::keymint::remote_prov {
+
+using bytevec = std::vector<uint8_t>;
+using namespace cppcose;
+
+extern bytevec kTestMacKey;
+
+/**
+ * Generates random bytes.
+ */
+bytevec randomBytes(size_t numBytes);
+
+struct EekChain {
+ bytevec chain;
+ bytevec last_pubkey;
+ bytevec last_privkey;
+};
+
+/**
+ * Generates an X25518 EEK with the specified eekId and an Ed25519 chain of the
+ * specified length. All keys are generated randomly.
+ */
+ErrMsgOr<EekChain> generateEekChain(size_t length, const bytevec& eekId);
+
+struct BccEntryData {
+ bytevec pubKey;
+};
+
+/**
+ * Validates the provided CBOR-encoded BCC, returning a vector of BccEntryData
+ * structs containing the BCC entry contents. If an entry contains no firmware
+ * digest, the corresponding BccEntryData.firmwareDigest will have length zero
+ * (there's no way to distinguish between an empty and missing firmware digest,
+ * which seems fine).
+ */
+ErrMsgOr<std::vector<BccEntryData>> validateBcc(const cppbor::Array* bcc);
+
+} // namespace aidl::android::hardware::security::keymint::remote_prov
diff --git a/security/keymint/support/remote_prov_utils.cpp b/security/keymint/support/remote_prov_utils.cpp
new file mode 100644
index 0000000..111cb30
--- /dev/null
+++ b/security/keymint/support/remote_prov_utils.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <remote_prov/remote_prov_utils.h>
+
+#include <openssl/rand.h>
+
+#include <cppbor.h>
+
+namespace aidl::android::hardware::security::keymint::remote_prov {
+
+bytevec kTestMacKey(32 /* count */, 0 /* byte value */);
+
+bytevec randomBytes(size_t numBytes) {
+ bytevec retval(numBytes);
+ RAND_bytes(retval.data(), numBytes);
+ return retval;
+}
+
+ErrMsgOr<EekChain> generateEekChain(size_t length, const bytevec& eekId) {
+ auto eekChain = cppbor::Array();
+
+ bytevec prev_priv_key;
+ for (size_t i = 0; i < length - 1; ++i) {
+ bytevec pub_key(ED25519_PUBLIC_KEY_LEN);
+ bytevec priv_key(ED25519_PRIVATE_KEY_LEN);
+
+ ED25519_keypair(pub_key.data(), priv_key.data());
+
+ // The first signing key is self-signed.
+ if (prev_priv_key.empty()) prev_priv_key = priv_key;
+
+ auto coseSign1 = constructCoseSign1(prev_priv_key,
+ cppbor::Map() /* payload CoseKey */
+ .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR)
+ .add(CoseKey::ALGORITHM, EDDSA)
+ .add(CoseKey::CURVE, ED25519)
+ .add(CoseKey::PUBKEY_X, pub_key)
+ .canonicalize()
+ .encode(),
+ {} /* AAD */);
+ if (!coseSign1) return coseSign1.moveMessage();
+ eekChain.add(coseSign1.moveValue());
+ }
+
+ bytevec pub_key(X25519_PUBLIC_VALUE_LEN);
+ bytevec priv_key(X25519_PRIVATE_KEY_LEN);
+ X25519_keypair(pub_key.data(), priv_key.data());
+
+ auto coseSign1 = constructCoseSign1(prev_priv_key,
+ cppbor::Map() /* payload CoseKey */
+ .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR)
+ .add(CoseKey::KEY_ID, eekId)
+ .add(CoseKey::ALGORITHM, ECDH_ES_HKDF_256)
+ .add(CoseKey::CURVE, cppcose::X25519)
+ .add(CoseKey::PUBKEY_X, pub_key)
+ .canonicalize()
+ .encode(),
+ {} /* AAD */);
+ if (!coseSign1) return coseSign1.moveMessage();
+ eekChain.add(coseSign1.moveValue());
+
+ return EekChain{eekChain.encode(), pub_key, priv_key};
+}
+
+ErrMsgOr<bytevec> verifyAndParseCoseSign1Cwt(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::Bstr* unprotectedParams = coseSign1->get(kCoseSign1UnprotectedParams)->asBstr();
+ 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";
+ }
+
+ // TODO(jbires): Handle CWTs as the CoseSign1 payload in a less hacky way. Since the CWT payload
+ // is extremely remote provisioning specific, probably just make a separate
+ // function there.
+ auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(payload);
+ if (!parsedPayload) return payloadErrMsg + " when parsing key";
+ if (!parsedPayload->asMap()) return "CWT must be a map";
+ auto serializedKey = parsedPayload->asMap()->get(-4670552)->clone();
+ if (!serializedKey || !serializedKey->asBstr()) return "Could not find key entry";
+
+ if (!ignoreSignature) {
+ bool selfSigned = signingCoseKey.empty();
+ auto key = CoseKey::parseEd25519(selfSigned ? serializedKey->asBstr()->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 serializedKey->asBstr()->value();
+}
+ErrMsgOr<std::vector<BccEntryData>> validateBcc(const cppbor::Array* bcc) {
+ if (!bcc || bcc->size() == 0) return "Invalid BCC";
+
+ std::vector<BccEntryData> result;
+
+ bytevec prevKey;
+ // TODO(jbires): Actually process the pubKey at the start of the new bcc entry
+ for (size_t i = 1; i < bcc->size(); ++i) {
+ const cppbor::Array* entry = bcc->get(i)->asArray();
+ if (!entry || entry->size() != kCoseSign1EntryCount) {
+ return "Invalid BCC entry " + std::to_string(i) + ": " + prettyPrint(entry);
+ }
+ auto payload = verifyAndParseCoseSign1Cwt(false /* ignoreSignature */, entry,
+ std::move(prevKey), bytevec{} /* AAD */);
+ if (!payload) {
+ return "Failed to verify entry " + std::to_string(i) + ": " + payload.moveMessage();
+ }
+
+ auto& certProtParms = entry->get(kCoseSign1ProtectedParams);
+ if (!certProtParms || !certProtParms->asBstr()) return "Invalid prot params";
+ auto [parsedProtParms, _, errMsg] = cppbor::parse(certProtParms->asBstr()->value());
+ if (!parsedProtParms || !parsedProtParms->asMap()) return "Invalid prot params";
+
+ result.push_back(BccEntryData{*payload});
+
+ // This entry's public key is the signing key for the next entry.
+ prevKey = payload.moveValue();
+ }
+
+ return result;
+}
+
+} // namespace aidl::android::hardware::security::keymint::remote_prov
diff --git a/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/ISecureClock.aidl b/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/ISecureClock.aidl
index c16b312..3778897 100644
--- a/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/ISecureClock.aidl
+++ b/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/ISecureClock.aidl
@@ -1,4 +1,17 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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.
+ * limitations under the License.
+ *////////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
@@ -20,5 +33,5 @@
@VintfStability
interface ISecureClock {
android.hardware.security.secureclock.TimeStampToken generateTimeStamp(in long challenge);
- const String TIME_STAMP_MAC_LABEL = "Time Verification";
+ const String TIME_STAMP_MAC_LABEL = "Auth Verification";
}
diff --git a/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/TimeStampToken.aidl b/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/TimeStampToken.aidl
index 21eeb74..00a8bb2 100644
--- a/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/TimeStampToken.aidl
+++ b/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/TimeStampToken.aidl
@@ -1,4 +1,18 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/Timestamp.aidl b/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/Timestamp.aidl
index f01fdc7..bebeb5c 100644
--- a/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/Timestamp.aidl
+++ b/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/Timestamp.aidl
@@ -1,4 +1,18 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * Copyright 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/security/secureclock/aidl/android/hardware/security/secureclock/ISecureClock.aidl b/security/secureclock/aidl/android/hardware/security/secureclock/ISecureClock.aidl
index 7d416dd..577dd8f 100644
--- a/security/secureclock/aidl/android/hardware/security/secureclock/ISecureClock.aidl
+++ b/security/secureclock/aidl/android/hardware/security/secureclock/ISecureClock.aidl
@@ -33,7 +33,7 @@
* String used as context in the HMAC computation signing the generated time stamp.
* See TimeStampToken.mac for details.
*/
- const String TIME_STAMP_MAC_LABEL = "Time Verification";
+ const String TIME_STAMP_MAC_LABEL = "Auth Verification";
/**
* Generates an authenticated timestamp.
diff --git a/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl b/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl
index 3fb5860..dd95732 100644
--- a/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl
+++ b/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl
@@ -39,18 +39,20 @@
* 32-byte HMAC-SHA256 of the above values, computed as:
*
* HMAC(H,
- * ISecureClock.TIME_STAMP_MAC_LABEL || challenge || timestamp)
+ * ISecureClock.TIME_STAMP_MAC_LABEL || challenge || timestamp || securityLevel )
*
* where:
*
* ``ISecureClock.TIME_STAMP_MAC_LABEL'' is a sting constant defined in ISecureClock.aidl.
*
- * ``H'' is the shared HMAC key (see computeSharedHmac() in ISharedHmacSecret).
+ * ``H'' is the shared HMAC key (see computeSharedHmac() in ISharedSecret).
*
* ``||'' represents concatenation
*
* The representation of challenge and timestamp is as 64-bit unsigned integers in big-endian
- * order. securityLevel is represented as a 32-bit unsigned integer in big-endian order.
+ * order. SecurityLevel is represented as a 32-bit unsigned integer in big-endian order as
+ * described in android.hardware.security.keymint.SecurityLevel. It represents the security
+ * level of the secure clock environment.
*/
byte[] mac;
}
diff --git a/security/secureclock/aidl/vts/functional/Android.bp b/security/secureclock/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..1619eab
--- /dev/null
+++ b/security/secureclock/aidl/vts/functional/Android.bp
@@ -0,0 +1,43 @@
+//
+// 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.
+//
+
+cc_test {
+ name: "VtsAidlSecureClockTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ ],
+ srcs: [
+ "SecureClockAidlTest.cpp",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ "libcrypto",
+ "libkeymint",
+ ],
+ static_libs: [
+ "android.hardware.security.keymint-V1-ndk_platform",
+ "android.hardware.security.secureclock-V1-ndk_platform",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
diff --git a/security/secureclock/aidl/vts/functional/AndroidTest.xml b/security/secureclock/aidl/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..4861c7c
--- /dev/null
+++ b/security/secureclock/aidl/vts/functional/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs VtsAidlSecureClockTargetTest.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push"
+ value="VtsAidlSecureClockTargetTest->/data/local/tmp/VtsAidlSecureClockTargetTest" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="VtsAidlSecureClockTargetTest" />
+ <option name="native-test-timeout" value="900000"/>
+ </test>
+</configuration>
diff --git a/security/secureclock/aidl/vts/functional/SecureClockAidlTest.cpp b/security/secureclock/aidl/vts/functional/SecureClockAidlTest.cpp
new file mode 100644
index 0000000..9ca1ee8
--- /dev/null
+++ b/security/secureclock/aidl/vts/functional/SecureClockAidlTest.cpp
@@ -0,0 +1,193 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "secureclock_test"
+#include <android-base/logging.h>
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <aidl/android/hardware/security/secureclock/ISecureClock.h>
+#include <android/binder_manager.h>
+#include <binder/ProcessState.h>
+#include <gtest/gtest.h>
+#include <vector>
+
+namespace aidl::android::hardware::security::secureclock::test {
+using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::hardware::security::keymint::ErrorCode;
+using ::std::shared_ptr;
+using ::std::string;
+using ::std::vector;
+
+class SecureClockAidlTest : public ::testing::TestWithParam<string> {
+ public:
+ struct TimestampTokenResult {
+ ErrorCode error;
+ TimeStampToken token;
+ };
+
+ TimestampTokenResult getTimestampToken(int64_t in_challenge) {
+ TimestampTokenResult result;
+ result.error =
+ GetReturnErrorCode(secureClock_->generateTimeStamp(in_challenge, &result.token));
+ return result;
+ }
+
+ uint64_t getTime() {
+ struct timespec timespec;
+ EXPECT_EQ(0, clock_gettime(CLOCK_BOOTTIME, ×pec));
+ return timespec.tv_sec * 1000 + timespec.tv_nsec / 1000000;
+ }
+
+ int sleep_ms(uint32_t milliseconds) {
+ struct timespec sleep_time = {static_cast<time_t>(milliseconds / 1000),
+ static_cast<long>(milliseconds % 1000) * 1000000};
+ while (sleep_time.tv_sec || sleep_time.tv_nsec) {
+ if (nanosleep(&sleep_time /* to wait */,
+ &sleep_time /* remaining (on interrruption) */) == 0) {
+ sleep_time = {};
+ } else {
+ if (errno != EINTR) return errno;
+ }
+ }
+ return 0;
+ }
+
+ ErrorCode GetReturnErrorCode(const Status& result) {
+ if (result.isOk()) return ErrorCode::OK;
+
+ if (result.getExceptionCode() == EX_SERVICE_SPECIFIC) {
+ return static_cast<ErrorCode>(result.getServiceSpecificError());
+ }
+
+ return ErrorCode::UNKNOWN_ERROR;
+ }
+
+ void InitializeSecureClock(std::shared_ptr<ISecureClock> secureClock) {
+ ASSERT_NE(secureClock, nullptr);
+ secureClock_ = secureClock;
+ }
+
+ ISecureClock& secureClock() { return *secureClock_; }
+
+ static vector<string> build_params() {
+ auto params = ::android::getAidlHalInstanceNames(ISecureClock::descriptor);
+ return params;
+ }
+
+ void SetUp() override {
+ if (AServiceManager_isDeclared(GetParam().c_str())) {
+ ::ndk::SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
+ InitializeSecureClock(ISecureClock::fromBinder(binder));
+ } else {
+ InitializeSecureClock(nullptr);
+ }
+ }
+
+ void TearDown() override {}
+
+ private:
+ std::shared_ptr<ISecureClock> secureClock_;
+};
+
+/*
+ * The precise capabilities required to generate TimeStampToken will vary depending on the specific
+ * vendor implementations. The only thing we really can test is that tokens can be created by
+ * secureclock services, and that the timestamps increase as expected.
+ */
+TEST_P(SecureClockAidlTest, TestCreation) {
+ auto result1 = getTimestampToken(1 /* challenge */);
+ auto result1_time = getTime();
+ EXPECT_EQ(ErrorCode::OK, result1.error);
+ EXPECT_EQ(1U, result1.token.challenge);
+ EXPECT_GT(result1.token.timestamp.milliSeconds, 0U);
+
+ unsigned long time_to_sleep = 200;
+ sleep_ms(time_to_sleep);
+
+ auto result2 = getTimestampToken(2 /* challenge */);
+ auto result2_time = getTime();
+ EXPECT_EQ(ErrorCode::OK, result2.error);
+ EXPECT_EQ(2U, result2.token.challenge);
+ EXPECT_GT(result2.token.timestamp.milliSeconds, 0U);
+
+ auto host_time_delta = result2_time - result1_time;
+
+ EXPECT_GE(host_time_delta, time_to_sleep)
+ << "We slept for " << time_to_sleep << " ms, the clock must have advanced by that much";
+ EXPECT_LE(host_time_delta, time_to_sleep + 100)
+ << "The getTimestampToken call took " << (host_time_delta - time_to_sleep)
+ << " ms? That's awful!";
+ EXPECT_GE(result2.token.timestamp.milliSeconds, result1.token.timestamp.milliSeconds);
+ unsigned long km_time_delta =
+ result2.token.timestamp.milliSeconds - result1.token.timestamp.milliSeconds;
+ // 20 ms of slop just to avoid test flakiness.
+ EXPECT_LE(host_time_delta, km_time_delta + 20);
+ EXPECT_LE(km_time_delta, host_time_delta + 20);
+ ASSERT_EQ(result1.token.mac.size(), result2.token.mac.size());
+ ASSERT_NE(0,
+ memcmp(result1.token.mac.data(), result2.token.mac.data(), result1.token.mac.size()));
+}
+
+/*
+ * Test that the mac changes when the time stamp changes. This is does not guarantee that the time
+ * stamp is included in the mac but on failure we know that it is not. Other than in the test
+ * case above we call getTimestampToken with the exact same set of parameters.
+ */
+TEST_P(SecureClockAidlTest, MacChangesOnChangingTimestamp) {
+ auto result1 = getTimestampToken(0 /* challenge */);
+ auto result1_time = getTime();
+ EXPECT_EQ(ErrorCode::OK, result1.error);
+ EXPECT_EQ(0U, result1.token.challenge);
+ EXPECT_GT(result1.token.timestamp.milliSeconds, 0U);
+
+ unsigned long time_to_sleep = 200;
+ sleep_ms(time_to_sleep);
+
+ auto result2 = getTimestampToken(1 /* challenge */);
+ auto result2_time = getTime();
+ EXPECT_EQ(ErrorCode::OK, result2.error);
+ EXPECT_EQ(1U, result2.token.challenge);
+ EXPECT_GT(result2.token.timestamp.milliSeconds, 0U);
+
+ auto host_time_delta = result2_time - result1_time;
+
+ EXPECT_GE(host_time_delta, time_to_sleep)
+ << "We slept for " << time_to_sleep << " ms, the clock must have advanced by that much";
+ EXPECT_LE(host_time_delta, time_to_sleep + 100)
+ << "The getTimestampToken call took " << (host_time_delta - time_to_sleep)
+ << " ms? That's awful!";
+
+ EXPECT_GE(result2.token.timestamp.milliSeconds, result1.token.timestamp.milliSeconds);
+ unsigned long km_time_delta =
+ result2.token.timestamp.milliSeconds - result1.token.timestamp.milliSeconds;
+
+ EXPECT_LE(host_time_delta, km_time_delta + 20);
+ EXPECT_LE(km_time_delta, host_time_delta + 20);
+ ASSERT_EQ(result1.token.mac.size(), result2.token.mac.size());
+ ASSERT_NE(0,
+ memcmp(result1.token.mac.data(), result2.token.mac.data(), result1.token.mac.size()));
+}
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, SecureClockAidlTest,
+ testing::ValuesIn(SecureClockAidlTest::build_params()),
+ ::android::PrintInstanceNameToString);
+} // namespace aidl::android::hardware::security::secureclock::test
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/security/sharedsecret/aidl/vts/functional/Android.bp b/security/sharedsecret/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..76bf7ff
--- /dev/null
+++ b/security/sharedsecret/aidl/vts/functional/Android.bp
@@ -0,0 +1,43 @@
+//
+// 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.
+//
+
+cc_test {
+ name: "VtsAidlSharedSecretTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: [
+ "SharedSecretAidlTest.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ "libcrypto",
+ "libkeymint",
+ ],
+ static_libs: [
+ "android.hardware.security.keymint-V1-ndk_platform",
+ "android.hardware.security.sharedsecret-V1-ndk_platform",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
diff --git a/security/sharedsecret/aidl/vts/functional/AndroidTest.xml b/security/sharedsecret/aidl/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..c6697bc
--- /dev/null
+++ b/security/sharedsecret/aidl/vts/functional/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs VtsAidlSharedSecretTargetTest.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push"
+ value="VtsAidlSharedSecretTargetTest->/data/local/tmp/VtsAidlSharedSecretTargetTest" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="VtsAidlSharedSecretTargetTest" />
+ <option name="native-test-timeout" value="900000"/>
+ </test>
+</configuration>
diff --git a/security/sharedsecret/aidl/vts/functional/SharedSecretAidlTest.cpp b/security/sharedsecret/aidl/vts/functional/SharedSecretAidlTest.cpp
new file mode 100644
index 0000000..83f6ef3
--- /dev/null
+++ b/security/sharedsecret/aidl/vts/functional/SharedSecretAidlTest.cpp
@@ -0,0 +1,268 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "sharedsecret_test"
+#include <android-base/logging.h>
+
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <aidl/android/hardware/security/sharedsecret/ISharedSecret.h>
+#include <android/binder_manager.h>
+#include <gtest/gtest.h>
+#include <vector>
+
+namespace aidl::android::hardware::security::sharedsecret::test {
+using ::aidl::android::hardware::security::keymint::ErrorCode;
+using ::std::shared_ptr;
+using ::std::vector;
+using Status = ::ndk::ScopedAStatus;
+
+class SharedSecretAidlTest : public ::testing::Test {
+ public:
+ struct GetParamsResult {
+ ErrorCode error;
+ SharedSecretParameters params;
+ auto tie() { return std::tie(error, params); }
+ };
+
+ struct ComputeResult {
+ ErrorCode error;
+ vector<uint8_t> sharing_check;
+ auto tie() { return std::tie(error, sharing_check); }
+ };
+
+ GetParamsResult getSharedSecretParameters(shared_ptr<ISharedSecret>& sharedSecret) {
+ SharedSecretParameters params;
+ auto error = GetReturnErrorCode(sharedSecret->getSharedSecretParameters(¶ms));
+ EXPECT_EQ(ErrorCode::OK, error);
+ GetParamsResult result;
+ result.tie() = std::tie(error, params);
+ return result;
+ }
+
+ vector<SharedSecretParameters> getAllSharedSecretParameters() {
+ vector<SharedSecretParameters> paramsVec;
+ for (auto& sharedSecret : allSharedSecrets_) {
+ auto result = getSharedSecretParameters(sharedSecret);
+ EXPECT_EQ(ErrorCode::OK, result.error);
+ if (result.error == ErrorCode::OK) paramsVec.push_back(std::move(result.params));
+ }
+ return paramsVec;
+ }
+
+ ComputeResult computeSharedSecret(shared_ptr<ISharedSecret>& sharedSecret,
+ const vector<SharedSecretParameters>& params) {
+ std::vector<uint8_t> sharingCheck;
+ auto error = GetReturnErrorCode(sharedSecret->computeSharedSecret(params, &sharingCheck));
+ ComputeResult result;
+ result.tie() = std::tie(error, sharingCheck);
+ return result;
+ }
+
+ vector<ComputeResult> computeAllSharedSecrets(const vector<SharedSecretParameters>& params) {
+ vector<ComputeResult> result;
+ for (auto& sharedSecret : allSharedSecrets_) {
+ result.push_back(computeSharedSecret(sharedSecret, params));
+ }
+ return result;
+ }
+
+ vector<vector<uint8_t>> copyNonces(const vector<SharedSecretParameters>& paramsVec) {
+ vector<vector<uint8_t>> nonces;
+ for (auto& param : paramsVec) {
+ nonces.push_back(param.nonce);
+ }
+ return nonces;
+ }
+
+ void verifyResponses(const vector<uint8_t>& expected, const vector<ComputeResult>& responses) {
+ for (auto& response : responses) {
+ EXPECT_EQ(ErrorCode::OK, response.error);
+ EXPECT_EQ(expected, response.sharing_check) << "Sharing check values should match.";
+ }
+ }
+
+ ErrorCode GetReturnErrorCode(const Status& result) {
+ if (result.isOk()) return ErrorCode::OK;
+ if (result.getExceptionCode() == EX_SERVICE_SPECIFIC) {
+ return static_cast<ErrorCode>(result.getServiceSpecificError());
+ }
+ return ErrorCode::UNKNOWN_ERROR;
+ }
+
+ static shared_ptr<ISharedSecret> getSharedSecretService(const char* name) {
+ if (AServiceManager_isDeclared(name)) {
+ ::ndk::SpAIBinder binder(AServiceManager_waitForService(name));
+ return ISharedSecret::fromBinder(binder);
+ }
+ return nullptr;
+ }
+
+ const vector<shared_ptr<ISharedSecret>>& allSharedSecrets() { return allSharedSecrets_; }
+
+ static void SetUpTestCase() {
+ if (allSharedSecrets_.empty()) {
+ auto names = ::android::getAidlHalInstanceNames(ISharedSecret::descriptor);
+ for (const auto& name : names) {
+ auto servicePtr = getSharedSecretService(name.c_str());
+ if (servicePtr != nullptr) allSharedSecrets_.push_back(std::move(servicePtr));
+ }
+ }
+ }
+ static void TearDownTestCase() {}
+ void SetUp() override {}
+ void TearDown() override {}
+
+ private:
+ static vector<shared_ptr<ISharedSecret>> allSharedSecrets_;
+};
+
+vector<shared_ptr<ISharedSecret>> SharedSecretAidlTest::allSharedSecrets_;
+
+TEST_F(SharedSecretAidlTest, GetParameters) {
+ auto sharedSecrets = allSharedSecrets();
+ for (auto sharedSecret : sharedSecrets) {
+ auto result1 = getSharedSecretParameters(sharedSecret);
+ EXPECT_EQ(ErrorCode::OK, result1.error);
+ auto result2 = getSharedSecretParameters(sharedSecret);
+ EXPECT_EQ(ErrorCode::OK, result2.error);
+ ASSERT_EQ(result1.params.seed, result2.params.seed)
+ << "A given shared secret service should always return the same seed.";
+ ASSERT_EQ(result1.params.nonce, result2.params.nonce)
+ << "A given shared secret service should always return the same nonce until "
+ "restart.";
+ }
+}
+
+TEST_F(SharedSecretAidlTest, ComputeSharedSecret) {
+ auto params = getAllSharedSecretParameters();
+ ASSERT_EQ(allSharedSecrets().size(), params.size())
+ << "One or more shared secret services failed to provide parameters.";
+ auto nonces = copyNonces(params);
+ EXPECT_EQ(allSharedSecrets().size(), nonces.size());
+ std::sort(nonces.begin(), nonces.end());
+ std::unique(nonces.begin(), nonces.end());
+ EXPECT_EQ(allSharedSecrets().size(), nonces.size());
+
+ auto responses = computeAllSharedSecrets(params);
+ ASSERT_GT(responses.size(), 0U);
+ verifyResponses(responses[0].sharing_check, responses);
+
+ // Do it a second time. Should get the same answers.
+ params = getAllSharedSecretParameters();
+ ASSERT_EQ(allSharedSecrets().size(), params.size())
+ << "One or more shared secret services failed to provide parameters.";
+
+ responses = computeAllSharedSecrets(params);
+ ASSERT_GT(responses.size(), 0U);
+ ASSERT_EQ(32U, responses[0].sharing_check.size());
+ verifyResponses(responses[0].sharing_check, responses);
+}
+
+template <class F>
+class final_action {
+ public:
+ explicit final_action(F f) : f_(std::move(f)) {}
+ ~final_action() { f_(); }
+
+ private:
+ F f_;
+};
+
+template <class F>
+inline final_action<F> finally(const F& f) {
+ return final_action<F>(f);
+}
+
+TEST_F(SharedSecretAidlTest, ComputeSharedSecretCorruptNonce) {
+ auto fixup_hmac = finally([&]() { computeAllSharedSecrets(getAllSharedSecretParameters()); });
+
+ auto params = getAllSharedSecretParameters();
+ ASSERT_EQ(allSharedSecrets().size(), params.size())
+ << "One or more shared secret services failed to provide parameters.";
+
+ // All should be well in the normal case
+ auto responses = computeAllSharedSecrets(params);
+
+ ASSERT_GT(responses.size(), 0U);
+ vector<uint8_t> correct_response = responses[0].sharing_check;
+ verifyResponses(correct_response, responses);
+
+ // Pick a random param, a random byte within the param's nonce, and a random bit within
+ // the byte. Flip that bit.
+ size_t param_to_tweak = rand() % params.size();
+ uint8_t byte_to_tweak = rand() % sizeof(params[param_to_tweak].nonce);
+ uint8_t bit_to_tweak = rand() % 8;
+ params[param_to_tweak].nonce[byte_to_tweak] ^= (1 << bit_to_tweak);
+
+ responses = computeAllSharedSecrets(params);
+ for (size_t i = 0; i < responses.size(); ++i) {
+ if (i == param_to_tweak) {
+ EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error)
+ << "Shared secret service that provided tweaked param should fail to compute "
+ "shared secret";
+ } else {
+ EXPECT_EQ(ErrorCode::OK, responses[i].error) << "Others should succeed";
+ EXPECT_NE(correct_response, responses[i].sharing_check)
+ << "Others should calculate a different shared secret, due to the tweaked "
+ "nonce.";
+ }
+ }
+}
+
+TEST_F(SharedSecretAidlTest, ComputeSharedSecretCorruptSeed) {
+ auto fixup_hmac = finally([&]() { computeAllSharedSecrets(getAllSharedSecretParameters()); });
+ auto params = getAllSharedSecretParameters();
+ ASSERT_EQ(allSharedSecrets().size(), params.size())
+ << "One or more shared secret service failed to provide parameters.";
+
+ // All should be well in the normal case
+ auto responses = computeAllSharedSecrets(params);
+
+ ASSERT_GT(responses.size(), 0U);
+ vector<uint8_t> correct_response = responses[0].sharing_check;
+ verifyResponses(correct_response, responses);
+
+ // Pick a random param and modify the seed. We just increase the seed length by 1. It doesn't
+ // matter what value is in the additional byte; it changes the seed regardless.
+ auto param_to_tweak = rand() % params.size();
+ auto& to_tweak = params[param_to_tweak].seed;
+ ASSERT_TRUE(to_tweak.size() == 32 || to_tweak.size() == 0);
+ if (!to_tweak.size()) {
+ to_tweak.resize(32); // Contents don't matter; a little randomization is nice.
+ }
+ to_tweak[0]++;
+
+ responses = computeAllSharedSecrets(params);
+ for (size_t i = 0; i < responses.size(); ++i) {
+ if (i == param_to_tweak) {
+ EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error)
+ << "Shared secret service that provided tweaked param should fail to compute "
+ "shared secret";
+ } else {
+ EXPECT_EQ(ErrorCode::OK, responses[i].error) << "Others should succeed";
+ EXPECT_NE(correct_response, responses[i].sharing_check)
+ << "Others should calculate a different shared secret, due to the tweaked "
+ "nonce.";
+ }
+ }
+}
+} // namespace aidl::android::hardware::security::sharedsecret::test
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp b/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp
index ad4ef12..ea9bcb5 100644
--- a/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp
+++ b/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp
@@ -469,7 +469,7 @@
}
}
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OffloadControlHidlTestBase);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OffloadControlTestV1_0_HalNotStarted);
INSTANTIATE_TEST_CASE_P(
PerInstance, OffloadControlTestV1_0_HalNotStarted,
testing::Combine(testing::ValuesIn(android::hardware::getAllHalInstanceNames(
@@ -478,7 +478,7 @@
IOffloadControl::descriptor))),
android::hardware::PrintInstanceTupleNameToString<>);
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OffloadControlHidlTest);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OffloadControlTestV1_0_HalStarted);
INSTANTIATE_TEST_CASE_P(
PerInstance, OffloadControlTestV1_0_HalStarted,
testing::Combine(testing::ValuesIn(android::hardware::getAllHalInstanceNames(
diff --git a/tv/cec/1.1/Android.bp b/tv/cec/1.1/Android.bp
new file mode 100644
index 0000000..c2d4e54
--- /dev/null
+++ b/tv/cec/1.1/Android.bp
@@ -0,0 +1,16 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.tv.cec@1.1",
+ root: "android.hardware",
+ srcs: [
+ "types.hal",
+ "IHdmiCec.hal",
+ "IHdmiCecCallback.hal",
+ ],
+ interfaces: [
+ "android.hardware.tv.cec@1.0",
+ "android.hidl.base@1.0",
+ ],
+ gen_java: true,
+}
diff --git a/tv/cec/1.1/IHdmiCec.hal b/tv/cec/1.1/IHdmiCec.hal
new file mode 100644
index 0000000..fe7bedf
--- /dev/null
+++ b/tv/cec/1.1/IHdmiCec.hal
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.tv.cec@1.1;
+
+import @1.0::IHdmiCec;
+import @1.0::Result;
+import @1.0::SendMessageResult;
+
+import IHdmiCecCallback;
+
+/**
+ * HDMI-CEC HAL interface definition.
+ */
+interface IHdmiCec extends @1.0::IHdmiCec {
+ /**
+ * Passes the logical address that must be used in this system.
+ *
+ * HAL must use it to configure the hardware so that the CEC commands
+ * addressed the given logical address can be filtered in. This method must
+ * be able to be called as many times as necessary in order to support
+ * multiple logical devices.
+ *
+ * @param addr Logical address that must be used in this system. It must be
+ * in the range of valid logical addresses for the call to succeed.
+ * @return result Result status of the operation. SUCCESS if successful,
+ * FAILURE_INVALID_ARGS if the given logical address is invalid,
+ * FAILURE_BUSY if device or resource is busy
+ */
+ addLogicalAddress_1_1(CecLogicalAddress addr) generates (Result result);
+
+ /**
+ * Transmits HDMI-CEC message to other HDMI device.
+ *
+ * The method must be designed to return in a certain amount of time and not
+ * hanging forever which may happen if CEC signal line is pulled low for
+ * some reason.
+ *
+ * It must try retransmission at least once as specified in the section '7.1
+ * Frame Re-transmissions' of the CEC Spec 1.4b.
+ *
+ * @param message CEC message to be sent to other HDMI device.
+ * @return result Result status of the operation. SUCCESS if successful,
+ * NACK if the sent message is not acknowledged,
+ * BUSY if the CEC bus is busy.
+ */
+ sendMessage_1_1(CecMessage message) generates (SendMessageResult result);
+
+ /**
+ * Sets a callback that HDMI-CEC HAL must later use for incoming CEC
+ * messages or internal HDMI events.
+ *
+ * @param callback Callback object to pass hdmi events to the system. The
+ * previously registered callback must be replaced with this one.
+ */
+ setCallback_1_1(IHdmiCecCallback callback);
+};
diff --git a/tv/cec/1.1/IHdmiCecCallback.hal b/tv/cec/1.1/IHdmiCecCallback.hal
new file mode 100644
index 0000000..3928f18
--- /dev/null
+++ b/tv/cec/1.1/IHdmiCecCallback.hal
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package android.hardware.tv.cec@1.1;
+
+import @1.0::IHdmiCecCallback;
+
+/**
+ * Callbacks from the HAL implementation to notify the system of new events.
+ */
+interface IHdmiCecCallback extends @1.0::IHdmiCecCallback {
+ /**
+ * The callback function that must be called by HAL implementation to notify
+ * the system of new CEC message arrival.
+ */
+ oneway onCecMessage_1_1(CecMessage message);
+};
diff --git a/tv/cec/1.1/default/Android.bp b/tv/cec/1.1/default/Android.bp
new file mode 100644
index 0000000..e0dff0d
--- /dev/null
+++ b/tv/cec/1.1/default/Android.bp
@@ -0,0 +1,23 @@
+cc_binary {
+ name: "android.hardware.tv.cec@1.1-service",
+ defaults: ["hidl_defaults"],
+ vintf_fragments: ["android.hardware.tv.cec@1.1-service.xml"],
+ relative_install_path: "hw",
+ vendor: true,
+ init_rc: ["android.hardware.tv.cec@1.1-service.rc"],
+ srcs: [
+ "serviceMock.cpp",
+ "HdmiCecMock.cpp",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libcutils",
+ "libbase",
+ "libutils",
+ "libhardware",
+ "libhidlbase",
+ "android.hardware.tv.cec@1.0",
+ "android.hardware.tv.cec@1.1",
+ ],
+}
diff --git a/tv/cec/1.1/default/HdmiCecMock.cpp b/tv/cec/1.1/default/HdmiCecMock.cpp
new file mode 100644
index 0000000..f65bab9
--- /dev/null
+++ b/tv/cec/1.1/default/HdmiCecMock.cpp
@@ -0,0 +1,371 @@
+/*
+ * 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 "android.hardware.tv.cec@1.1"
+#include <android-base/logging.h>
+#include <utils/Log.h>
+
+#include <hardware/hardware.h>
+#include <hardware/hdmi_cec.h>
+#include "HdmiCecMock.h"
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace cec {
+namespace V1_1 {
+namespace implementation {
+
+class WrappedCallback : public ::android::hardware::tv::cec::V1_1::IHdmiCecCallback {
+ public:
+ WrappedCallback(sp<::android::hardware::tv::cec::V1_0::IHdmiCecCallback> callback) {
+ mCallback = callback;
+ }
+
+ Return<void> onCecMessage(const ::android::hardware::tv::cec::V1_0::CecMessage& message) {
+ mCallback->onCecMessage(message);
+ return Void();
+ }
+ Return<void> onCecMessage_1_1(const ::android::hardware::tv::cec::V1_1::CecMessage& message) {
+ ::android::hardware::tv::cec::V1_0::CecMessage cecMessage;
+ cecMessage.initiator =
+ ::android::hardware::tv::cec::V1_0::CecLogicalAddress(message.initiator);
+ cecMessage.destination =
+ ::android::hardware::tv::cec::V1_0::CecLogicalAddress(message.destination);
+ cecMessage.body = message.body;
+ mCallback->onCecMessage(cecMessage);
+ return Void();
+ }
+ Return<void> onHotplugEvent(const ::android::hardware::tv::cec::V1_0::HotplugEvent& event) {
+ mCallback->onHotplugEvent(event);
+ return Void();
+ }
+
+ private:
+ sp<::android::hardware::tv::cec::V1_0::IHdmiCecCallback> mCallback;
+};
+
+/*
+ * (*set_option)() passes flags controlling the way HDMI-CEC service works down
+ * to HAL implementation. Those flags will be used in case the feature needs
+ * update in HAL itself, firmware or microcontroller.
+ */
+void HdmiCecMock::cec_set_option(int flag, int value) {
+ // maintain options and set them accordingly
+ switch (flag) {
+ case HDMI_OPTION_WAKEUP:
+ mOptionWakeUp = value;
+ break;
+ case HDMI_OPTION_ENABLE_CEC:
+ mOptionEnableCec = value;
+ break;
+ case HDMI_OPTION_SYSTEM_CEC_CONTROL:
+ mOptionSystemCecControl = value;
+ break;
+ case HDMI_OPTION_SET_LANG:
+ mOptionLanguage = value;
+ break;
+ }
+}
+
+// Methods from ::android::hardware::tv::cec::V1_0::IHdmiCec follow.
+Return<Result> HdmiCecMock::addLogicalAddress(CecLogicalAddress addr) {
+ return addLogicalAddress_1_1(::android::hardware::tv::cec::V1_1::CecLogicalAddress(addr));
+}
+
+Return<void> HdmiCecMock::clearLogicalAddress() {
+ // remove logical address from the list
+ mLogicalAddresses = {};
+ return Void();
+}
+
+Return<void> HdmiCecMock::getPhysicalAddress(getPhysicalAddress_cb _hidl_cb) {
+ // maintain a physical address and return it
+ // default 0xFFFF, update on hotplug event
+ _hidl_cb(Result::SUCCESS, mPhysicalAddress);
+ return Void();
+}
+
+Return<SendMessageResult> HdmiCecMock::sendMessage(const CecMessage& message) {
+ ::android::hardware::tv::cec::V1_1::CecMessage cecMessage;
+ cecMessage.initiator = ::android::hardware::tv::cec::V1_1::CecLogicalAddress(message.initiator);
+ cecMessage.destination =
+ ::android::hardware::tv::cec::V1_1::CecLogicalAddress(message.destination);
+ cecMessage.body = message.body;
+ return sendMessage_1_1(cecMessage);
+}
+
+Return<void> HdmiCecMock::setCallback(const sp<IHdmiCecCallback>& callback) {
+ return setCallback_1_1(new WrappedCallback(callback));
+}
+
+Return<int32_t> HdmiCecMock::getCecVersion() {
+ // maintain a cec version and return it
+ return mCecVersion;
+}
+
+Return<uint32_t> HdmiCecMock::getVendorId() {
+ return mCecVendorId;
+}
+
+Return<void> HdmiCecMock::getPortInfo(getPortInfo_cb _hidl_cb) {
+ // TODO ready port info from device specific config
+ _hidl_cb(mPortInfo);
+ return Void();
+}
+
+Return<void> HdmiCecMock::setOption(OptionKey key, bool value) {
+ cec_set_option(static_cast<int>(key), value ? 1 : 0);
+ return Void();
+}
+
+Return<void> HdmiCecMock::setLanguage(const hidl_string& language) {
+ if (language.size() != 3) {
+ LOG(ERROR) << "Wrong language code: expected 3 letters, but it was " << language.size()
+ << ".";
+ return Void();
+ }
+ // TODO validate if language is a valid language code
+ const char* languageStr = language.c_str();
+ int convertedLanguage = ((languageStr[0] & 0xFF) << 16) | ((languageStr[1] & 0xFF) << 8) |
+ (languageStr[2] & 0xFF);
+ cec_set_option(HDMI_OPTION_SET_LANG, convertedLanguage);
+ return Void();
+}
+
+Return<void> HdmiCecMock::enableAudioReturnChannel(int32_t portId __unused, bool enable __unused) {
+ // Maintain ARC status
+ return Void();
+}
+
+Return<bool> HdmiCecMock::isConnected(int32_t portId) {
+ // maintain port connection status and update on hotplug event
+ if (portId < mTotalPorts && portId >= 0) {
+ return mPortConnectionStatus[portId];
+ }
+ return false;
+}
+
+// Methods from ::android::hardware::tv::cec::V1_1::IHdmiCec follow.
+Return<Result> HdmiCecMock::addLogicalAddress_1_1(
+ ::android::hardware::tv::cec::V1_1::CecLogicalAddress addr) {
+ // have a list to maintain logical addresses
+ int size = mLogicalAddresses.size();
+ mLogicalAddresses.resize(size + 1);
+ mLogicalAddresses[size + 1] = addr;
+ return Result::SUCCESS;
+}
+
+Return<SendMessageResult> HdmiCecMock::sendMessage_1_1(
+ const ::android::hardware::tv::cec::V1_1::CecMessage& message) {
+ if (message.body.size() == 0) {
+ return SendMessageResult::NACK;
+ }
+ sendMessageToFifo(message);
+ return SendMessageResult::SUCCESS;
+}
+
+Return<void> HdmiCecMock::setCallback_1_1(
+ const sp<::android::hardware::tv::cec::V1_1::IHdmiCecCallback>& callback) {
+ if (mCallback != nullptr) {
+ mCallback = nullptr;
+ }
+
+ if (callback != nullptr) {
+ mCallback = callback;
+ mCallback->linkToDeath(this, 0 /*cookie*/);
+
+ mInputFile = open(CEC_MSG_IN_FIFO, O_RDWR);
+ mOutputFile = open(CEC_MSG_OUT_FIFO, O_RDWR);
+ pthread_create(&mThreadId, NULL, __threadLoop, this);
+ pthread_setname_np(mThreadId, "hdmi_cec_loop");
+ }
+ return Void();
+}
+
+void* HdmiCecMock::__threadLoop(void* user) {
+ HdmiCecMock* const self = static_cast<HdmiCecMock*>(user);
+ self->threadLoop();
+ return 0;
+}
+
+int HdmiCecMock::readMessageFromFifo(unsigned char* buf, int msgCount) {
+ if (msgCount <= 0 || !buf) {
+ return 0;
+ }
+
+ int ret = -1;
+ /* maybe blocked at driver */
+ ret = read(mInputFile, buf, msgCount);
+ if (ret < 0) {
+ ALOGE("[halimp] read :%s failed, ret:%d\n", CEC_MSG_IN_FIFO, ret);
+ return -1;
+ }
+
+ return ret;
+}
+
+int HdmiCecMock::sendMessageToFifo(const ::android::hardware::tv::cec::V1_1::CecMessage& message) {
+ unsigned char msgBuf[CEC_MESSAGE_BODY_MAX_LENGTH];
+ int ret = -1;
+
+ memset(msgBuf, 0, sizeof(msgBuf));
+ msgBuf[0] = ((static_cast<uint8_t>(message.initiator) & 0xf) << 4) |
+ (static_cast<uint8_t>(message.destination) & 0xf);
+
+ size_t length = std::min(static_cast<size_t>(message.body.size()),
+ static_cast<size_t>(MaxLength::MESSAGE_BODY));
+ for (size_t i = 0; i < length; ++i) {
+ msgBuf[i + 1] = static_cast<unsigned char>(message.body[i]);
+ }
+
+ // open the output pipe for writing outgoing cec message
+ mOutputFile = open(CEC_MSG_OUT_FIFO, O_WRONLY);
+ if (mOutputFile < 0) {
+ ALOGD("[halimp] file open failed for writing");
+ return -1;
+ }
+
+ // write message into the output pipe
+ ret = write(mOutputFile, msgBuf, length + 1);
+ close(mOutputFile);
+ if (ret < 0) {
+ ALOGE("[halimp] write :%s failed, ret:%d\n", CEC_MSG_OUT_FIFO, ret);
+ return -1;
+ }
+ return ret;
+}
+
+void HdmiCecMock::printCecMsgBuf(const char* msg_buf, int len) {
+ char buf[64] = {};
+ int i, size = 0;
+ memset(buf, 0, sizeof(buf));
+ for (i = 0; i < len; i++) {
+ size += sprintf(buf + size, " %02x", msg_buf[i]);
+ }
+ ALOGD("[halimp] %s, msg:%s", __FUNCTION__, buf);
+}
+
+void HdmiCecMock::handleHotplugMessage(unsigned char* msgBuf) {
+ HotplugEvent hotplugEvent{.connected = ((msgBuf[3]) & 0xf) > 0,
+ .portId = static_cast<uint32_t>(msgBuf[0] & 0xf)};
+
+ if (hotplugEvent.portId >= mPortInfo.size()) {
+ ALOGD("[halimp] ignore hot plug message, id %x does not exist", hotplugEvent.portId);
+ return;
+ }
+
+ ALOGD("[halimp] hot plug port id %x, is connected %x", (msgBuf[0] & 0xf), (msgBuf[3] & 0xf));
+ if (mPortInfo[hotplugEvent.portId].type == HdmiPortType::OUTPUT) {
+ mPhysicalAddress =
+ ((hotplugEvent.connected == 0) ? 0xffff : ((msgBuf[1] << 8) | (msgBuf[2])));
+ mPortInfo[hotplugEvent.portId].physicalAddress = mPhysicalAddress;
+ ALOGD("[halimp] hot plug physical address %x", mPhysicalAddress);
+ }
+
+ // todo update connection status
+
+ if (mCallback != nullptr) {
+ mCallback->onHotplugEvent(hotplugEvent);
+ }
+}
+
+void HdmiCecMock::handleCecMessage(unsigned char* msgBuf, int megSize) {
+ ::android::hardware::tv::cec::V1_1::CecMessage message;
+ size_t length = std::min(static_cast<size_t>(megSize - 1),
+ static_cast<size_t>(MaxLength::MESSAGE_BODY));
+ message.body.resize(length);
+
+ for (size_t i = 0; i < length; ++i) {
+ message.body[i] = static_cast<uint8_t>(msgBuf[i + 1]);
+ ALOGD("[halimp] msg body %x", message.body[i]);
+ }
+
+ message.initiator = static_cast<::android::hardware::tv::cec::V1_1::CecLogicalAddress>(
+ (msgBuf[0] >> 4) & 0xf);
+ ALOGD("[halimp] msg init %x", message.initiator);
+ message.destination = static_cast<::android::hardware::tv::cec::V1_1::CecLogicalAddress>(
+ (msgBuf[0] >> 0) & 0xf);
+ ALOGD("[halimp] msg dest %x", message.destination);
+
+ // messageValidateAndHandle(&event);
+
+ if (mCallback != nullptr) {
+ mCallback->onCecMessage_1_1(message);
+ }
+}
+
+void HdmiCecMock::threadLoop() {
+ ALOGD("[halimp] threadLoop start.");
+ unsigned char msgBuf[CEC_MESSAGE_BODY_MAX_LENGTH];
+ int r = -1;
+
+ // open the input pipe
+ while (mInputFile < 0) {
+ usleep(1000 * 1000);
+ mInputFile = open(CEC_MSG_IN_FIFO, O_RDONLY);
+ }
+ ALOGD("[halimp] file open ok, fd = %d.", mInputFile);
+
+ while (mCecThreadRun) {
+ if (!mOptionSystemCecControl) {
+ usleep(1000 * 1000);
+ continue;
+ }
+
+ memset(msgBuf, 0, sizeof(msgBuf));
+ // try to get a message from dev.
+ // echo -n -e '\x04\x83' >> /dev/cec
+ r = readMessageFromFifo(msgBuf, CEC_MESSAGE_BODY_MAX_LENGTH);
+ if (r <= 1) {
+ // ignore received ping messages
+ continue;
+ }
+
+ printCecMsgBuf((const char*)msgBuf, r);
+
+ if (((msgBuf[0] >> 4) & 0xf) == 0xf) {
+ // the message is a hotplug event
+ handleHotplugMessage(msgBuf);
+ continue;
+ }
+
+ handleCecMessage(msgBuf, r);
+ }
+
+ ALOGD("[halimp] thread end.");
+ // mCecDevice.mExited = true;
+}
+
+HdmiCecMock::HdmiCecMock() {
+ ALOGE("[halimp] Opening a virtual HAL for testing and virtual machine.");
+ mCallback = nullptr;
+ mPortInfo.resize(mTotalPorts);
+ mPortConnectionStatus.resize(mTotalPorts);
+ mPortInfo[0] = {.type = HdmiPortType::OUTPUT,
+ .portId = static_cast<uint32_t>(1),
+ .cecSupported = true,
+ .arcSupported = false,
+ .physicalAddress = mPhysicalAddress};
+ mPortConnectionStatus[0] = false;
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace cec
+} // namespace tv
+} // namespace hardware
+} // namespace android
\ No newline at end of file
diff --git a/tv/cec/1.1/default/HdmiCecMock.h b/tv/cec/1.1/default/HdmiCecMock.h
new file mode 100644
index 0000000..0205f8d
--- /dev/null
+++ b/tv/cec/1.1/default/HdmiCecMock.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/hardware/tv/cec/1.1/IHdmiCec.h>
+#include <hidl/Status.h>
+#include <algorithm>
+#include <vector>
+
+using namespace std;
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace cec {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::tv::cec::V1_0::CecLogicalAddress;
+using ::android::hardware::tv::cec::V1_0::CecMessage;
+using ::android::hardware::tv::cec::V1_0::HdmiPortInfo;
+using ::android::hardware::tv::cec::V1_0::HdmiPortType;
+using ::android::hardware::tv::cec::V1_0::HotplugEvent;
+using ::android::hardware::tv::cec::V1_0::IHdmiCecCallback;
+using ::android::hardware::tv::cec::V1_0::MaxLength;
+using ::android::hardware::tv::cec::V1_0::OptionKey;
+using ::android::hardware::tv::cec::V1_0::Result;
+using ::android::hardware::tv::cec::V1_0::SendMessageResult;
+using ::android::hardware::tv::cec::V1_1::IHdmiCec;
+
+#define CEC_MSG_IN_FIFO "/dev/cec_in_pipe"
+#define CEC_MSG_OUT_FIFO "/dev/cec_out_pipe"
+
+struct HdmiCecMock : public IHdmiCec, public hidl_death_recipient {
+ HdmiCecMock();
+ // Methods from ::android::hardware::tv::cec::V1_0::IHdmiCec follow.
+ Return<Result> addLogicalAddress(CecLogicalAddress addr) override;
+ Return<void> clearLogicalAddress() override;
+ Return<void> getPhysicalAddress(getPhysicalAddress_cb _hidl_cb) override;
+ Return<SendMessageResult> sendMessage(const CecMessage& message) override;
+ Return<void> setCallback(
+ const sp<::android::hardware::tv::cec::V1_0::IHdmiCecCallback>& callback) override;
+ Return<int32_t> getCecVersion() override;
+ Return<uint32_t> getVendorId() override;
+ Return<void> getPortInfo(getPortInfo_cb _hidl_cb) override;
+ Return<void> setOption(OptionKey key, bool value) override;
+ Return<void> setLanguage(const hidl_string& language) override;
+ Return<void> enableAudioReturnChannel(int32_t portId, bool enable) override;
+ Return<bool> isConnected(int32_t portId) override;
+
+ // Methods from ::android::hardware::tv::cec::V1_1::IHdmiCec follow.
+ Return<Result> addLogicalAddress_1_1(
+ ::android::hardware::tv::cec::V1_1::CecLogicalAddress addr) override;
+ Return<SendMessageResult> sendMessage_1_1(
+ const ::android::hardware::tv::cec::V1_1::CecMessage& message) override;
+ Return<void> setCallback_1_1(
+ const sp<::android::hardware::tv::cec::V1_1::IHdmiCecCallback>& callback) override;
+
+ virtual void serviceDied(uint64_t /*cookie*/,
+ const wp<::android::hidl::base::V1_0::IBase>& /*who*/) {
+ setCallback(nullptr);
+ }
+
+ void cec_set_option(int flag, int value);
+ void printCecMsgBuf(const char* msg_buf, int len);
+
+ private:
+ static void* __threadLoop(void* data);
+ void threadLoop();
+ int readMessageFromFifo(unsigned char* buf, int msgCount);
+ int sendMessageToFifo(const ::android::hardware::tv::cec::V1_1::CecMessage& message);
+ void handleHotplugMessage(unsigned char* msgBuf);
+ void handleCecMessage(unsigned char* msgBuf, int length);
+
+ private:
+ sp<::android::hardware::tv::cec::V1_1::IHdmiCecCallback> mCallback;
+
+ // Variables for the virtual cec hal impl
+ uint16_t mPhysicalAddress = 0xFFFF;
+ vector<::android::hardware::tv::cec::V1_1::CecLogicalAddress> mLogicalAddresses;
+ int32_t mCecVersion = 0x06;
+ uint32_t mCecVendorId = 0x01;
+
+ // Port configuration
+ int mTotalPorts = 1;
+ hidl_vec<HdmiPortInfo> mPortInfo;
+ hidl_vec<bool> mPortConnectionStatus;
+
+ // CEC Option value
+ int mOptionWakeUp = 0;
+ int mOptionEnableCec = 0;
+ int mOptionSystemCecControl = 0;
+ int mOptionLanguage = 0;
+
+ // Testing variables
+ // Input file descriptor
+ int mInputFile;
+ // Output file descriptor
+ int mOutputFile;
+ bool mCecThreadRun = true;
+ pthread_t mThreadId = 0;
+};
+} // namespace implementation
+} // namespace V1_1
+} // namespace cec
+} // namespace tv
+} // namespace hardware
+} // namespace android
\ No newline at end of file
diff --git a/tv/cec/1.1/default/android.hardware.tv.cec@1.1-service.rc b/tv/cec/1.1/default/android.hardware.tv.cec@1.1-service.rc
new file mode 100644
index 0000000..e150c91
--- /dev/null
+++ b/tv/cec/1.1/default/android.hardware.tv.cec@1.1-service.rc
@@ -0,0 +1,6 @@
+service vendor.cec-hal-1-1 /vendor/bin/hw/android.hardware.tv.cec@1.1-service
+ interface android.hardware.tv.cec@1.0::IHdmiCec default
+ interface android.hardware.tv.cec@1.1::IHdmiCec default
+ class hal
+ user system
+ group system
\ No newline at end of file
diff --git a/tv/cec/1.1/default/android.hardware.tv.cec@1.1-service.xml b/tv/cec/1.1/default/android.hardware.tv.cec@1.1-service.xml
new file mode 100644
index 0000000..492369e
--- /dev/null
+++ b/tv/cec/1.1/default/android.hardware.tv.cec@1.1-service.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.tv.cec</name>
+ <transport>hwbinder</transport>
+ <version>1.1</version>
+ <interface>
+ <name>IHdmiCec</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/tv/cec/1.1/default/serviceMock.cpp b/tv/cec/1.1/default/serviceMock.cpp
new file mode 100644
index 0000000..72fc311
--- /dev/null
+++ b/tv/cec/1.1/default/serviceMock.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.
+ */
+
+#define LOG_TAG "android.hardware.tv.cec@1.1-service-shim"
+
+#include <android/hardware/tv/cec/1.1/IHdmiCec.h>
+#include <hidl/LegacySupport.h>
+#include "HdmiCecMock.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+using android::hardware::tv::cec::V1_1::IHdmiCec;
+using android::hardware::tv::cec::V1_1::implementation::HdmiCecMock;
+
+int main() {
+ configureRpcThreadpool(8, true /* callerWillJoin */);
+
+ // Setup hwbinder service
+ android::sp<IHdmiCec> service = new HdmiCecMock();
+ android::status_t status;
+ status = service->registerAsService();
+ LOG_ALWAYS_FATAL_IF(status != android::OK, "Error while registering mock cec service: %d",
+ status);
+
+ joinRpcThreadpool();
+ return 0;
+}
diff --git a/tv/cec/1.1/types.hal b/tv/cec/1.1/types.hal
new file mode 100644
index 0000000..a117519
--- /dev/null
+++ b/tv/cec/1.1/types.hal
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+package android.hardware.tv.cec@1.1;
+
+import @1.0::CecLogicalAddress;
+import @1.0::CecMessageType;
+
+enum CecLogicalAddress : @1.0::CecLogicalAddress {
+ BACKUP_1 = 12,
+ BACKUP_2 = 13,
+};
+
+enum CecMessageType : @1.0::CecMessageType {
+ GIVE_FEATURES = 0xA5,
+ REPORT_FEATURES = 0xA6,
+ REQUEST_CURRENT_LATENCY = 0xA7,
+ REPORT_CURRENT_LATENCY = 0xA8,
+};
+
+struct CecMessage {
+ /** logical address of the initiator */
+ CecLogicalAddress initiator;
+
+ /** logical address of destination */
+ CecLogicalAddress destination;
+
+ /**
+ * The maximum size of body is 15 (MaxLength::MESSAGE_BODY) as specified in
+ * the section 6 of the CEC Spec 1.4b. Overflowed data must be ignored. */
+ vec<uint8_t> body;
+};
diff --git a/tv/cec/1.1/vts/functional/Android.bp b/tv/cec/1.1/vts/functional/Android.bp
new file mode 100644
index 0000000..5fc7093
--- /dev/null
+++ b/tv/cec/1.1/vts/functional/Android.bp
@@ -0,0 +1,14 @@
+cc_test {
+ name: "VtsHalTvCecV1_1TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: ["VtsHalTvCecV1_1TargetTest.cpp"],
+ static_libs: [
+ "android.hardware.tv.cec@1.1",
+ "android.hardware.tv.cec@1.0",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+ disable_framework: true,
+}
diff --git a/tv/cec/1.1/vts/functional/VtsHalTvCecV1_1TargetTest.cpp b/tv/cec/1.1/vts/functional/VtsHalTvCecV1_1TargetTest.cpp
new file mode 100644
index 0000000..1eb4643
--- /dev/null
+++ b/tv/cec/1.1/vts/functional/VtsHalTvCecV1_1TargetTest.cpp
@@ -0,0 +1,199 @@
+/*
+ * 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 "HdmiCec_hal_test"
+#include <android-base/logging.h>
+
+#include <android/hardware/tv/cec/1.1/IHdmiCec.h>
+#include <android/hardware/tv/cec/1.1/types.h>
+#include <utils/Log.h>
+#include <sstream>
+#include <vector>
+
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+using ::android::sp;
+using ::android::hardware::hidl_death_recipient;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::tv::cec::V1_0::CecDeviceType;
+using ::android::hardware::tv::cec::V1_0::HdmiPortInfo;
+using ::android::hardware::tv::cec::V1_0::HdmiPortType;
+using ::android::hardware::tv::cec::V1_0::HotplugEvent;
+using ::android::hardware::tv::cec::V1_0::OptionKey;
+using ::android::hardware::tv::cec::V1_0::Result;
+using ::android::hardware::tv::cec::V1_0::SendMessageResult;
+using ::android::hardware::tv::cec::V1_1::CecLogicalAddress;
+using ::android::hardware::tv::cec::V1_1::CecMessage;
+using ::android::hardware::tv::cec::V1_1::IHdmiCec;
+using ::android::hardware::tv::cec::V1_1::IHdmiCecCallback;
+
+#define CEC_VERSION 0x05
+#define INCORRECT_VENDOR_ID 0x00
+
+// The main test class for TV CEC HAL.
+class HdmiCecTest : public ::testing::TestWithParam<std::string> {
+ public:
+ void SetUp() override {
+ hdmiCec = IHdmiCec::getService(GetParam());
+ ASSERT_NE(hdmiCec, nullptr);
+ ALOGI("%s: getService() for hdmiCec is %s", __func__,
+ hdmiCec->isRemote() ? "remote" : "local");
+
+ hdmiCec_death_recipient = new HdmiCecDeathRecipient();
+ hdmiCecCallback = new CecCallback();
+ ASSERT_NE(hdmiCec_death_recipient, nullptr);
+ ASSERT_TRUE(hdmiCec->linkToDeath(hdmiCec_death_recipient, 0).isOk());
+ }
+
+ std::vector<int> getDeviceTypes() {
+ std::vector<int> deviceTypes;
+ FILE* p = popen("getprop ro.hdmi.device_type", "re");
+ if (p) {
+ char* line = NULL;
+ size_t len = 0;
+ if (getline(&line, &len, p) > 0) {
+ std::istringstream stream(line);
+ std::string number{};
+ while (std::getline(stream, number, ',')) {
+ deviceTypes.push_back(stoi(number));
+ }
+ }
+ pclose(p);
+ }
+ return deviceTypes;
+ }
+
+ bool hasDeviceType(CecDeviceType type) {
+ std::vector<int> deviceTypes = getDeviceTypes();
+ return std::find(deviceTypes.begin(), deviceTypes.end(), (int)type) != deviceTypes.end();
+ }
+
+ class CecCallback : public IHdmiCecCallback {
+ public:
+ Return<void> onCecMessage(
+ const ::android::hardware::tv::cec::V1_0::CecMessage& /* message */) {
+ return Void();
+ }
+ Return<void> onCecMessage_1_1(
+ const ::android::hardware::tv::cec::V1_1::CecMessage& /* message */) {
+ return Void();
+ }
+ Return<void> onHotplugEvent(
+ const ::android::hardware::tv::cec::V1_0::HotplugEvent& /* event */) {
+ return Void();
+ }
+ };
+
+ class HdmiCecDeathRecipient : public hidl_death_recipient {
+ public:
+ void serviceDied(uint64_t /*cookie*/,
+ const android::wp<::android::hidl::base::V1_0::IBase>& /*who*/) override {
+ FAIL();
+ }
+ };
+
+ sp<IHdmiCec> hdmiCec;
+ sp<IHdmiCecCallback> hdmiCecCallback;
+ sp<HdmiCecDeathRecipient> hdmiCec_death_recipient;
+};
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HdmiCecTest);
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, HdmiCecTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(IHdmiCec::descriptor)),
+ android::hardware::PrintInstanceNameToString);
+
+TEST_P(HdmiCecTest, ClearAddLogicalAddress) {
+ hdmiCec->clearLogicalAddress();
+ Return<Result> ret = hdmiCec->addLogicalAddress_1_1(CecLogicalAddress::PLAYBACK_3);
+ EXPECT_EQ(ret, Result::SUCCESS);
+}
+
+TEST_P(HdmiCecTest, SendMessage) {
+ CecMessage message;
+ message.initiator = CecLogicalAddress::PLAYBACK_1;
+ message.destination = CecLogicalAddress::BROADCAST;
+ message.body.resize(1);
+ message.body[0] = 131;
+ SendMessageResult ret = hdmiCec->sendMessage_1_1(message);
+ EXPECT_EQ(ret, SendMessageResult::SUCCESS);
+}
+
+TEST_P(HdmiCecTest, CecVersion) {
+ Return<int32_t> ret = hdmiCec->getCecVersion();
+ EXPECT_GE(ret, CEC_VERSION);
+}
+
+TEST_P(HdmiCecTest, SetCallback) {
+ Return<void> ret = hdmiCec->setCallback_1_1(new CecCallback());
+ ASSERT_TRUE(ret.isOk());
+}
+
+TEST_P(HdmiCecTest, VendorId) {
+ Return<uint32_t> ret = hdmiCec->getVendorId();
+ EXPECT_NE(ret, INCORRECT_VENDOR_ID);
+}
+
+TEST_P(HdmiCecTest, GetPortInfo) {
+ hidl_vec<HdmiPortInfo> ports;
+ Return<void> ret =
+ hdmiCec->getPortInfo([&ports](hidl_vec<HdmiPortInfo> list) { ports = list; });
+ ASSERT_TRUE(ret.isOk());
+ bool cecSupportedOnDevice = false;
+ for (size_t i = 0; i < ports.size(); ++i) {
+ EXPECT_TRUE((ports[i].type == HdmiPortType::OUTPUT) ||
+ (ports[i].type == HdmiPortType::INPUT));
+ if (ports[i].portId == 0) {
+ ALOGW("%s: Port id should start from 1", __func__);
+ }
+ cecSupportedOnDevice = cecSupportedOnDevice | ports[i].cecSupported;
+ }
+ EXPECT_NE(cecSupportedOnDevice, false) << "At least one port should support CEC";
+}
+
+TEST_P(HdmiCecTest, SetOption) {
+ Return<void> wakeup = hdmiCec->setOption(OptionKey::WAKEUP, false);
+ ASSERT_TRUE(wakeup.isOk());
+ Return<void> enableCec = hdmiCec->setOption(OptionKey::ENABLE_CEC, false);
+ ASSERT_TRUE(enableCec.isOk());
+ Return<void> systemCecControl = hdmiCec->setOption(OptionKey::SYSTEM_CEC_CONTROL, true);
+ ASSERT_TRUE(systemCecControl.isOk());
+ // Restore option keys to their default values
+ hdmiCec->setOption(OptionKey::WAKEUP, true);
+ hdmiCec->setOption(OptionKey::ENABLE_CEC, true);
+ hdmiCec->setOption(OptionKey::SYSTEM_CEC_CONTROL, false);
+}
+
+TEST_P(HdmiCecTest, SetLanguage) {
+ Return<void> ret = hdmiCec->setLanguage("eng");
+ ASSERT_TRUE(ret.isOk());
+}
+
+TEST_P(HdmiCecTest, EnableAudioReturnChannel) {
+ hidl_vec<HdmiPortInfo> ports;
+ Return<void> ret =
+ hdmiCec->getPortInfo([&ports](hidl_vec<HdmiPortInfo> list) { ports = list; });
+ for (size_t i = 0; i < ports.size(); ++i) {
+ if (ports[i].arcSupported) {
+ Return<void> ret = hdmiCec->enableAudioReturnChannel(ports[i].portId, true);
+ ASSERT_TRUE(ret.isOk());
+ }
+ }
+}
\ No newline at end of file
diff --git a/tv/tuner/1.1/default/Frontend.cpp b/tv/tuner/1.1/default/Frontend.cpp
index 6956f30..243891c 100644
--- a/tv/tuner/1.1/default/Frontend.cpp
+++ b/tv/tuner/1.1/default/Frontend.cpp
@@ -196,7 +196,7 @@
}
case FrontendStatusType::MODULATION: {
FrontendModulationStatus modulationStatus;
- modulationStatus.isdbt(FrontendIsdbtModulation::MOD_16QAM); // value = 1 << 3
+ modulationStatus.isdbs(FrontendIsdbsModulation::MOD_BPSK); // value = 1 << 1
status.modulation(modulationStatus);
break;
}
@@ -281,12 +281,14 @@
for (int i = 0; i < statusTypes.size(); i++) {
V1_1::FrontendStatusTypeExt1_1 type = statusTypes[i];
V1_1::FrontendStatusExt1_1 status;
+
// assign randomly selected values for testing.
+ // TODO: assign status values according to the frontend type
switch (type) {
case V1_1::FrontendStatusTypeExt1_1::MODULATIONS: {
vector<V1_1::FrontendModulation> modulations;
V1_1::FrontendModulation modulation;
- modulation.isdbt(FrontendIsdbtModulation::MOD_16QAM); // value = 1 << 3
+ modulation.isdbs(FrontendIsdbsModulation::MOD_BPSK); // value = 1 << 1
modulations.push_back(modulation);
status.modulations(modulations);
break;
@@ -347,7 +349,7 @@
}
case V1_1::FrontendStatusTypeExt1_1::ROLL_OFF: {
V1_1::FrontendRollOff rollOff;
- rollOff.dvbs(FrontendDvbsRolloff::ROLLOFF_0_35);
+ rollOff.isdbs(FrontendIsdbsRolloff::ROLLOFF_0_35);
status.rollOff(rollOff);
break;
}
diff --git a/tv/tuner/1.1/default/Tuner.cpp b/tv/tuner/1.1/default/Tuner.cpp
index c3dcd1d..6cc9949 100644
--- a/tv/tuner/1.1/default/Tuner.cpp
+++ b/tv/tuner/1.1/default/Tuner.cpp
@@ -34,7 +34,7 @@
// Static Frontends array to maintain local frontends information
// Array index matches their FrontendId in the default impl
mFrontendSize = 9;
- mFrontends[0] = new Frontend(FrontendType::DVBT, 0, this);
+ mFrontends[0] = new Frontend(FrontendType::ISDBS, 0, this);
mFrontends[1] = new Frontend(FrontendType::ATSC, 1, this);
mFrontends[2] = new Frontend(FrontendType::DVBC, 2, this);
mFrontends[3] = new Frontend(FrontendType::DVBS, 3, this);
@@ -47,7 +47,7 @@
FrontendInfo::FrontendCapabilities caps;
caps = FrontendInfo::FrontendCapabilities();
- caps.dvbtCaps(FrontendDvbtCapabilities());
+ caps.isdbsCaps(FrontendIsdbsCapabilities());
mFrontendCaps[0] = caps;
caps = FrontendInfo::FrontendCapabilities();
@@ -168,6 +168,8 @@
FrontendStatusType::PLP_ID,
FrontendStatusType::LAYER_ERROR,
FrontendStatusType::ATSC3_PLP_INFO,
+ static_cast<FrontendStatusType>(V1_1::FrontendStatusTypeExt1_1::MODULATIONS),
+ static_cast<FrontendStatusType>(V1_1::FrontendStatusTypeExt1_1::ROLL_OFF),
};
// assign randomly selected values for testing.
info = {
diff --git a/usb/1.3/Android.bp b/usb/1.3/Android.bp
new file mode 100644
index 0000000..17367d3
--- /dev/null
+++ b/usb/1.3/Android.bp
@@ -0,0 +1,16 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.usb@1.3",
+ root: "android.hardware",
+ srcs: [
+ "IUsb.hal",
+ ],
+ interfaces: [
+ "android.hardware.usb@1.0",
+ "android.hardware.usb@1.1",
+ "android.hardware.usb@1.2",
+ "android.hidl.base@1.0",
+ ],
+ gen_java: true,
+}
diff --git a/usb/1.3/IUsb.hal b/usb/1.3/IUsb.hal
new file mode 100644
index 0000000..3d1d380
--- /dev/null
+++ b/usb/1.3/IUsb.hal
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package android.hardware.usb@1.3;
+
+import android.hardware.usb@1.2::IUsb;
+
+interface IUsb extends @1.2::IUsb {
+ /**
+ * This function is used to enable/disable USB controller when some
+ * scenarios need. This function can stop and restore USB data signaling.
+ *
+ * @param enable true Enable USB data signaling.
+ * false Disable USB data signaling.
+ * @return true enable or disable USB data successfully
+ * false if something wrong
+ */
+ enableUsbDataSignal(bool enable) generates(bool result);
+};
diff --git a/usb/1.3/vts/OWNERS b/usb/1.3/vts/OWNERS
new file mode 100644
index 0000000..a6a1e54
--- /dev/null
+++ b/usb/1.3/vts/OWNERS
@@ -0,0 +1,2 @@
+albertccwang@google.com
+badhri@google.com
diff --git a/usb/1.3/vts/functional/Android.bp b/usb/1.3/vts/functional/Android.bp
new file mode 100644
index 0000000..b62bb9d
--- /dev/null
+++ b/usb/1.3/vts/functional/Android.bp
@@ -0,0 +1,31 @@
+//
+// 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.
+//
+
+cc_test {
+ name: "VtsHalUsbV1_3TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: ["VtsHalUsbV1_3TargetTest.cpp"],
+ static_libs: [
+ "android.hardware.usb@1.0",
+ "android.hardware.usb@1.1",
+ "android.hardware.usb@1.2",
+ "android.hardware.usb@1.3",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
diff --git a/usb/1.3/vts/functional/VtsHalUsbV1_3TargetTest.cpp b/usb/1.3/vts/functional/VtsHalUsbV1_3TargetTest.cpp
new file mode 100644
index 0000000..ed35d42
--- /dev/null
+++ b/usb/1.3/vts/functional/VtsHalUsbV1_3TargetTest.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VtsHalUsbV1_3TargetTest"
+#include <android-base/logging.h>
+
+#include <android/hardware/usb/1.3/IUsb.h>
+
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+#include <log/log.h>
+#include <stdlib.h>
+#include <condition_variable>
+
+using ::android::sp;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::usb::V1_0::Status;
+using ::android::hardware::usb::V1_3::IUsb;
+using ::android::hidl::base::V1_0::IBase;
+
+// The main test class for the USB hidl HAL
+class UsbHidlTest : public ::testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ ALOGI(__FUNCTION__);
+ usb = IUsb::getService(GetParam());
+ ASSERT_NE(usb, nullptr);
+ }
+
+ virtual void TearDown() override { ALOGI("Teardown"); }
+
+ // USB hidl hal Proxy
+ sp<IUsb> usb;
+};
+
+/*
+ * Check to see if enable usb data signal succeeds.
+ * HAL service should call enableUsbDataSignal.
+ */
+TEST_P(UsbHidlTest, enableUsbDataSignal) {
+ Return<bool> ret = usb->enableUsbDataSignal(true);
+ ASSERT_TRUE(ret.isOk());
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UsbHidlTest);
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, UsbHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(IUsb::descriptor)),
+ android::hardware::PrintInstanceNameToString);
diff --git a/wifi/1.5/IWifiChip.hal b/wifi/1.5/IWifiChip.hal
index 209190a..5a3e288 100644
--- a/wifi/1.5/IWifiChip.hal
+++ b/wifi/1.5/IWifiChip.hal
@@ -236,19 +236,54 @@
setCountryCode(int8_t[2] code) generates (WifiStatus status);
/**
+ * Usable Wifi channels filter masks.
+ */
+ enum UsableChannelFilter : uint32_t {
+ /**
+ * Filter Wifi channels that should be avoided due to extreme
+ * cellular coexistence restrictions. Some Wifi channels can have
+ * extreme interference from/to cellular due to short frequency
+ * seperation with neighboring cellular channels or when there
+ * is harmonic and intermodulation interference. Channels which
+ * only have some performance degradation (e.g. power back off is
+ * sufficient to deal with coexistence issue) can be included and
+ * should not be filtered out.
+ */
+ CELLULAR_COEXISTENCE = 1 << 0,
+ /**
+ * Filter based on concurrency state.
+ * Examples:
+ * - 5GHz SAP operation may be supported in standalone mode, but if
+ * there is STA connection on 5GHz DFS channel, none of the 5GHz
+ * channels are usable for SAP if device does not support DFS SAP mode.
+ * - P2P GO may not be supported on indoor channels in EU during
+ * standalone mode but if there is a STA connection on indoor channel,
+ * P2P GO may be supported by some vendors on the same STA channel.
+ */
+ CONCURRENCY = 1 << 1,
+ };
+
+ /**
* Retrieve list of usable Wifi channels for the specified band &
* operational modes.
*
* The list of usable Wifi channels in a given band depends on factors
- * like current country code, operational mode (e.g. STA, SAP, CLI, GO,
- * TDLS, NAN) and any hard restrictons due to DFS, LTE Coex and
- * MCC(multi channel-concurrency).
+ * like current country code, operational mode (e.g. STA, SAP, WFD-CLI,
+ * WFD-GO, TDLS, NAN) and other restrictons due to DFS, cellular coexistence
+ * and conncurency state of the device.
*
* @param band |WifiBand| for which list of usable channels is requested.
* @param ifaceModeMask Bitmask of the modes represented by |WifiIfaceMode|
* Bitmask respresents all the modes that the caller is interested
- * in (e.g. STA, SAP, CLI, GO, TDLS, NAN).
- * Note: Bitmask does not represent concurrency matrix.
+ * in (e.g. STA, SAP, CLI, GO, TDLS, NAN). E.g. If the caller is
+ * interested in knowing usable channels for P2P CLI, P2P GO & NAN,
+ * ifaceModeMask would be set to
+ * IFACE_MODE_P2P_CLIENT|IFACE_MODE_P2P_GO|IFACE_MODE_NAN.
+ * @param filterMask Bitmask of filters represented by
+ * |UsableChannelFilter|. Specifies whether driver should filter
+ * channels based on additional criteria. If no filter is specified
+ * driver should return usable channels purely based on regulatory
+ * constraints.
* @return status WifiStatus of the operation.
* Possible status codes:
* |WifiStatusCode.SUCCESS|,
@@ -257,10 +292,15 @@
* |WifiStatusCode.FAILURE_UNKNOWN|
* @return channels List of channels represented by |WifiUsableChannel|
* Each entry represents a channel frequency, bandwidth and
- * bitmask of operational modes (e.g. STA, SAP, CLI, GO, TDLS, NAN)
- * allowed on that channel.
- * Note: Bitmask does not represent concurrency matrix.
+ * bitmask of modes (e.g. STA, SAP, CLI, GO, TDLS, NAN) that are
+ * allowed on that channel. E.g. If only STA mode can be supported
+ * on an indoor channel, only the IFACE_MODE_STA bit would be set
+ * for that channel. If 5GHz SAP cannot be supported, then none of
+ * the 5GHz channels will have IFACE_MODE_SOFTAP bit set.
+ * Note: Bits do not represent concurrency state. Each bit only
+ * represents whether particular mode is allowed on that channel.
*/
- getUsableChannels(WifiBand band, bitfield<WifiIfaceMode> ifaceModeMask)
+ getUsableChannels(WifiBand band, bitfield<WifiIfaceMode> ifaceModeMask,
+ bitfield<UsableChannelFilter> filterMask)
generates (WifiStatus status, vec<WifiUsableChannel> channels);
};
diff --git a/wifi/1.5/default/hidl_struct_util.cpp b/wifi/1.5/default/hidl_struct_util.cpp
index 7cee4cd..3c69da5 100644
--- a/wifi/1.5/default/hidl_struct_util.cpp
+++ b/wifi/1.5/default/hidl_struct_util.cpp
@@ -445,6 +445,20 @@
return hidl_iface_mask;
}
+uint32_t convertHidlUsableChannelFilterToLegacy(uint32_t hidl_filter_mask) {
+ uint32_t legacy_filter_mask = 0;
+ if (hidl_filter_mask &
+ IWifiChip::UsableChannelFilter::CELLULAR_COEXISTENCE) {
+ legacy_filter_mask |=
+ legacy_hal::WIFI_USABLE_CHANNEL_FILTER_CELLULAR_COEXISTENCE;
+ }
+ if (hidl_filter_mask & IWifiChip::UsableChannelFilter::CONCURRENCY) {
+ legacy_filter_mask |=
+ legacy_hal::WIFI_USABLE_CHANNEL_FILTER_CONCURRENCY;
+ }
+ return legacy_filter_mask;
+}
+
bool convertLegacyWifiUsableChannelToHidl(
const legacy_hal::wifi_usable_channel& legacy_usable_channel,
V1_5::WifiUsableChannel* hidl_usable_channel) {
diff --git a/wifi/1.5/default/hidl_struct_util.h b/wifi/1.5/default/hidl_struct_util.h
index c0d7bf8..8b81033 100644
--- a/wifi/1.5/default/hidl_struct_util.h
+++ b/wifi/1.5/default/hidl_struct_util.h
@@ -208,6 +208,7 @@
std::vector<V1_4::RttResult>* hidl_results);
uint32_t convertHidlWifiBandToLegacyMacBand(V1_5::WifiBand band);
uint32_t convertHidlWifiIfaceModeToLegacy(uint32_t hidl_iface_mask);
+uint32_t convertHidlUsableChannelFilterToLegacy(uint32_t hidl_filter_mask);
bool convertLegacyWifiUsableChannelsToHidl(
const std::vector<legacy_hal::wifi_usable_channel>& legacy_usable_channels,
std::vector<V1_5::WifiUsableChannel>* hidl_usable_channels);
diff --git a/wifi/1.5/default/wifi_chip.cpp b/wifi/1.5/default/wifi_chip.cpp
index 2dc7314..0450a7b 100644
--- a/wifi/1.5/default/wifi_chip.cpp
+++ b/wifi/1.5/default/wifi_chip.cpp
@@ -740,10 +740,11 @@
Return<void> WifiChip::getUsableChannels(
WifiBand band, hidl_bitfield<WifiIfaceMode> ifaceModeMask,
+ hidl_bitfield<UsableChannelFilter> filterMask,
getUsableChannels_cb _hidl_cb) {
return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
&WifiChip::getUsableChannelsInternal, _hidl_cb, band,
- ifaceModeMask);
+ ifaceModeMask, filterMask);
}
void WifiChip::invalidateAndRemoveAllIfaces() {
@@ -1500,13 +1501,17 @@
}
std::pair<WifiStatus, std::vector<WifiUsableChannel>>
-WifiChip::getUsableChannelsInternal(WifiBand band, uint32_t ifaceModeMask) {
+WifiChip::getUsableChannelsInternal(WifiBand band, uint32_t ifaceModeMask,
+ uint32_t filterMask) {
legacy_hal::wifi_error legacy_status;
std::vector<legacy_hal::wifi_usable_channel> legacy_usable_channels;
std::tie(legacy_status, legacy_usable_channels) =
legacy_hal_.lock()->getUsableChannels(
hidl_struct_util::convertHidlWifiBandToLegacyMacBand(band),
- hidl_struct_util::convertHidlWifiIfaceModeToLegacy(ifaceModeMask));
+ hidl_struct_util::convertHidlWifiIfaceModeToLegacy(ifaceModeMask),
+ hidl_struct_util::convertHidlUsableChannelFilterToLegacy(
+ filterMask));
+
if (legacy_status != legacy_hal::WIFI_SUCCESS) {
return {createWifiStatusFromLegacyError(legacy_status), {}};
}
diff --git a/wifi/1.5/default/wifi_chip.h b/wifi/1.5/default/wifi_chip.h
index d542792..b4ed30e 100644
--- a/wifi/1.5/default/wifi_chip.h
+++ b/wifi/1.5/default/wifi_chip.h
@@ -180,9 +180,10 @@
setCoexUnsafeChannels_cb hidl_status_cb) override;
Return<void> setCountryCode(const hidl_array<int8_t, 2>& code,
setCountryCode_cb _hidl_cb) override;
- Return<void> getUsableChannels(WifiBand band,
- hidl_bitfield<WifiIfaceMode> ifaceModeMask,
- getUsableChannels_cb _hidl_cb) override;
+ Return<void> getUsableChannels(
+ WifiBand band, hidl_bitfield<WifiIfaceMode> ifaceModeMask,
+ hidl_bitfield<UsableChannelFilter> filterMask,
+ getUsableChannels_cb _hidl_cb) override;
private:
void invalidateAndRemoveAllIfaces();
@@ -265,7 +266,8 @@
std::vector<CoexUnsafeChannel> unsafe_channels, uint32_t restrictions);
WifiStatus setCountryCodeInternal(const std::array<int8_t, 2>& code);
std::pair<WifiStatus, std::vector<WifiUsableChannel>>
- getUsableChannelsInternal(WifiBand band, uint32_t ifaceModeMask);
+ getUsableChannelsInternal(WifiBand band, uint32_t ifaceModeMask,
+ uint32_t filterMask);
WifiStatus handleChipConfiguration(
std::unique_lock<std::recursive_mutex>* lock, ChipModeId mode_id);
WifiStatus registerDebugRingBufferCallback();
diff --git a/wifi/1.5/default/wifi_legacy_hal.cpp b/wifi/1.5/default/wifi_legacy_hal.cpp
index 94603b3..f5ca753 100644
--- a/wifi/1.5/default/wifi_legacy_hal.cpp
+++ b/wifi/1.5/default/wifi_legacy_hal.cpp
@@ -1638,12 +1638,14 @@
}
std::pair<wifi_error, std::vector<wifi_usable_channel>>
-WifiLegacyHal::getUsableChannels(uint32_t band_mask, uint32_t iface_mode_mask) {
+WifiLegacyHal::getUsableChannels(uint32_t band_mask, uint32_t iface_mode_mask,
+ uint32_t filter_mask) {
std::vector<wifi_usable_channel> channels;
channels.resize(kMaxWifiUsableChannels);
uint32_t size = 0;
wifi_error status = global_func_table_.wifi_get_usable_channels(
- global_handle_, band_mask, iface_mode_mask, channels.size(), &size,
+ global_handle_, band_mask, iface_mode_mask, filter_mask,
+ channels.size(), &size,
reinterpret_cast<wifi_usable_channel*>(channels.data()));
CHECK(size >= 0 && size <= kMaxWifiUsableChannels);
channels.resize(size);
diff --git a/wifi/1.5/default/wifi_legacy_hal.h b/wifi/1.5/default/wifi_legacy_hal.h
index dc641ae..03ca841 100644
--- a/wifi/1.5/default/wifi_legacy_hal.h
+++ b/wifi/1.5/default/wifi_legacy_hal.h
@@ -313,6 +313,8 @@
using ::wifi_tx_packet_fate;
using ::wifi_tx_report;
using ::wifi_usable_channel;
+using ::WIFI_USABLE_CHANNEL_FILTER_CELLULAR_COEXISTENCE;
+using ::WIFI_USABLE_CHANNEL_FILTER_CONCURRENCY;
using ::WLAN_MAC_2_4_BAND;
using ::WLAN_MAC_5_0_BAND;
using ::WLAN_MAC_60_0_BAND;
@@ -705,7 +707,7 @@
// Retrieve the list of usable channels in the requested bands
// for the requested modes
std::pair<wifi_error, std::vector<wifi_usable_channel>> getUsableChannels(
- uint32_t band_mask, uint32_t iface_mode_mask);
+ uint32_t band_mask, uint32_t iface_mode_mask, uint32_t filter_mask);
private:
// Retrieve interface handles for all the available interfaces.
diff --git a/wifi/1.5/vts/functional/wifi_chip_hidl_test.cpp b/wifi/1.5/vts/functional/wifi_chip_hidl_test.cpp
index 509f1bd..5ac747d 100644
--- a/wifi/1.5/vts/functional/wifi_chip_hidl_test.cpp
+++ b/wifi/1.5/vts/functional/wifi_chip_hidl_test.cpp
@@ -195,10 +195,12 @@
TEST_P(WifiChipHidlTest, getUsableChannels) {
uint32_t ifaceModeMask =
WifiIfaceMode::IFACE_MODE_P2P_CLIENT | WifiIfaceMode::IFACE_MODE_P2P_GO;
+ uint32_t filterMask = IWifiChip::UsableChannelFilter::CELLULAR_COEXISTENCE |
+ IWifiChip::UsableChannelFilter::CONCURRENCY;
configureChipForIfaceType(IfaceType::STA, true);
WifiBand band = WifiBand::BAND_24GHZ_5GHZ_6GHZ;
- const auto& statusNonEmpty =
- HIDL_INVOKE(wifi_chip_, getUsableChannels, band, ifaceModeMask);
+ const auto& statusNonEmpty = HIDL_INVOKE(wifi_chip_, getUsableChannels,
+ band, ifaceModeMask, filterMask);
if (statusNonEmpty.first.code != WifiStatusCode::SUCCESS) {
EXPECT_EQ(WifiStatusCode::ERROR_NOT_SUPPORTED,
statusNonEmpty.first.code);