Merge "Wifi: add 3 new fields to ConnectionCapabilities."
diff --git a/audio/common/6.0/types.hal b/audio/common/6.0/types.hal
index 563e05d..26e8acb 100644
--- a/audio/common/6.0/types.hal
+++ b/audio/common/6.0/types.hal
@@ -762,6 +762,10 @@
VIRTUAL_SOURCE = 15,
ASSISTANT = 16,
CALL_ASSISTANT = 17,
+ EMERGENCY = 1000,
+ SAFETY = 1001,
+ VEHICLE_STATUS = 1002,
+ ANNOUNCEMENT = 1003,
};
/** Type of audio generated by an application. */
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 4179503..2523d8b 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -204,6 +204,8 @@
</hal>
<hal format="hidl" optional="false">
<name>android.hardware.graphics.allocator</name>
+ <!-- TODO(b/136016160): deprecating 2.0. New devices should not use this -->
+ <version>2.0</version>
<version>3.0</version>
<version>4.0</version>
<interface>
@@ -221,6 +223,8 @@
</hal>
<hal format="hidl" optional="false">
<name>android.hardware.graphics.mapper</name>
+ <!-- TODO(b/136016160): deprecating 2.1. New devices should not use this -->
+ <version>2.1</version>
<version>3.0</version>
<version>4.0</version>
<interface>
diff --git a/current.txt b/current.txt
index d9ee41f..e8ae1d8 100644
--- a/current.txt
+++ b/current.txt
@@ -607,7 +607,7 @@
2df5d5866b37776f25079c0e54b54350a2abe4e025a59c9e02a7d3abe8ca00e8 android.hardware.audio@6.0::IStreamIn
78e4138cc8307c11fc777c3bd376e581ba4ba48196b05ca1d7cdfa515c87b48a android.hardware.audio@6.0::IStreamOut
997fdaad7a9d17ee7e01feb7031a753e2365e72ad30b11d950e9183fabdf3844 android.hardware.audio@6.0::IStreamOutCallback
-8c4232772efeb9905b4c287723e0ee8b2c4bf5ba11728d051171b070e3d79144 android.hardware.audio.common@6.0::types
+43a3303378f5b9852c2da9ac2c1d440965aab7ba02a800229e7b3c84e2167e06 android.hardware.audio.common@6.0::types
817930d58412d662cb45e641c50cb62c727e4a3e3ffe7029a53cad9677b97d58 android.hardware.audio.effect@6.0::types
525bec6b44f1103869c269a128d51b8dccd73af5340ba863c8886c68357c7faf android.hardware.audio.effect@6.0::IAcousticEchoCancelerEffect
8d76bbe3719d051a8e9a1dcf9244f37f5b0a491feb249fa48391edf7cb4f3131 android.hardware.audio.effect@6.0::IAutomaticGainControlEffect
@@ -650,11 +650,12 @@
ac429fca0da4ce91218768ec31b64ded88251f8a26d8c4f27c06abdc5b1926d9 android.hardware.keymaster@4.1::types
df9c79c4fdde2821550c6d5c3d07f5ec0adfb1b702561ce543c906ddef698703 android.hardware.media.c2@1.1::IComponent
a3eddd9bbdc87e8c22764070037dd1154f1cf006e6fba93364c4f85d4c134a19 android.hardware.media.c2@1.1::IComponentStore
-4b5c8546533db9412fec6d32c0ef42b22e5e68dbf390c775ec3c22bb2d501102 android.hardware.neuralnetworks@1.3::IBuffer
-234cc547d63d2f24a447aee0a9a76cab68b31c080adadc5a960598b827a69fa2 android.hardware.neuralnetworks@1.3::IDevice
-058b48f0e2e725bb2b3fa2b7917b0f0a696383d03a4c57afe26f0eadb6a7af28 android.hardware.neuralnetworks@1.3::IPreparedModel
-94e803236398bed1febb11cc21051bc42ec003700139b099d6c479e02a7ca3c3 android.hardware.neuralnetworks@1.3::IPreparedModelCallback
-2576ba54711218ce0d7f207baa533fca9af3c630756938ede6e73fe197b7ea38 android.hardware.neuralnetworks@1.3::types
+65c16331e57f6dd68b3971f06f78fe9e3209afb60630c31705aa355f9a52bf0d android.hardware.neuralnetworks@1.3::IBuffer
+d1f382d14e1384b907d5bb5780df7f01934650d556fedbed2f15a90773c657d6 android.hardware.neuralnetworks@1.3::IDevice
+4167dc3ad35e9cd0d2057d4868c7675ae2c3c9d05bbd614c1f5dccfa5fd68797 android.hardware.neuralnetworks@1.3::IExecutionCallback
+7d23020248194abbee8091cc624f39a5a6d7ccba338b172d5d2d3df0cceffbee android.hardware.neuralnetworks@1.3::IPreparedModel
+0439a1fbbec7f16e5e4c653d85ac685d51bfafbae15b8f8cca530acdd7d6a8ce android.hardware.neuralnetworks@1.3::IPreparedModelCallback
+ee65638f8af3f9f4f222e7208eaa9f1f8e7f8e0a21545846ba67d0e27624efa1 android.hardware.neuralnetworks@1.3::types
3e01d4446cd69fd1c48f8572efd97487bc179564b32bd795800b97bbe10be37b android.hardware.wifi@1.4::IWifi
c67aaf26a7a40d14ea61e70e20afacbd0bb906df1704d585ac8599fbb69dd44b android.hardware.wifi.hostapd@1.2::IHostapd
11f6448d15336361180391c8ebcdfd2d7cf77b3782d577e594d583aadc9c2877 android.hardware.wifi.hostapd@1.2::types
@@ -666,14 +667,14 @@
##
# BEGIN Radio HAL Merge Conflict Avoidance Buffer - STOPSHIP if present
##
-73b5418353fe52721267d64592d4d4c1b77fbd1ef4261d964865de88e62ee0be android.hardware.radio@1.5::types
+70cbe7cbeb51834f124a8b5940336dc9ec158a17487ab8b905ae9cf62f66f476 android.hardware.radio@1.5::types
996f98ffe508a2f6f1755c1511b50067f7883f7c445dea9f3e931385f020b7ab android.hardware.radio@1.5::IRadio
20d52e66fd548f89bcb98cda42749a591ce8f439a2a7148617adac0c967ad937 android.hardware.radio@1.5::IRadioIndication
1512f6e1198e1aa0ebcbdb1694d0ed500a3e7791d6f305327866112331d82b66 android.hardware.radio@1.5::IRadioResponse
-55f0a15642869ec98a55ea0a5ac049d3e1a6245ff7750deb6bcb7182057eee83 android.hardware.radio.config@1.3::types
-b27ab0cd40b0b078cdcd024bfe1061c4c4c065f3519eeb9347fa359a3268a5ae android.hardware.radio.config@1.3::IRadioConfig
+5971a891d7d8843e9fb9f44583a9a0a265ec42fd5e4e1c95c9803454d21fabf7 android.hardware.radio.config@1.3::types
+a2977755bc5f1ef47f04b7f2400632efda6218e1515dba847da487145cfabc4f android.hardware.radio.config@1.3::IRadioConfig
742360c775313438b0f82256eac62fb5bbc76a6ae6f388573f3aa142fb2c1eea android.hardware.radio.config@1.3::IRadioConfigIndication
-7683fed9d253956071f18b152e6be657719536f98d9b534433d5e411bcde5061 android.hardware.radio.config@1.3::IRadioConfigResponse
+0006ab8e8b0910cbd3bbb08d5f17d5fac7d65a2bdad5f2334e4851db9d1e6fa8 android.hardware.radio.config@1.3::IRadioConfigResponse
##
# END Radio HAL Merge Conflict Avoidance Buffer - STOPSHIP if present
##
diff --git a/keymaster/4.0/support/Android.bp b/keymaster/4.0/support/Android.bp
index 2f40282..9c5fbab 100644
--- a/keymaster/4.0/support/Android.bp
+++ b/keymaster/4.0/support/Android.bp
@@ -27,11 +27,10 @@
"authorization_set.cpp",
"key_param_output.cpp",
"keymaster_utils.cpp",
- "Keymaster.cpp",
- "Keymaster3.cpp",
- "Keymaster4.cpp",
],
- export_include_dirs: ["include"],
+ export_include_dirs: [
+ "include",
+ ],
shared_libs: [
"android.hardware.keymaster@3.0",
"android.hardware.keymaster@4.0",
@@ -39,6 +38,5 @@
"libcrypto",
"libhardware",
"libhidlbase",
- "libutils",
- ]
+ ],
}
diff --git a/keymaster/4.0/support/Keymaster4.cpp b/keymaster/4.0/support/Keymaster4.cpp
deleted file mode 100644
index cc3d656..0000000
--- a/keymaster/4.0/support/Keymaster4.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-**
-** Copyright 2017, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include <keymasterV4_0/Keymaster4.h>
-
-#include <android-base/logging.h>
-
-namespace android {
-namespace hardware {
-namespace keymaster {
-namespace V4_0 {
-namespace support {
-
-void Keymaster4::getVersionIfNeeded() {
- if (haveVersion_) return;
-
- auto rc =
- dev_->getHardwareInfo([&](SecurityLevel securityLevel, const hidl_string& keymasterName,
- const hidl_string& authorName) {
- version_ = {keymasterName, authorName, 4 /* major version */, securityLevel,
- true /* supportsEc */};
- haveVersion_ = true;
- });
-
- CHECK(rc.isOk()) << "Got error " << rc.description() << " trying to get hardware info";
-}
-
-} // namespace support
-} // namespace V4_0
-} // namespace keymaster
-} // namespace hardware
-} // namespace android
diff --git a/keymaster/4.0/support/OWNERS b/keymaster/4.0/support/OWNERS
index 335660d..a9efe66 100644
--- a/keymaster/4.0/support/OWNERS
+++ b/keymaster/4.0/support/OWNERS
@@ -1,2 +1,3 @@
jdanis@google.com
swillden@google.com
+jbires@google.com
diff --git a/keymaster/4.0/support/attestation_record.cpp b/keymaster/4.0/support/attestation_record.cpp
index 000d46e..27e00c1 100644
--- a/keymaster/4.0/support/attestation_record.cpp
+++ b/keymaster/4.0/support/attestation_record.cpp
@@ -321,19 +321,20 @@
LOG(ERROR) << AT << "Failed record parsing";
return ErrorCode::UNKNOWN_ERROR;
}
- if (!record->tee_enforced) {
- LOG(ERROR) << AT << "Failed hardware characteristic parsing";
+
+ KM_ROOT_OF_TRUST* root_of_trust = nullptr;
+ if (record->tee_enforced && record->tee_enforced->root_of_trust) {
+ root_of_trust = record->tee_enforced->root_of_trust;
+ } else if (record->software_enforced && record->software_enforced->root_of_trust) {
+ root_of_trust = record->software_enforced->root_of_trust;
+ } else {
+ LOG(ERROR) << AT << " Failed root of trust parsing";
return ErrorCode::INVALID_ARGUMENT;
}
- if (!record->tee_enforced->root_of_trust) {
- LOG(ERROR) << AT << "Failed root of trust parsing";
+ if (!root_of_trust->verified_boot_key) {
+ LOG(ERROR) << AT << " Failed verified boot key parsing";
return ErrorCode::INVALID_ARGUMENT;
}
- if (!record->tee_enforced->root_of_trust->verified_boot_key) {
- LOG(ERROR) << AT << "Failed verified boot key parsing";
- return ErrorCode::INVALID_ARGUMENT;
- }
- KM_ROOT_OF_TRUST* root_of_trust = record->tee_enforced->root_of_trust;
auto& vb_key = root_of_trust->verified_boot_key;
verified_boot_key->resize(vb_key->length);
@@ -342,19 +343,19 @@
*verified_boot_state = static_cast<keymaster_verified_boot_t>(
ASN1_ENUMERATED_get(root_of_trust->verified_boot_state));
if (!verified_boot_state) {
- LOG(ERROR) << AT << "Failed verified boot state parsing";
+ LOG(ERROR) << AT << " Failed verified boot state parsing";
return ErrorCode::INVALID_ARGUMENT;
}
*device_locked = root_of_trust->device_locked;
if (!device_locked) {
- LOG(ERROR) << AT << "Failed device locked parsing";
+ LOG(ERROR) << AT << " Failed device locked parsing";
return ErrorCode::INVALID_ARGUMENT;
}
auto& vb_hash = root_of_trust->verified_boot_hash;
if (!vb_hash) {
- LOG(ERROR) << AT << "Failed verified boot hash parsing";
+ LOG(ERROR) << AT << " Failed verified boot hash parsing";
return ErrorCode::INVALID_ARGUMENT;
}
verified_boot_hash->resize(vb_hash->length);
diff --git a/keymaster/4.0/support/authorization_set.cpp b/keymaster/4.0/support/authorization_set.cpp
index d6b50f5..a024ff9 100644
--- a/keymaster/4.0/support/authorization_set.cpp
+++ b/keymaster/4.0/support/authorization_set.cpp
@@ -25,7 +25,7 @@
namespace keymaster {
namespace V4_0 {
-inline bool keyParamLess(const KeyParameter& a, const KeyParameter& b) {
+bool keyParamLess(const KeyParameter& a, const KeyParameter& b) {
if (a.tag != b.tag) return a.tag < b.tag;
int retval;
switch (typeFromTag(a.tag)) {
@@ -58,7 +58,7 @@
return false;
}
-inline bool keyParamEqual(const KeyParameter& a, const KeyParameter& b) {
+bool keyParamEqual(const KeyParameter& a, const KeyParameter& b) {
if (a.tag != b.tag) return false;
switch (typeFromTag(a.tag)) {
diff --git a/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h b/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h
deleted file mode 100644
index dfd03ef..0000000
--- a/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- **
- ** Copyright 2017, The Android Open Source Project
- **
- ** Licensed under the Apache License, Version 2.0 (the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
- **
- ** http://www.apache.org/licenses/LICENSE-2.0
- **
- ** Unless required by applicable law or agreed to in writing, software
- ** distributed under the License is distributed on an "AS IS" BASIS,
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ** See the License for the specific language governing permissions and
- ** limitations under the License.
- */
-
-#ifndef HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_4_H_
-#define HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_4_H_
-
-#include "Keymaster.h"
-
-namespace android {
-namespace hardware {
-namespace keymaster {
-namespace V4_0 {
-namespace support {
-
-using android::sp;
-using IKeymaster4Device = ::android::hardware::keymaster::V4_0::IKeymasterDevice;
-
-class Keymaster4 : public Keymaster {
- public:
- using WrappedIKeymasterDevice = IKeymaster4Device;
- Keymaster4(sp<IKeymasterDevice> km4_dev, const hidl_string& instanceName)
- : Keymaster(IKeymaster4Device::descriptor, instanceName),
- haveVersion_(false),
- dev_(km4_dev) {}
-
- const VersionResult& halVersion() const override {
- const_cast<Keymaster4*>(this)->getVersionIfNeeded();
- return version_;
- }
-
- Return<void> getHardwareInfo(getHardwareInfo_cb _hidl_cb) override {
- return dev_->getHardwareInfo(_hidl_cb);
- }
-
- Return<void> getHmacSharingParameters(getHmacSharingParameters_cb _hidl_cb) override {
- return dev_->getHmacSharingParameters(_hidl_cb);
- }
-
- Return<void> computeSharedHmac(const hidl_vec<HmacSharingParameters>& params,
- computeSharedHmac_cb _hidl_cb) override {
- return dev_->computeSharedHmac(params, _hidl_cb);
- }
-
- Return<void> verifyAuthorization(uint64_t operationHandle, const hidl_vec<KeyParameter>& params,
- const HardwareAuthToken& authToken,
- verifyAuthorization_cb _hidl_cb) override {
- return dev_->verifyAuthorization(operationHandle, params, authToken, _hidl_cb);
- }
-
- Return<ErrorCode> addRngEntropy(const hidl_vec<uint8_t>& data) override {
- return dev_->addRngEntropy(data);
- }
-
- Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,
- generateKey_cb _hidl_cb) override {
- return dev_->generateKey(keyParams, _hidl_cb);
- }
-
- Return<void> getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,
- const hidl_vec<uint8_t>& clientId,
- const hidl_vec<uint8_t>& appData,
- getKeyCharacteristics_cb _hidl_cb) override {
- return dev_->getKeyCharacteristics(keyBlob, clientId, appData, _hidl_cb);
- }
-
- Return<void> importKey(const hidl_vec<KeyParameter>& params, KeyFormat keyFormat,
- const hidl_vec<uint8_t>& keyData, importKey_cb _hidl_cb) override {
- return dev_->importKey(params, keyFormat, keyData, _hidl_cb);
- }
-
- Return<void> importWrappedKey(const hidl_vec<uint8_t>& wrappedKeyData,
- const hidl_vec<uint8_t>& wrappingKeyBlob,
- const hidl_vec<uint8_t>& maskingKey,
- const hidl_vec<KeyParameter>& unwrappingParams,
- uint64_t passwordSid, uint64_t biometricSid,
- importWrappedKey_cb _hidl_cb) {
- return dev_->importWrappedKey(wrappedKeyData, wrappingKeyBlob, maskingKey, unwrappingParams,
- passwordSid, biometricSid, _hidl_cb);
- }
-
- Return<void> exportKey(KeyFormat exportFormat, const hidl_vec<uint8_t>& keyBlob,
- const hidl_vec<uint8_t>& clientId, const hidl_vec<uint8_t>& appData,
- exportKey_cb _hidl_cb) override {
- return dev_->exportKey(exportFormat, keyBlob, clientId, appData, _hidl_cb);
- }
-
- Return<void> attestKey(const hidl_vec<uint8_t>& keyToAttest,
- const hidl_vec<KeyParameter>& attestParams,
- attestKey_cb _hidl_cb) override {
- return dev_->attestKey(keyToAttest, attestParams, _hidl_cb);
- }
-
- Return<void> upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
- const hidl_vec<KeyParameter>& upgradeParams,
- upgradeKey_cb _hidl_cb) override {
- return dev_->upgradeKey(keyBlobToUpgrade, upgradeParams, _hidl_cb);
- }
-
- Return<ErrorCode> deleteKey(const hidl_vec<uint8_t>& keyBlob) override {
- return dev_->deleteKey(keyBlob);
- }
-
- Return<ErrorCode> deleteAllKeys() override { return dev_->deleteAllKeys(); }
-
- Return<ErrorCode> destroyAttestationIds() override { return dev_->destroyAttestationIds(); }
-
- Return<void> begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
- const hidl_vec<KeyParameter>& inParams, const HardwareAuthToken& authToken,
- begin_cb _hidl_cb) override {
- return dev_->begin(purpose, key, inParams, authToken, _hidl_cb);
- }
-
- Return<void> update(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
- const hidl_vec<uint8_t>& input, const HardwareAuthToken& authToken,
- const VerificationToken& verificationToken, update_cb _hidl_cb) override {
- return dev_->update(operationHandle, inParams, input, authToken, verificationToken,
- _hidl_cb);
- }
-
- Return<void> finish(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
- const hidl_vec<uint8_t>& input, const hidl_vec<uint8_t>& signature,
- const HardwareAuthToken& authToken,
- const VerificationToken& verificationToken, finish_cb _hidl_cb) override {
- return dev_->finish(operationHandle, inParams, input, signature, authToken,
- verificationToken, _hidl_cb);
- }
-
- Return<ErrorCode> abort(uint64_t operationHandle) override {
- return dev_->abort(operationHandle);
- }
-
- private:
- void getVersionIfNeeded();
-
- bool haveVersion_;
- VersionResult version_;
- sp<IKeymaster4Device> dev_;
-};
-
-} // namespace support
-} // namespace V4_0
-} // namespace keymaster
-} // namespace hardware
-} // namespace android
-
-#endif // HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_4_H_
diff --git a/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h b/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
index cb29c64..bc7f311 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
@@ -355,95 +355,61 @@
return accessTagValue(ttag, param);
}
+inline bool operator<(const KeyParameter& a, const KeyParameter& b) {
+ if (a.tag != b.tag) return a.tag < b.tag;
+ int retval;
+ switch (typeFromTag(a.tag)) {
+ case TagType::INVALID:
+ case TagType::BOOL:
+ return false;
+ case TagType::ENUM:
+ case TagType::ENUM_REP:
+ case TagType::UINT:
+ case TagType::UINT_REP:
+ return a.f.integer < b.f.integer;
+ case TagType::ULONG:
+ case TagType::ULONG_REP:
+ return a.f.longInteger < b.f.longInteger;
+ case TagType::DATE:
+ return a.f.dateTime < b.f.dateTime;
+ case TagType::BIGNUM:
+ case TagType::BYTES:
+ // Handle the empty cases.
+ if (a.blob.size() == 0) return b.blob.size() != 0;
+ if (b.blob.size() == 0) return false;
+
+ retval = memcmp(&a.blob[0], &b.blob[0], std::min(a.blob.size(), b.blob.size()));
+ // if one is the prefix of the other the longer wins
+ if (retval == 0) return a.blob.size() < b.blob.size();
+ // Otherwise a is less if a is less.
+ else
+ return retval < 0;
+ }
+ return false;
+}
+
inline bool operator==(const KeyParameter& a, const KeyParameter& b) {
- if (a.tag != b.tag) {
- return false;
- }
+ if (a.tag != b.tag) return false;
- switch (a.tag) {
- /* Boolean tags */
- case Tag::INVALID:
- case Tag::CALLER_NONCE:
- case Tag::INCLUDE_UNIQUE_ID:
- case Tag::BOOTLOADER_ONLY:
- case Tag::NO_AUTH_REQUIRED:
- case Tag::ALLOW_WHILE_ON_BODY:
- case Tag::UNLOCKED_DEVICE_REQUIRED:
- case Tag::ROLLBACK_RESISTANCE:
- case Tag::RESET_SINCE_ID_ROTATION:
- case Tag::TRUSTED_CONFIRMATION_REQUIRED:
- case Tag::TRUSTED_USER_PRESENCE_REQUIRED:
+ switch (typeFromTag(a.tag)) {
+ case TagType::INVALID:
+ case TagType::BOOL:
return true;
-
- /* Integer tags */
- case Tag::KEY_SIZE:
- case Tag::MIN_MAC_LENGTH:
- case Tag::MIN_SECONDS_BETWEEN_OPS:
- case Tag::MAX_USES_PER_BOOT:
- case Tag::OS_VERSION:
- case Tag::OS_PATCHLEVEL:
- case Tag::MAC_LENGTH:
- case Tag::USER_ID:
- case Tag::AUTH_TIMEOUT:
- case Tag::VENDOR_PATCHLEVEL:
- case Tag::BOOT_PATCHLEVEL:
+ case TagType::ENUM:
+ case TagType::ENUM_REP:
+ case TagType::UINT:
+ case TagType::UINT_REP:
return a.f.integer == b.f.integer;
-
- /* Long integer tags */
- case Tag::RSA_PUBLIC_EXPONENT:
- case Tag::USER_SECURE_ID:
+ case TagType::ULONG:
+ case TagType::ULONG_REP:
return a.f.longInteger == b.f.longInteger;
-
- /* Date-time tags */
- case Tag::ACTIVE_DATETIME:
- case Tag::ORIGINATION_EXPIRE_DATETIME:
- case Tag::USAGE_EXPIRE_DATETIME:
- case Tag::CREATION_DATETIME:
+ case TagType::DATE:
return a.f.dateTime == b.f.dateTime;
-
- /* Bytes tags */
- case Tag::APPLICATION_ID:
- case Tag::APPLICATION_DATA:
- case Tag::ROOT_OF_TRUST:
- case Tag::UNIQUE_ID:
- case Tag::ATTESTATION_CHALLENGE:
- case Tag::ATTESTATION_APPLICATION_ID:
- case Tag::ATTESTATION_ID_BRAND:
- case Tag::ATTESTATION_ID_DEVICE:
- case Tag::ATTESTATION_ID_PRODUCT:
- case Tag::ATTESTATION_ID_SERIAL:
- case Tag::ATTESTATION_ID_IMEI:
- case Tag::ATTESTATION_ID_MEID:
- case Tag::ATTESTATION_ID_MANUFACTURER:
- case Tag::ATTESTATION_ID_MODEL:
- case Tag::ASSOCIATED_DATA:
- case Tag::CONFIRMATION_TOKEN:
- case Tag::NONCE:
- return a.blob == b.blob;
-
- /* Enum tags */
- case Tag::PURPOSE:
- return a.f.purpose == b.f.purpose;
- case Tag::ALGORITHM:
- return a.f.algorithm == b.f.algorithm;
- case Tag::BLOCK_MODE:
- return a.f.blockMode == b.f.blockMode;
- case Tag::DIGEST:
- return a.f.digest == b.f.digest;
- case Tag::PADDING:
- return a.f.paddingMode == b.f.paddingMode;
- case Tag::EC_CURVE:
- return a.f.ecCurve == b.f.ecCurve;
- case Tag::BLOB_USAGE_REQUIREMENTS:
- return a.f.keyBlobUsageRequirements == b.f.keyBlobUsageRequirements;
- case Tag::USER_AUTH_TYPE:
- return a.f.integer == b.f.integer;
- case Tag::ORIGIN:
- return a.f.origin == b.f.origin;
- case Tag::HARDWARE_TYPE:
- return a.f.hardwareType == b.f.hardwareType;
+ case TagType::BIGNUM:
+ case TagType::BYTES:
+ if (a.blob.size() != b.blob.size()) return false;
+ return a.blob.size() == 0 || memcmp(&a.blob[0], &b.blob[0], a.blob.size()) == 0;
}
-
return false;
}
diff --git a/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h b/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h
index 5e5ae8d..61645f8 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h
@@ -52,6 +52,9 @@
HardwareAuthToken hidlVec2AuthToken(const hidl_vec<uint8_t>& buffer);
hidl_vec<uint8_t> authToken2HidlVec(const HardwareAuthToken& token);
+uint32_t getOsVersion();
+uint32_t getOsPatchlevel();
+
} // namespace support
} // namespace V4_0
} // namespace keymaster
diff --git a/keymaster/4.0/support/include/keymasterV4_0/openssl_utils.h b/keymaster/4.0/support/include/keymasterV4_0/openssl_utils.h
index cc71dd1..b3869f4 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/openssl_utils.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/openssl_utils.h
@@ -18,6 +18,8 @@
#define HARDWARE_INTERFACES_KEYMASTER_4_0_SUPPORT_OPENSSL_UTILS_H_
#include <android/hardware/keymaster/4.0/types.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
template <typename T, void (*F)(T*)>
struct UniquePtrDeleter {
diff --git a/keymaster/4.0/support/keymaster_utils.cpp b/keymaster/4.0/support/keymaster_utils.cpp
index e35fdd3..850a776 100644
--- a/keymaster/4.0/support/keymaster_utils.cpp
+++ b/keymaster/4.0/support/keymaster_utils.cpp
@@ -14,11 +14,13 @@
* limitations under the License.
*/
+#include <regex.h>
+
+#include <android-base/properties.h>
#include <hardware/hw_auth_token.h>
#include <keymasterV4_0/keymaster_utils.h>
-namespace android {
-namespace hardware {
+namespace android::hardware {
inline static bool operator<(const hidl_vec<uint8_t>& a, const hidl_vec<uint8_t>& b) {
auto result = memcmp(a.data(), b.data(), std::min(a.size(), b.size()));
@@ -32,8 +34,7 @@
return memcmp(a.data(), b.data(), SIZE) == -1;
}
-namespace keymaster {
-namespace V4_0 {
+namespace keymaster::V4_0 {
bool operator<(const HmacSharingParameters& a, const HmacSharingParameters& b) {
return std::tie(a.seed, a.nonce) < std::tie(b.seed, b.nonce);
@@ -58,9 +59,9 @@
hidl_vec<uint8_t> authToken2HidlVec(const HardwareAuthToken& token) {
static_assert(1 /* version size */ + sizeof(token.challenge) + sizeof(token.userId) +
- sizeof(token.authenticatorId) + sizeof(token.authenticatorType) +
- sizeof(token.timestamp) + kHmacSize ==
- sizeof(hw_auth_token_t),
+ sizeof(token.authenticatorId) + sizeof(token.authenticatorType) +
+ sizeof(token.timestamp) + kHmacSize ==
+ sizeof(hw_auth_token_t),
"HardwareAuthToken content size does not match hw_auth_token_t size");
hidl_vec<uint8_t> result;
@@ -86,9 +87,9 @@
HardwareAuthToken hidlVec2AuthToken(const hidl_vec<uint8_t>& buffer) {
HardwareAuthToken token;
static_assert(1 /* version size */ + sizeof(token.challenge) + sizeof(token.userId) +
- sizeof(token.authenticatorId) + sizeof(token.authenticatorType) +
- sizeof(token.timestamp) + kHmacSize ==
- sizeof(hw_auth_token_t),
+ sizeof(token.authenticatorId) + sizeof(token.authenticatorType) +
+ sizeof(token.timestamp) + kHmacSize ==
+ sizeof(hw_auth_token_t),
"HardwareAuthToken content size does not match hw_auth_token_t size");
if (buffer.size() != sizeof(hw_auth_token_t)) return {};
@@ -100,7 +101,7 @@
pos = copy_bytes_from_iterator(&token.authenticatorId, pos);
pos = copy_bytes_from_iterator(&token.authenticatorType, pos);
token.authenticatorType = static_cast<HardwareAuthenticatorType>(
- ntohl(static_cast<uint32_t>(token.authenticatorType)));
+ ntohl(static_cast<uint32_t>(token.authenticatorType)));
pos = copy_bytes_from_iterator(&token.timestamp, pos);
token.timestamp = ntohq(token.timestamp);
token.mac.resize(kHmacSize);
@@ -109,8 +110,93 @@
return token;
}
+namespace {
+
+constexpr char kPlatformVersionProp[] = "ro.build.version.release";
+constexpr char kPlatformVersionRegex[] = "^([0-9]{1,2})(\\.([0-9]{1,2}))?(\\.([0-9]{1,2}))?";
+constexpr size_t kMajorVersionMatch = 1;
+constexpr size_t kMinorVersionMatch = 3;
+constexpr size_t kSubminorVersionMatch = 5;
+constexpr size_t kPlatformVersionMatchCount = kSubminorVersionMatch + 1;
+
+constexpr char kPlatformPatchlevelProp[] = "ro.build.version.security_patch";
+constexpr char kPlatformPatchlevelRegex[] = "^([0-9]{4})-([0-9]{2})-[0-9]{2}$";
+constexpr size_t kYearMatch = 1;
+constexpr size_t kMonthMatch = 2;
+constexpr size_t kPlatformPatchlevelMatchCount = kMonthMatch + 1;
+
+uint32_t match_to_uint32(const char* expression, const regmatch_t& match) {
+ if (match.rm_so == -1) return 0;
+
+ size_t len = match.rm_eo - match.rm_so;
+ std::string s(expression + match.rm_so, len);
+ return std::stoul(s);
+}
+
+std::string wait_and_get_property(const char* prop) {
+ std::string prop_value;
+ while (!android::base::WaitForPropertyCreation(prop))
+ ;
+ prop_value = android::base::GetProperty(prop, "" /* default */);
+ return prop_value;
+}
+
+} // anonymous namespace
+
+uint32_t getOsVersion(const char* version_str) {
+ regex_t regex;
+ if (regcomp(®ex, kPlatformVersionRegex, REG_EXTENDED)) {
+ return 0;
+ }
+
+ regmatch_t matches[kPlatformVersionMatchCount];
+ int not_match =
+ regexec(®ex, version_str, kPlatformVersionMatchCount, matches, 0 /* flags */);
+ regfree(®ex);
+ if (not_match) {
+ return 0;
+ }
+
+ uint32_t major = match_to_uint32(version_str, matches[kMajorVersionMatch]);
+ uint32_t minor = match_to_uint32(version_str, matches[kMinorVersionMatch]);
+ uint32_t subminor = match_to_uint32(version_str, matches[kSubminorVersionMatch]);
+
+ return (major * 100 + minor) * 100 + subminor;
+}
+
+uint32_t getOsVersion() {
+ std::string version = wait_and_get_property(kPlatformVersionProp);
+ return getOsVersion(version.c_str());
+}
+
+uint32_t getOsPatchlevel(const char* patchlevel_str) {
+ regex_t regex;
+ if (regcomp(®ex, kPlatformPatchlevelRegex, REG_EXTENDED) != 0) {
+ return 0;
+ }
+
+ regmatch_t matches[kPlatformPatchlevelMatchCount];
+ int not_match =
+ regexec(®ex, patchlevel_str, kPlatformPatchlevelMatchCount, matches, 0 /* flags */);
+ regfree(®ex);
+ if (not_match) {
+ return 0;
+ }
+
+ uint32_t year = match_to_uint32(patchlevel_str, matches[kYearMatch]);
+ uint32_t month = match_to_uint32(patchlevel_str, matches[kMonthMatch]);
+
+ if (month < 1 || month > 12) {
+ return 0;
+ }
+ return year * 100 + month;
+}
+
+uint32_t getOsPatchlevel() {
+ std::string patchlevel = wait_and_get_property(kPlatformPatchlevelProp);
+ return getOsPatchlevel(patchlevel.c_str());
+}
+
} // namespace support
-} // namespace V4_0
-} // namespace keymaster
-} // namespace hardware
-} // namespace android
+} // namespace keymaster::V4_0
+} // namespace android::hardware
diff --git a/keymaster/4.0/vts/functional/Android.bp b/keymaster/4.0/vts/functional/Android.bp
index 5649f20..db50080 100644
--- a/keymaster/4.0/vts/functional/Android.bp
+++ b/keymaster/4.0/vts/functional/Android.bp
@@ -19,7 +19,6 @@
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
"HmacKeySharingTest.cpp",
- "KeymasterHidlTest.cpp",
"VerificationTokenTest.cpp",
"keymaster_hidl_hal_test.cpp",
],
@@ -27,7 +26,25 @@
"android.hardware.keymaster@4.0",
"libcrypto_static",
"libkeymaster4support",
- "libsoftkeymasterdevice",
+ "libkeymaster4vtstest",
],
- test_suites: ["general-tests", "vts-core"],
+ test_suites: [
+ "general-tests",
+ "vts-core",
+ ],
+}
+
+cc_test_library {
+ name: "libkeymaster4vtstest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
+ "KeymasterHidlTest.cpp",
+ ],
+ export_include_dirs: [
+ ".",
+ ],
+ static_libs: [
+ "android.hardware.keymaster@4.0",
+ "libkeymaster4support",
+ ],
}
diff --git a/keymaster/4.0/vts/functional/HmacKeySharingTest.cpp b/keymaster/4.0/vts/functional/HmacKeySharingTest.cpp
index c228ef7..f57a668 100644
--- a/keymaster/4.0/vts/functional/HmacKeySharingTest.cpp
+++ b/keymaster/4.0/vts/functional/HmacKeySharingTest.cpp
@@ -28,6 +28,16 @@
*/
class HmacKeySharingTest : public KeymasterHidlTest {
protected:
+ const std::vector<sp<IKeymasterDevice>>& allKeymasters() {
+ if (all_keymasters_.empty()) {
+ auto names = android::hardware::getAllHalInstanceNames(IKeymasterDevice::descriptor);
+ for (const auto& name : names) {
+ all_keymasters_.push_back(IKeymasterDevice::getService(name));
+ }
+ }
+ return all_keymasters_;
+ }
+
struct GetParamsResult {
ErrorCode error;
HmacSharingParameters params;
@@ -99,8 +109,13 @@
EXPECT_EQ(expected, response.sharing_check) << "Sharing check values should match.";
}
}
+
+ private:
+ static std::vector<sp<IKeymasterDevice>> all_keymasters_;
};
+std::vector<sp<IKeymasterDevice>> HmacKeySharingTest::all_keymasters_;
+
TEST_P(HmacKeySharingTest, GetParameters) {
auto result1 = getHmacSharingParameters(keymaster());
EXPECT_EQ(ErrorCode::OK, result1.error);
@@ -115,26 +130,26 @@
}
TEST_P(HmacKeySharingTest, ComputeSharedHmac) {
- auto params = getHmacSharingParameters(all_keymasters());
- ASSERT_EQ(all_keymasters().size(), params.size())
- << "One or more keymasters failed to provide parameters.";
+ auto params = getHmacSharingParameters(allKeymasters());
+ ASSERT_EQ(allKeymasters().size(), params.size())
+ << "One or more keymasters failed to provide parameters.";
auto nonces = copyNonces(params);
- EXPECT_EQ(all_keymasters().size(), nonces.size());
+ EXPECT_EQ(allKeymasters().size(), nonces.size());
std::sort(nonces.begin(), nonces.end());
std::unique(nonces.begin(), nonces.end());
- EXPECT_EQ(all_keymasters().size(), nonces.size());
+ EXPECT_EQ(allKeymasters().size(), nonces.size());
- auto responses = computeSharedHmac(all_keymasters(), params);
+ auto responses = computeSharedHmac(allKeymasters(), params);
ASSERT_GT(responses.size(), 0U);
verifyResponses(responses[0].sharing_check, responses);
// Do it a second time. Should get the same answers.
- params = getHmacSharingParameters(all_keymasters());
- ASSERT_EQ(all_keymasters().size(), params.size())
- << "One or more keymasters failed to provide parameters.";
+ params = getHmacSharingParameters(allKeymasters());
+ ASSERT_EQ(allKeymasters().size(), params.size())
+ << "One or more keymasters failed to provide parameters.";
- responses = computeSharedHmac(all_keymasters(), params);
+ responses = computeSharedHmac(allKeymasters(), params);
ASSERT_GT(responses.size(), 0U);
ASSERT_EQ(32U, responses[0].sharing_check.size());
verifyResponses(responses[0].sharing_check, responses);
@@ -160,15 +175,16 @@
// sync with respect to the HMAC key. Granted that VTS tests aren't run on in-use production
// devices, this still has the potential to cause confusion. To mitigate that, we always
// (barring crashes :-/) re-run the unmodified agreement process on our way out.
- auto fixup_hmac = finally(
- [&]() { computeSharedHmac(all_keymasters(), getHmacSharingParameters(all_keymasters())); });
+ auto fixup_hmac = finally([&]() {
+ computeSharedHmac(allKeymasters(), getHmacSharingParameters(allKeymasters()));
+ });
- auto params = getHmacSharingParameters(all_keymasters());
- ASSERT_EQ(all_keymasters().size(), params.size())
- << "One or more keymasters failed to provide parameters.";
+ auto params = getHmacSharingParameters(allKeymasters());
+ ASSERT_EQ(allKeymasters().size(), params.size())
+ << "One or more keymasters failed to provide parameters.";
// All should be well in the normal case
- auto responses = computeSharedHmac(all_keymasters(), params);
+ auto responses = computeSharedHmac(allKeymasters(), params);
ASSERT_GT(responses.size(), 0U);
HidlBuf correct_response = responses[0].sharing_check;
@@ -181,7 +197,7 @@
uint8_t bit_to_tweak = rand() % 8;
params[param_to_tweak].nonce[byte_to_tweak] ^= (1 << bit_to_tweak);
- responses = computeSharedHmac(all_keymasters(), params);
+ responses = computeSharedHmac(allKeymasters(), params);
for (size_t i = 0; i < responses.size(); ++i) {
if (i == param_to_tweak) {
EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error)
@@ -199,15 +215,16 @@
// sync with respect to the HMAC key. Granted that VTS tests aren't run on in-use production
// devices, this still has the potential to cause confusion. To mitigate that, we always
// (barring crashes :-/) re-run the unmodified agreement process on our way out.
- auto fixup_hmac = finally(
- [&]() { computeSharedHmac(all_keymasters(), getHmacSharingParameters(all_keymasters())); });
+ auto fixup_hmac = finally([&]() {
+ computeSharedHmac(allKeymasters(), getHmacSharingParameters(allKeymasters()));
+ });
- auto params = getHmacSharingParameters(all_keymasters());
- ASSERT_EQ(all_keymasters().size(), params.size())
- << "One or more keymasters failed to provide parameters.";
+ auto params = getHmacSharingParameters(allKeymasters());
+ ASSERT_EQ(allKeymasters().size(), params.size())
+ << "One or more keymasters failed to provide parameters.";
// All should be well in the normal case
- auto responses = computeSharedHmac(all_keymasters(), params);
+ auto responses = computeSharedHmac(allKeymasters(), params);
ASSERT_GT(responses.size(), 0U);
HidlBuf correct_response = responses[0].sharing_check;
@@ -223,7 +240,7 @@
}
to_tweak[0]++;
- responses = computeSharedHmac(all_keymasters(), params);
+ responses = computeSharedHmac(allKeymasters(), params);
for (size_t i = 0; i < responses.size(); ++i) {
if (i == param_to_tweak) {
EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error)
@@ -236,10 +253,7 @@
}
}
-INSTANTIATE_TEST_SUITE_P(
- PerInstance, HmacKeySharingTest,
- testing::ValuesIn(android::hardware::getAllHalInstanceNames(IKeymasterDevice::descriptor)),
- android::hardware::PrintInstanceNameToString);
+INSTANTIATE_KEYMASTER_HIDL_TEST(HmacKeySharingTest);
} // namespace test
} // namespace V4_0
diff --git a/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp b/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp
index 7241984..d0ad433 100644
--- a/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp
+++ b/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp
@@ -23,6 +23,7 @@
#include <android/hidl/manager/1.0/IServiceManager.h>
#include <keymasterV4_0/key_param_output.h>
+#include <keymasterV4_0/keymaster_utils.h>
namespace android {
namespace hardware {
@@ -41,39 +42,26 @@
namespace test {
-void KeymasterHidlTest::InitializeKeymaster() {
- service_name_ = GetParam();
- keymaster_ = IKeymasterDevice::getService(service_name_);
- ASSERT_NE(keymaster_, nullptr);
+using namespace std::literals::chrono_literals;
+void KeymasterHidlTest::InitializeKeymaster(sp<IKeymasterDevice> keymaster) {
+ ASSERT_NE(keymaster, nullptr);
+ keymaster_ = keymaster;
ASSERT_TRUE(keymaster_
- ->getHardwareInfo([&](SecurityLevel securityLevel, const hidl_string& name,
- const hidl_string& author) {
- securityLevel_ = securityLevel;
- name_ = name;
- author_ = author;
- })
- .isOk());
+ ->getHardwareInfo([&](SecurityLevel securityLevel, const hidl_string& name,
+ const hidl_string& author) {
+ securityLevel_ = securityLevel;
+ name_ = name;
+ author_ = author;
+ })
+ .isOk());
+
+ os_version_ = support::getOsVersion();
+ os_patch_level_ = support::getOsPatchlevel();
}
void KeymasterHidlTest::SetUp() {
- InitializeKeymaster();
-
- os_version_ = ::keymaster::GetOsVersion();
- os_patch_level_ = ::keymaster::GetOsPatchlevel();
-
- auto service_manager = android::hidl::manager::V1_0::IServiceManager::getService();
- ASSERT_NE(nullptr, service_manager.get());
- all_keymasters_.push_back(keymaster_);
- service_manager->listByInterface(
- IKeymasterDevice::descriptor, [&](const hidl_vec<hidl_string>& names) {
- for (auto& name : names) {
- if (name == service_name_) continue;
- auto keymaster = IKeymasterDevice::getService(name);
- ASSERT_NE(keymaster, nullptr);
- all_keymasters_.push_back(keymaster);
- }
- });
+ InitializeKeymaster(IKeymasterDevice::getService(GetParam()));
}
ErrorCode KeymasterHidlTest::GenerateKey(const AuthorizationSet& key_desc, HidlBuf* key_blob,
@@ -138,7 +126,7 @@
string masking_key,
const AuthorizationSet& unwrapping_params) {
ErrorCode error;
- ImportKey(wrapping_key_desc, KeyFormat::PKCS8, wrapping_key);
+ EXPECT_EQ(ErrorCode::OK, ImportKey(wrapping_key_desc, KeyFormat::PKCS8, wrapping_key));
EXPECT_TRUE(keymaster_
->importWrappedKey(HidlBuf(wrapped_key), key_blob_, HidlBuf(masking_key),
unwrapping_params.hidl_data(), 0 /* passwordSid */,
@@ -207,7 +195,9 @@
HidlBuf empty_buf = {};
EXPECT_EQ(ErrorCode::OK,
GetCharacteristics(key_blob, client_id, app_data, key_characteristics));
- EXPECT_GT(key_characteristics->hardwareEnforced.size(), 0);
+ if (SecLevel() != SecurityLevel::SOFTWARE) {
+ EXPECT_GT(key_characteristics->hardwareEnforced.size(), 0);
+ }
EXPECT_GT(key_characteristics->softwareEnforced.size(), 0);
EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
@@ -647,23 +637,25 @@
switch (algorithm) {
case Algorithm::RSA:
switch (SecLevel()) {
+ case SecurityLevel::SOFTWARE:
case SecurityLevel::TRUSTED_ENVIRONMENT:
return {2048, 3072, 4096};
case SecurityLevel::STRONGBOX:
return {2048};
default:
- CHECK(false) << "Invalid security level " << uint32_t(SecLevel());
+ ADD_FAILURE() << "Invalid security level " << uint32_t(SecLevel());
break;
}
break;
case Algorithm::EC:
switch (SecLevel()) {
+ case SecurityLevel::SOFTWARE:
case SecurityLevel::TRUSTED_ENVIRONMENT:
return {224, 256, 384, 521};
case SecurityLevel::STRONGBOX:
return {256};
default:
- CHECK(false) << "Invalid security level " << uint32_t(SecLevel());
+ ADD_FAILURE() << "Invalid security level " << uint32_t(SecLevel());
break;
}
break;
@@ -678,25 +670,27 @@
return retval;
}
default:
- CHECK(false) << "Invalid Algorithm: " << algorithm;
+ ADD_FAILURE() << "Invalid Algorithm: " << algorithm;
return {};
}
- CHECK(false) << "Should be impossible to get here";
+ ADD_FAILURE() << "Should be impossible to get here";
return {};
}
+
std::vector<uint32_t> KeymasterHidlTest::InvalidKeySizes(Algorithm algorithm) {
- if (SecLevel() == SecurityLevel::TRUSTED_ENVIRONMENT) return {};
- CHECK(SecLevel() == SecurityLevel::STRONGBOX);
- switch (algorithm) {
- case Algorithm::RSA:
- return {3072, 4096};
- case Algorithm::EC:
- return {224, 384, 521};
- case Algorithm::AES:
- return {192};
- default:
- return {};
+ if (SecLevel() == SecurityLevel::STRONGBOX) {
+ switch (algorithm) {
+ case Algorithm::RSA:
+ return {3072, 4096};
+ case Algorithm::EC:
+ return {224, 384, 521};
+ case Algorithm::AES:
+ return {192};
+ default:
+ return {};
+ }
}
+ return {};
}
std::vector<EcCurve> KeymasterHidlTest::ValidCurves() {
@@ -715,6 +709,7 @@
std::vector<Digest> KeymasterHidlTest::ValidDigests(bool withNone, bool withMD5) {
switch (SecLevel()) {
+ case SecurityLevel::SOFTWARE:
case SecurityLevel::TRUSTED_ENVIRONMENT:
if (withNone) {
if (withMD5)
@@ -740,10 +735,10 @@
return {Digest::SHA_2_256};
break;
default:
- CHECK(false) << "Invalid security level " << uint32_t(SecLevel());
+ ADD_FAILURE() << "Invalid security level " << uint32_t(SecLevel());
break;
}
- CHECK(false) << "Should be impossible to get here";
+ ADD_FAILURE() << "Should be impossible to get here";
return {};
}
diff --git a/keymaster/4.0/vts/functional/KeymasterHidlTest.h b/keymaster/4.0/vts/functional/KeymasterHidlTest.h
index 4bd8b26..f495516 100644
--- a/keymaster/4.0/vts/functional/KeymasterHidlTest.h
+++ b/keymaster/4.0/vts/functional/KeymasterHidlTest.h
@@ -21,7 +21,6 @@
#include <gtest/gtest.h>
#include <hidl/GtestPrinter.h>
#include <hidl/ServiceManagement.h>
-#include <keymaster/keymaster_configuration.h>
#include <keymasterV4_0/authorization_set.h>
@@ -35,16 +34,18 @@
namespace test {
using ::android::sp;
-using ::std::string;
using hidl::base::V1_0::DebugInfo;
+using ::std::string;
class HidlBuf : public hidl_vec<uint8_t> {
- typedef hidl_vec<uint8_t> super;
+ using super = hidl_vec<uint8_t>;
- public:
+ public:
HidlBuf() {}
HidlBuf(const super& other) : super(other) {}
- HidlBuf(super&& other) : super(std::move(other)) {}
+ HidlBuf(super&& other) : super(std::move(other)) { other = {}; }
+ HidlBuf(const HidlBuf& other) : super(other) {}
+ HidlBuf(HidlBuf&& other) : super(std::move(other)) { other = HidlBuf(); }
explicit HidlBuf(const std::string& other) : HidlBuf() { *this = other; }
HidlBuf& operator=(const super& other) {
@@ -54,6 +55,18 @@
HidlBuf& operator=(super&& other) {
super::operator=(std::move(other));
+ other = {};
+ return *this;
+ }
+
+ HidlBuf& operator=(const HidlBuf& other) {
+ super::operator=(other);
+ return *this;
+ }
+
+ HidlBuf& operator=(HidlBuf&& other) {
+ super::operator=(std::move(other));
+ other.super::operator=({});
return *this;
}
@@ -70,20 +83,16 @@
class KeymasterHidlTest : public ::testing::TestWithParam<std::string> {
public:
- void SetUp();
+ void SetUp() override;
void TearDown() override {
if (key_blob_.size()) {
CheckedDeleteKey();
}
AbortIfNeeded();
- keymaster_.clear();
- all_keymasters_.clear();
}
- void InitializeKeymaster();
-
+ void InitializeKeymaster(sp<IKeymasterDevice> keymaster);
IKeymasterDevice& keymaster() { return *keymaster_; }
- const std::vector<sp<IKeymasterDevice>>& all_keymasters() { return all_keymasters_; }
uint32_t os_version() { return os_version_; }
uint32_t os_patch_level() { return os_patch_level_; }
@@ -209,18 +218,26 @@
KeyCharacteristics key_characteristics_;
OperationHandle op_handle_ = kOpHandleSentinel;
- private:
- sp<IKeymasterDevice> keymaster_;
- std::vector<sp<IKeymasterDevice>> all_keymasters_;
- uint32_t os_version_;
- uint32_t os_patch_level_;
+ static std::vector<std::string> build_params() {
+ auto params = android::hardware::getAllHalInstanceNames(IKeymasterDevice::descriptor);
+ return params;
+ }
- SecurityLevel securityLevel_;
- hidl_string name_;
- hidl_string author_;
- string service_name_;
+ private:
+ sp<IKeymasterDevice> keymaster_;
+ uint32_t os_version_;
+ uint32_t os_patch_level_;
+
+ SecurityLevel securityLevel_;
+ hidl_string name_;
+ hidl_string author_;
};
+#define INSTANTIATE_KEYMASTER_HIDL_TEST(name) \
+ INSTANTIATE_TEST_SUITE_P(PerInstance, name, \
+ testing::ValuesIn(KeymasterHidlTest::build_params()), \
+ android::hardware::PrintInstanceNameToString)
+
} // namespace test
} // namespace V4_0
} // namespace keymaster
diff --git a/keymaster/4.0/vts/functional/VerificationTokenTest.cpp b/keymaster/4.0/vts/functional/VerificationTokenTest.cpp
index 693f4ae..bab1439 100644
--- a/keymaster/4.0/vts/functional/VerificationTokenTest.cpp
+++ b/keymaster/4.0/vts/functional/VerificationTokenTest.cpp
@@ -185,10 +185,7 @@
memcmp(result1.token.mac.data(), result2.token.mac.data(), result1.token.mac.size()));
}
-INSTANTIATE_TEST_SUITE_P(
- PerInstance, VerificationTokenTest,
- testing::ValuesIn(android::hardware::getAllHalInstanceNames(IKeymasterDevice::descriptor)),
- android::hardware::PrintInstanceNameToString);
+INSTANTIATE_KEYMASTER_HIDL_TEST(VerificationTokenTest);
} // namespace test
} // namespace V4_0
diff --git a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
index 66132ad..6cbe4da 100644
--- a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
+++ b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
@@ -352,11 +352,11 @@
EXPECT_EQ(ErrorCode::OK, error);
if (error != ErrorCode::OK) return false;
- EXPECT_TRUE(att_attestation_version == 3);
+ EXPECT_GE(att_attestation_version, 3U);
expected_sw_enforced.push_back(TAG_ATTESTATION_APPLICATION_ID, HidlBuf(app_id));
- EXPECT_EQ(att_keymaster_version, 4U);
+ EXPECT_GE(att_keymaster_version, 4U);
EXPECT_EQ(security_level, att_keymaster_security_level);
EXPECT_EQ(security_level, att_attestation_security_level);
@@ -397,10 +397,16 @@
// 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.
- EXPECT_TRUE(expected_hw_enforced.Contains(TAG_NO_AUTH_REQUIRED));
+ 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));
@@ -461,10 +467,10 @@
verified_boot_key.size()));
} else if (!strcmp(property_value, "red")) {
EXPECT_EQ(verified_boot_state, KM_VERIFIED_BOOT_FAILED);
- EXPECT_EQ(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
- verified_boot_key.size()));
} else {
- EXPECT_TRUE(false);
+ EXPECT_EQ(verified_boot_state, KM_VERIFIED_BOOT_UNVERIFIED);
+ EXPECT_NE(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
+ verified_boot_key.size()));
}
att_sw_enforced.Sort();
@@ -840,6 +846,8 @@
.Authorization(TAG_MIN_MAC_LENGTH, 128)));
}
+INSTANTIATE_KEYMASTER_HIDL_TEST(NewKeyGenerationTest);
+
typedef KeymasterHidlTest SigningOperationsTest;
/*
@@ -1509,6 +1517,8 @@
}
}
+INSTANTIATE_KEYMASTER_HIDL_TEST(SigningOperationsTest);
+
typedef KeymasterHidlTest VerificationOperationsTest;
/*
@@ -1749,6 +1759,8 @@
CheckedDeleteKey(&verification_key);
}
+INSTANTIATE_KEYMASTER_HIDL_TEST(VerificationOperationsTest);
+
typedef KeymasterHidlTest ExportKeyTest;
/*
@@ -1828,6 +1840,8 @@
EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_FORMAT, ExportKey(KeyFormat::RAW, &export_data));
}
+INSTANTIATE_KEYMASTER_HIDL_TEST(ExportKeyTest);
+
class ImportKeyTest : public KeymasterHidlTest {
public:
template <TagType tag_type, Tag tag, typename ValueT>
@@ -2093,6 +2107,8 @@
VerifyMessage(message, signature, AuthorizationSetBuilder().Digest(Digest::SHA_2_256));
}
+INSTANTIATE_KEYMASTER_HIDL_TEST(ImportKeyTest);
+
auto wrapped_key = hex2str(
"3082017902010004820100934bf94e2aa28a3f83c9f79297250262fbe3276b5a1c91159bbfa3ef8957aac84b59b30b"
"455a79c2973480823d8b3863c3deef4a8e243590268d80e18751a0e130f67ce6a1ace9f79b95e097474febc981195b"
@@ -2214,6 +2230,8 @@
.Padding(PaddingMode::RSA_OAEP)));
}
+INSTANTIATE_KEYMASTER_HIDL_TEST(ImportWrappedKeyTest);
+
typedef KeymasterHidlTest EncryptionOperationsTest;
/*
@@ -4111,6 +4129,8 @@
EXPECT_EQ(message, plaintext);
}
+INSTANTIATE_KEYMASTER_HIDL_TEST(EncryptionOperationsTest);
+
typedef KeymasterHidlTest MaxOperationsTest;
/*
@@ -4166,6 +4186,8 @@
EXPECT_EQ(ErrorCode::KEY_MAX_OPS_EXCEEDED, Begin(KeyPurpose::SIGN, params));
}
+INSTANTIATE_KEYMASTER_HIDL_TEST(MaxOperationsTest);
+
typedef KeymasterHidlTest AddEntropyTest;
/*
@@ -4196,6 +4218,8 @@
EXPECT_EQ(ErrorCode::OK, keymaster().addRngEntropy(HidlBuf(string(2 * 1024, 'a'))));
}
+INSTANTIATE_KEYMASTER_HIDL_TEST(AddEntropyTest);
+
typedef KeymasterHidlTest AttestationTest;
/*
@@ -4373,6 +4397,8 @@
&cert_chain));
}
+INSTANTIATE_KEYMASTER_HIDL_TEST(AttestationTest);
+
typedef KeymasterHidlTest KeyDeletionTest;
/**
@@ -4478,6 +4504,8 @@
}
}
+INSTANTIATE_KEYMASTER_HIDL_TEST(KeyDeletionTest);
+
using UpgradeKeyTest = KeymasterHidlTest;
/*
@@ -4497,6 +4525,8 @@
EXPECT_EQ(result, std::make_pair(ErrorCode::OK, HidlBuf()));
}
+INSTANTIATE_KEYMASTER_HIDL_TEST(UpgradeKeyTest);
+
using ClearOperationsTest = KeymasterHidlTest;
/*
@@ -4533,51 +4563,14 @@
AbortIfNeeded();
}
-/*
- * ClearSlotsTest.ServiceDeath
- *
- * Verifies that the service is restarted after death and the ongoing
- * operations are cleared.
- */
-TEST_P(ClearOperationsTest, ServiceDeath) {
- ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
- .Authorization(TAG_NO_AUTH_REQUIRED)
- .RsaEncryptionKey(2048, 65537)
- .Padding(PaddingMode::NONE)));
-
- auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
- int max_operations = SecLevel() == SecurityLevel::STRONGBOX ? 4 : 16;
- OperationHandle op_handles[max_operations];
- AuthorizationSet out_params;
- for(int i=0; i<max_operations; i++) {
- EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params, &(op_handles[i])));
- }
- EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS,
- Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params, &op_handle_));
-
- DebugInfo debug_info;
- GetDebugInfo(&debug_info);
- kill(debug_info.pid, SIGKILL);
- // wait 1 second for keymaster to restart
- sleep(1);
- InitializeKeymaster();
-
- for(int i=0; i<max_operations; i++) {
- EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params, &(op_handles[i])));
- }
- EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS,
- Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params, &op_handle_));
- for(int i=0; i<max_operations; i++) {
- EXPECT_EQ(ErrorCode::OK, Abort(op_handles[i]));
- }
-}
+INSTANTIATE_KEYMASTER_HIDL_TEST(ClearOperationsTest);
typedef KeymasterHidlTest TransportLimitTest;
/*
- * TransportLimitTest.LargeFinishInput
+ * TransportLimitTest.FinishInput
*
- * Verifies that passing large input data to finish either succeeds or fails as expected.
+ * Verifies that passing input data to finish succeeds as expected.
*/
TEST_P(TransportLimitTest, LargeFinishInput) {
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
@@ -4586,7 +4579,7 @@
.BlockMode(BlockMode::ECB)
.Padding(PaddingMode::NONE)));
- for (int msg_size = 10 /*1KB*/; msg_size <= 17 /*128KB*/; msg_size++) {
+ for (int msg_size = 8 /* 256 bytes */; msg_size <= 11 /* 2 KiB */; msg_size++) {
auto cipher_params =
AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
@@ -4597,71 +4590,22 @@
string encrypted_message;
auto rc = Finish(plain_message, &encrypted_message);
- if (rc == ErrorCode::OK) {
- EXPECT_EQ(plain_message.size(), encrypted_message.size())
- << "Encrypt finish returned OK, but did not consume all of the given input";
- } else {
- EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, rc)
- << "Encrypt finish failed in an unexpected way when given a large input";
- continue;
- }
+ EXPECT_EQ(ErrorCode::OK, rc);
+ EXPECT_EQ(plain_message.size(), encrypted_message.size())
+ << "Encrypt finish returned OK, but did not consume all of the given input";
cipher_params.push_back(out_params);
EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, cipher_params));
string decrypted_message;
rc = Finish(encrypted_message, &decrypted_message);
-
- if (rc == ErrorCode::OK) {
- EXPECT_EQ(plain_message.size(), decrypted_message.size())
- << "Decrypt finish returned OK, did not consume all of the given input";
- } else {
- EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, rc)
- << "Encrypt finish failed in an unexpected way when given a large input";
- }
+ EXPECT_EQ(ErrorCode::OK, rc);
+ EXPECT_EQ(plain_message.size(), decrypted_message.size())
+ << "Decrypt finish returned OK, did not consume all of the given input";
}
-
- CheckedDeleteKey();
}
-static const auto kKeymasterDeviceChoices =
- testing::ValuesIn(android::hardware::getAllHalInstanceNames(IKeymasterDevice::descriptor));
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, NewKeyGenerationTest, kKeymasterDeviceChoices,
- android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, ImportKeyTest, kKeymasterDeviceChoices,
- android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, ImportWrappedKeyTest, kKeymasterDeviceChoices,
- android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, SigningOperationsTest, kKeymasterDeviceChoices,
- android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, VerificationOperationsTest, kKeymasterDeviceChoices,
- android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, ExportKeyTest, kKeymasterDeviceChoices,
- android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, EncryptionOperationsTest, kKeymasterDeviceChoices,
- android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, MaxOperationsTest, kKeymasterDeviceChoices,
- android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, AddEntropyTest, kKeymasterDeviceChoices,
- android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, AttestationTest, kKeymasterDeviceChoices,
- android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, KeyDeletionTest, kKeymasterDeviceChoices,
- android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, TransportLimitTest, kKeymasterDeviceChoices,
- android::hardware::PrintInstanceNameToString);
+INSTANTIATE_KEYMASTER_HIDL_TEST(TransportLimitTest);
} // namespace test
} // namespace V4_0
diff --git a/keymaster/4.1/default/Android.bp b/keymaster/4.1/default/Android.bp
index b06878b..27297b9 100644
--- a/keymaster/4.1/default/Android.bp
+++ b/keymaster/4.1/default/Android.bp
@@ -31,6 +31,7 @@
"libhidlbase",
"libkeymaster4",
"libkeymaster41",
+ "libkeymaster4_1support",
"liblog",
"libutils",
],
diff --git a/keymaster/4.1/support/Android.bp b/keymaster/4.1/support/Android.bp
index 34b6108..bdd0ca8 100644
--- a/keymaster/4.1/support/Android.bp
+++ b/keymaster/4.1/support/Android.bp
@@ -22,11 +22,25 @@
"-Wextra",
"-Werror",
],
+ srcs: [
+ "attestation_record.cpp",
+ "Keymaster.cpp",
+ "Keymaster3.cpp",
+ "Keymaster4.cpp",
+ ],
export_include_dirs: ["include"],
shared_libs: [
"android.hardware.keymaster@3.0",
"android.hardware.keymaster@4.0",
"android.hardware.keymaster@4.1",
+ "libbase",
+ "libcrypto",
+ "libhidlbase",
"libkeymaster4support",
- ]
+ "libutils",
+ ],
+ export_shared_lib_headers: [
+ "android.hardware.keymaster@4.1",
+ "libkeymaster4support",
+ ],
}
diff --git a/keymaster/4.0/support/Keymaster.cpp b/keymaster/4.1/support/Keymaster.cpp
similarity index 85%
rename from keymaster/4.0/support/Keymaster.cpp
rename to keymaster/4.1/support/Keymaster.cpp
index f20f951..ea6604e 100644
--- a/keymaster/4.0/support/Keymaster.cpp
+++ b/keymaster/4.1/support/Keymaster.cpp
@@ -14,19 +14,18 @@
** limitations under the License.
*/
-#include <keymasterV4_0/Keymaster.h>
+#include <keymasterV4_1/Keymaster.h>
#include <iomanip>
#include <android-base/logging.h>
#include <android/hidl/manager/1.2/IServiceManager.h>
-#include <keymasterV4_0/Keymaster3.h>
-#include <keymasterV4_0/Keymaster4.h>
#include <keymasterV4_0/key_param_output.h>
#include <keymasterV4_0/keymaster_utils.h>
+#include <keymasterV4_1/Keymaster3.h>
+#include <keymasterV4_1/Keymaster4.h>
-namespace android {
-namespace hardware {
+namespace android::hardware {
template <class T>
std::ostream& operator<<(std::ostream& os, const hidl_vec<T>& vec) {
@@ -57,6 +56,7 @@
}
namespace keymaster {
+
namespace V4_0 {
std::ostream& operator<<(std::ostream& os, const HmacSharingParameters& params) {
@@ -66,7 +66,9 @@
return os;
}
-namespace support {
+} // namespace V4_0
+
+namespace V4_1::support {
using ::android::sp;
using ::android::hidl::manager::V1_2::IServiceManager;
@@ -140,14 +142,14 @@
}
static hidl_vec<HmacSharingParameters> getHmacParameters(
- const Keymaster::KeymasterSet& keymasters) {
+ const Keymaster::KeymasterSet& keymasters) {
std::vector<HmacSharingParameters> params_vec;
params_vec.reserve(keymasters.size());
for (auto& keymaster : keymasters) {
if (keymaster->halVersion().majorVersion < 4) continue;
auto rc = keymaster->getHmacSharingParameters([&](auto error, auto& params) {
- CHECK(error == ErrorCode::OK)
- << "Failed to get HMAC parameters from " << *keymaster << " error " << error;
+ CHECK(error == V4_0::ErrorCode::OK)
+ << "Failed to get HMAC parameters from " << *keymaster << " error " << error;
params_vec.push_back(params);
});
CHECK(rc.isOk()) << "Failed to communicate with " << *keymaster
@@ -169,18 +171,18 @@
if (keymaster->halVersion().majorVersion < 4) continue;
LOG(DEBUG) << "Computing HMAC for " << *keymaster;
auto rc = keymaster->computeSharedHmac(
- params, [&](ErrorCode error, const hidl_vec<uint8_t>& curSharingCheck) {
- CHECK(error == ErrorCode::OK)
- << "Failed to get HMAC parameters from " << *keymaster << " error " << error;
- if (firstKeymaster) {
- sharingCheck = curSharingCheck;
- firstKeymaster = false;
- }
- if (curSharingCheck != sharingCheck)
- LOG(WARNING) << "HMAC computation failed for " << *keymaster //
- << " Expected: " << sharingCheck //
- << " got: " << curSharingCheck;
- });
+ params, [&](V4_0::ErrorCode error, const hidl_vec<uint8_t>& curSharingCheck) {
+ CHECK(error == V4_0::ErrorCode::OK) << "Failed to get HMAC parameters from "
+ << *keymaster << " error " << error;
+ if (firstKeymaster) {
+ sharingCheck = curSharingCheck;
+ firstKeymaster = false;
+ }
+ if (curSharingCheck != sharingCheck)
+ LOG(WARNING) << "HMAC computation failed for " << *keymaster //
+ << " Expected: " << sharingCheck //
+ << " got: " << curSharingCheck;
+ });
CHECK(rc.isOk()) << "Failed to communicate with " << *keymaster
<< " error: " << rc.description();
}
@@ -190,8 +192,6 @@
computeHmac(keymasters, getHmacParameters(keymasters));
}
-} // namespace support
-} // namespace V4_0
+} // namespace V4_1::support
} // namespace keymaster
-} // namespace hardware
-} // namespace android
+} // namespace android::hardware
diff --git a/keymaster/4.0/support/Keymaster3.cpp b/keymaster/4.1/support/Keymaster3.cpp
similarity index 81%
rename from keymaster/4.0/support/Keymaster3.cpp
rename to keymaster/4.1/support/Keymaster3.cpp
index b2cdbd9..b665689 100644
--- a/keymaster/4.0/support/Keymaster3.cpp
+++ b/keymaster/4.1/support/Keymaster3.cpp
@@ -15,23 +15,19 @@
** limitations under the License.
*/
-#include <keymasterV4_0/Keymaster3.h>
+#include <keymasterV4_1/Keymaster3.h>
#include <android-base/logging.h>
#include <keymasterV4_0/keymaster_utils.h>
-namespace android {
-namespace hardware {
-namespace keymaster {
-namespace V4_0 {
-namespace support {
+namespace android::hardware::keymaster::V4_1::support {
using android::hardware::details::StatusOf;
namespace {
-ErrorCode convert(V3_0::ErrorCode error) {
- return static_cast<ErrorCode>(error);
+V4_0::ErrorCode convert(V3_0::ErrorCode error) {
+ return static_cast<V4_0::ErrorCode>(error);
}
V3_0::KeyPurpose convert(KeyPurpose purpose) {
@@ -53,7 +49,7 @@
KeyParameter convert(const V3_0::KeyParameter& param) {
KeyParameter converted;
- converted.tag = static_cast<Tag>(param.tag);
+ converted.tag = static_cast<V4_0::Tag>(param.tag);
static_assert(sizeof(converted.f) == sizeof(param.f), "This function assumes sizes match");
memcpy(&converted.f, ¶m.f, sizeof(param.f));
converted.blob = param.blob;
@@ -89,7 +85,7 @@
converted[i] = convert(params[i]);
}
converted[params.size()].tag = V3_0::Tag::AUTH_TOKEN;
- converted[params.size()].blob = authToken2HidlVec(authToken);
+ converted[params.size()].blob = V4_0::support::authToken2HidlVec(authToken);
return converted;
}
@@ -107,16 +103,19 @@
if (haveVersion_) return;
auto rc = km3_dev_->getHardwareFeatures(
- [&](bool isSecure, bool supportsEllipticCurve, bool supportsSymmetricCryptography,
- bool supportsAttestation, bool supportsAllDigests, const hidl_string& keymasterName,
- const hidl_string& keymasterAuthorName) {
- version_ = {keymasterName, keymasterAuthorName, 0 /* major version, filled below */,
- isSecure ? SecurityLevel::TRUSTED_ENVIRONMENT : SecurityLevel::SOFTWARE,
- supportsEllipticCurve};
- supportsSymmetricCryptography_ = supportsSymmetricCryptography;
- supportsAttestation_ = supportsAttestation;
- supportsAllDigests_ = supportsAllDigests;
- });
+ [&](bool isSecure, bool supportsEllipticCurve, bool supportsSymmetricCryptography,
+ bool supportsAttestation, bool supportsAllDigests, const hidl_string& keymasterName,
+ const hidl_string& keymasterAuthorName) {
+ version_ = {keymasterName,
+ keymasterAuthorName,
+ 0 /* major version, filled below */,
+ 0 /* minor version */,
+ isSecure ? SecurityLevel::TRUSTED_ENVIRONMENT : SecurityLevel::SOFTWARE,
+ supportsEllipticCurve};
+ supportsSymmetricCryptography_ = supportsSymmetricCryptography;
+ supportsAttestation_ = supportsAttestation;
+ supportsAllDigests_ = supportsAllDigests;
+ });
CHECK(rc.isOk()) << "Got error " << rc.description() << " trying to get hardware features";
@@ -139,10 +138,10 @@
return Void();
}
-Return<ErrorCode> Keymaster3::addRngEntropy(const hidl_vec<uint8_t>& data) {
+Return<V4_0::ErrorCode> Keymaster3::addRngEntropy(const hidl_vec<uint8_t>& data) {
auto rc = km3_dev_->addRngEntropy(data);
if (!rc.isOk()) {
- return StatusOf<V3_0::ErrorCode, ErrorCode>(rc);
+ return StatusOf<V3_0::ErrorCode, V4_0::ErrorCode>(rc);
}
return convert(rc);
}
@@ -215,21 +214,21 @@
return rc;
}
-Return<ErrorCode> Keymaster3::deleteKey(const hidl_vec<uint8_t>& keyBlob) {
+Return<V4_0::ErrorCode> Keymaster3::deleteKey(const hidl_vec<uint8_t>& keyBlob) {
auto rc = km3_dev_->deleteKey(keyBlob);
- if (!rc.isOk()) return StatusOf<V3_0::ErrorCode, ErrorCode>(rc);
+ if (!rc.isOk()) return StatusOf<V3_0::ErrorCode, V4_0::ErrorCode>(rc);
return convert(rc);
}
-Return<ErrorCode> Keymaster3::deleteAllKeys() {
+Return<V4_0::ErrorCode> Keymaster3::deleteAllKeys() {
auto rc = km3_dev_->deleteAllKeys();
- if (!rc.isOk()) return StatusOf<V3_0::ErrorCode, ErrorCode>(rc);
+ if (!rc.isOk()) return StatusOf<V3_0::ErrorCode, V4_0::ErrorCode>(rc);
return convert(rc);
}
-Return<ErrorCode> Keymaster3::destroyAttestationIds() {
+Return<V4_0::ErrorCode> Keymaster3::destroyAttestationIds() {
auto rc = km3_dev_->destroyAttestationIds();
- if (!rc.isOk()) return StatusOf<V3_0::ErrorCode, ErrorCode>(rc);
+ if (!rc.isOk()) return StatusOf<V3_0::ErrorCode, V4_0::ErrorCode>(rc);
return convert(rc);
}
@@ -242,7 +241,7 @@
};
auto rc =
- km3_dev_->begin(convert(purpose), key, convertAndAddAuthToken(inParams, authToken), cb);
+ km3_dev_->begin(convert(purpose), key, convertAndAddAuthToken(inParams, authToken), cb);
rc.isOk(); // move ctor prereq
return rc;
}
@@ -256,8 +255,8 @@
_hidl_cb(convert(error), inputConsumed, convert(outParams), output);
};
- auto rc =
- km3_dev_->update(operationHandle, convertAndAddAuthToken(inParams, authToken), input, cb);
+ auto rc = km3_dev_->update(operationHandle, convertAndAddAuthToken(inParams, authToken), input,
+ cb);
rc.isOk(); // move ctor prereq
return rc;
}
@@ -278,14 +277,10 @@
return rc;
}
-Return<ErrorCode> Keymaster3::abort(uint64_t operationHandle) {
+Return<V4_0::ErrorCode> Keymaster3::abort(uint64_t operationHandle) {
auto rc = km3_dev_->abort(operationHandle);
- if (!rc.isOk()) return StatusOf<V3_0::ErrorCode, ErrorCode>(rc);
+ if (!rc.isOk()) return StatusOf<V3_0::ErrorCode, V4_0::ErrorCode>(rc);
return convert(rc);
}
-} // namespace support
-} // namespace V4_0
-} // namespace keymaster
-} // namespace hardware
-} // namespace android
+} // namespace android::hardware::keymaster::V4_1::support
diff --git a/keymaster/4.1/support/Keymaster4.cpp b/keymaster/4.1/support/Keymaster4.cpp
new file mode 100644
index 0000000..33f4bb1
--- /dev/null
+++ b/keymaster/4.1/support/Keymaster4.cpp
@@ -0,0 +1,41 @@
+/*
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <keymasterV4_1/Keymaster4.h>
+
+#include <android-base/logging.h>
+
+namespace android::hardware::keymaster::V4_1::support {
+
+void Keymaster4::getVersionIfNeeded() {
+ if (haveVersion_) return;
+
+ auto rc = km4_0_dev_->getHardwareInfo([&](SecurityLevel securityLevel,
+ const hidl_string& keymasterName,
+ const hidl_string& authorName) {
+ version_ = {keymasterName,
+ authorName,
+ 4 /* major version */,
+ static_cast<uint8_t>((km4_1_dev_) ? 1 : 0) /* minor version */,
+ securityLevel,
+ true /* supportsEc */};
+ haveVersion_ = true;
+ });
+
+ CHECK(rc.isOk()) << "Got error " << rc.description() << " trying to get hardware info";
+}
+
+} // namespace android::hardware::keymaster::V4_1::support
diff --git a/keymaster/4.1/support/OWNERS b/keymaster/4.1/support/OWNERS
new file mode 100644
index 0000000..a9efe66
--- /dev/null
+++ b/keymaster/4.1/support/OWNERS
@@ -0,0 +1,3 @@
+jdanis@google.com
+swillden@google.com
+jbires@google.com
diff --git a/keymaster/4.1/support/attestation_record.cpp b/keymaster/4.1/support/attestation_record.cpp
new file mode 100644
index 0000000..9eab1db
--- /dev/null
+++ b/keymaster/4.1/support/attestation_record.cpp
@@ -0,0 +1,387 @@
+/*
+ * 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.
+ */
+
+#include <keymasterV4_1/attestation_record.h>
+
+#include <android-base/logging.h>
+#include <assert.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/bn.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+#include <keymasterV4_0/authorization_set.h>
+#include <keymasterV4_0/openssl_utils.h>
+
+#define AT __FILE__ ":" << __LINE__
+
+/*
+ * NOTE: The contents of this file are *extremely* similar to the contents of the V4_0 copy of the
+ * same support file. Unfortunately, small changes in the scheme mean that the schema types have to
+ * be distinct, which drives almost everything else to be different as well. In the next version we
+ * plan to abandon not just this openssl mechanism for parsing ASN.1, but ASN.1 entirely, so
+ * eventually all of this duplication can be removed.
+ */
+
+namespace android {
+namespace hardware {
+namespace keymaster {
+namespace V4_1 {
+
+struct stack_st_ASN1_TYPE_Delete {
+ void operator()(stack_st_ASN1_TYPE* p) { sk_ASN1_TYPE_free(p); }
+};
+
+struct ASN1_STRING_Delete {
+ void operator()(ASN1_STRING* p) { ASN1_STRING_free(p); }
+};
+
+struct ASN1_TYPE_Delete {
+ void operator()(ASN1_TYPE* p) { ASN1_TYPE_free(p); }
+};
+
+#define ASN1_INTEGER_SET STACK_OF(ASN1_INTEGER)
+
+typedef struct km_root_of_trust {
+ ASN1_OCTET_STRING* verified_boot_key;
+ ASN1_BOOLEAN* device_locked;
+ ASN1_ENUMERATED* verified_boot_state;
+ ASN1_OCTET_STRING* verified_boot_hash;
+} KM_ROOT_OF_TRUST;
+
+ASN1_SEQUENCE(KM_ROOT_OF_TRUST) = {
+ ASN1_SIMPLE(KM_ROOT_OF_TRUST, verified_boot_key, ASN1_OCTET_STRING),
+ ASN1_SIMPLE(KM_ROOT_OF_TRUST, device_locked, ASN1_BOOLEAN),
+ ASN1_SIMPLE(KM_ROOT_OF_TRUST, verified_boot_state, ASN1_ENUMERATED),
+ ASN1_SIMPLE(KM_ROOT_OF_TRUST, verified_boot_hash, ASN1_OCTET_STRING),
+} ASN1_SEQUENCE_END(KM_ROOT_OF_TRUST);
+IMPLEMENT_ASN1_FUNCTIONS(KM_ROOT_OF_TRUST);
+
+typedef struct km_auth_list {
+ ASN1_INTEGER_SET* purpose;
+ ASN1_INTEGER* algorithm;
+ ASN1_INTEGER* key_size;
+ ASN1_INTEGER_SET* digest;
+ ASN1_INTEGER_SET* padding;
+ ASN1_INTEGER* ec_curve;
+ ASN1_INTEGER* rsa_public_exponent;
+ ASN1_INTEGER* active_date_time;
+ ASN1_INTEGER* origination_expire_date_time;
+ ASN1_INTEGER* usage_expire_date_time;
+ ASN1_NULL* no_auth_required;
+ ASN1_INTEGER* user_auth_type;
+ ASN1_INTEGER* auth_timeout;
+ ASN1_NULL* allow_while_on_body;
+ ASN1_NULL* all_applications;
+ ASN1_OCTET_STRING* application_id;
+ ASN1_INTEGER* creation_date_time;
+ ASN1_INTEGER* origin;
+ ASN1_NULL* rollback_resistance;
+ KM_ROOT_OF_TRUST* root_of_trust;
+ ASN1_INTEGER* os_version;
+ ASN1_INTEGER* os_patchlevel;
+ ASN1_OCTET_STRING* attestation_application_id;
+ ASN1_NULL* trusted_user_presence_required;
+ ASN1_NULL* trusted_confirmation_required;
+ ASN1_NULL* unlocked_device_required;
+ ASN1_INTEGER* vendor_patchlevel;
+ ASN1_INTEGER* boot_patchlevel;
+ ASN1_NULL* early_boot_only;
+ ASN1_NULL* device_unique_attestation;
+} KM_AUTH_LIST;
+
+ASN1_SEQUENCE(KM_AUTH_LIST) = {
+ ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, purpose, ASN1_INTEGER, TAG_PURPOSE.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, algorithm, ASN1_INTEGER, TAG_ALGORITHM.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, key_size, ASN1_INTEGER, TAG_KEY_SIZE.maskedTag()),
+ ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, digest, ASN1_INTEGER, TAG_DIGEST.maskedTag()),
+ ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, padding, ASN1_INTEGER, TAG_PADDING.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, ec_curve, ASN1_INTEGER, TAG_EC_CURVE.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, rsa_public_exponent, ASN1_INTEGER,
+ TAG_RSA_PUBLIC_EXPONENT.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, rollback_resistance, ASN1_NULL,
+ TAG_ROLLBACK_RESISTANCE.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, active_date_time, ASN1_INTEGER, TAG_ACTIVE_DATETIME.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, origination_expire_date_time, ASN1_INTEGER,
+ TAG_ORIGINATION_EXPIRE_DATETIME.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, usage_expire_date_time, ASN1_INTEGER,
+ TAG_USAGE_EXPIRE_DATETIME.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, no_auth_required, ASN1_NULL, TAG_NO_AUTH_REQUIRED.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, user_auth_type, ASN1_INTEGER, TAG_USER_AUTH_TYPE.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, auth_timeout, ASN1_INTEGER, TAG_AUTH_TIMEOUT.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, allow_while_on_body, ASN1_NULL,
+ TAG_ALLOW_WHILE_ON_BODY.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, trusted_user_presence_required, ASN1_NULL,
+ TAG_TRUSTED_USER_PRESENCE_REQUIRED.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, trusted_confirmation_required, ASN1_NULL,
+ TAG_TRUSTED_CONFIRMATION_REQUIRED.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, unlocked_device_required, ASN1_NULL,
+ TAG_UNLOCKED_DEVICE_REQUIRED.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, creation_date_time, ASN1_INTEGER,
+ TAG_CREATION_DATETIME.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, origin, ASN1_INTEGER, TAG_ORIGIN.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, root_of_trust, KM_ROOT_OF_TRUST, TAG_ROOT_OF_TRUST.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, os_version, ASN1_INTEGER, TAG_OS_VERSION.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, os_patchlevel, ASN1_INTEGER, TAG_OS_PATCHLEVEL.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, vendor_patchlevel, ASN1_INTEGER,
+ TAG_VENDOR_PATCHLEVEL.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, boot_patchlevel, ASN1_INTEGER, TAG_BOOT_PATCHLEVEL.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, attestation_application_id, ASN1_OCTET_STRING,
+ TAG_ATTESTATION_APPLICATION_ID.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, early_boot_only, ASN1_NULL, TAG_EARLY_BOOT_ONLY.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, device_unique_attestation, ASN1_NULL,
+ TAG_DEVICE_UNIQUE_ATTESTATION.maskedTag()),
+} ASN1_SEQUENCE_END(KM_AUTH_LIST);
+IMPLEMENT_ASN1_FUNCTIONS(KM_AUTH_LIST);
+
+typedef struct km_key_description {
+ ASN1_INTEGER* attestation_version;
+ ASN1_ENUMERATED* attestation_security_level;
+ ASN1_INTEGER* keymaster_version;
+ ASN1_ENUMERATED* keymaster_security_level;
+ ASN1_OCTET_STRING* attestation_challenge;
+ KM_AUTH_LIST* software_enforced;
+ KM_AUTH_LIST* tee_enforced;
+ ASN1_INTEGER* unique_id;
+} KM_KEY_DESCRIPTION;
+
+ASN1_SEQUENCE(KM_KEY_DESCRIPTION) = {
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, attestation_version, ASN1_INTEGER),
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, attestation_security_level, ASN1_ENUMERATED),
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, keymaster_version, ASN1_INTEGER),
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, keymaster_security_level, ASN1_ENUMERATED),
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, attestation_challenge, ASN1_OCTET_STRING),
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, unique_id, ASN1_OCTET_STRING),
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, software_enforced, KM_AUTH_LIST),
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, tee_enforced, KM_AUTH_LIST),
+} ASN1_SEQUENCE_END(KM_KEY_DESCRIPTION);
+IMPLEMENT_ASN1_FUNCTIONS(KM_KEY_DESCRIPTION);
+
+template <V4_0::Tag tag>
+void copyAuthTag(const stack_st_ASN1_INTEGER* stack, TypedTag<TagType::ENUM_REP, tag> ttag,
+ AuthorizationSet* auth_list) {
+ typedef typename V4_0::TypedTag2ValueType<decltype(ttag)>::type ValueT;
+ for (size_t i = 0; i < sk_ASN1_INTEGER_num(stack); ++i) {
+ auth_list->push_back(
+ ttag, static_cast<ValueT>(ASN1_INTEGER_get(sk_ASN1_INTEGER_value(stack, i))));
+ }
+}
+
+template <V4_0::Tag tag>
+void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag<TagType::ENUM, tag> ttag,
+ AuthorizationSet* auth_list) {
+ typedef typename V4_0::TypedTag2ValueType<decltype(ttag)>::type ValueT;
+ if (!asn1_int) return;
+ auth_list->push_back(ttag, static_cast<ValueT>(ASN1_INTEGER_get(asn1_int)));
+}
+
+template <V4_0::Tag tag>
+void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag<TagType::UINT, tag> ttag,
+ AuthorizationSet* auth_list) {
+ if (!asn1_int) return;
+ auth_list->push_back(ttag, ASN1_INTEGER_get(asn1_int));
+}
+
+BIGNUM* construct_uint_max() {
+ BIGNUM* value = BN_new();
+ BIGNUM_Ptr one(BN_new());
+ BN_one(one.get());
+ BN_lshift(value, one.get(), 32);
+ return value;
+}
+
+uint64_t BignumToUint64(BIGNUM* num) {
+ static_assert((sizeof(BN_ULONG) == sizeof(uint32_t)) || (sizeof(BN_ULONG) == sizeof(uint64_t)),
+ "This implementation only supports 32 and 64-bit BN_ULONG");
+ if (sizeof(BN_ULONG) == sizeof(uint32_t)) {
+ BIGNUM_Ptr uint_max(construct_uint_max());
+ BIGNUM_Ptr hi(BN_new()), lo(BN_new());
+ BN_CTX_Ptr ctx(BN_CTX_new());
+ BN_div(hi.get(), lo.get(), num, uint_max.get(), ctx.get());
+ return static_cast<uint64_t>(BN_get_word(hi.get())) << 32 | BN_get_word(lo.get());
+ } else if (sizeof(BN_ULONG) == sizeof(uint64_t)) {
+ return BN_get_word(num);
+ } else {
+ return 0;
+ }
+}
+
+template <V4_0::Tag tag>
+void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag<TagType::ULONG, tag> ttag,
+ AuthorizationSet* auth_list) {
+ if (!asn1_int) return;
+ BIGNUM_Ptr num(ASN1_INTEGER_to_BN(asn1_int, nullptr));
+ auth_list->push_back(ttag, BignumToUint64(num.get()));
+}
+
+template <V4_0::Tag tag>
+void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag<TagType::DATE, tag> ttag,
+ AuthorizationSet* auth_list) {
+ if (!asn1_int) return;
+ BIGNUM_Ptr num(ASN1_INTEGER_to_BN(asn1_int, nullptr));
+ auth_list->push_back(ttag, BignumToUint64(num.get()));
+}
+
+template <V4_0::Tag tag>
+void copyAuthTag(const ASN1_NULL* asn1_null, TypedTag<TagType::BOOL, tag> ttag,
+ AuthorizationSet* auth_list) {
+ if (!asn1_null) return;
+ auth_list->push_back(ttag);
+}
+
+template <V4_0::Tag tag>
+void copyAuthTag(const ASN1_OCTET_STRING* asn1_string, TypedTag<TagType::BYTES, tag> ttag,
+ AuthorizationSet* auth_list) {
+ if (!asn1_string) return;
+ hidl_vec<uint8_t> buf;
+ buf.setToExternal(asn1_string->data, asn1_string->length);
+ auth_list->push_back(ttag, buf);
+}
+
+// Extract the values from the specified ASN.1 record and place them in auth_list.
+static ErrorCode extract_auth_list(const KM_AUTH_LIST* record, AuthorizationSet* auth_list) {
+ if (!record) return ErrorCode::OK;
+
+ copyAuthTag(record->active_date_time, TAG_ACTIVE_DATETIME, auth_list);
+ copyAuthTag(record->algorithm, TAG_ALGORITHM, auth_list);
+ copyAuthTag(record->application_id, TAG_APPLICATION_ID, auth_list);
+ copyAuthTag(record->auth_timeout, TAG_AUTH_TIMEOUT, auth_list);
+ copyAuthTag(record->creation_date_time, TAG_CREATION_DATETIME, auth_list);
+ copyAuthTag(record->digest, TAG_DIGEST, auth_list);
+ copyAuthTag(record->ec_curve, TAG_EC_CURVE, auth_list);
+ copyAuthTag(record->key_size, TAG_KEY_SIZE, auth_list);
+ copyAuthTag(record->no_auth_required, TAG_NO_AUTH_REQUIRED, auth_list);
+ copyAuthTag(record->origin, TAG_ORIGIN, auth_list);
+ copyAuthTag(record->origination_expire_date_time, TAG_ORIGINATION_EXPIRE_DATETIME, auth_list);
+ copyAuthTag(record->os_patchlevel, TAG_OS_PATCHLEVEL, auth_list);
+ copyAuthTag(record->os_version, TAG_OS_VERSION, auth_list);
+ copyAuthTag(record->padding, TAG_PADDING, auth_list);
+ copyAuthTag(record->purpose, TAG_PURPOSE, auth_list);
+ copyAuthTag(record->rollback_resistance, TAG_ROLLBACK_RESISTANCE, auth_list);
+ copyAuthTag(record->rsa_public_exponent, TAG_RSA_PUBLIC_EXPONENT, auth_list);
+ copyAuthTag(record->usage_expire_date_time, TAG_USAGE_EXPIRE_DATETIME, auth_list);
+ copyAuthTag(record->user_auth_type, TAG_USER_AUTH_TYPE, auth_list);
+ copyAuthTag(record->attestation_application_id, TAG_ATTESTATION_APPLICATION_ID, auth_list);
+ copyAuthTag(record->vendor_patchlevel, TAG_VENDOR_PATCHLEVEL, auth_list);
+ copyAuthTag(record->boot_patchlevel, TAG_BOOT_PATCHLEVEL, auth_list);
+ copyAuthTag(record->trusted_user_presence_required, TAG_TRUSTED_USER_PRESENCE_REQUIRED,
+ auth_list);
+ copyAuthTag(record->trusted_confirmation_required, TAG_TRUSTED_CONFIRMATION_REQUIRED,
+ auth_list);
+ copyAuthTag(record->unlocked_device_required, TAG_UNLOCKED_DEVICE_REQUIRED, auth_list);
+ copyAuthTag(record->early_boot_only, TAG_EARLY_BOOT_ONLY, auth_list);
+ copyAuthTag(record->device_unique_attestation, TAG_DEVICE_UNIQUE_ATTESTATION, auth_list);
+
+ return ErrorCode::OK;
+}
+
+MAKE_OPENSSL_PTR_TYPE(KM_KEY_DESCRIPTION)
+
+// Parse the DER-encoded attestation record, placing the results in keymaster_version,
+// attestation_challenge, software_enforced, tee_enforced and unique_id.
+std::tuple<ErrorCode, AttestationRecord> parse_attestation_record(const hidl_vec<uint8_t>& cert) {
+ const uint8_t* p = cert.data();
+ X509_Ptr x509(d2i_X509(nullptr, &p, cert.size()));
+
+ ASN1_OBJECT_Ptr oid(OBJ_txt2obj(kAttestionRecordOid, 1 /* dotted string format */));
+ if (!oid.get()) {
+ LOG(ERROR) << "Error parsing OID";
+ return {ErrorCode::UNKNOWN_ERROR, {}};
+ }
+
+ int location = X509_get_ext_by_OBJ(x509.get(), oid.get(), -1 /* search from beginning */);
+ if (location == -1) {
+ LOG(ERROR) << "Attestation extension not found in certificate";
+ return {ErrorCode::UNKNOWN_ERROR, {}};
+ }
+
+ X509_EXTENSION* attest_rec_ext = X509_get_ext(x509.get(), location);
+ if (!attest_rec_ext) {
+ LOG(ERROR) << "Found extension but couldn't retrieve it. Probably BoringSSL bug.";
+ return {ErrorCode::UNKNOWN_ERROR, {}};
+ }
+
+ ASN1_OCTET_STRING* attest_rec = X509_EXTENSION_get_data(attest_rec_ext);
+ if (!attest_rec_ext) {
+ LOG(ERROR) << "Attestation extension contained no data";
+ return {ErrorCode::UNKNOWN_ERROR, {}};
+ }
+
+ p = attest_rec->data;
+ KM_KEY_DESCRIPTION_Ptr record(d2i_KM_KEY_DESCRIPTION(nullptr, &p, attest_rec->length));
+ if (!record.get()) return {ErrorCode::UNKNOWN_ERROR, {}};
+
+ AttestationRecord result;
+
+ result.attestation_version = ASN1_INTEGER_get(record->attestation_version);
+ result.attestation_security_level =
+ static_cast<SecurityLevel>(ASN1_ENUMERATED_get(record->attestation_security_level));
+ result.keymaster_version = ASN1_INTEGER_get(record->keymaster_version);
+ result.keymaster_security_level =
+ static_cast<SecurityLevel>(ASN1_ENUMERATED_get(record->keymaster_security_level));
+
+ auto& chall = record->attestation_challenge;
+ result.attestation_challenge.resize(chall->length);
+ memcpy(result.attestation_challenge.data(), chall->data, chall->length);
+ auto& uid = record->unique_id;
+ result.unique_id.resize(uid->length);
+ memcpy(result.unique_id.data(), uid->data, uid->length);
+
+ ErrorCode error = extract_auth_list(record->software_enforced, &result.software_enforced);
+ if (error != ErrorCode::OK) return {error, {}};
+
+ error = extract_auth_list(record->tee_enforced, &result.hardware_enforced);
+ if (error != ErrorCode::OK) return {error, {}};
+
+ KM_ROOT_OF_TRUST* root_of_trust = nullptr;
+ if (record->tee_enforced && record->tee_enforced->root_of_trust) {
+ root_of_trust = record->tee_enforced->root_of_trust;
+ } else if (record->software_enforced && record->software_enforced->root_of_trust) {
+ root_of_trust = record->software_enforced->root_of_trust;
+ } else {
+ LOG(ERROR) << AT << " Failed root of trust parsing";
+ return {ErrorCode::INVALID_ARGUMENT, {}};
+ }
+ if (!root_of_trust->verified_boot_key) {
+ LOG(ERROR) << AT << " Failed verified boot key parsing";
+ return {ErrorCode::INVALID_ARGUMENT, {}};
+ }
+
+ RootOfTrust& rot = result.root_of_trust;
+ auto& vb_key = root_of_trust->verified_boot_key;
+ rot.verified_boot_key.resize(vb_key->length);
+ memcpy(rot.verified_boot_key.data(), vb_key->data, vb_key->length);
+
+ rot.verified_boot_state = static_cast<keymaster_verified_boot_t>(
+ ASN1_ENUMERATED_get(root_of_trust->verified_boot_state));
+ rot.device_locked = root_of_trust->device_locked;
+
+ auto& vb_hash = root_of_trust->verified_boot_hash;
+ if (!vb_hash) {
+ LOG(ERROR) << AT << " Failed verified boot hash parsing";
+ return {ErrorCode::INVALID_ARGUMENT, {}};
+ }
+ rot.verified_boot_hash.resize(vb_hash->length);
+ memcpy(rot.verified_boot_hash.data(), vb_hash->data, vb_hash->length);
+
+ return {ErrorCode::OK, result};
+}
+
+} // namespace V4_1
+} // namespace keymaster
+} // namespace hardware
+} // namespace android
diff --git a/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h b/keymaster/4.1/support/include/keymasterV4_1/Keymaster.h
similarity index 73%
rename from keymaster/4.0/support/include/keymasterV4_0/Keymaster.h
rename to keymaster/4.1/support/include/keymasterV4_1/Keymaster.h
index ad83f17..1f49e18 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h
+++ b/keymaster/4.1/support/include/keymasterV4_1/Keymaster.h
@@ -15,28 +15,28 @@
** limitations under the License.
*/
-#ifndef HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_H_
-#define HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_H_
-
-#include <android/hardware/keymaster/4.0/IKeymasterDevice.h>
+#pragma once
#include <memory>
#include <vector>
-namespace android {
-namespace hardware {
-namespace keymaster {
-namespace V4_0 {
-namespace support {
+#include <android/hardware/keymaster/4.1/IKeymasterDevice.h>
+#include <keymasterV4_1/keymaster_tags.h>
+
+namespace android::hardware::keymaster::V4_1::support {
/**
- * Keymaster abstracts the underlying V4_0::IKeymasterDevice. There is one implementation
- * (Keymaster4) which is a trivial passthrough and one that wraps a V3_0::IKeymasterDevice.
+ * Keymaster abstracts the underlying V4_1::IKeymasterDevice. There are two implementations,
+ * Keymaster3 which wraps a V3_0::IKeymasterDevice and Keymaster4, which wraps either a
+ * V4_0::IKeymasterDevice or a V4_1::IKeymasterDevice. There is a V3_0::IKeymasterDevice
+ * implementation that is used to wrap pre-HIDL keymaster implementations, and Keymaster3 will wrap
+ * that.
*
* The reason for adding this additional layer, rather than simply using the latest HAL directly and
* subclassing it to wrap any older HAL, is because this provides a place to put additional methods
* which clients can use when they need to distinguish between different underlying HAL versions,
- * while still having to use only the latest interface.
+ * while still having to use only the latest interface. Plus it's a handy place to keep some
+ * convenience methods.
*/
class Keymaster : public IKeymasterDevice {
public:
@@ -50,12 +50,14 @@
hidl_string keymasterName;
hidl_string authorName;
uint8_t majorVersion;
+ uint8_t minorVersion;
SecurityLevel securityLevel;
bool supportsEc;
bool operator>(const VersionResult& other) const {
- auto lhs = std::tie(securityLevel, majorVersion, supportsEc);
- auto rhs = std::tie(other.securityLevel, other.majorVersion, other.supportsEc);
+ auto lhs = std::tie(securityLevel, majorVersion, minorVersion, supportsEc);
+ auto rhs = std::tie(other.securityLevel, other.majorVersion, other.minorVersion,
+ other.supportsEc);
return lhs > rhs;
}
};
@@ -69,6 +71,9 @@
* There are no side effects otherwise.
*/
void logIfKeymasterVendorError(ErrorCode ec) const;
+ void logIfKeymasterVendorError(V4_0::ErrorCode ec) const {
+ logIfKeymasterVendorError(static_cast<ErrorCode>(ec));
+ }
/**
* Returns all available Keymaster3 and Keymaster4 instances, in order of most secure to least
@@ -93,10 +98,4 @@
std::ostream& operator<<(std::ostream& os, const Keymaster& keymaster);
-} // namespace support
-} // namespace V4_0
-} // namespace keymaster
-} // namespace hardware
-} // namespace android
-
-#endif // HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_H_
+} // namespace android::hardware::keymaster::V4_1::support
diff --git a/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h b/keymaster/4.1/support/include/keymasterV4_1/Keymaster3.h
similarity index 72%
rename from keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h
rename to keymaster/4.1/support/include/keymasterV4_1/Keymaster3.h
index c40be7c..c201e8c 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h
+++ b/keymaster/4.1/support/include/keymasterV4_1/Keymaster3.h
@@ -1,5 +1,4 @@
/*
- **
** Copyright 2017, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,18 +14,14 @@
** limitations under the License.
*/
-#ifndef HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_3_H_
-#define HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_3_H_
+#pragma once
#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
#include "Keymaster.h"
+#include "Operation.h"
-namespace android {
-namespace hardware {
-namespace keymaster {
-namespace V4_0 {
-namespace support {
+namespace android::hardware::keymaster::V4_1::support {
using IKeymaster3Device = ::android::hardware::keymaster::V3_0::IKeymasterDevice;
@@ -38,8 +33,10 @@
using ::android::hardware::details::return_status;
class Keymaster3 : public Keymaster {
- public:
+ public:
+ // This definition is used for device enumeration.
using WrappedIKeymasterDevice = IKeymaster3Device;
+
Keymaster3(sp<IKeymaster3Device> km3_dev, const hidl_string& instanceName)
: Keymaster(IKeymaster3Device::descriptor, instanceName),
km3_dev_(km3_dev),
@@ -53,24 +50,24 @@
Return<void> getHardwareInfo(getHardwareInfo_cb _hidl_cb);
Return<void> getHmacSharingParameters(getHmacSharingParameters_cb _hidl_cb) override {
- _hidl_cb(ErrorCode::UNIMPLEMENTED, {});
+ _hidl_cb(V4_0::ErrorCode::UNIMPLEMENTED, {});
return Void();
}
Return<void> computeSharedHmac(const hidl_vec<HmacSharingParameters>&,
computeSharedHmac_cb _hidl_cb) override {
- _hidl_cb(ErrorCode::UNIMPLEMENTED, {});
+ _hidl_cb(V4_0::ErrorCode::UNIMPLEMENTED, {});
return Void();
}
Return<void> verifyAuthorization(uint64_t, const hidl_vec<KeyParameter>&,
const HardwareAuthToken&,
verifyAuthorization_cb _hidl_cb) override {
- _hidl_cb(ErrorCode::UNIMPLEMENTED, {});
+ _hidl_cb(V4_0::ErrorCode::UNIMPLEMENTED, {});
return Void();
}
- Return<ErrorCode> addRngEntropy(const hidl_vec<uint8_t>& data) override;
+ Return<V4_0::ErrorCode> addRngEntropy(const hidl_vec<uint8_t>& data) override;
Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,
generateKey_cb _hidl_cb) override;
Return<void> getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,
@@ -86,7 +83,7 @@
const hidl_vec<KeyParameter>& /* unwrappingParams */,
uint64_t /* passwordSid */, uint64_t /* biometricSid */,
importWrappedKey_cb _hidl_cb) {
- _hidl_cb(ErrorCode::UNIMPLEMENTED, {}, {});
+ _hidl_cb(V4_0::ErrorCode::UNIMPLEMENTED, {}, {});
return Void();
}
@@ -99,9 +96,9 @@
Return<void> upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
const hidl_vec<KeyParameter>& upgradeParams,
upgradeKey_cb _hidl_cb) override;
- Return<ErrorCode> deleteKey(const hidl_vec<uint8_t>& keyBlob) override;
- Return<ErrorCode> deleteAllKeys() override;
- Return<ErrorCode> destroyAttestationIds() override;
+ Return<V4_0::ErrorCode> deleteKey(const hidl_vec<uint8_t>& keyBlob) override;
+ Return<V4_0::ErrorCode> deleteAllKeys() override;
+ Return<V4_0::ErrorCode> destroyAttestationIds() override;
Return<void> begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
const hidl_vec<KeyParameter>& inParams, const HardwareAuthToken& authToken,
begin_cb _hidl_cb) override;
@@ -112,9 +109,31 @@
const hidl_vec<uint8_t>& input, const hidl_vec<uint8_t>& signature,
const HardwareAuthToken& authToken,
const VerificationToken& verificationToken, finish_cb _hidl_cb) override;
- Return<ErrorCode> abort(uint64_t operationHandle) override;
+ Return<V4_0::ErrorCode> abort(uint64_t operationHandle) override;
- private:
+ /**********************************
+ * V4_1::IKeymasterDevice methods *
+ *********************************/
+
+ Return<ErrorCode> deviceLocked(bool /* passwordOnly */,
+ const VerificationToken& /* verificationToken */) override {
+ return ErrorCode::UNIMPLEMENTED;
+ }
+
+ Return<ErrorCode> earlyBootEnded() override { return ErrorCode::UNIMPLEMENTED; }
+
+ Return<void> beginOp(KeyPurpose purpose, const hidl_vec<uint8_t>& keyBlob,
+ const hidl_vec<KeyParameter>& inParams, const HardwareAuthToken& authToken,
+ beginOp_cb _hidl_cb) override {
+ return begin(purpose, keyBlob, inParams, authToken,
+ [&_hidl_cb](V4_0::ErrorCode errorCode, const hidl_vec<KeyParameter>& outParams,
+ OperationHandle operationHandle) {
+ _hidl_cb(static_cast<ErrorCode>(errorCode), outParams,
+ new Operation(operationHandle));
+ });
+ }
+
+ private:
void getVersionIfNeeded();
sp<IKeymaster3Device> km3_dev_;
@@ -126,10 +145,4 @@
bool supportsAllDigests_;
};
-} // namespace support
-} // namespace V4_0
-} // namespace keymaster
-} // namespace hardware
-} // namespace android
-
-#endif // HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_3_H_
+} // namespace android::hardware::keymaster::V4_1::support
diff --git a/keymaster/4.1/support/include/keymasterV4_1/Keymaster4.h b/keymaster/4.1/support/include/keymasterV4_1/Keymaster4.h
new file mode 100644
index 0000000..6d74d98
--- /dev/null
+++ b/keymaster/4.1/support/include/keymasterV4_1/Keymaster4.h
@@ -0,0 +1,197 @@
+/*
+ ** 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.
+ */
+
+#pragma once
+
+#include "Keymaster.h"
+#include "Operation.h"
+
+namespace android::hardware::keymaster::V4_1::support {
+
+using android::sp;
+
+/**
+ * This class can wrap either a V4_0 or V4_1 IKeymasterDevice.
+ */
+class Keymaster4 : public Keymaster {
+ public:
+ // This definition is used for device enumeration; enumerating 4.0 devices will also
+ // enumerate 4.1. devices.
+ using WrappedIKeymasterDevice = V4_0::IKeymasterDevice;
+
+ Keymaster4(sp<V4_1::IKeymasterDevice> km4_1_dev, const hidl_string& instanceName)
+ : Keymaster(V4_1::IKeymasterDevice::descriptor, instanceName),
+ haveVersion_(false),
+ km4_0_dev_(km4_1_dev),
+ km4_1_dev_(km4_1_dev) {}
+
+ Keymaster4(sp<V4_0::IKeymasterDevice> km4_0_dev, const hidl_string& instanceName)
+ : Keymaster(V4_1::IKeymasterDevice::descriptor, instanceName),
+ haveVersion_(false),
+ km4_0_dev_(km4_0_dev),
+ km4_1_dev_() {}
+
+ const VersionResult& halVersion() const override {
+ const_cast<Keymaster4*>(this)->getVersionIfNeeded();
+ return version_;
+ }
+
+ /**********************************
+ * V4_0::IKeymasterDevice methods *
+ *********************************/
+
+ Return<void> getHardwareInfo(getHardwareInfo_cb _hidl_cb) override {
+ return km4_0_dev_->getHardwareInfo(_hidl_cb);
+ }
+
+ Return<void> getHmacSharingParameters(getHmacSharingParameters_cb _hidl_cb) override {
+ return km4_0_dev_->getHmacSharingParameters(_hidl_cb);
+ }
+
+ Return<void> computeSharedHmac(const hidl_vec<HmacSharingParameters>& params,
+ computeSharedHmac_cb _hidl_cb) override {
+ return km4_0_dev_->computeSharedHmac(params, _hidl_cb);
+ }
+
+ Return<void> verifyAuthorization(uint64_t operationHandle, const hidl_vec<KeyParameter>& params,
+ const HardwareAuthToken& authToken,
+ verifyAuthorization_cb _hidl_cb) override {
+ return km4_0_dev_->verifyAuthorization(operationHandle, params, authToken, _hidl_cb);
+ }
+
+ Return<V4_0::ErrorCode> addRngEntropy(const hidl_vec<uint8_t>& data) override {
+ return km4_0_dev_->addRngEntropy(data);
+ }
+
+ Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,
+ generateKey_cb _hidl_cb) override {
+ return km4_0_dev_->generateKey(keyParams, _hidl_cb);
+ }
+
+ Return<void> getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,
+ const hidl_vec<uint8_t>& clientId,
+ const hidl_vec<uint8_t>& appData,
+ getKeyCharacteristics_cb _hidl_cb) override {
+ return km4_0_dev_->getKeyCharacteristics(keyBlob, clientId, appData, _hidl_cb);
+ }
+
+ Return<void> importKey(const hidl_vec<KeyParameter>& params, KeyFormat keyFormat,
+ const hidl_vec<uint8_t>& keyData, importKey_cb _hidl_cb) override {
+ return km4_0_dev_->importKey(params, keyFormat, keyData, _hidl_cb);
+ }
+
+ Return<void> importWrappedKey(const hidl_vec<uint8_t>& wrappedKeyData,
+ const hidl_vec<uint8_t>& wrappingKeyBlob,
+ const hidl_vec<uint8_t>& maskingKey,
+ const hidl_vec<KeyParameter>& unwrappingParams,
+ uint64_t passwordSid, uint64_t biometricSid,
+ importWrappedKey_cb _hidl_cb) {
+ return km4_0_dev_->importWrappedKey(wrappedKeyData, wrappingKeyBlob, maskingKey,
+ unwrappingParams, passwordSid, biometricSid, _hidl_cb);
+ }
+
+ Return<void> exportKey(KeyFormat exportFormat, const hidl_vec<uint8_t>& keyBlob,
+ const hidl_vec<uint8_t>& clientId, const hidl_vec<uint8_t>& appData,
+ exportKey_cb _hidl_cb) override {
+ return km4_0_dev_->exportKey(exportFormat, keyBlob, clientId, appData, _hidl_cb);
+ }
+
+ Return<void> attestKey(const hidl_vec<uint8_t>& keyToAttest,
+ const hidl_vec<KeyParameter>& attestParams,
+ attestKey_cb _hidl_cb) override {
+ return km4_0_dev_->attestKey(keyToAttest, attestParams, _hidl_cb);
+ }
+
+ Return<void> upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
+ const hidl_vec<KeyParameter>& upgradeParams,
+ upgradeKey_cb _hidl_cb) override {
+ return km4_0_dev_->upgradeKey(keyBlobToUpgrade, upgradeParams, _hidl_cb);
+ }
+
+ Return<V4_0::ErrorCode> deleteKey(const hidl_vec<uint8_t>& keyBlob) override {
+ return km4_0_dev_->deleteKey(keyBlob);
+ }
+
+ Return<V4_0::ErrorCode> deleteAllKeys() override { return km4_0_dev_->deleteAllKeys(); }
+
+ Return<V4_0::ErrorCode> destroyAttestationIds() override {
+ return km4_0_dev_->destroyAttestationIds();
+ }
+
+ Return<void> begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
+ const hidl_vec<KeyParameter>& inParams, const HardwareAuthToken& authToken,
+ begin_cb _hidl_cb) override {
+ return km4_0_dev_->begin(purpose, key, inParams, authToken, _hidl_cb);
+ }
+
+ Return<void> update(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
+ const hidl_vec<uint8_t>& input, const HardwareAuthToken& authToken,
+ const VerificationToken& verificationToken, update_cb _hidl_cb) override {
+ return km4_0_dev_->update(operationHandle, inParams, input, authToken, verificationToken,
+ _hidl_cb);
+ }
+
+ Return<void> finish(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
+ const hidl_vec<uint8_t>& input, const hidl_vec<uint8_t>& signature,
+ const HardwareAuthToken& authToken,
+ const VerificationToken& verificationToken, finish_cb _hidl_cb) override {
+ return km4_0_dev_->finish(operationHandle, inParams, input, signature, authToken,
+ verificationToken, _hidl_cb);
+ }
+
+ Return<V4_0::ErrorCode> abort(uint64_t operationHandle) override {
+ return km4_0_dev_->abort(operationHandle);
+ }
+
+ /**********************************
+ * V4_1::IKeymasterDevice methods *
+ *********************************/
+
+ Return<ErrorCode> deviceLocked(bool passwordOnly,
+ const VerificationToken& verificationToken) override {
+ if (km4_1_dev_) return km4_1_dev_->deviceLocked(passwordOnly, verificationToken);
+ return ErrorCode::UNIMPLEMENTED;
+ }
+
+ Return<ErrorCode> earlyBootEnded() override {
+ if (km4_1_dev_) return km4_1_dev_->earlyBootEnded();
+ return ErrorCode::UNIMPLEMENTED;
+ }
+
+ Return<void> beginOp(KeyPurpose purpose, const hidl_vec<uint8_t>& keyBlob,
+ const hidl_vec<KeyParameter>& inParams, const HardwareAuthToken& authToken,
+ beginOp_cb _hidl_cb) override {
+ if (km4_1_dev_) return km4_1_dev_->beginOp(purpose, keyBlob, inParams, authToken, _hidl_cb);
+
+ return km4_0_dev_->begin(
+ purpose, keyBlob, inParams, authToken,
+ [&_hidl_cb](V4_0::ErrorCode errorCode, const hidl_vec<KeyParameter>& outParams,
+ OperationHandle operationHandle) {
+ _hidl_cb(static_cast<ErrorCode>(errorCode), outParams,
+ new Operation(operationHandle));
+ });
+ }
+
+ private:
+ void getVersionIfNeeded();
+
+ bool haveVersion_;
+ VersionResult version_;
+ sp<V4_0::IKeymasterDevice> km4_0_dev_;
+ sp<V4_1::IKeymasterDevice> km4_1_dev_;
+}; // namespace android::hardware::keymaster::V4_1::support
+
+} // namespace android::hardware::keymaster::V4_1::support
diff --git a/keymaster/4.1/support/include/keymasterV4_1/Operation.h b/keymaster/4.1/support/include/keymasterV4_1/Operation.h
new file mode 100644
index 0000000..902d49a
--- /dev/null
+++ b/keymaster/4.1/support/include/keymasterV4_1/Operation.h
@@ -0,0 +1,38 @@
+/*
+ ** 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.
+ */
+
+#pragma once
+
+#include <android/hardware/keymaster/4.1/IOperation.h>
+
+#include <keymasterV4_1/keymaster_tags.h>
+
+namespace android::hardware::keymaster::V4_1::support {
+
+class Operation : public IOperation {
+ public:
+ Operation(OperationHandle handle) : handle_(handle) {}
+
+ Return<void> getOperationChallenge(getOperationChallenge_cb _hidl_cb) override {
+ _hidl_cb(V4_1::ErrorCode::OK, handle_);
+ return Void();
+ }
+
+ private:
+ OperationHandle handle_;
+};
+
+} // namespace android::hardware::keymaster::V4_1::support
diff --git a/keymaster/4.1/support/include/keymasterV4_1/attestation_record.h b/keymaster/4.1/support/include/keymasterV4_1/attestation_record.h
new file mode 100644
index 0000000..b543bdd
--- /dev/null
+++ b/keymaster/4.1/support/include/keymasterV4_1/attestation_record.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android/hardware/keymaster/4.1/IKeymasterDevice.h>
+#include <keymasterV4_0/attestation_record.h>
+#include <keymasterV4_0/openssl_utils.h>
+#include <keymasterV4_1/authorization_set.h>
+
+namespace android {
+namespace hardware {
+namespace keymaster {
+namespace V4_1 {
+
+using V4_0::kAttestionRecordOid;
+using V4_0::keymaster_verified_boot_t;
+
+struct RootOfTrust {
+ SecurityLevel security_level;
+ hidl_vec<uint8_t> verified_boot_key;
+ hidl_vec<uint8_t> verified_boot_hash;
+ keymaster_verified_boot_t verified_boot_state;
+ bool device_locked;
+};
+
+struct AttestationRecord {
+ RootOfTrust root_of_trust;
+ uint32_t attestation_version;
+ SecurityLevel attestation_security_level;
+ uint32_t keymaster_version;
+ SecurityLevel keymaster_security_level;
+ hidl_vec<uint8_t> attestation_challenge;
+ AuthorizationSet software_enforced;
+ AuthorizationSet hardware_enforced;
+ hidl_vec<uint8_t> unique_id;
+};
+
+std::tuple<ErrorCode, AttestationRecord> parse_attestation_record(const hidl_vec<uint8_t>& cert);
+
+} // namespace V4_1
+} // namespace keymaster
+} // namespace hardware
+} // namespace android
diff --git a/keymaster/4.1/support/include/keymasterV4_1/authorization_set.h b/keymaster/4.1/support/include/keymasterV4_1/authorization_set.h
index afc0eaf..01605b7 100644
--- a/keymaster/4.1/support/include/keymasterV4_1/authorization_set.h
+++ b/keymaster/4.1/support/include/keymasterV4_1/authorization_set.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef HARDWARE_INTERFACES_KEYMASTER_V4_1_SUPPORT_INCLUDE_AUTHORIZATION_SET_H_
-#define HARDWARE_INTERFACES_KEYMASTER_V4_1_SUPPORT_INCLUDE_AUTHORIZATION_SET_H_
+#pragma once
#include <keymasterV4_0/authorization_set.h>
@@ -28,5 +27,3 @@
using V4_0::KeyParameter;
} // namespace android::hardware::keymaster::V4_1
-
-#endif // HARDWARE_INTERFACES_KEYMASTER_V4_1_SUPPORT_INCLUDE_AUTHORIZATION_SET_H_
diff --git a/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h b/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h
index 6ffe8e1..c5ce950 100644
--- a/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h
+++ b/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h
@@ -23,16 +23,26 @@
namespace android::hardware::keymaster::V4_1 {
+using V4_0::Algorithm;
using V4_0::BlockMode;
using V4_0::Digest;
using V4_0::EcCurve;
-using V4_0::ErrorCode;
+using V4_0::HardwareAuthenticatorType;
using V4_0::HardwareAuthToken;
+using V4_0::HmacSharingParameters;
+using V4_0::KeyBlobUsageRequirements;
+using V4_0::KeyCharacteristics;
+using V4_0::KeyFormat;
+using V4_0::KeyOrigin;
using V4_0::KeyParameter;
+using V4_0::KeyPurpose;
+using V4_0::OperationHandle;
using V4_0::PaddingMode;
+using V4_0::SecurityLevel;
using V4_0::TagType;
using V4_0::VerificationToken;
+using V4_0::NullOr;
using V4_0::TypedTag;
using V4_0::TAG_ACTIVE_DATETIME;
diff --git a/keymaster/4.1/support/include/keymasterV4_1/keymaster_utils.h b/keymaster/4.1/support/include/keymasterV4_1/keymaster_utils.h
new file mode 100644
index 0000000..2e28002
--- /dev/null
+++ b/keymaster/4.1/support/include/keymasterV4_1/keymaster_utils.h
@@ -0,0 +1,26 @@
+/*
+ * 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 <keymasterV4_0/keymaster_utils.h>
+
+namespace android::hardware::keymaster::V4_1::support {
+
+using V4_0::support::blob2hidlVec;
+using V4_0::support::hidlVec2AuthToken;
+
+} // namespace android::hardware::keymaster::V4_1::support
diff --git a/keymaster/4.1/vts/functional/Android.bp b/keymaster/4.1/vts/functional/Android.bp
index f5a0c9c..c2d7fa3 100644
--- a/keymaster/4.1/vts/functional/Android.bp
+++ b/keymaster/4.1/vts/functional/Android.bp
@@ -19,12 +19,24 @@
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
"EarlyBootKeyTest.cpp",
+ "DeviceUniqueAttestationTest.cpp",
+ "Keymaster4_1HidlTest.cpp",
+ "UnlockedDeviceRequiredTest.cpp",
],
static_libs: [
"android.hardware.keymaster@4.0",
"android.hardware.keymaster@4.1",
- "libkeymaster4support",
+ "libcrypto_static",
"libkeymaster4_1support",
+ "libkeymaster4support",
+ "libkeymaster4vtstest",
],
- test_suites: ["vts-core"],
+ cflags: [
+ "-Wall",
+ "-O0",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts-core",
+ ],
}
diff --git a/keymaster/4.1/vts/functional/DeviceUniqueAttestationTest.cpp b/keymaster/4.1/vts/functional/DeviceUniqueAttestationTest.cpp
new file mode 100644
index 0000000..7ea3275
--- /dev/null
+++ b/keymaster/4.1/vts/functional/DeviceUniqueAttestationTest.cpp
@@ -0,0 +1,278 @@
+/*
+ * 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 "Keymaster4_1HidlTest.h"
+
+#include <cutils/properties.h>
+
+#include <openssl/x509.h>
+
+#include <keymasterV4_1/attestation_record.h>
+#include <keymasterV4_1/authorization_set.h>
+
+namespace android::hardware::keymaster::V4_0 {
+
+bool operator==(const AuthorizationSet& a, const AuthorizationSet& b) {
+ return std::equal(a.begin(), a.end(), b.begin(), b.end());
+}
+
+} // namespace android::hardware::keymaster::V4_0
+
+namespace android::hardware::keymaster::V4_1 {
+
+inline ::std::ostream& operator<<(::std::ostream& os, Tag tag) {
+ return os << toString(tag);
+}
+
+namespace test {
+
+using std::string;
+using std::tuple;
+
+namespace {
+
+char nibble2hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+string bin2hex(const hidl_vec<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;
+}
+
+struct AuthorizationSetDifferences {
+ string aName;
+ string bName;
+ AuthorizationSet aWhackB;
+ AuthorizationSet bWhackA;
+};
+
+std::ostream& operator<<(std::ostream& o, const AuthorizationSetDifferences& diffs) {
+ if (!diffs.aWhackB.empty()) {
+ o << "Set " << diffs.aName << " contains the following that " << diffs.bName << " does not"
+ << diffs.aWhackB;
+ if (!diffs.bWhackA.empty()) o << std::endl;
+ }
+
+ if (!diffs.bWhackA.empty()) {
+ o << "Set " << diffs.bName << " contains the following that " << diffs.aName << " does not"
+ << diffs.bWhackA;
+ }
+ return o;
+}
+
+// Computes and returns a \ b and b \ a ('\' is the set-difference operator, a \ b means all the
+// elements that are in a but not b, i.e. take a and whack all the elements in b) to the provided
+// stream. The sets must be sorted.
+//
+// This provides a simple and clear view of how the two sets differ, generally much
+// easier than scrutinizing printouts of the two sets.
+AuthorizationSetDifferences difference(string aName, const AuthorizationSet& a, string bName,
+ const AuthorizationSet& b) {
+ AuthorizationSetDifferences diffs = {std::move(aName), std::move(bName), {}, {}};
+ std::set_difference(a.begin(), a.end(), b.begin(), b.end(), std::back_inserter(diffs.aWhackB));
+ std::set_difference(b.begin(), b.end(), a.begin(), a.end(), std::back_inserter(diffs.bWhackA));
+ return diffs;
+}
+
+#define DIFFERENCE(a, b) difference(#a, a, #b, b)
+
+void check_root_of_trust(const RootOfTrust& root_of_trust) {
+ char vb_meta_device_state[PROPERTY_VALUE_MAX];
+ if (property_get("ro.boot.vbmeta.device_state", vb_meta_device_state, "") == 0) return;
+
+ char vb_meta_digest[PROPERTY_VALUE_MAX];
+ EXPECT_GT(property_get("ro.boot.vbmeta.digest", vb_meta_digest, ""), 0);
+ EXPECT_EQ(vb_meta_digest, bin2hex(root_of_trust.verified_boot_hash));
+
+ // Verified boot key should be all 0's if the boot state is not verified or self signed
+ HidlBuf empty_boot_key(string(32, '\0'));
+
+ char vb_meta_bootstate[PROPERTY_VALUE_MAX];
+ auto& verified_boot_key = root_of_trust.verified_boot_key;
+ auto& verified_boot_state = root_of_trust.verified_boot_state;
+ EXPECT_GT(property_get("ro.boot.verifiedbootstate", vb_meta_bootstate, ""), 0);
+ if (!strcmp(vb_meta_bootstate, "green")) {
+ EXPECT_EQ(verified_boot_state, V4_0::KM_VERIFIED_BOOT_VERIFIED);
+ EXPECT_NE(verified_boot_key, empty_boot_key);
+ } else if (!strcmp(vb_meta_bootstate, "yellow")) {
+ EXPECT_EQ(verified_boot_state, V4_0::KM_VERIFIED_BOOT_SELF_SIGNED);
+ EXPECT_NE(verified_boot_key, empty_boot_key);
+ } else if (!strcmp(vb_meta_bootstate, "orange")) {
+ EXPECT_EQ(verified_boot_state, V4_0::KM_VERIFIED_BOOT_UNVERIFIED);
+ EXPECT_EQ(verified_boot_key, empty_boot_key);
+ } else if (!strcmp(vb_meta_bootstate, "red")) {
+ EXPECT_EQ(verified_boot_state, V4_0::KM_VERIFIED_BOOT_FAILED);
+ } else {
+ EXPECT_EQ(verified_boot_state, V4_0::KM_VERIFIED_BOOT_UNVERIFIED);
+ EXPECT_EQ(verified_boot_key, empty_boot_key);
+ }
+}
+
+void check_attestation_record(AttestationRecord attestation, const HidlBuf& challenge,
+ AuthorizationSet expected_sw_enforced,
+ AuthorizationSet expected_hw_enforced,
+ SecurityLevel expected_security_level) {
+ EXPECT_EQ(41U, attestation.keymaster_version);
+ EXPECT_EQ(4U, attestation.attestation_version);
+ EXPECT_EQ(expected_security_level, attestation.attestation_security_level);
+ EXPECT_EQ(expected_security_level, attestation.keymaster_security_level);
+ EXPECT_EQ(challenge, attestation.attestation_challenge);
+
+ check_root_of_trust(attestation.root_of_trust);
+
+ // Sort all of the authorization lists, so that equality matching works.
+ expected_sw_enforced.Sort();
+ expected_hw_enforced.Sort();
+ attestation.software_enforced.Sort();
+ attestation.hardware_enforced.Sort();
+
+ EXPECT_EQ(expected_sw_enforced, attestation.software_enforced)
+ << DIFFERENCE(expected_sw_enforced, attestation.software_enforced);
+ EXPECT_EQ(expected_hw_enforced, attestation.hardware_enforced)
+ << DIFFERENCE(expected_hw_enforced, attestation.hardware_enforced);
+}
+
+} // namespace
+
+using std::string;
+using DeviceUniqueAttestationTest = Keymaster4_1HidlTest;
+
+TEST_P(DeviceUniqueAttestationTest, StrongBoxOnly) {
+ if (SecLevel() != SecurityLevel::STRONGBOX) return;
+
+ ASSERT_EQ(ErrorCode::OK, convert(GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaSigningKey(2048, 65537)
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+ .Authorization(TAG_INCLUDE_UNIQUE_ID))));
+
+ hidl_vec<hidl_vec<uint8_t>> cert_chain;
+ EXPECT_EQ(ErrorCode::UNIMPLEMENTED,
+ convert(AttestKey(
+ AuthorizationSetBuilder()
+ .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
+ .Authorization(TAG_ATTESTATION_CHALLENGE, HidlBuf("challenge"))
+ .Authorization(TAG_ATTESTATION_APPLICATION_ID, HidlBuf("foo")),
+ &cert_chain)));
+ CheckedDeleteKey();
+
+ ASSERT_EQ(ErrorCode::OK, convert(GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(EcCurve::P_256)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_INCLUDE_UNIQUE_ID))));
+
+ EXPECT_EQ(ErrorCode::UNIMPLEMENTED,
+ convert(AttestKey(
+ AuthorizationSetBuilder()
+ .Authorization(TAG_ATTESTATION_CHALLENGE, HidlBuf("challenge"))
+ .Authorization(TAG_ATTESTATION_APPLICATION_ID, HidlBuf("foo")),
+ &cert_chain)));
+}
+
+TEST_P(DeviceUniqueAttestationTest, Rsa) {
+ if (SecLevel() != SecurityLevel::STRONGBOX) return;
+ ASSERT_EQ(ErrorCode::OK,
+ convert(GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaSigningKey(2048, 65537)
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+ .Authorization(TAG_CREATION_DATETIME, 1))));
+
+ hidl_vec<hidl_vec<uint8_t>> cert_chain;
+ HidlBuf challenge("challenge");
+ HidlBuf app_id("foo");
+ EXPECT_EQ(ErrorCode::OK,
+ convert(AttestKey(AuthorizationSetBuilder()
+ .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
+ .Authorization(TAG_ATTESTATION_CHALLENGE, challenge)
+ .Authorization(TAG_ATTESTATION_APPLICATION_ID, app_id),
+ &cert_chain)));
+
+ EXPECT_EQ(1U, cert_chain.size());
+ auto [err, attestation] = parse_attestation_record(cert_chain[0]);
+ EXPECT_EQ(ErrorCode::OK, err);
+
+ check_attestation_record(attestation, challenge,
+ /* sw_enforced */
+ AuthorizationSetBuilder()
+ .Authorization(TAG_CREATION_DATETIME, 1)
+ .Authorization(TAG_ATTESTATION_APPLICATION_ID, app_id),
+ /* hw_enforced */
+ AuthorizationSetBuilder()
+ .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaSigningKey(2048, 65537)
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+ .Authorization(TAG_ORIGIN, KeyOrigin::GENERATED)
+ .Authorization(TAG_OS_VERSION, os_version())
+ .Authorization(TAG_OS_PATCHLEVEL, os_patch_level()),
+ SecLevel());
+}
+
+TEST_P(DeviceUniqueAttestationTest, Ecdsa) {
+ if (SecLevel() != SecurityLevel::STRONGBOX) return;
+ ASSERT_EQ(ErrorCode::OK,
+ convert(GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(256)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_CREATION_DATETIME, 1))));
+
+ hidl_vec<hidl_vec<uint8_t>> cert_chain;
+ HidlBuf challenge("challenge");
+ HidlBuf app_id("foo");
+ EXPECT_EQ(ErrorCode::OK,
+ convert(AttestKey(AuthorizationSetBuilder()
+ .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
+ .Authorization(TAG_ATTESTATION_CHALLENGE, challenge)
+ .Authorization(TAG_ATTESTATION_APPLICATION_ID, app_id),
+ &cert_chain)));
+
+ EXPECT_EQ(1U, cert_chain.size());
+ auto [err, attestation] = parse_attestation_record(cert_chain[0]);
+ EXPECT_EQ(ErrorCode::OK, err);
+
+ check_attestation_record(attestation, challenge,
+ /* sw_enforced */
+ AuthorizationSetBuilder()
+ .Authorization(TAG_CREATION_DATETIME, 1)
+ .Authorization(TAG_ATTESTATION_APPLICATION_ID, app_id),
+ /* hw_enforced */
+ AuthorizationSetBuilder()
+ .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(256)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_EC_CURVE, EcCurve::P_256)
+ .Authorization(TAG_ORIGIN, KeyOrigin::GENERATED)
+ .Authorization(TAG_OS_VERSION, os_version())
+ .Authorization(TAG_OS_PATCHLEVEL, os_patch_level()),
+ SecLevel());
+}
+
+INSTANTIATE_KEYMASTER_4_1_HIDL_TEST(DeviceUniqueAttestationTest);
+
+} // namespace test
+} // namespace android::hardware::keymaster::V4_1
diff --git a/keymaster/4.1/vts/functional/EarlyBootKeyTest.cpp b/keymaster/4.1/vts/functional/EarlyBootKeyTest.cpp
index 4a19010..a26c688 100644
--- a/keymaster/4.1/vts/functional/EarlyBootKeyTest.cpp
+++ b/keymaster/4.1/vts/functional/EarlyBootKeyTest.cpp
@@ -14,8 +14,78 @@
* limitations under the License.
*/
+#include "Keymaster4_1HidlTest.h"
+
+#include <keymasterV4_1/authorization_set.h>
+
namespace android::hardware::keymaster::V4_1::test {
-// TODO(swillden): Put tests here.
+using std::string;
+
+using EarlyBootKeyTest = Keymaster4_1HidlTest;
+
+// Because VTS tests are run on fully-booted machines, we can only run negative tests for early boot
+// keys, which cannot be created or used after /data is mounted. This is the only test we can run
+// in the normal case. The positive test will have to be done by the Android system, when it
+// creates/uses early boot keys during boot. It should fail to boot if the early boot key usage
+// fails.
+TEST_P(EarlyBootKeyTest, CannotCreateEarlyBootKeys) {
+ auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] =
+ CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::EARLY_BOOT_ENDED);
+
+ CheckedDeleteKeyData(&aesKeyData);
+ CheckedDeleteKeyData(&hmacKeyData);
+ CheckedDeleteKeyData(&rsaKeyData);
+ CheckedDeleteKeyData(&ecdsaKeyData);
+}
+
+// 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) {
+ // Should be able to create keys, since early boot has not ended
+ auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] =
+ CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::OK);
+
+ // TAG_EARLY_BOOT_ONLY should be in hw-enforced.
+ EXPECT_TRUE(contains(aesKeyData.characteristics.hardwareEnforced, TAG_EARLY_BOOT_ONLY));
+ EXPECT_TRUE(contains(hmacKeyData.characteristics.hardwareEnforced, TAG_EARLY_BOOT_ONLY));
+ EXPECT_TRUE(contains(rsaKeyData.characteristics.hardwareEnforced, TAG_EARLY_BOOT_ONLY));
+ EXPECT_TRUE(contains(ecdsaKeyData.characteristics.hardwareEnforced, 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
+ Return<ErrorCode> earlyBootResult = keymaster().earlyBootEnded();
+ EXPECT_TRUE(earlyBootResult.isOk());
+ 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));
+
+ CheckedDeleteKeyData(&aesKeyData);
+ CheckedDeleteKeyData(&hmacKeyData);
+ CheckedDeleteKeyData(&rsaKeyData);
+ CheckedDeleteKeyData(&ecdsaKeyData);
+
+ // Should not be able to create new keys
+ std::tie(aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData) =
+ CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::EARLY_BOOT_ENDED);
+
+ CheckedDeleteKeyData(&aesKeyData);
+ CheckedDeleteKeyData(&hmacKeyData);
+ CheckedDeleteKeyData(&rsaKeyData);
+ CheckedDeleteKeyData(&ecdsaKeyData);
+}
+
+INSTANTIATE_KEYMASTER_4_1_HIDL_TEST(EarlyBootKeyTest);
} // namespace android::hardware::keymaster::V4_1::test
diff --git a/keymaster/4.1/vts/functional/Keymaster4_1HidlTest.cpp b/keymaster/4.1/vts/functional/Keymaster4_1HidlTest.cpp
new file mode 100644
index 0000000..efedf28
--- /dev/null
+++ b/keymaster/4.1/vts/functional/Keymaster4_1HidlTest.cpp
@@ -0,0 +1,59 @@
+/*
+ * 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 "Keymaster4_1HidlTest.h"
+
+namespace android::hardware::keymaster::V4_1::test {
+
+using std::string;
+
+void Keymaster4_1HidlTest::SetUp() {
+ keymaster41_ = IKeymasterDevice::getService(GetParam());
+ InitializeKeymaster(keymaster41_);
+}
+
+auto Keymaster4_1HidlTest::ProcessMessage(const HidlBuf& key_blob, KeyPurpose operation,
+ const string& message, const AuthorizationSet& in_params)
+ -> std::tuple<ErrorCode, string, AuthorizationSet /* out_params */> {
+ AuthorizationSet begin_out_params;
+ V4_0::ErrorCode result = Begin(operation, key_blob, in_params, &begin_out_params, &op_handle_);
+ AuthorizationSet out_params(std::move(begin_out_params));
+ if (result != V4_0::ErrorCode::OK) {
+ return {convert(result), {}, out_params};
+ }
+
+ string output;
+ size_t consumed = 0;
+ AuthorizationSet update_params;
+ AuthorizationSet update_out_params;
+ result = Update(op_handle_, update_params, message, &update_out_params, &output, &consumed);
+ out_params.push_back(update_out_params);
+ if (result != V4_0::ErrorCode::OK) {
+ return {convert(result), output, out_params};
+ }
+
+ string unused;
+ AuthorizationSet finish_params;
+ AuthorizationSet finish_out_params;
+ result = Finish(op_handle_, finish_params, message.substr(consumed), unused, &finish_out_params,
+ &output);
+ op_handle_ = V4_0::test::kOpHandleSentinel;
+ out_params.push_back(finish_out_params);
+
+ return {convert(result), output, out_params};
+}
+
+} // namespace android::hardware::keymaster::V4_1::test
diff --git a/keymaster/4.1/vts/functional/Keymaster4_1HidlTest.h b/keymaster/4.1/vts/functional/Keymaster4_1HidlTest.h
new file mode 100644
index 0000000..6332c43
--- /dev/null
+++ b/keymaster/4.1/vts/functional/Keymaster4_1HidlTest.h
@@ -0,0 +1,158 @@
+/*
+ * 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 <android/hardware/keymaster/4.1/IKeymasterDevice.h>
+
+#include <KeymasterHidlTest.h>
+#include <keymasterV4_1/authorization_set.h>
+
+namespace android::hardware::keymaster::V4_1::test {
+
+using V4_0::test::HidlBuf;
+
+class Keymaster4_1HidlTest : public V4_0::test::KeymasterHidlTest {
+ public:
+ using super = V4_0::test::KeymasterHidlTest;
+
+ ErrorCode convert(V4_0::ErrorCode error_code) { return static_cast<ErrorCode>(error_code); }
+
+ // These methods hide the base class versions.
+ void SetUp();
+ IKeymasterDevice& keymaster() { return *keymaster41_; };
+
+ struct KeyData {
+ HidlBuf blob;
+ KeyCharacteristics characteristics;
+ };
+
+ std::tuple<ErrorCode, KeyData> GenerateKeyData(const AuthorizationSet& keyDescription) {
+ KeyData keyData;
+ ErrorCode errorCode = convert(
+ super::GenerateKey(keyDescription, &keyData.blob, &keyData.characteristics));
+ return {errorCode, keyData};
+ }
+
+ void CheckedDeleteKeyData(KeyData* keyData) { CheckedDeleteKey(&keyData->blob); }
+
+ template <typename TagType>
+ std::tuple<KeyData /* aesKey */, KeyData /* hmacKey */, KeyData /* rsaKey */,
+ KeyData /* ecdsaKey */>
+ CreateTestKeys(TagType tagToTest, ErrorCode expectedReturn) {
+ ErrorCode errorCode;
+
+ /* AES */
+ KeyData aesKeyData;
+ std::tie(errorCode, aesKeyData) =
+ GenerateKeyData(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(tagToTest)
+ .BlockMode(BlockMode::ECB)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_NO_AUTH_REQUIRED));
+ EXPECT_EQ(expectedReturn, errorCode);
+
+ /* HMAC */
+ KeyData hmacKeyData;
+ std::tie(errorCode, hmacKeyData) =
+ GenerateKeyData(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Authorization(tagToTest)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)
+ .Authorization(TAG_NO_AUTH_REQUIRED));
+ EXPECT_EQ(expectedReturn, errorCode);
+
+ /* RSA */
+ KeyData rsaKeyData;
+ std::tie(errorCode, rsaKeyData) =
+ GenerateKeyData(AuthorizationSetBuilder()
+ .RsaSigningKey(2048, 65537)
+ .Authorization(tagToTest)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_NO_AUTH_REQUIRED));
+ EXPECT_EQ(expectedReturn, errorCode);
+
+ /* ECDSA */
+ KeyData ecdsaKeyData;
+ std::tie(errorCode, ecdsaKeyData) =
+ GenerateKeyData(AuthorizationSetBuilder()
+ .EcdsaSigningKey(256)
+ .Authorization(tagToTest)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_NO_AUTH_REQUIRED));
+ EXPECT_EQ(expectedReturn, errorCode);
+
+ return {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData};
+ }
+
+ std::tuple<ErrorCode, std::string /* processedMessage */, AuthorizationSet /* out_params */>
+ ProcessMessage(const HidlBuf& key_blob, KeyPurpose operation, const std::string& message,
+ const AuthorizationSet& in_params);
+
+ ErrorCode UseAesKey(const HidlBuf& aesKeyBlob) {
+ auto [result, ciphertext, out_params] = ProcessMessage(
+ aesKeyBlob, KeyPurpose::ENCRYPT, "1234567890123456",
+ AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE));
+ return result;
+ }
+
+ ErrorCode UseHmacKey(const HidlBuf& hmacKeyBlob) {
+ auto [result, mac, out_params] =
+ ProcessMessage(hmacKeyBlob, KeyPurpose::SIGN, "1234567890123456",
+ AuthorizationSetBuilder().Authorization(TAG_MAC_LENGTH, 128));
+ return result;
+ }
+
+ ErrorCode UseRsaKey(const HidlBuf& 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 UseEcdsaKey(const HidlBuf& ecdsaKeyBlob) {
+ auto [result, signature, out_params] =
+ ProcessMessage(ecdsaKeyBlob, KeyPurpose::SIGN, "a",
+ AuthorizationSetBuilder().Digest(Digest::SHA_2_256));
+ return result;
+ }
+
+ static std::vector<std::string> build_params() {
+ auto params = android::hardware::getAllHalInstanceNames(IKeymasterDevice::descriptor);
+ return params;
+ }
+
+ private:
+ sp<IKeymasterDevice> keymaster41_;
+};
+
+template <typename TypedTag>
+bool contains(hidl_vec<KeyParameter>& set, TypedTag typedTag) {
+ return std::find_if(set.begin(), set.end(), [&](const KeyParameter& param) {
+ return param.tag == static_cast<V4_0::Tag>(typedTag);
+ }) != set.end();
+}
+
+#define INSTANTIATE_KEYMASTER_4_1_HIDL_TEST(name) \
+ INSTANTIATE_TEST_SUITE_P(PerInstance, name, \
+ testing::ValuesIn(Keymaster4_1HidlTest::build_params()), \
+ android::hardware::PrintInstanceNameToString)
+
+} // namespace android::hardware::keymaster::V4_1::test
diff --git a/keymaster/4.1/vts/functional/UnlockedDeviceRequiredTest.cpp b/keymaster/4.1/vts/functional/UnlockedDeviceRequiredTest.cpp
new file mode 100644
index 0000000..671bbbf
--- /dev/null
+++ b/keymaster/4.1/vts/functional/UnlockedDeviceRequiredTest.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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 "Keymaster4_1HidlTest.h"
+
+#include <keymasterV4_1/authorization_set.h>
+
+namespace android::hardware::keymaster::V4_1::test {
+
+using UnlockedDeviceRequiredTest = Keymaster4_1HidlTest;
+
+// 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.
+//
+// TODO(swillden): Use the Gatekeeper HAL to enroll some test credentials which we can verify to get
+// an unlock auth token. If that works, enable the improved test.
+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));
+
+ Return<ErrorCode> rc =
+ keymaster().deviceLocked(false /* passwordOnly */, {} /* verificationToken */);
+ ASSERT_TRUE(rc.isOk());
+ ASSERT_EQ(ErrorCode::OK, static_cast<ErrorCode>(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));
+
+ CheckedDeleteKeyData(&aesKeyData);
+ CheckedDeleteKeyData(&hmacKeyData);
+ CheckedDeleteKeyData(&rsaKeyData);
+ CheckedDeleteKeyData(&ecdsaKeyData);
+}
+
+INSTANTIATE_KEYMASTER_4_1_HIDL_TEST(UnlockedDeviceRequiredTest);
+
+} // namespace android::hardware::keymaster::V4_1::test
diff --git a/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
index 4909214..599fd1d 100644
--- a/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
@@ -272,7 +272,7 @@
int n;
std::tie(n, outputShapes, timing, std::ignore) =
controller->compute(request, testConfig.measureTiming, keys);
- executionStatus = nn::convertResultCodeToErrorStatus(n);
+ executionStatus = nn::convertToV1_0(nn::convertResultCodeToErrorStatus(n));
break;
}
diff --git a/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp b/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp
index 416744f..ec9629b 100644
--- a/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp
+++ b/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp
@@ -296,7 +296,8 @@
// collect serialized result by running regular burst
const auto [nRegular, outputShapesRegular, timingRegular, fallbackRegular] =
controllerRegular->compute(request, MeasureTiming::NO, keys);
- const ErrorStatus statusRegular = nn::convertResultCodeToErrorStatus(nRegular);
+ const ErrorStatus statusRegular =
+ nn::convertToV1_0(nn::convertResultCodeToErrorStatus(nRegular));
EXPECT_FALSE(fallbackRegular);
// skip test if regular burst output isn't useful for testing a failure
@@ -312,7 +313,7 @@
// large enough to return the serialized result
const auto [nSmall, outputShapesSmall, timingSmall, fallbackSmall] =
controllerSmall->compute(request, MeasureTiming::NO, keys);
- const ErrorStatus statusSmall = nn::convertResultCodeToErrorStatus(nSmall);
+ const ErrorStatus statusSmall = nn::convertToV1_0(nn::convertResultCodeToErrorStatus(nSmall));
EXPECT_NE(ErrorStatus::NONE, statusSmall);
EXPECT_EQ(0u, outputShapesSmall.size());
EXPECT_TRUE(badTiming(timingSmall));
diff --git a/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
index 2d83b81..7b5ff9b 100644
--- a/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
+++ b/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
@@ -107,7 +107,7 @@
// execute and verify
const auto [n, outputShapes, timing, fallback] = burst->compute(request, measure, keys);
- const ErrorStatus status = nn::convertResultCodeToErrorStatus(n);
+ const ErrorStatus status = nn::convertToV1_0(nn::convertResultCodeToErrorStatus(n));
EXPECT_EQ(ErrorStatus::INVALID_ARGUMENT, status);
EXPECT_EQ(outputShapes.size(), 0);
EXPECT_TRUE(badTiming(timing));
diff --git a/neuralnetworks/1.3/Android.bp b/neuralnetworks/1.3/Android.bp
index 08e824d..56011e2 100644
--- a/neuralnetworks/1.3/Android.bp
+++ b/neuralnetworks/1.3/Android.bp
@@ -10,6 +10,7 @@
"types.hal",
"IBuffer.hal",
"IDevice.hal",
+ "IExecutionCallback.hal",
"IPreparedModel.hal",
"IPreparedModelCallback.hal",
],
diff --git a/neuralnetworks/1.3/IBuffer.hal b/neuralnetworks/1.3/IBuffer.hal
index 84241c5..dfc57fe 100644
--- a/neuralnetworks/1.3/IBuffer.hal
+++ b/neuralnetworks/1.3/IBuffer.hal
@@ -16,7 +16,7 @@
package android.hardware.neuralnetworks@1.3;
-import @1.0::ErrorStatus;
+import ErrorStatus;
/**
* This interface represents a device memory buffer.
diff --git a/neuralnetworks/1.3/IDevice.hal b/neuralnetworks/1.3/IDevice.hal
index 9afd778..610db79 100644
--- a/neuralnetworks/1.3/IDevice.hal
+++ b/neuralnetworks/1.3/IDevice.hal
@@ -16,7 +16,6 @@
package android.hardware.neuralnetworks@1.3;
-import @1.0::ErrorStatus;
import @1.1::ExecutionPreference;
import @1.2::Constant;
import @1.2::DeviceType;
@@ -25,7 +24,10 @@
import BufferDesc;
import BufferRole;
import Capabilities;
+import ErrorStatus;
import Model;
+import OptionalTimePoint;
+import Priority;
import IBuffer;
import IPreparedModel;
import IPreparedModelCallback;
@@ -46,11 +48,29 @@
getCapabilities_1_3() generates (ErrorStatus status, Capabilities capabilities);
/**
+ * Returns whether the device is able to complete or abort a task within a
+ * specified duration.
+ *
+ * @return prepareModelDeadline 'true' if the device supports completing or
+ * aborting model preparation by the deadline when the deadline is supplied,
+ * 'false' otherwise.
+ * @return executionDeadline 'true' if the device supports completing or
+ * aborting an execution by the deadline when the deadline is supplied,
+ * 'false' otherwise.
+ */
+ supportsDeadlines() generates (bool prepareModelDeadline, bool executionDeadline);
+
+ /**
* Gets the supported operations in a model.
*
- * getSupportedOperations indicates which operations of a model are fully
- * supported by the vendor driver. If an operation may not be supported for
- * any reason, getSupportedOperations must return false for that operation.
+ * getSupportedOperations indicates which operations of the top-level
+ * subgraph are fully supported by the vendor driver. If an operation may
+ * not be supported for any reason, getSupportedOperations must return
+ * false for that operation.
+ *
+ * The {@link OperationType::IF} and {@link OperationType::WHILE}
+ * operations may only be fully supported if the vendor driver fully
+ * supports all operations in the referenced subgraphs.
*
* @param model A model whose operations--and their corresponding operands--
* are to be verified by the driver.
@@ -113,6 +133,22 @@
* the callback object must be invoked with the appropriate ErrorStatus
* value and nullptr for the IPreparedModel.
*
+ * The model is prepared with a priority. This priority is relative to other
+ * prepared models owned by the same client. Higher priority executions may
+ * use more compute resources than lower priority executions, and may
+ * preempt or starve lower priority executions.
+ *
+ * prepareModel_1_3 can be called with an optional deadline. If the model
+ * is not able to be prepared before the provided deadline, the model
+ * preparation must be aborted, and either {@link
+ * ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
+ * ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The error due
+ * to an abort must be sent the same way as other errors, described above.
+ * If the service reports that it does not support preparation deadlines via
+ * IDevice::supportsDeadlines, and prepareModel_1_3 is called with a
+ * deadline, then the argument is invalid, and {@link
+ * ErrorStatus::INVALID_ARGUMENT} must be returned.
+ *
* Optionally, the driver may save the prepared model to cache during the
* asynchronous preparation. Any error that occurs when saving to cache must
* not affect the status of preparing the model. Even if the input arguments
@@ -134,6 +170,11 @@
* @param model The model to be prepared for execution.
* @param preference Indicates the intended execution behavior of a prepared
* model.
+ * @param priority The priority of the prepared model relative to other
+ * prepared models owned by the client.
+ * @param deadline The time by which the model must be prepared. If the
+ * model cannot be prepared by the deadline, the preparation must be
+ * aborted.
* @param modelCache A vector of handles with each entry holding exactly one
* cache file descriptor for the security-sensitive cache. The length of
* the vector must either be 0 indicating that caching information is
@@ -168,8 +209,12 @@
* - GENERAL_FAILURE if there is an unspecified error
* - INVALID_ARGUMENT if one of the input arguments related to preparing
* the model is invalid
+ * - MISSED_DEADLINE_* if the deadline for preparing a model cannot be
+ * met
+ * - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
*/
prepareModel_1_3(Model model, ExecutionPreference preference,
+ Priority priority, OptionalTimePoint deadline,
vec<handle> modelCache, vec<handle> dataCache,
uint8_t[Constant:BYTE_SIZE_OF_CACHE_TOKEN] token,
IPreparedModelCallback callback)
@@ -215,6 +260,22 @@
* the model, the callback object must be invoked with the appropriate
* ErrorStatus value and nullptr for the IPreparedModel.
*
+ * The model is prepared with a priority. This priority is relative to other
+ * prepared models owned by the same client. Higher priority executions may
+ * use more compute resources than lower priority executions, and may
+ * preempt or starve lower priority executions.
+ *
+ * prepareModelFromCache_1_3 can be called with an optional deadline. If the
+ * model is not able to prepared before the provided deadline, the model
+ * preparation must be aborted, and either {@link
+ * ErrorStatus::MISSED_DEADLINE_TRANSIENT}
+ * or {@link ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The
+ * error due to an abort must be sent the same way as other errors,
+ * described above. If the service reports that it does not support
+ * preparation deadlines via IDevice::supportsDeadlines, and
+ * prepareModelFromCache_1_3 is called with a deadline, then the argument is
+ * invalid, and {@link ErrorStatus::INVALID_ARGUMENT} must be returned.
+ *
* The only information that may be unknown to the model at this stage is
* the shape of the tensors, which may only be known at execution time. As
* such, some driver services may return partially prepared models, where
@@ -223,6 +284,11 @@
* used with different shapes of inputs on different (possibly concurrent)
* executions.
*
+ * @param priority The priority of the prepared model relative to other
+ * prepared models owned by the client.
+ * @param deadline The time by which the model must be prepared. If the
+ * model cannot be prepared by the deadline, the preparation must be
+ * aborted.
* @param modelCache A vector of handles with each entry holding exactly one
* cache file descriptor for the security-sensitive cache. The length of
* the vector must match the numModelCache returned from getNumberOfCacheFilesNeeded.
@@ -248,8 +314,12 @@
* - GENERAL_FAILURE if caching is not supported or if there is an
* unspecified error
* - INVALID_ARGUMENT if one of the input arguments is invalid
+ * - MISSED_DEADLINE_* if the deadline for preparing a model cannot be
+ * met
+ * - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
*/
- prepareModelFromCache_1_3(vec<handle> modelCache, vec<handle> dataCache,
+ prepareModelFromCache_1_3(Priority priority, OptionalTimePoint deadline,
+ vec<handle> modelCache, vec<handle> dataCache,
uint8_t[Constant:BYTE_SIZE_OF_CACHE_TOKEN] token,
IPreparedModelCallback callback)
generates (ErrorStatus status);
diff --git a/neuralnetworks/1.3/IExecutionCallback.hal b/neuralnetworks/1.3/IExecutionCallback.hal
new file mode 100644
index 0000000..439428a
--- /dev/null
+++ b/neuralnetworks/1.3/IExecutionCallback.hal
@@ -0,0 +1,64 @@
+/*
+ * 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.neuralnetworks@1.3;
+
+import @1.2::IExecutionCallback;
+import @1.2::OutputShape;
+import @1.2::Timing;
+
+/**
+ * IExecutionCallback must be used to return the error status result from an
+ * execution asynchronously launched from IPreparedModel::execute*.
+ */
+interface IExecutionCallback extends @1.2::IExecutionCallback {
+
+ /**
+ * There are three notify methods declared for the IExecutionCallback
+ * interface: notify_1_3, notify_1_2, and notify. One of the three notify
+ * methods must be invoked immediately after the asynchronous task has
+ * finished performing the execution. One of the notify methods must be
+ * provided with the ErrorStatus from the execution. If the asynchronous
+ * task is not launched, one of the notify methods must be invoked with the
+ * appropriate error.
+ *
+ * @param status Error status returned from launching the asynchronous task
+ * (if the launch fails) or from the asynchronous task itself
+ * (if the launch succeeds). Must be:
+ * - NONE if the asynchronous execution was successful
+ * - DEVICE_UNAVAILABLE if driver is offline or busy
+ * - GENERAL_FAILURE if the asynchronous task resulted in an
+ * unspecified error
+ * - OUTPUT_INSUFFICIENT_SIZE if at least one output
+ * operand buffer is not large enough to store the
+ * corresponding output
+ * - INVALID_ARGUMENT if one of the input arguments to
+ * prepareModel is invalid
+ * - MISSED_DEADLINE_* if the deadline could not be met
+ * - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
+ * @param outputShapes A list of shape information of model output operands.
+ * The index into "outputShapes" corresponds with to index
+ * of the output operand in the Request outputs vector.
+ * outputShapes must be empty unless the status is either
+ * NONE or OUTPUT_INSUFFICIENT_SIZE.
+ * @param timing Duration of execution. Unless MeasureTiming::YES was passed when
+ * launching the execution and status is NONE, all times must
+ * be reported as UINT64_MAX. A driver may choose to report
+ * any time as UINT64_MAX, indicating that particular measurement is
+ * not available.
+ */
+ oneway notify_1_3(ErrorStatus status, vec<OutputShape> outputShapes, Timing timing);
+};
diff --git a/neuralnetworks/1.3/IPreparedModel.hal b/neuralnetworks/1.3/IPreparedModel.hal
index 00adc1f..bce6ee2 100644
--- a/neuralnetworks/1.3/IPreparedModel.hal
+++ b/neuralnetworks/1.3/IPreparedModel.hal
@@ -16,13 +16,14 @@
package android.hardware.neuralnetworks@1.3;
-import @1.0::ErrorStatus;
-import @1.2::IExecutionCallback;
import @1.2::IPreparedModel;
import @1.2::MeasureTiming;
import @1.2::OutputShape;
import @1.2::Timing;
+import ErrorStatus;
+import OptionalTimePoint;
import Request;
+import IExecutionCallback;
/**
* IPreparedModel describes a model that has been prepared for execution and
@@ -65,6 +66,17 @@
* values, the execution should complete successfully (ErrorStatus::NONE):
* There must be no failure unless the device itself is in a bad state.
*
+ * execute_1_3 can be called with an optional deadline. If the execution
+ * is not able to completed before the provided deadline, the execution
+ * must be aborted, and either {@link
+ * ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
+ * ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The error due
+ * to an abort must be sent the same way as other errors, described above.
+ * If the service reports that it does not support execution deadlines via
+ * IDevice::supportsDeadlines, and execute_1_3 is called with a deadline,
+ * then the argument is invalid, and {@link ErrorStatus::INVALID_ARGUMENT}
+ * must be returned.
+ *
* Any number of calls to the execute* and executeSynchronously* functions,
* in any combination, may be made concurrently, even on the same
* IPreparedModel object.
@@ -75,6 +87,9 @@
* The duration runs from the time the driver sees the call
* to the execute_1_3 function to the time the driver invokes
* the callback.
+ * @param deadline The time by which execution must complete. If the
+ * execution cannot be finished by the deadline, the
+ * execution must be aborted.
* @param callback A callback object used to return the error status of
* the execution. The callback object's notify function must
* be called exactly once, even if the execution was
@@ -87,8 +102,13 @@
* not large enough to store the resultant values
* - INVALID_ARGUMENT if one of the input arguments is
* invalid
+ * - MISSED_DEADLINE_* if the deadline for executing a model
+ * cannot be met
+ * - RESOURCE_EXHAUSTED_* if the task was aborted by the
+ * driver
*/
- execute_1_3(Request request, MeasureTiming measure, IExecutionCallback callback)
+ execute_1_3(Request request, MeasureTiming measure, OptionalTimePoint deadline,
+ IExecutionCallback callback)
generates (ErrorStatus status);
/**
@@ -116,6 +136,17 @@
* (ErrorStatus::NONE): There must be no failure unless the device itself is
* in a bad state.
*
+ * executeSynchronously_1_3 can be called with an optional deadline. If the
+ * execution is not able to completed before the provided deadline, the
+ * execution must be aborted, and either {@link
+ * ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
+ * ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The error due
+ * to an abort must be sent the same way as other errors, described above.
+ * If the service reports that it does not support execution deadlines via
+ * IDevice::supportsDeadlines, and executeSynchronously_1_3 is called with a
+ * deadline, then the argument is invalid, and
+ * {@link ErrorStatus::INVALID_ARGUMENT} must be returned.
+ *
* Any number of calls to the execute* and executeSynchronously* functions,
* in any combination, may be made concurrently, even on the same
* IPreparedModel object.
@@ -126,6 +157,9 @@
* The duration runs from the time the driver sees the call
* to the executeSynchronously_1_3 function to the time the driver
* returns from the function.
+ * @param deadline The time by which execution must complete. If the
+ * execution cannot be finished by the deadline, the
+ * execution must be aborted.
* @return status Error status of the execution, must be:
* - NONE if execution is performed successfully
* - DEVICE_UNAVAILABLE if driver is offline or busy
@@ -135,16 +169,22 @@
* corresponding output
* - INVALID_ARGUMENT if one of the input arguments is
* invalid
+ * - MISSED_DEADLINE_* if the deadline for executing a model
+ * cannot be met
+ * - RESOURCE_EXHAUSTED_* if the task was aborted by the
+ * driver
* @return outputShapes A list of shape information of model output operands.
* The index into "outputShapes" corresponds to the index
* of the output operand in the Request outputs vector.
* outputShapes must be empty unless the status is either
* NONE or OUTPUT_INSUFFICIENT_SIZE.
- * @return Timing Duration of execution. Unless measure is YES and status is
+ * @return timing Duration of execution. Unless measure is YES and status is
* NONE, all times must be reported as UINT64_MAX. A driver may
* choose to report any time as UINT64_MAX, indicating that
* measurement is not available.
*/
- executeSynchronously_1_3(Request request, MeasureTiming measure)
- generates (ErrorStatus status, vec<OutputShape> outputShapes, Timing timing);
+ executeSynchronously_1_3(Request request, MeasureTiming measure,
+ OptionalTimePoint deadline)
+ generates (ErrorStatus status, vec<OutputShape> outputShapes,
+ Timing timing);
};
diff --git a/neuralnetworks/1.3/IPreparedModelCallback.hal b/neuralnetworks/1.3/IPreparedModelCallback.hal
index ff295a2..11ebbf4 100644
--- a/neuralnetworks/1.3/IPreparedModelCallback.hal
+++ b/neuralnetworks/1.3/IPreparedModelCallback.hal
@@ -16,7 +16,6 @@
package android.hardware.neuralnetworks@1.3;
-import @1.0::ErrorStatus;
import @1.2::IPreparedModelCallback;
import IPreparedModel;
@@ -48,6 +47,10 @@
* unspecified error
* - INVALID_ARGUMENT if one of the input arguments to
* prepareModel is invalid
+ * - MISSED_DEADLINE_* if the deadline for executing a model
+ * cannot be met
+ * - RESOURCE_EXHAUSTED_* if the task was aborted by the
+ * driver
* @param preparedModel A model that has been asynchronously prepared for
* execution. If the model was unable to be prepared
* due to an error, nullptr must be passed in place of
diff --git a/neuralnetworks/1.3/types.hal b/neuralnetworks/1.3/types.hal
index 6c8fe43..b330b50 100644
--- a/neuralnetworks/1.3/types.hal
+++ b/neuralnetworks/1.3/types.hal
@@ -17,9 +17,11 @@
package android.hardware.neuralnetworks@1.3;
import @1.0::DataLocation;
-import @1.0::OperandLifeTime;
+import @1.0::ErrorStatus;
import @1.0::PerformanceInfo;
import @1.0::RequestArgument;
+import @1.2::Model.ExtensionNameAndPrefix;
+import @1.2::Model.ExtensionTypeEncoding;
import @1.2::OperandType;
import @1.2::OperationType;
import @1.2::SymmPerChannelQuantParams;
@@ -40,6 +42,13 @@
*/
TENSOR_QUANT8_ASYMM_SIGNED = 14,
+ /**
+ * A reference to a subgraph.
+ *
+ * Must have the lifetime {@link OperandLifeTime::SUBGRAPH}.
+ */
+ SUBGRAPH = 15,
+
/*
* DEPRECATED. Since HAL version 1.2, extensions are the preferred
* alternative to OEM operation and data types.
@@ -68,7 +77,7 @@
enum OperandTypeRange : uint32_t {
BASE_MIN = 0,
FUNDAMENTAL_MIN = 0,
- FUNDAMENTAL_MAX = 14,
+ FUNDAMENTAL_MAX = 15,
OEM_MIN = 10000,
OEM_MAX = 10001,
BASE_MAX = 0xFFFF,
@@ -4877,6 +4886,92 @@
QUANTIZED_LSTM = 95,
/**
+ * Executes one of the two referenced subgraphs as determined by a boolean
+ * value.
+ *
+ * The inputs and outputs of the two referenced subgraphs must agree with the
+ * signature of this operation. That is, if the operation has (3 + n) inputs
+ * and m outputs, both subgraphs must have n inputs and m outputs with the same
+ * types as the corresponding operation inputs and outputs.
+ *
+ * Inputs:
+ * * 0: A value of type {@link OperandType::TENSOR_BOOL8} and shape [1]
+ * that determines which of the two referenced subgraphs to execute.
+ * * 1: A {@link OperandType::SUBGRAPH} reference to the subgraph to be
+ * executed if the condition is true.
+ * * 2: A {@link OperandType::SUBGRAPH} reference to the subgraph to be
+ * executed if the condition is false.
+ * * 3 ~ (n + 2): Inputs to be passed to the subgraph selected for execution.
+ *
+ * Outputs:
+ * * 0 ~ (m - 1): Outputs produced by the selected subgraph.
+ */
+ IF = 96,
+
+ /**
+ * Executes the body subgraph until the condition subgraph outputs false.
+ *
+ * The inputs to this operation are the condition subgraph, the body subgraph,
+ * and operand values for the first iteration of the loop. The values are
+ * implicitly split into three groups of input-output, state-only, and
+ * input-only values, as described below.
+ *
+ * The outputs of this operation are the final values of input-output
+ * operands.
+ *
+ * Both the condition and body subgraph receive (m + k + n) inputs.
+ * * The first m (m >= 1) inputs are input-output operands. For the first
+ * iteration, these are initialized from the corresponding inputs of the
+ * WHILE operation. In subsequent iterations, their values come from the
+ * corresponding outputs of the body subgraph produced during the previous
+ * iteration.
+ * * The next k (k >= 0) inputs are state-only operands. They are similar to
+ * the input-output operands, except that their values are no longer
+ * available after the loop terminates.
+ * * The last n (n >= 0) inputs are input-only operands. Their values come
+ * from the corresponding inputs of the WHILE operation.
+ *
+ * The body subgraph produces (m + k) outputs.
+ * * The first m outputs are input-output operands. They become the outputs
+ * of the WHILE operation when a termination condition is reached.
+ * * The last k outputs are state-only operands. Their values are no longer
+ * available after the loop terminates.
+ *
+ * The numbers m, k, and n are inferred by the driver as follows:
+ * m = (WHILE operation output count)
+ * k = (body subgraph output count) - m
+ * n = (body subgraph input count) - m - k
+ *
+ * The pseudo-code below illustrates the flow of a WHILE operation with
+ * inputs condition, body, initial_input_output, initial_state, input_only
+ * (m = 1, k = 1, n = 1):
+ *
+ * input_output = initial_input_output
+ * state = initial_state
+ * while condition(input_output, state, input_only):
+ * input_output, state = body(input_output, state, input_only)
+ * return input_output
+ *
+ * Inputs:
+ * * 0: A {@link OperandType::SUBGRAPH} reference to the condition
+ * subgraph. The subgraph must have (m + k + n) inputs with
+ * the same types as the corresponding inputs of the WHILE operation
+ * and exactly one output of {@link OperandType::TENSOR_BOOL8}
+ * and shape [1].
+ * * 1: A {@link OperandType::SUBGRAPH} reference to the body subgraph.
+ * The subgraph must have (m + k + n) inputs and (m + k) outputs with
+ * the same types as the corresponding inputs and outputs of the WHILE
+ * operation.
+ * * (m inputs): Initial values for input-output operands.
+ * * (k inputs): Initial values for state-only operands.
+ * * (n inputs): Values for input-only operands.
+ *
+ * Outputs:
+ * * 0 ~ (m - 1): Outputs produced by the loop.
+ */
+ WHILE = 97,
+
+ /**
* DEPRECATED. Since NNAPI 1.2, extensions are the preferred alternative to
* OEM operation and data types.
*
@@ -4898,12 +4993,21 @@
enum OperationTypeRange : uint32_t {
BASE_MIN = 0,
FUNDAMENTAL_MIN = 0,
- FUNDAMENTAL_MAX = 95,
+ FUNDAMENTAL_MAX = 97,
OEM_MIN = 10000,
OEM_MAX = 10000,
BASE_MAX = 0xFFFF,
};
+/**
+ * Priority given to a prepared model for execution.
+ */
+enum Priority : int32_t {
+ LOW,
+ MEDIUM,
+ HIGH,
+};
+
/**
* The capabilities of a driver.
@@ -4966,6 +5070,59 @@
};
/**
+ * How an operand is used.
+ */
+enum OperandLifeTime : int32_t {
+ /**
+ * The operand is internal to the model. It's created by an operation and
+ * consumed by other operations. It must be an output operand of
+ * exactly one operation.
+ */
+ TEMPORARY_VARIABLE,
+
+ /**
+ * The operand is an input of a subgraph. It must not be an output
+ * operand of any operation.
+ *
+ * An operand can't be both input and output of a subgraph.
+ */
+ SUBGRAPH_INPUT,
+
+ /**
+ * The operand is an output of a subgraph. It must be an output
+ * operand of exactly one operation.
+ *
+ * An operand can't be both input and output of a subgraph.
+ */
+ SUBGRAPH_OUTPUT,
+
+ /**
+ * The operand is a constant found in Model.operandValues. It must
+ * not be an output operand of any operation.
+ */
+ CONSTANT_COPY,
+
+ /**
+ * The operand is a constant that was specified via a Memory
+ * object. It must not be an output operand of any operation.
+ */
+ CONSTANT_REFERENCE,
+
+ /**
+ * The operand does not have a value. This is valid only for optional
+ * arguments of operations.
+ */
+ NO_VALUE,
+
+ /**
+ * The operand is a reference to a subgraph. It must be an input to one
+ * or more {@link OperationType::IF} or {@link OperationType::WHILE}
+ * operations.
+ */
+ SUBGRAPH,
+};
+
+/**
* Describes one operand of the model's graph.
*/
struct Operand {
@@ -5001,7 +5158,7 @@
* . The operand has lifetime CONSTANT_COPY or
* CONSTANT_REFERENCE.
*
- * . The operand has lifetime MODEL_INPUT. Fully
+ * . The operand has lifetime SUBGRAPH_INPUT. Fully
* specified dimensions must either be present in the
* Operand or they must be provided in the corresponding
* RequestArgument.
@@ -5049,8 +5206,8 @@
/**
* Where to find the data for this operand.
- * If the lifetime is TEMPORARY_VARIABLE, MODEL_INPUT, MODEL_OUTPUT, or
- * NO_VALUE:
+ * If the lifetime is TEMPORARY_VARIABLE, SUBGRAPH_INPUT, SUBGRAPH_OUTPUT,
+ * or NO_VALUE:
* - All the fields must be 0.
* If the lifetime is CONSTANT_COPY:
* - location.poolIndex is 0.
@@ -5060,6 +5217,11 @@
* - location.poolIndex is set.
* - location.offset is the offset in bytes into the specified pool.
* - location.length is set.
+ * If the lifetime is SUBGRAPH:
+ * - location.poolIndex is 0.
+ * - location.offset is the index of the referenced subgraph in
+ * {@link Model::referenced}.
+ * - location.length is 0.
*/
DataLocation location;
@@ -5098,32 +5260,19 @@
*/
struct Model {
/**
- * All operands included in the model.
+ * The top-level subgraph.
*/
- vec<Operand> operands;
+ Subgraph main;
/**
- * All operations included in the model.
+ * Referenced subgraphs.
*
- * The operations are sorted into execution order. Every operand
- * with lifetime MODEL_OUTPUT or TEMPORARY_VARIABLE must be
- * written before it is read.
- */
- vec<Operation> operations;
-
- /**
- * Input indexes of the model. There must be at least one.
+ * Each subgraph is referenced by the main subgraph or at least one other
+ * referenced subgraph.
*
- * Each value corresponds to the index of the operand in "operands".
+ * There must be no reference cycles.
*/
- vec<uint32_t> inputIndexes;
-
- /**
- * Output indexes of the model. There must be at least one.
- *
- * Each value corresponds to the index of the operand in "operands".
- */
- vec<uint32_t> outputIndexes;
+ vec<Subgraph> referenced;
/**
* A byte buffer containing operand data that were copied into the model.
@@ -5157,9 +5306,9 @@
* {@link OperandTypeRange::BASE_MAX} or
* {@link OperationTypeRange::BASE_MAX} respectively should be interpreted
* as an extension operand. The low
- * {@link Model::ExtensionTypeEncoding::LOW_BITS_TYPE} bits of the value
- * correspond to the type ID within the extension and the high
- * {@link Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX} bits encode
+ * {@link @1.2::Model::ExtensionTypeEncoding::LOW_BITS_TYPE} bits of the
+ * value correspond to the type ID within the extension and the high
+ * {@link @1.2::Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX} bits encode
* the "prefix", which maps uniquely to the extension name.
*
* For example, if a model contains an operation whose value is
@@ -5172,39 +5321,40 @@
* prefix corresponding to each extension name and at most one extension
* name corresponding to each prefix.
*/
- vec<ExtensionNameAndPrefix> extensionNameToPrefix;
+ vec<@1.2::Model.ExtensionNameAndPrefix> extensionNameToPrefix;
+};
+
+/**
+ * An excerpt of the execution graph.
+ */
+struct Subgraph {
+ /**
+ * All operands included in the subgraph.
+ */
+ vec<Operand> operands;
/**
- * A correspondence between an extension name and a prefix of operand and
- * operation type values.
+ * All operations included in the subgraph.
+ *
+ * The operations are sorted into execution order. Every operand
+ * with lifetime SUBGRAPH_OUTPUT or TEMPORARY_VARIABLE must be
+ * written before it is read.
*/
- struct ExtensionNameAndPrefix {
- /**
- * The extension name.
- *
- * See {@link Extension::name} for the format specification.
- */
- string name;
-
- /**
- * The unique extension identifier within the model.
- *
- * See {@link Model::extensionNameToPrefix}.
- */
- uint16_t prefix;
- };
+ vec<Operation> operations;
/**
- * Numeric values of extension operand and operation types have the
- * following structure:
- * - 16 high bits represent the "prefix", which corresponds uniquely to the
- * extension name.
- * - 16 low bits represent the type ID within the extension.
+ * Input indexes of the subgraph. There must be at least one.
+ *
+ * Each value corresponds to the index of the operand in "operands".
*/
- enum ExtensionTypeEncoding : uint8_t {
- HIGH_BITS_PREFIX = 16,
- LOW_BITS_TYPE = 16,
- };
+ vec<uint32_t> inputIndexes;
+
+ /**
+ * Output indexes of the subgraph. There must be at least one.
+ *
+ * Each value corresponds to the index of the operand in "operands".
+ */
+ vec<uint32_t> outputIndexes;
};
/**
@@ -5295,3 +5445,49 @@
*/
vec<MemoryPool> pools;
};
+
+/**
+ * Optional time point of the steady clock (as from std::chrono::steady_clock)
+ * measured in nanoseconds.
+ */
+safe_union OptionalTimePoint {
+ /** No time point provided. */
+ Monostate none;
+
+ /**
+ * Time point of the steady clock (as from std::chrono::steady_clock)
+ * measured in nanoseconds.
+ */
+ uint64_t nanoseconds;
+};
+
+/**
+ * Return status of a function.
+ */
+enum ErrorStatus : @1.0::ErrorStatus {
+ /**
+ * Failure because a deadline could not be met for a task, but future
+ * deadlines may still be met for the same task after a short delay.
+ */
+ MISSED_DEADLINE_TRANSIENT,
+
+ /**
+ * Failure because a deadline could not be met for a task, and future
+ * deadlines will likely also not be met for the same task even after a
+ * short delay.
+ */
+ MISSED_DEADLINE_PERSISTENT,
+
+ /**
+ * Failure because of a resource limitation within the driver, but future
+ * calls for the same task may still succeed after a short delay.
+ */
+ RESOURCE_EXHAUSTED_TRANSIENT,
+
+ /**
+ * Failure because of a resource limitation within the driver, and future
+ * calls for the same task will likely also fail even after a short
+ * delay.
+ */
+ RESOURCE_EXHAUSTED_PERSISTENT,
+};
diff --git a/neuralnetworks/1.3/types.t b/neuralnetworks/1.3/types.t
index b1c72a9..a973923 100644
--- a/neuralnetworks/1.3/types.t
+++ b/neuralnetworks/1.3/types.t
@@ -19,9 +19,11 @@
package android.hardware.neuralnetworks@1.3;
import @1.0::DataLocation;
-import @1.0::OperandLifeTime;
+import @1.0::ErrorStatus;
import @1.0::PerformanceInfo;
import @1.0::RequestArgument;
+import @1.2::Model.ExtensionNameAndPrefix;
+import @1.2::Model.ExtensionTypeEncoding;
import @1.2::OperandType;
import @1.2::OperationType;
import @1.2::SymmPerChannelQuantParams;
@@ -88,6 +90,15 @@
BASE_MAX = 0xFFFF,
};
+/**
+ * Priority given to a prepared model for execution.
+ */
+enum Priority : int32_t {
+ LOW,
+ MEDIUM,
+ HIGH,
+};
+
/**
* The capabilities of a driver.
@@ -150,6 +161,59 @@
};
/**
+ * How an operand is used.
+ */
+enum OperandLifeTime : int32_t {
+ /**
+ * The operand is internal to the model. It's created by an operation and
+ * consumed by other operations. It must be an output operand of
+ * exactly one operation.
+ */
+ TEMPORARY_VARIABLE,
+
+ /**
+ * The operand is an input of a subgraph. It must not be an output
+ * operand of any operation.
+ *
+ * An operand can't be both input and output of a subgraph.
+ */
+ SUBGRAPH_INPUT,
+
+ /**
+ * The operand is an output of a subgraph. It must be an output
+ * operand of exactly one operation.
+ *
+ * An operand can't be both input and output of a subgraph.
+ */
+ SUBGRAPH_OUTPUT,
+
+ /**
+ * The operand is a constant found in Model.operandValues. It must
+ * not be an output operand of any operation.
+ */
+ CONSTANT_COPY,
+
+ /**
+ * The operand is a constant that was specified via a Memory
+ * object. It must not be an output operand of any operation.
+ */
+ CONSTANT_REFERENCE,
+
+ /**
+ * The operand does not have a value. This is valid only for optional
+ * arguments of operations.
+ */
+ NO_VALUE,
+
+ /**
+ * The operand is a reference to a subgraph. It must be an input to one
+ * or more {@link OperationType::IF} or {@link OperationType::WHILE}
+ * operations.
+ */
+ SUBGRAPH,
+};
+
+/**
* Describes one operand of the model's graph.
*/
struct Operand {
@@ -185,7 +249,7 @@
* . The operand has lifetime CONSTANT_COPY or
* CONSTANT_REFERENCE.
*
- * . The operand has lifetime MODEL_INPUT. Fully
+ * . The operand has lifetime SUBGRAPH_INPUT. Fully
* specified dimensions must either be present in the
* Operand or they must be provided in the corresponding
* RequestArgument.
@@ -233,8 +297,8 @@
/**
* Where to find the data for this operand.
- * If the lifetime is TEMPORARY_VARIABLE, MODEL_INPUT, MODEL_OUTPUT, or
- * NO_VALUE:
+ * If the lifetime is TEMPORARY_VARIABLE, SUBGRAPH_INPUT, SUBGRAPH_OUTPUT,
+ * or NO_VALUE:
* - All the fields must be 0.
* If the lifetime is CONSTANT_COPY:
* - location.poolIndex is 0.
@@ -244,6 +308,11 @@
* - location.poolIndex is set.
* - location.offset is the offset in bytes into the specified pool.
* - location.length is set.
+ * If the lifetime is SUBGRAPH:
+ * - location.poolIndex is 0.
+ * - location.offset is the index of the referenced subgraph in
+ * {@link Model::referenced}.
+ * - location.length is 0.
*/
DataLocation location;
@@ -282,32 +351,19 @@
*/
struct Model {
/**
- * All operands included in the model.
+ * The top-level subgraph.
*/
- vec<Operand> operands;
+ Subgraph main;
/**
- * All operations included in the model.
+ * Referenced subgraphs.
*
- * The operations are sorted into execution order. Every operand
- * with lifetime MODEL_OUTPUT or TEMPORARY_VARIABLE must be
- * written before it is read.
- */
- vec<Operation> operations;
-
- /**
- * Input indexes of the model. There must be at least one.
+ * Each subgraph is referenced by the main subgraph or at least one other
+ * referenced subgraph.
*
- * Each value corresponds to the index of the operand in "operands".
+ * There must be no reference cycles.
*/
- vec<uint32_t> inputIndexes;
-
- /**
- * Output indexes of the model. There must be at least one.
- *
- * Each value corresponds to the index of the operand in "operands".
- */
- vec<uint32_t> outputIndexes;
+ vec<Subgraph> referenced;
/**
* A byte buffer containing operand data that were copied into the model.
@@ -341,9 +397,9 @@
* {@link OperandTypeRange::BASE_MAX} or
* {@link OperationTypeRange::BASE_MAX} respectively should be interpreted
* as an extension operand. The low
- * {@link Model::ExtensionTypeEncoding::LOW_BITS_TYPE} bits of the value
- * correspond to the type ID within the extension and the high
- * {@link Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX} bits encode
+ * {@link @1.2::Model::ExtensionTypeEncoding::LOW_BITS_TYPE} bits of the
+ * value correspond to the type ID within the extension and the high
+ * {@link @1.2::Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX} bits encode
* the "prefix", which maps uniquely to the extension name.
*
* For example, if a model contains an operation whose value is
@@ -356,39 +412,40 @@
* prefix corresponding to each extension name and at most one extension
* name corresponding to each prefix.
*/
- vec<ExtensionNameAndPrefix> extensionNameToPrefix;
+ vec<@1.2::Model.ExtensionNameAndPrefix> extensionNameToPrefix;
+};
+
+/**
+ * An excerpt of the execution graph.
+ */
+struct Subgraph {
+ /**
+ * All operands included in the subgraph.
+ */
+ vec<Operand> operands;
/**
- * A correspondence between an extension name and a prefix of operand and
- * operation type values.
+ * All operations included in the subgraph.
+ *
+ * The operations are sorted into execution order. Every operand
+ * with lifetime SUBGRAPH_OUTPUT or TEMPORARY_VARIABLE must be
+ * written before it is read.
*/
- struct ExtensionNameAndPrefix {
- /**
- * The extension name.
- *
- * See {@link Extension::name} for the format specification.
- */
- string name;
-
- /**
- * The unique extension identifier within the model.
- *
- * See {@link Model::extensionNameToPrefix}.
- */
- uint16_t prefix;
- };
+ vec<Operation> operations;
/**
- * Numeric values of extension operand and operation types have the
- * following structure:
- * - 16 high bits represent the "prefix", which corresponds uniquely to the
- * extension name.
- * - 16 low bits represent the type ID within the extension.
+ * Input indexes of the subgraph. There must be at least one.
+ *
+ * Each value corresponds to the index of the operand in "operands".
*/
- enum ExtensionTypeEncoding : uint8_t {
- HIGH_BITS_PREFIX = 16,
- LOW_BITS_TYPE = 16,
- };
+ vec<uint32_t> inputIndexes;
+
+ /**
+ * Output indexes of the subgraph. There must be at least one.
+ *
+ * Each value corresponds to the index of the operand in "operands".
+ */
+ vec<uint32_t> outputIndexes;
};
/**
@@ -479,3 +536,49 @@
*/
vec<MemoryPool> pools;
};
+
+/**
+ * Optional time point of the steady clock (as from std::chrono::steady_clock)
+ * measured in nanoseconds.
+ */
+safe_union OptionalTimePoint {
+ /** No time point provided. */
+ Monostate none;
+
+ /**
+ * Time point of the steady clock (as from std::chrono::steady_clock)
+ * measured in nanoseconds.
+ */
+ uint64_t nanoseconds;
+};
+
+/**
+ * Return status of a function.
+ */
+enum ErrorStatus : @1.0::ErrorStatus {
+ /**
+ * Failure because a deadline could not be met for a task, but future
+ * deadlines may still be met for the same task after a short delay.
+ */
+ MISSED_DEADLINE_TRANSIENT,
+
+ /**
+ * Failure because a deadline could not be met for a task, and future
+ * deadlines will likely also not be met for the same task even after a
+ * short delay.
+ */
+ MISSED_DEADLINE_PERSISTENT,
+
+ /**
+ * Failure because of a resource limitation within the driver, but future
+ * calls for the same task may still succeed after a short delay.
+ */
+ RESOURCE_EXHAUSTED_TRANSIENT,
+
+ /**
+ * Failure because of a resource limitation within the driver, and future
+ * calls for the same task will likely also fail even after a short
+ * delay.
+ */
+ RESOURCE_EXHAUSTED_PERSISTENT,
+};
diff --git a/neuralnetworks/1.3/vts/functional/Android.bp b/neuralnetworks/1.3/vts/functional/Android.bp
index e2795de..ce2d3a9 100644
--- a/neuralnetworks/1.3/vts/functional/Android.bp
+++ b/neuralnetworks/1.3/vts/functional/Android.bp
@@ -15,11 +15,12 @@
//
cc_library_static {
- name: "VtsHalNeuralNetworksV1_3Callbacks",
+ name: "VtsHalNeuralNetworksV1_3_utils",
defaults: ["VtsHalTargetTestDefaults"],
export_include_dirs: ["include"],
srcs: [
"Callbacks.cpp",
+ "Utils.cpp",
],
static_libs: [
"android.hardware.neuralnetworks@1.0",
@@ -29,7 +30,7 @@
],
header_libs: [
"libbase_headers",
- ]
+ ],
}
cc_test {
@@ -39,6 +40,7 @@
"BasicTests.cpp",
"CompilationCachingTests.cpp",
"GeneratedTestHarness.cpp",
+ "QualityOfServiceTests.cpp",
"TestAssertions.cpp",
"ValidateBurst.cpp",
"ValidateModel.cpp",
@@ -50,6 +52,9 @@
"libnativewindow",
],
static_libs: [
+ "VtsHalNeuralNetworksV1_0_utils",
+ "VtsHalNeuralNetworksV1_2Callbacks",
+ "VtsHalNeuralNetworksV1_3_utils",
"android.hardware.neuralnetworks@1.0",
"android.hardware.neuralnetworks@1.1",
"android.hardware.neuralnetworks@1.2",
@@ -60,9 +65,6 @@
"libhidlmemory",
"libneuralnetworks_generated_test_harness",
"libneuralnetworks_utils",
- "VtsHalNeuralNetworksV1_0_utils",
- "VtsHalNeuralNetworksV1_2Callbacks",
- "VtsHalNeuralNetworksV1_3Callbacks",
],
whole_static_libs: [
"neuralnetworks_generated_V1_0_example",
diff --git a/neuralnetworks/1.3/vts/functional/BasicTests.cpp b/neuralnetworks/1.3/vts/functional/BasicTests.cpp
index b64dc2f..891850c 100644
--- a/neuralnetworks/1.3/vts/functional/BasicTests.cpp
+++ b/neuralnetworks/1.3/vts/functional/BasicTests.cpp
@@ -21,7 +21,6 @@
namespace android::hardware::neuralnetworks::V1_3::vts::functional {
using V1_0::DeviceStatus;
-using V1_0::ErrorStatus;
using V1_0::PerformanceInfo;
using V1_2::Constant;
using V1_2::DeviceType;
diff --git a/neuralnetworks/1.3/vts/functional/Callbacks.cpp b/neuralnetworks/1.3/vts/functional/Callbacks.cpp
index 4f08e72..5768e37 100644
--- a/neuralnetworks/1.3/vts/functional/Callbacks.cpp
+++ b/neuralnetworks/1.3/vts/functional/Callbacks.cpp
@@ -24,12 +24,16 @@
namespace android::hardware::neuralnetworks::V1_3::implementation {
-using V1_0::ErrorStatus;
+using V1_2::OutputShape;
+using V1_2::Timing;
+
+constexpr Timing kNoTiming = {.timeOnDevice = std::numeric_limits<uint64_t>::max(),
+ .timeInDriver = std::numeric_limits<uint64_t>::max()};
// PreparedModelCallback methods begin here
-Return<void> PreparedModelCallback::notify(ErrorStatus errorStatus,
- const sp<V1_0::IPreparedModel>& preparedModel) {
+Return<void> PreparedModelCallback::notifyInternal(ErrorStatus errorStatus,
+ const sp<V1_0::IPreparedModel>& preparedModel) {
{
std::lock_guard<std::mutex> hold(mMutex);
@@ -48,14 +52,19 @@
return Void();
}
-Return<void> PreparedModelCallback::notify_1_2(ErrorStatus errorStatus,
- const sp<V1_2::IPreparedModel>& preparedModel) {
- return notify(errorStatus, preparedModel);
+Return<void> PreparedModelCallback::notify(V1_0::ErrorStatus errorStatus,
+ const sp<V1_0::IPreparedModel>& preparedModel) {
+ return notifyInternal(static_cast<ErrorStatus>(errorStatus), preparedModel);
}
-Return<void> PreparedModelCallback::notify_1_3(ErrorStatus errorStatus,
+Return<void> PreparedModelCallback::notify_1_2(V1_0::ErrorStatus errorStatus,
+ const sp<V1_2::IPreparedModel>& preparedModel) {
+ return notifyInternal(static_cast<ErrorStatus>(errorStatus), preparedModel);
+}
+
+Return<void> PreparedModelCallback::notify_1_3(V1_3::ErrorStatus errorStatus,
const sp<V1_3::IPreparedModel>& preparedModel) {
- return notify(errorStatus, preparedModel);
+ return notifyInternal(errorStatus, preparedModel);
}
void PreparedModelCallback::wait() const {
@@ -73,4 +82,82 @@
return mPreparedModel;
}
+// ExecutionCallback methods begin here
+
+Return<void> ExecutionCallback::notify(V1_0::ErrorStatus errorStatus) {
+ return notifyInternal(static_cast<ErrorStatus>(errorStatus), {}, kNoTiming);
+}
+
+Return<void> ExecutionCallback::notify_1_2(V1_0::ErrorStatus errorStatus,
+ const hidl_vec<OutputShape>& outputShapes,
+ const Timing& timing) {
+ return notifyInternal(static_cast<ErrorStatus>(errorStatus), outputShapes, timing);
+}
+
+Return<void> ExecutionCallback::notify_1_3(V1_3::ErrorStatus errorStatus,
+ const hidl_vec<OutputShape>& outputShapes,
+ const Timing& timing) {
+ return notifyInternal(errorStatus, outputShapes, timing);
+}
+
+void ExecutionCallback::wait() const {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mCondition.wait(lock, [this] { return mNotified; });
+}
+
+ErrorStatus ExecutionCallback::getStatus() const {
+ wait();
+ return mErrorStatus;
+}
+
+const std::vector<OutputShape>& ExecutionCallback::getOutputShapes() const {
+ wait();
+ return mOutputShapes;
+}
+
+Timing ExecutionCallback::getTiming() const {
+ wait();
+ return mTiming;
+}
+
+Return<void> ExecutionCallback::notifyInternal(ErrorStatus errorStatus,
+ hidl_vec<OutputShape> outputShapes, Timing timing) {
+ // check results
+ if (errorStatus == ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
+ // outputShapes must not be empty if OUTPUT_INSUFFICIENT_SIZE.
+ if (outputShapes.size() == 0) {
+ LOG(ERROR) << "Notifid with empty output shape vector when OUTPUT_INSUFFICIENT_SIZE";
+ errorStatus = ErrorStatus::GENERAL_FAILURE;
+ outputShapes = {};
+ timing = kNoTiming;
+ }
+ } else if (errorStatus != ErrorStatus::NONE) {
+ // outputShapes must be empty if errorStatus is neither NONE nor OUTPUT_INSUFFICIENT_SIZE.
+ if (outputShapes.size() != 0) {
+ LOG(ERROR) << "Notified with non-empty output shape vector when error status is "
+ "neither NONE nor OUTPUT_INSUFFICIENT_SIZE";
+ errorStatus = ErrorStatus::GENERAL_FAILURE;
+ outputShapes = {};
+ timing = kNoTiming;
+ }
+ }
+
+ // store results
+ {
+ std::lock_guard<std::mutex> hold(mMutex);
+
+ // quick-return if object has already been notified
+ if (mNotified) {
+ return Void();
+ }
+
+ mErrorStatus = errorStatus;
+ mOutputShapes = std::move(outputShapes);
+ mTiming = timing;
+ mNotified = true;
+ }
+ mCondition.notify_all();
+ return Void();
+}
+
} // namespace android::hardware::neuralnetworks::V1_3::implementation
diff --git a/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp b/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp
index fe8d907..576e524 100644
--- a/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp
+++ b/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp
@@ -29,6 +29,7 @@
#include <thread>
#include "1.3/Callbacks.h"
+#include "1.3/Utils.h"
#include "GeneratedTestHarness.h"
#include "MemoryUtils.h"
#include "TestHarness.h"
@@ -49,7 +50,6 @@
using namespace test_helper;
using implementation::PreparedModelCallback;
-using V1_0::ErrorStatus;
using V1_1::ExecutionPreference;
using V1_2::Constant;
using V1_2::OperationType;
@@ -238,8 +238,8 @@
mCacheDir.push_back('/');
Return<void> ret = kDevice->getNumberOfCacheFilesNeeded(
- [this](ErrorStatus status, uint32_t numModelCache, uint32_t numDataCache) {
- EXPECT_EQ(ErrorStatus::NONE, status);
+ [this](V1_0::ErrorStatus status, uint32_t numModelCache, uint32_t numDataCache) {
+ EXPECT_EQ(V1_0::ErrorStatus::NONE, status);
mNumModelCache = numModelCache;
mNumDataCache = numDataCache;
});
@@ -308,7 +308,7 @@
model,
[&fullySupportsModel, &model](ErrorStatus status, const hidl_vec<bool>& supported) {
ASSERT_EQ(ErrorStatus::NONE, status);
- ASSERT_EQ(supported.size(), model.operations.size());
+ ASSERT_EQ(supported.size(), model.main.operations.size());
fullySupportsModel = std::all_of(supported.begin(), supported.end(),
[](bool valid) { return valid; });
});
@@ -324,9 +324,9 @@
// Launch prepare model.
sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
hidl_array<uint8_t, sizeof(mToken)> cacheToken(mToken);
- Return<ErrorStatus> prepareLaunchStatus =
- kDevice->prepareModel_1_3(model, ExecutionPreference::FAST_SINGLE_ANSWER,
- modelCache, dataCache, cacheToken, preparedModelCallback);
+ Return<ErrorStatus> prepareLaunchStatus = kDevice->prepareModel_1_3(
+ model, ExecutionPreference::FAST_SINGLE_ANSWER, kDefaultPriority, {}, modelCache,
+ dataCache, cacheToken, preparedModelCallback);
ASSERT_TRUE(prepareLaunchStatus.isOk());
ASSERT_EQ(static_cast<ErrorStatus>(prepareLaunchStatus), ErrorStatus::NONE);
@@ -370,7 +370,7 @@
sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
hidl_array<uint8_t, sizeof(mToken)> cacheToken(mToken);
Return<ErrorStatus> prepareLaunchStatus = kDevice->prepareModelFromCache_1_3(
- modelCache, dataCache, cacheToken, preparedModelCallback);
+ kDefaultPriority, {}, modelCache, dataCache, cacheToken, preparedModelCallback);
ASSERT_TRUE(prepareLaunchStatus.isOk());
if (static_cast<ErrorStatus>(prepareLaunchStatus) != ErrorStatus::NONE) {
*preparedModel = nullptr;
diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
index 4f747f4..a2c0c4e 100644
--- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
@@ -44,8 +44,8 @@
#include <vector>
#include "1.0/Utils.h"
-#include "1.2/Callbacks.h"
#include "1.3/Callbacks.h"
+#include "1.3/Utils.h"
#include "ExecutionBurstController.h"
#include "MemoryUtils.h"
#include "TestHarness.h"
@@ -56,10 +56,9 @@
using namespace test_helper;
using hidl::memory::V1_0::IMemory;
+using implementation::ExecutionCallback;
using implementation::PreparedModelCallback;
using V1_0::DataLocation;
-using V1_0::ErrorStatus;
-using V1_0::OperandLifeTime;
using V1_0::RequestArgument;
using V1_1::ExecutionPreference;
using V1_2::Constant;
@@ -67,7 +66,6 @@
using V1_2::OutputShape;
using V1_2::SymmPerChannelQuantParams;
using V1_2::Timing;
-using V1_2::implementation::ExecutionCallback;
using HidlToken = hidl_array<uint8_t, static_cast<uint32_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
namespace {
@@ -269,10 +267,10 @@
}
}
- return {.operands = std::move(operands),
- .operations = std::move(operations),
- .inputIndexes = testModel.inputIndexes,
- .outputIndexes = testModel.outputIndexes,
+ return {.main = {.operands = std::move(operands),
+ .operations = std::move(operations),
+ .inputIndexes = testModel.inputIndexes,
+ .outputIndexes = testModel.outputIndexes},
.operandValues = std::move(operandValues),
.pools = std::move(pools),
.relaxComputationFloat32toFloat16 = testModel.isRelaxed};
@@ -290,8 +288,8 @@
}
static void makeOutputDimensionsUnspecified(Model* model) {
- for (auto i : model->outputIndexes) {
- auto& dims = model->operands[i].dimensions;
+ for (auto i : model->main.outputIndexes) {
+ auto& dims = model->main.operands[i].dimensions;
std::fill(dims.begin(), dims.end(), 0);
}
}
@@ -454,7 +452,7 @@
static Return<ErrorStatus> ExecutePreparedModel(const sp<IPreparedModel>& preparedModel,
const Request& request, MeasureTiming measure,
sp<ExecutionCallback>& callback) {
- return preparedModel->execute_1_3(request, measure, callback);
+ return preparedModel->execute_1_3(request, measure, {}, callback);
}
static Return<ErrorStatus> ExecutePreparedModel(const sp<IPreparedModel>& preparedModel,
const Request& request, MeasureTiming measure,
@@ -462,7 +460,7 @@
Timing* timing) {
ErrorStatus result;
Return<void> ret = preparedModel->executeSynchronously_1_3(
- request, measure,
+ request, measure, {},
[&result, outputShapes, timing](ErrorStatus error, const hidl_vec<OutputShape>& shapes,
const Timing& time) {
result = error;
@@ -717,7 +715,8 @@
} break;
case TestKind::QUANTIZATION_COUPLING: {
ASSERT_TRUE(testModel.hasQuant8CoupledOperands());
- createPreparedModel(device, model, &preparedModel, /*reportSkipping*/ false);
+ createPreparedModel(device, model, &preparedModel,
+ /*reportSkipping*/ false);
TestModel signedQuantizedModel = convertQuant8AsymmOperandsToSigned(testModel);
sp<IPreparedModel> preparedCoupledModel;
createPreparedModel(device, createModel(signedQuantizedModel), &preparedCoupledModel,
@@ -746,6 +745,12 @@
void GeneratedTestBase::SetUp() {
testing::TestWithParam<GeneratedTestParam>::SetUp();
ASSERT_NE(kDevice, nullptr);
+
+ const Return<void> ret =
+ kDevice->supportsDeadlines([this](bool prepareModelDeadline, bool executionDeadline) {
+ mSupportsDeadlines = {prepareModelDeadline, executionDeadline};
+ });
+ ASSERT_TRUE(ret.isOk());
}
std::vector<NamedModel> getNamedModels(const FilterFn& filter) {
diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
index 2273e3b..fe695b4 100644
--- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
+++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
@@ -36,6 +36,7 @@
void SetUp() override;
const sp<IDevice> kDevice = getData(std::get<NamedDevice>(GetParam()));
const test_helper::TestModel& kTestModel = *getData(std::get<NamedModel>(GetParam()));
+ std::pair<bool, bool> mSupportsDeadlines;
};
using FilterFn = std::function<bool(const test_helper::TestModel&)>;
diff --git a/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp b/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp
new file mode 100644
index 0000000..62ffcda
--- /dev/null
+++ b/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp
@@ -0,0 +1,299 @@
+/*
+ * 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 "1.0/Utils.h"
+#include "1.3/Callbacks.h"
+#include "1.3/Utils.h"
+#include "GeneratedTestHarness.h"
+#include "Utils.h"
+
+namespace android::hardware::neuralnetworks::V1_3::vts::functional {
+
+using implementation::ExecutionCallback;
+using implementation::PreparedModelCallback;
+using test_helper::TestBuffer;
+using test_helper::TestModel;
+using V1_1::ExecutionPreference;
+using V1_2::MeasureTiming;
+using V1_2::OutputShape;
+using V1_2::Timing;
+
+using HidlToken =
+ hidl_array<uint8_t, static_cast<uint32_t>(V1_2::Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
+
+enum class DeadlineBoundType { NOW, UNLIMITED };
+constexpr std::array<DeadlineBoundType, 2> deadlineBounds = {DeadlineBoundType::NOW,
+ DeadlineBoundType::UNLIMITED};
+std::string toString(DeadlineBoundType type) {
+ switch (type) {
+ case DeadlineBoundType::NOW:
+ return "NOW";
+ case DeadlineBoundType::UNLIMITED:
+ return "UNLIMITED";
+ }
+ LOG(FATAL) << "Unrecognized DeadlineBoundType: " << static_cast<int>(type);
+ return {};
+}
+
+using Results = std::tuple<ErrorStatus, hidl_vec<OutputShape>, Timing>;
+using MaybeResults = std::optional<Results>;
+
+using ExecutionFunction =
+ std::function<MaybeResults(const sp<IPreparedModel>& preparedModel, const Request& request,
+ DeadlineBoundType deadlineBound)>;
+
+static OptionalTimePoint makeOptionalTimePoint(DeadlineBoundType deadlineBoundType) {
+ OptionalTimePoint deadline;
+ switch (deadlineBoundType) {
+ case DeadlineBoundType::NOW: {
+ const auto currentTime = std::chrono::steady_clock::now();
+ const auto currentTimeInNanoseconds =
+ std::chrono::time_point_cast<std::chrono::nanoseconds>(currentTime);
+ const uint64_t nanosecondsSinceEpoch =
+ currentTimeInNanoseconds.time_since_epoch().count();
+ deadline.nanoseconds(nanosecondsSinceEpoch);
+ } break;
+ case DeadlineBoundType::UNLIMITED: {
+ uint64_t unlimited = std::numeric_limits<uint64_t>::max();
+ deadline.nanoseconds(unlimited);
+ } break;
+ }
+ return deadline;
+}
+
+void runPrepareModelTest(const sp<IDevice>& device, const Model& model, Priority priority,
+ std::optional<DeadlineBoundType> deadlineBound) {
+ OptionalTimePoint deadline;
+ if (deadlineBound.has_value()) {
+ deadline = makeOptionalTimePoint(deadlineBound.value());
+ }
+
+ // see if service can handle model
+ bool fullySupportsModel = false;
+ const Return<void> supportedCall = device->getSupportedOperations_1_3(
+ model, [&fullySupportsModel](ErrorStatus status, const hidl_vec<bool>& supported) {
+ ASSERT_EQ(ErrorStatus::NONE, status);
+ ASSERT_NE(0ul, supported.size());
+ fullySupportsModel = std::all_of(supported.begin(), supported.end(),
+ [](bool valid) { return valid; });
+ });
+ ASSERT_TRUE(supportedCall.isOk());
+
+ // launch prepare model
+ const sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
+ const Return<ErrorStatus> prepareLaunchStatus = device->prepareModel_1_3(
+ model, ExecutionPreference::FAST_SINGLE_ANSWER, priority, deadline,
+ hidl_vec<hidl_handle>(), hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
+ ASSERT_TRUE(prepareLaunchStatus.isOk());
+ ASSERT_EQ(ErrorStatus::NONE, static_cast<ErrorStatus>(prepareLaunchStatus));
+
+ // retrieve prepared model
+ preparedModelCallback->wait();
+ const ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
+ const sp<V1_0::IPreparedModel> preparedModelV1_0 = preparedModelCallback->getPreparedModel();
+ const sp<IPreparedModel> preparedModel =
+ IPreparedModel::castFrom(preparedModelV1_0).withDefault(nullptr);
+
+ // The getSupportedOperations_1_3 call returns a list of operations that are
+ // guaranteed not to fail if prepareModel_1_3 is called, and
+ // 'fullySupportsModel' is true i.f.f. the entire model is guaranteed.
+ // If a driver has any doubt that it can prepare an operation, it must
+ // return false. So here, if a driver isn't sure if it can support an
+ // operation, but reports that it successfully prepared the model, the test
+ // can continue.
+ if (!fullySupportsModel && prepareReturnStatus != ErrorStatus::NONE) {
+ ASSERT_EQ(nullptr, preparedModel.get());
+ return;
+ }
+
+ // verify return status
+ if (!deadlineBound.has_value()) {
+ EXPECT_EQ(ErrorStatus::NONE, prepareReturnStatus);
+ } else {
+ switch (deadlineBound.value()) {
+ case DeadlineBoundType::NOW:
+ // If the execution was launched with a deadline of NOW, the
+ // deadline has already passed when the driver would launch the
+ // execution. In this case, the driver must return
+ // MISSED_DEADLINE_*.
+ EXPECT_TRUE(prepareReturnStatus == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
+ prepareReturnStatus == ErrorStatus::MISSED_DEADLINE_PERSISTENT);
+ break;
+ case DeadlineBoundType::UNLIMITED:
+ // If an unlimited deadline is supplied, we expect the execution to
+ // proceed normally. In this case, check it normally by breaking out
+ // of the switch statement.
+ EXPECT_EQ(ErrorStatus::NONE, prepareReturnStatus);
+ break;
+ }
+ }
+ ASSERT_EQ(prepareReturnStatus == ErrorStatus::NONE, preparedModel.get() != nullptr);
+}
+
+void runPrepareModelTests(const sp<IDevice>& device, const Model& model,
+ bool supportsPrepareModelDeadline) {
+ // test priority
+ for (auto priority : hidl_enum_range<Priority>{}) {
+ SCOPED_TRACE("priority: " + toString(priority));
+ if (priority == kDefaultPriority) continue;
+ runPrepareModelTest(device, model, priority, {});
+ }
+
+ // test deadline
+ if (supportsPrepareModelDeadline) {
+ for (auto deadlineBound : deadlineBounds) {
+ SCOPED_TRACE("deadlineBound: " + toString(deadlineBound));
+ runPrepareModelTest(device, model, kDefaultPriority, deadlineBound);
+ }
+ }
+}
+
+static MaybeResults executeAsynchronously(const sp<IPreparedModel>& preparedModel,
+ const Request& request, DeadlineBoundType deadlineBound) {
+ SCOPED_TRACE("asynchronous");
+ const MeasureTiming measure = MeasureTiming::NO;
+ const OptionalTimePoint deadline = makeOptionalTimePoint(deadlineBound);
+
+ // launch execution
+ const sp<ExecutionCallback> callback = new ExecutionCallback();
+ Return<ErrorStatus> ret = preparedModel->execute_1_3(request, measure, deadline, callback);
+ EXPECT_TRUE(ret.isOk());
+ EXPECT_EQ(ErrorStatus::NONE, ret.withDefault(ErrorStatus::GENERAL_FAILURE));
+ if (!ret.isOk() || ret != ErrorStatus::NONE) return std::nullopt;
+
+ // retrieve execution results
+ callback->wait();
+ const ErrorStatus status = callback->getStatus();
+ hidl_vec<OutputShape> outputShapes = callback->getOutputShapes();
+ const Timing timing = callback->getTiming();
+
+ // return results
+ return Results{status, std::move(outputShapes), timing};
+}
+
+static MaybeResults executeSynchronously(const sp<IPreparedModel>& preparedModel,
+ const Request& request, DeadlineBoundType deadlineBound) {
+ SCOPED_TRACE("synchronous");
+ const MeasureTiming measure = MeasureTiming::NO;
+ const OptionalTimePoint deadline = makeOptionalTimePoint(deadlineBound);
+
+ // configure results callback
+ MaybeResults results;
+ const auto cb = [&results](const auto&... args) { *results = {args...}; };
+
+ // run execution
+ const Return<void> ret =
+ preparedModel->executeSynchronously_1_3(request, measure, deadline, cb);
+ EXPECT_TRUE(ret.isOk());
+ if (!ret.isOk()) return std::nullopt;
+
+ // return results
+ return results;
+}
+
+void runExecutionTest(const sp<IPreparedModel>& preparedModel, const TestModel& testModel,
+ const Request& request, bool synchronous, DeadlineBoundType deadlineBound) {
+ const ExecutionFunction execute = synchronous ? executeSynchronously : executeAsynchronously;
+
+ // Perform execution and unpack results.
+ const auto results = execute(preparedModel, request, deadlineBound);
+ if (!results.has_value()) return;
+ const auto& [status, outputShapes, timing] = results.value();
+
+ // Verify no timing information was returned
+ EXPECT_EQ(UINT64_MAX, timing.timeOnDevice);
+ EXPECT_EQ(UINT64_MAX, timing.timeInDriver);
+
+ // Validate deadline information if applicable.
+ switch (deadlineBound) {
+ case DeadlineBoundType::NOW:
+ // If the execution was launched with a deadline of NOW, the
+ // deadline has already passed when the driver would launch the
+ // execution. In this case, the driver must return
+ // MISSED_DEADLINE_*.
+ ASSERT_TRUE(status == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
+ status == ErrorStatus::MISSED_DEADLINE_PERSISTENT);
+ return;
+ case DeadlineBoundType::UNLIMITED:
+ // If an unlimited deadline is supplied, we expect the execution to
+ // proceed normally. In this case, check it normally by breaking out
+ // of the switch statement.
+ ASSERT_EQ(ErrorStatus::NONE, status);
+ break;
+ }
+
+ // If the model output operands are fully specified, outputShapes must be either
+ // either empty, or have the same number of elements as the number of outputs.
+ ASSERT_TRUE(outputShapes.size() == 0 || outputShapes.size() == testModel.outputIndexes.size());
+
+ // Go through all outputs, check returned output shapes.
+ for (uint32_t i = 0; i < outputShapes.size(); i++) {
+ EXPECT_TRUE(outputShapes[i].isSufficient);
+ const auto& expect = testModel.operands[testModel.outputIndexes[i]].dimensions;
+ const std::vector<uint32_t> actual = outputShapes[i].dimensions;
+ EXPECT_EQ(expect, actual);
+ }
+
+ // Retrieve execution results.
+ ASSERT_TRUE(nn::compliantWithV1_0(request));
+ const V1_0::Request request10 = nn::convertToV1_0(request);
+ const std::vector<TestBuffer> outputs = getOutputBuffers(request10);
+
+ // We want "close-enough" results.
+ checkResults(testModel, outputs);
+}
+
+void runExecutionTests(const sp<IPreparedModel>& preparedModel, const TestModel& testModel,
+ const Request& request) {
+ for (bool synchronous : {false, true}) {
+ for (auto deadlineBound : deadlineBounds) {
+ runExecutionTest(preparedModel, testModel, request, synchronous, deadlineBound);
+ }
+ }
+}
+
+void runTests(const sp<IDevice>& device, const TestModel& testModel,
+ std::pair<bool, bool> supportsDeadlines) {
+ // setup
+ const auto [supportsPrepareModelDeadline, supportsExecutionDeadline] = supportsDeadlines;
+ if (!supportsPrepareModelDeadline && !supportsExecutionDeadline) return;
+ const Model model = createModel(testModel);
+
+ // run prepare model tests
+ runPrepareModelTests(device, model, supportsPrepareModelDeadline);
+
+ if (supportsExecutionDeadline) {
+ // prepare model
+ sp<IPreparedModel> preparedModel;
+ createPreparedModel(device, model, &preparedModel);
+ if (preparedModel == nullptr) return;
+
+ // run execution tests
+ const Request request = nn::convertToV1_3(createRequest(testModel));
+ runExecutionTests(preparedModel, testModel, request);
+ }
+}
+
+class DeadlineTest : public GeneratedTestBase {};
+
+TEST_P(DeadlineTest, Test) {
+ runTests(kDevice, kTestModel, mSupportsDeadlines);
+}
+
+INSTANTIATE_GENERATED_TEST(DeadlineTest,
+ [](const TestModel& testModel) { return !testModel.expectFailure; });
+
+} // namespace android::hardware::neuralnetworks::V1_3::vts::functional
diff --git a/neuralnetworks/1.3/vts/functional/Utils.cpp b/neuralnetworks/1.3/vts/functional/Utils.cpp
new file mode 100644
index 0000000..23e2af8
--- /dev/null
+++ b/neuralnetworks/1.3/vts/functional/Utils.cpp
@@ -0,0 +1,27 @@
+/*
+ * 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 "1.3/Utils.h"
+
+#include <iostream>
+
+namespace android::hardware::neuralnetworks::V1_3 {
+
+::std::ostream& operator<<(::std::ostream& os, ErrorStatus errorStatus) {
+ return os << toString(errorStatus);
+}
+
+} // namespace android::hardware::neuralnetworks::V1_3
diff --git a/neuralnetworks/1.3/vts/functional/ValidateBurst.cpp b/neuralnetworks/1.3/vts/functional/ValidateBurst.cpp
index 7df8046..6ff9dfd 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateBurst.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateBurst.cpp
@@ -34,7 +34,6 @@
using nn::ExecutionBurstController;
using nn::RequestChannelSender;
using nn::ResultChannelReceiver;
-using V1_0::ErrorStatus;
using V1_0::Request;
using V1_2::FmqRequestDatum;
using V1_2::FmqResultDatum;
@@ -80,16 +79,17 @@
ASSERT_NE(nullptr, fmqResultDescriptor);
// configure burst
- ErrorStatus errorStatus;
+ V1_0::ErrorStatus errorStatus;
sp<IBurstContext> burstContext;
const Return<void> ret = preparedModel->configureExecutionBurst(
callback, *fmqRequestDescriptor, *fmqResultDescriptor,
- [&errorStatus, &burstContext](ErrorStatus status, const sp<IBurstContext>& context) {
+ [&errorStatus, &burstContext](V1_0::ErrorStatus status,
+ const sp<IBurstContext>& context) {
errorStatus = status;
burstContext = context;
});
ASSERT_TRUE(ret.isOk());
- ASSERT_EQ(ErrorStatus::NONE, errorStatus);
+ ASSERT_EQ(V1_0::ErrorStatus::NONE, errorStatus);
ASSERT_NE(nullptr, burstContext.get());
// return values
@@ -144,7 +144,7 @@
auto results = receiver->getBlocking();
ASSERT_TRUE(results.has_value());
const auto [status, outputShapes, timing] = std::move(*results);
- EXPECT_NE(ErrorStatus::NONE, status);
+ EXPECT_NE(V1_0::ErrorStatus::NONE, status);
EXPECT_EQ(0u, outputShapes.size());
EXPECT_TRUE(badTiming(timing));
}
@@ -302,14 +302,15 @@
// collect serialized result by running regular burst
const auto [nRegular, outputShapesRegular, timingRegular, fallbackRegular] =
controllerRegular->compute(request, MeasureTiming::NO, keys);
- const ErrorStatus statusRegular = nn::convertResultCodeToErrorStatus(nRegular);
+ const V1_0::ErrorStatus statusRegular =
+ nn::convertToV1_0(nn::convertResultCodeToErrorStatus(nRegular));
EXPECT_FALSE(fallbackRegular);
// skip test if regular burst output isn't useful for testing a failure
// caused by having too small of a length for the result FMQ
const std::vector<FmqResultDatum> serialized =
android::nn::serialize(statusRegular, outputShapesRegular, timingRegular);
- if (statusRegular != ErrorStatus::NONE ||
+ if (statusRegular != V1_0::ErrorStatus::NONE ||
serialized.size() <= kExecutionBurstChannelSmallLength) {
return;
}
@@ -318,8 +319,9 @@
// large enough to return the serialized result
const auto [nSmall, outputShapesSmall, timingSmall, fallbackSmall] =
controllerSmall->compute(request, MeasureTiming::NO, keys);
- const ErrorStatus statusSmall = nn::convertResultCodeToErrorStatus(nSmall);
- EXPECT_NE(ErrorStatus::NONE, statusSmall);
+ const V1_0::ErrorStatus statusSmall =
+ nn::convertToV1_0(nn::convertResultCodeToErrorStatus(nSmall));
+ EXPECT_NE(V1_0::ErrorStatus::NONE, statusSmall);
EXPECT_EQ(0u, outputShapesSmall.size());
EXPECT_TRUE(badTiming(timingSmall));
EXPECT_FALSE(fallbackSmall);
diff --git a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
index 8395111..a211428 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
@@ -18,14 +18,13 @@
#include "1.0/Utils.h"
#include "1.3/Callbacks.h"
+#include "1.3/Utils.h"
#include "GeneratedTestHarness.h"
#include "VtsHalNeuralnetworks.h"
namespace android::hardware::neuralnetworks::V1_3::vts::functional {
using implementation::PreparedModelCallback;
-using V1_0::ErrorStatus;
-using V1_0::OperandLifeTime;
using V1_1::ExecutionPreference;
using V1_2::SymmPerChannelQuantParams;
using HidlToken =
@@ -45,13 +44,19 @@
}
static void validatePrepareModel(const sp<IDevice>& device, const std::string& message,
- const Model& model, ExecutionPreference preference) {
+ const Model& model, ExecutionPreference preference,
+ bool testDeadline) {
SCOPED_TRACE(message + " [prepareModel_1_3]");
+ OptionalTimePoint deadline;
+ if (testDeadline) {
+ deadline.nanoseconds(std::numeric_limits<uint64_t>::max());
+ }
+
sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
- Return<ErrorStatus> prepareLaunchStatus =
- device->prepareModel_1_3(model, preference, hidl_vec<hidl_handle>(),
- hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
+ Return<ErrorStatus> prepareLaunchStatus = device->prepareModel_1_3(
+ model, preference, kDefaultPriority, deadline, hidl_vec<hidl_handle>(),
+ hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
ASSERT_TRUE(prepareLaunchStatus.isOk());
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(prepareLaunchStatus));
@@ -74,31 +79,32 @@
// to the model does not leave this function.
static void validate(const sp<IDevice>& device, const std::string& message, Model model,
const std::function<void(Model*)>& mutation,
- ExecutionPreference preference = ExecutionPreference::FAST_SINGLE_ANSWER) {
+ ExecutionPreference preference = ExecutionPreference::FAST_SINGLE_ANSWER,
+ bool testDeadline = false) {
mutation(&model);
- if (validExecutionPreference(preference)) {
+ if (validExecutionPreference(preference) && !testDeadline) {
validateGetSupportedOperations(device, message, model);
}
- validatePrepareModel(device, message, model, preference);
+ validatePrepareModel(device, message, model, preference, testDeadline);
}
static uint32_t addOperand(Model* model) {
- return hidl_vec_push_back(&model->operands,
+ return hidl_vec_push_back(&model->main.operands,
{
.type = OperandType::INT32,
.dimensions = {},
.numberOfConsumers = 0,
.scale = 0.0f,
.zeroPoint = 0,
- .lifetime = OperandLifeTime::MODEL_INPUT,
+ .lifetime = OperandLifeTime::SUBGRAPH_INPUT,
.location = {.poolIndex = 0, .offset = 0, .length = 0},
});
}
static uint32_t addOperand(Model* model, OperandLifeTime lifetime) {
uint32_t index = addOperand(model);
- model->operands[index].numberOfConsumers = 1;
- model->operands[index].lifetime = lifetime;
+ model->main.operands[index].numberOfConsumers = 1;
+ model->main.operands[index].lifetime = lifetime;
return index;
}
@@ -112,13 +118,13 @@
};
static void mutateOperandTypeTest(const sp<IDevice>& device, const Model& model) {
- for (size_t operand = 0; operand < model.operands.size(); ++operand) {
+ for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
for (uint32_t invalidOperandType : invalidOperandTypes) {
const std::string message = "mutateOperandTypeTest: operand " +
std::to_string(operand) + " set to value " +
std::to_string(invalidOperandType);
validate(device, message, model, [operand, invalidOperandType](Model* model) {
- model->operands[operand].type = static_cast<OperandType>(invalidOperandType);
+ model->main.operands[operand].type = static_cast<OperandType>(invalidOperandType);
});
}
}
@@ -150,15 +156,15 @@
}
static void mutateOperandRankTest(const sp<IDevice>& device, const Model& model) {
- for (size_t operand = 0; operand < model.operands.size(); ++operand) {
- const uint32_t invalidRank = getInvalidRank(model.operands[operand].type);
+ for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
+ const uint32_t invalidRank = getInvalidRank(model.main.operands[operand].type);
if (invalidRank == 0) {
continue;
}
const std::string message = "mutateOperandRankTest: operand " + std::to_string(operand) +
" has rank of " + std::to_string(invalidRank);
validate(device, message, model, [operand, invalidRank](Model* model) {
- model->operands[operand].dimensions = std::vector<uint32_t>(invalidRank, 0);
+ model->main.operands[operand].dimensions = std::vector<uint32_t>(invalidRank, 0);
});
}
}
@@ -190,12 +196,12 @@
}
static void mutateOperandScaleTest(const sp<IDevice>& device, const Model& model) {
- for (size_t operand = 0; operand < model.operands.size(); ++operand) {
- const float invalidScale = getInvalidScale(model.operands[operand].type);
+ for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
+ const float invalidScale = getInvalidScale(model.main.operands[operand].type);
const std::string message = "mutateOperandScaleTest: operand " + std::to_string(operand) +
" has scale of " + std::to_string(invalidScale);
validate(device, message, model, [operand, invalidScale](Model* model) {
- model->operands[operand].scale = invalidScale;
+ model->main.operands[operand].scale = invalidScale;
});
}
}
@@ -229,15 +235,15 @@
}
static void mutateOperandZeroPointTest(const sp<IDevice>& device, const Model& model) {
- for (size_t operand = 0; operand < model.operands.size(); ++operand) {
+ for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
const std::vector<int32_t> invalidZeroPoints =
- getInvalidZeroPoints(model.operands[operand].type);
+ getInvalidZeroPoints(model.main.operands[operand].type);
for (int32_t invalidZeroPoint : invalidZeroPoints) {
const std::string message = "mutateOperandZeroPointTest: operand " +
std::to_string(operand) + " has zero point of " +
std::to_string(invalidZeroPoint);
validate(device, message, model, [operand, invalidZeroPoint](Model* model) {
- model->operands[operand].zeroPoint = invalidZeroPoint;
+ model->main.operands[operand].zeroPoint = invalidZeroPoint;
});
}
}
@@ -310,11 +316,11 @@
static bool mutateOperationOperandTypeSkip(size_t operand, OperandType type, const Model& model) {
// Do not test OEM types
- if (type == model.operands[operand].type || type == OperandType::OEM ||
+ if (type == model.main.operands[operand].type || type == OperandType::OEM ||
type == OperandType::TENSOR_OEM_BYTE) {
return true;
}
- for (const Operation& operation : model.operations) {
+ for (const Operation& operation : model.main.operations) {
// Skip mutateOperationOperandTypeTest for the following operations.
// - LSH_PROJECTION's second argument is allowed to have any type.
// - ARGMIN and ARGMAX's first argument can be any of
@@ -401,7 +407,7 @@
}
static void mutateOperationOperandTypeTest(const sp<IDevice>& device, const Model& model) {
- for (size_t operand = 0; operand < model.operands.size(); ++operand) {
+ for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
for (OperandType invalidOperandType : hidl_enum_range<OperandType>{}) {
if (mutateOperationOperandTypeSkip(operand, invalidOperandType, model)) {
continue;
@@ -410,7 +416,7 @@
std::to_string(operand) + " set to type " +
toString(invalidOperandType);
validate(device, message, model, [operand, invalidOperandType](Model* model) {
- mutateOperand(&model->operands[operand], invalidOperandType);
+ mutateOperand(&model->main.operands[operand], invalidOperandType);
});
}
}
@@ -425,13 +431,13 @@
};
static void mutateOperationTypeTest(const sp<IDevice>& device, const Model& model) {
- for (size_t operation = 0; operation < model.operations.size(); ++operation) {
+ for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
for (uint32_t invalidOperationType : invalidOperationTypes) {
const std::string message = "mutateOperationTypeTest: operation " +
std::to_string(operation) + " set to value " +
std::to_string(invalidOperationType);
validate(device, message, model, [operation, invalidOperationType](Model* model) {
- model->operations[operation].type =
+ model->main.operations[operation].type =
static_cast<OperationType>(invalidOperationType);
});
}
@@ -441,14 +447,14 @@
///////////////////////// VALIDATE MODEL OPERATION INPUT OPERAND INDEX /////////////////////////
static void mutateOperationInputOperandIndexTest(const sp<IDevice>& device, const Model& model) {
- for (size_t operation = 0; operation < model.operations.size(); ++operation) {
- const uint32_t invalidOperand = model.operands.size();
- for (size_t input = 0; input < model.operations[operation].inputs.size(); ++input) {
+ for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
+ const uint32_t invalidOperand = model.main.operands.size();
+ for (size_t input = 0; input < model.main.operations[operation].inputs.size(); ++input) {
const std::string message = "mutateOperationInputOperandIndexTest: operation " +
std::to_string(operation) + " input " +
std::to_string(input);
validate(device, message, model, [operation, input, invalidOperand](Model* model) {
- model->operations[operation].inputs[input] = invalidOperand;
+ model->main.operations[operation].inputs[input] = invalidOperand;
});
}
}
@@ -457,14 +463,15 @@
///////////////////////// VALIDATE MODEL OPERATION OUTPUT OPERAND INDEX /////////////////////////
static void mutateOperationOutputOperandIndexTest(const sp<IDevice>& device, const Model& model) {
- for (size_t operation = 0; operation < model.operations.size(); ++operation) {
- const uint32_t invalidOperand = model.operands.size();
- for (size_t output = 0; output < model.operations[operation].outputs.size(); ++output) {
+ for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
+ const uint32_t invalidOperand = model.main.operands.size();
+ for (size_t output = 0; output < model.main.operations[operation].outputs.size();
+ ++output) {
const std::string message = "mutateOperationOutputOperandIndexTest: operation " +
std::to_string(operation) + " output " +
std::to_string(output);
validate(device, message, model, [operation, output, invalidOperand](Model* model) {
- model->operations[operation].outputs[output] = invalidOperand;
+ model->main.operations[operation].outputs[output] = invalidOperand;
});
}
}
@@ -485,17 +492,17 @@
}
static void removeOperand(Model* model, uint32_t index) {
- hidl_vec_removeAt(&model->operands, index);
- for (Operation& operation : model->operations) {
+ hidl_vec_removeAt(&model->main.operands, index);
+ for (Operation& operation : model->main.operations) {
removeValueAndDecrementGreaterValues(&operation.inputs, index);
removeValueAndDecrementGreaterValues(&operation.outputs, index);
}
- removeValueAndDecrementGreaterValues(&model->inputIndexes, index);
- removeValueAndDecrementGreaterValues(&model->outputIndexes, index);
+ removeValueAndDecrementGreaterValues(&model->main.inputIndexes, index);
+ removeValueAndDecrementGreaterValues(&model->main.outputIndexes, index);
}
static bool removeOperandSkip(size_t operand, const Model& model) {
- for (const Operation& operation : model.operations) {
+ for (const Operation& operation : model.main.operations) {
// Skip removeOperandTest for the following operations.
// - SPLIT's outputs are not checked during prepareModel.
if (operation.type == OperationType::SPLIT) {
@@ -520,7 +527,7 @@
}
static void removeOperandTest(const sp<IDevice>& device, const Model& model) {
- for (size_t operand = 0; operand < model.operands.size(); ++operand) {
+ for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
if (removeOperandSkip(operand, model)) {
continue;
}
@@ -533,14 +540,14 @@
///////////////////////// REMOVE OPERATION /////////////////////////
static void removeOperation(Model* model, uint32_t index) {
- for (uint32_t operand : model->operations[index].inputs) {
- model->operands[operand].numberOfConsumers--;
+ for (uint32_t operand : model->main.operations[index].inputs) {
+ model->main.operands[operand].numberOfConsumers--;
}
- hidl_vec_removeAt(&model->operations, index);
+ hidl_vec_removeAt(&model->main.operations, index);
}
static void removeOperationTest(const sp<IDevice>& device, const Model& model) {
- for (size_t operation = 0; operation < model.operations.size(); ++operation) {
+ for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
const std::string message = "removeOperationTest: operation " + std::to_string(operation);
validate(device, message, model,
[operation](Model* model) { removeOperation(model, operation); });
@@ -615,9 +622,9 @@
}
static void removeOperationInputTest(const sp<IDevice>& device, const Model& model) {
- for (size_t operation = 0; operation < model.operations.size(); ++operation) {
- for (size_t input = 0; input < model.operations[operation].inputs.size(); ++input) {
- const Operation& op = model.operations[operation];
+ for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
+ for (size_t input = 0; input < model.main.operations[operation].inputs.size(); ++input) {
+ const Operation& op = model.main.operations[operation];
if (removeOperationInputSkip(op, input)) {
continue;
}
@@ -625,9 +632,9 @@
std::to_string(operation) + ", input " +
std::to_string(input);
validate(device, message, model, [operation, input](Model* model) {
- uint32_t operand = model->operations[operation].inputs[input];
- model->operands[operand].numberOfConsumers--;
- hidl_vec_removeAt(&model->operations[operation].inputs, input);
+ uint32_t operand = model->main.operations[operation].inputs[input];
+ model->main.operands[operand].numberOfConsumers--;
+ hidl_vec_removeAt(&model->main.operations[operation].inputs, input);
});
}
}
@@ -636,13 +643,14 @@
///////////////////////// REMOVE OPERATION OUTPUT /////////////////////////
static void removeOperationOutputTest(const sp<IDevice>& device, const Model& model) {
- for (size_t operation = 0; operation < model.operations.size(); ++operation) {
- for (size_t output = 0; output < model.operations[operation].outputs.size(); ++output) {
+ for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
+ for (size_t output = 0; output < model.main.operations[operation].outputs.size();
+ ++output) {
const std::string message = "removeOperationOutputTest: operation " +
std::to_string(operation) + ", output " +
std::to_string(output);
validate(device, message, model, [operation, output](Model* model) {
- hidl_vec_removeAt(&model->operations[operation].outputs, output);
+ hidl_vec_removeAt(&model->main.operations[operation].outputs, output);
});
}
}
@@ -669,15 +677,15 @@
}
static void addOperationInputTest(const sp<IDevice>& device, const Model& model) {
- for (size_t operation = 0; operation < model.operations.size(); ++operation) {
- if (addOperationInputSkip(model.operations[operation])) {
+ for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
+ if (addOperationInputSkip(model.main.operations[operation])) {
continue;
}
const std::string message = "addOperationInputTest: operation " + std::to_string(operation);
validate(device, message, model, [operation](Model* model) {
- uint32_t index = addOperand(model, OperandLifeTime::MODEL_INPUT);
- hidl_vec_push_back(&model->operations[operation].inputs, index);
- hidl_vec_push_back(&model->inputIndexes, index);
+ uint32_t index = addOperand(model, OperandLifeTime::SUBGRAPH_INPUT);
+ hidl_vec_push_back(&model->main.operations[operation].inputs, index);
+ hidl_vec_push_back(&model->main.inputIndexes, index);
});
}
}
@@ -685,13 +693,13 @@
///////////////////////// ADD OPERATION OUTPUT /////////////////////////
static void addOperationOutputTest(const sp<IDevice>& device, const Model& model) {
- for (size_t operation = 0; operation < model.operations.size(); ++operation) {
+ for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
const std::string message =
"addOperationOutputTest: operation " + std::to_string(operation);
validate(device, message, model, [operation](Model* model) {
- uint32_t index = addOperand(model, OperandLifeTime::MODEL_OUTPUT);
- hidl_vec_push_back(&model->operations[operation].outputs, index);
- hidl_vec_push_back(&model->outputIndexes, index);
+ uint32_t index = addOperand(model, OperandLifeTime::SUBGRAPH_OUTPUT);
+ hidl_vec_push_back(&model->main.operations[operation].outputs, index);
+ hidl_vec_push_back(&model->main.outputIndexes, index);
});
}
}
@@ -713,9 +721,19 @@
}
}
+///////////////////////// DEADLINE /////////////////////////
+
+static void deadlineTest(const sp<IDevice>& device, const Model& model) {
+ const std::string message = "deadlineTest: deadline not supported";
+ const auto noop = [](Model*) {};
+ validate(device, message, model, noop, ExecutionPreference::FAST_SINGLE_ANSWER,
+ /*testDeadline=*/true);
+}
+
////////////////////////// ENTRY POINT //////////////////////////////
-void validateModel(const sp<IDevice>& device, const Model& model) {
+void validateModel(const sp<IDevice>& device, const Model& model,
+ bool prepareModelDeadlineSupported) {
mutateOperandTypeTest(device, model);
mutateOperandRankTest(device, model);
mutateOperandScaleTest(device, model);
@@ -731,6 +749,9 @@
addOperationInputTest(device, model);
addOperationOutputTest(device, model);
mutateExecutionPreferenceTest(device, model);
+ if (!prepareModelDeadlineSupported) {
+ deadlineTest(device, model);
+ }
}
} // namespace android::hardware::neuralnetworks::V1_3::vts::functional
diff --git a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
index 96dc589..be4112a 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
@@ -18,7 +18,7 @@
#include <chrono>
#include "1.0/Utils.h"
-#include "1.2/Callbacks.h"
+#include "1.3/Callbacks.h"
#include "ExecutionBurstController.h"
#include "GeneratedTestHarness.h"
#include "TestHarness.h"
@@ -27,11 +27,10 @@
namespace android::hardware::neuralnetworks::V1_3::vts::functional {
-using V1_0::ErrorStatus;
+using implementation::ExecutionCallback;
using V1_2::MeasureTiming;
using V1_2::OutputShape;
using V1_2::Timing;
-using V1_2::implementation::ExecutionCallback;
///////////////////////// UTILITY FUNCTIONS /////////////////////////
@@ -44,7 +43,8 @@
// that use the request. Note that the request here is passed by value, and any
// mutation to the request does not leave this function.
static void validate(const sp<IPreparedModel>& preparedModel, const std::string& message,
- Request request, const std::function<void(Request*)>& mutation) {
+ Request request, const std::function<void(Request*)>& mutation,
+ bool testDeadline = false) {
mutation(&request);
// We'd like to test both with timing requested and without timing
@@ -57,13 +57,18 @@
};
MeasureTiming measure = (hash & 1) ? MeasureTiming::YES : MeasureTiming::NO;
+ OptionalTimePoint deadline;
+ if (testDeadline) {
+ deadline.nanoseconds(std::numeric_limits<uint64_t>::max());
+ }
+
// asynchronous
{
SCOPED_TRACE(message + " [execute_1_3]");
sp<ExecutionCallback> executionCallback = new ExecutionCallback();
Return<ErrorStatus> executeLaunchStatus =
- preparedModel->execute_1_3(request, measure, executionCallback);
+ preparedModel->execute_1_3(request, measure, deadline, executionCallback);
ASSERT_TRUE(executeLaunchStatus.isOk());
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(executeLaunchStatus));
@@ -81,7 +86,7 @@
SCOPED_TRACE(message + " [executeSynchronously_1_3]");
Return<void> executeStatus = preparedModel->executeSynchronously_1_3(
- request, measure,
+ request, measure, deadline,
[](ErrorStatus error, const hidl_vec<OutputShape>& outputShapes,
const Timing& timing) {
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
@@ -93,7 +98,7 @@
// burst
// TODO(butlermichael): Check if we need to test burst in V1_3 if the interface remains V1_2.
- {
+ if (!testDeadline) {
SCOPED_TRACE(message + " [burst]");
ASSERT_TRUE(nn::compliantWithV1_0(request));
@@ -153,17 +158,29 @@
}
}
+///////////////////////// DEADLINE ////////////////////////////////////
+
+static void deadlineTest(const sp<IPreparedModel>& preparedModel, const Request& request) {
+ const std::string message = "deadlineTest: deadline not supported";
+ const auto noop = [](Request*) {};
+ validate(preparedModel, message, request, noop, /*testDeadline=*/true);
+}
+
///////////////////////////// ENTRY POINT //////////////////////////////////
-void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request) {
+void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request,
+ bool executionDeadlineSupported) {
removeInputTest(preparedModel, request);
removeOutputTest(preparedModel, request);
+ if (!executionDeadlineSupported) {
+ deadlineTest(preparedModel, request);
+ }
}
void validateRequestFailure(const sp<IPreparedModel>& preparedModel, const Request& request) {
SCOPED_TRACE("Expecting request to fail [executeSynchronously_1_3]");
Return<void> executeStatus = preparedModel->executeSynchronously_1_3(
- request, MeasureTiming::NO,
+ request, MeasureTiming::NO, {},
[](ErrorStatus error, const hidl_vec<OutputShape>& outputShapes, const Timing& timing) {
ASSERT_NE(ErrorStatus::NONE, error);
EXPECT_EQ(outputShapes.size(), 0);
diff --git a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
index 1140b68..93c8f13 100644
--- a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
+++ b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
@@ -23,6 +23,7 @@
#include <utility>
#include "1.0/Utils.h"
#include "1.3/Callbacks.h"
+#include "1.3/Utils.h"
#include "GeneratedTestHarness.h"
#include "TestHarness.h"
#include "Utils.h"
@@ -32,7 +33,6 @@
using HidlToken =
hidl_array<uint8_t, static_cast<uint32_t>(V1_2::Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
using implementation::PreparedModelCallback;
-using V1_0::ErrorStatus;
using V1_1::ExecutionPreference;
// internal helper function
@@ -55,8 +55,8 @@
// launch prepare model
const sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
const Return<ErrorStatus> prepareLaunchStatus = device->prepareModel_1_3(
- model, ExecutionPreference::FAST_SINGLE_ANSWER, hidl_vec<hidl_handle>(),
- hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
+ model, ExecutionPreference::FAST_SINGLE_ANSWER, kDefaultPriority, {},
+ hidl_vec<hidl_handle>(), hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
ASSERT_TRUE(prepareLaunchStatus.isOk());
ASSERT_EQ(ErrorStatus::NONE, static_cast<ErrorStatus>(prepareLaunchStatus));
@@ -84,6 +84,7 @@
<< std::endl;
GTEST_SKIP();
}
+
ASSERT_EQ(ErrorStatus::NONE, prepareReturnStatus);
ASSERT_NE(nullptr, preparedModel->get());
}
@@ -122,23 +123,27 @@
INSTANTIATE_DEVICE_TEST(NeuralnetworksHidlTest);
// Forward declaration from ValidateModel.cpp
-void validateModel(const sp<IDevice>& device, const Model& model);
+void validateModel(const sp<IDevice>& device, const Model& model,
+ bool prepareModelDeadlineSupported);
// Forward declaration from ValidateRequest.cpp
-void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request);
+void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request,
+ bool executionDeadlineSupported);
// Forward declaration from ValidateRequest.cpp
void validateRequestFailure(const sp<IPreparedModel>& preparedModel, const Request& request);
// Forward declaration from ValidateBurst.cpp
void validateBurst(const sp<IPreparedModel>& preparedModel, const V1_0::Request& request);
-void validateEverything(const sp<IDevice>& device, const Model& model, const Request& request) {
- validateModel(device, model);
+void validateEverything(const sp<IDevice>& device, const Model& model, const Request& request,
+ std::pair<bool, bool> supportsDeadlines) {
+ const auto [prepareModelDeadlineSupported, executionDeadlineSupported] = supportsDeadlines;
+ validateModel(device, model, prepareModelDeadlineSupported);
// Create IPreparedModel.
sp<IPreparedModel> preparedModel;
createPreparedModel(device, model, &preparedModel);
if (preparedModel == nullptr) return;
- validateRequest(preparedModel, request);
+ validateRequest(preparedModel, request, executionDeadlineSupported);
// TODO(butlermichael): Check if we need to test burst in V1_3 if the interface remains V1_2.
ASSERT_TRUE(nn::compliantWithV1_0(request));
@@ -146,10 +151,12 @@
validateBurst(preparedModel, request10);
}
-void validateFailure(const sp<IDevice>& device, const Model& model, const Request& request) {
+void validateFailure(const sp<IDevice>& device, const Model& model, const Request& request,
+ std::pair<bool, bool> supportsDeadlines) {
+ const bool prepareModelDeadlineSupported = supportsDeadlines.first;
// TODO: Should this always succeed?
// What if the invalid input is part of the model (i.e., a parameter).
- validateModel(device, model);
+ validateModel(device, model, prepareModelDeadlineSupported);
// Create IPreparedModel.
sp<IPreparedModel> preparedModel;
@@ -163,9 +170,9 @@
const Model model = createModel(kTestModel);
const Request request = nn::convertToV1_3(createRequest(kTestModel));
if (kTestModel.expectFailure) {
- validateFailure(kDevice, model, request);
+ validateFailure(kDevice, model, request, mSupportsDeadlines);
} else {
- validateEverything(kDevice, model, request);
+ validateEverything(kDevice, model, request, mSupportsDeadlines);
}
}
diff --git a/neuralnetworks/1.3/vts/functional/include/1.3/Callbacks.h b/neuralnetworks/1.3/vts/functional/include/1.3/Callbacks.h
index fb19a84..e9dec2d 100644
--- a/neuralnetworks/1.3/vts/functional/include/1.3/Callbacks.h
+++ b/neuralnetworks/1.3/vts/functional/include/1.3/Callbacks.h
@@ -18,8 +18,11 @@
#define ANDROID_HARDWARE_NEURALNETWORKS_V1_3_CALLBACKS_H
#include <android-base/thread_annotations.h>
+#include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
#include <android/hardware/neuralnetworks/1.0/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.2/IExecutionCallback.h>
#include <android/hardware/neuralnetworks/1.2/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.3/IExecutionCallback.h>
#include <android/hardware/neuralnetworks/1.3/IPreparedModelCallback.h>
#include <hidl/Status.h>
#include <condition_variable>
@@ -136,7 +139,7 @@
* @param preparedModel Returned model that has been prepared for execution,
* nullptr if the model was unable to be prepared.
*/
- Return<void> notify_1_3(V1_0::ErrorStatus status,
+ Return<void> notify_1_3(V1_3::ErrorStatus status,
const sp<V1_3::IPreparedModel>& preparedModel) override;
/**
@@ -158,7 +161,7 @@
* - GENERAL_FAILURE if there is an unspecified error
* - INVALID_ARGUMENT if the input model is invalid
*/
- V1_0::ErrorStatus getStatus() const;
+ ErrorStatus getStatus() const;
/**
* Retrieves the model that has been prepared for execution from the
@@ -173,13 +176,216 @@
sp<V1_0::IPreparedModel> getPreparedModel() const;
private:
+ Return<void> notifyInternal(ErrorStatus status, const sp<V1_0::IPreparedModel>& preparedModel);
+
mutable std::mutex mMutex;
mutable std::condition_variable mCondition;
bool mNotified GUARDED_BY(mMutex) = false;
- V1_0::ErrorStatus mErrorStatus = V1_0::ErrorStatus::GENERAL_FAILURE;
+ ErrorStatus mErrorStatus = ErrorStatus::GENERAL_FAILURE;
sp<V1_0::IPreparedModel> mPreparedModel;
};
+/**
+ * The ExecutionCallback class is used to receive the results of the execution
+ * from a task executing asynchronously with respect to the runtime. If a
+ * calling thread calls wait or get* on a ExecutionCallback object and the
+ * corresponding asynchronous task has not finished the execution, the calling
+ * thread will block until the asynchronous task has either called one of the
+ * notify* methods.
+ *
+ * If the callback object is notified more than once, only the results of the
+ * first call to notify* are used, and the results from subsequent calls are
+ * discarded.
+ *
+ * This callback object is passed as an argument to IPreparedModel::execute*.
+ */
+class ExecutionCallback : public IExecutionCallback {
+ public:
+ /**
+ * IExecutionCallback::notify marks the callback object with the return
+ * status of the asynchronous execution that held this callback and enables
+ * all prior and future wait calls on the ExecutionCallback object to
+ * proceed.
+ *
+ * One of the IExecutionCallback::notify* methods must be called on a given
+ * ExecutionCallback object.
+ *
+ * If the callback object is notified more than once, only the results of
+ * the first call to notify* are used, and the results from subsequent calls
+ * are discarded.
+ *
+ * @param status Error status returned from launching the asynchronous task
+ * (if the launch fails) or from the asynchronous task itself (if the
+ * launch succeeds). Must be:
+ * - NONE if the asynchronous execution was successful
+ * - DEVICE_UNAVAILABLE if driver is offline or busy
+ * - GENERAL_FAILURE if there is an unspecified error
+ * - OUTPUT_INSUFFICIENT_SIZE if provided output buffer is not large
+ * enough to store the resultant values
+ * - INVALID_ARGUMENT if the input request is invalid
+ */
+ Return<void> notify(V1_0::ErrorStatus status) override;
+
+ /**
+ * IExecutionCallback::notify_1_2 marks the callback object with the results
+ * (error status, dynamic output shapes, and timing information) of the
+ * asynchronous execution that held this callback and enables all prior and
+ * future wait calls on the ExecutionCallback object to proceed.
+ *
+ * One of the IExecutionCallback::notify* methods must be called on a given
+ * ExecutionCallback object.
+ *
+ * If the callback object is notified more than once, only the results of
+ * the first call to notify* are used, and the results from subsequent calls
+ * are discarded.
+ *
+ * @param status Error status returned from launching the asynchronous task
+ * (if the launch fails) or from the asynchronous task itself (if the
+ * launch succeeds). Must be:
+ * - NONE if the asynchronous execution was successful
+ * - DEVICE_UNAVAILABLE if driver is offline or busy
+ * - GENERAL_FAILURE if the asynchronous task resulted in an unspecified
+ * error
+ * - OUTPUT_INSUFFICIENT_SIZE if at least one output operand buffer is
+ * not large enough to store the corresponding output
+ * - INVALID_ARGUMENT if one of the input arguments to prepareModel is
+ * invalid
+ * @param outputShapes A list of shape information of model output operands.
+ * The index into "outputShapes" corresponds to the index of the output
+ * operand in the Request outputs vector. outputShapes must be empty
+ * unless the status is either NONE or OUTPUT_INSUFFICIENT_SIZE.
+ * @param Timing Duration of execution. Unless MeasureTiming::YES was passed
+ * when launching the execution and status is NONE, all times must be
+ * reported as UINT64_MAX. A driver may choose to report any time as
+ * UINT64_MAX, indicating that particular measurement is not available.
+ */
+ Return<void> notify_1_2(V1_0::ErrorStatus status,
+ const hidl_vec<V1_2::OutputShape>& outputShapes,
+ const V1_2::Timing& timing) override;
+
+ /**
+ * IExecutionCallback::notify_1_3 marks the callback object with the results
+ * (error status, dynamic output shapes, and timing information) of the
+ * asynchronous execution that held this callback and enables all prior and
+ * future wait calls on the ExecutionCallback object to proceed.
+ *
+ * One of the IExecutionCallback::notify* methods must be called on a given
+ * ExecutionCallback object.
+ *
+ * If the callback object is notified more than once, only the results of
+ * the first call to notify* are used, and the results from subsequent calls
+ * are discarded.
+ *
+ * @param status Error status returned from launching the asynchronous task
+ * (if the launch fails) or from the asynchronous task itself (if the
+ * launch succeeds). Must be:
+ * - NONE if the asynchronous execution was successful
+ * - DEVICE_UNAVAILABLE if driver is offline or busy
+ * - GENERAL_FAILURE if the asynchronous task resulted in an unspecified
+ * error
+ * - OUTPUT_INSUFFICIENT_SIZE if at least one output operand buffer is
+ * not large enough to store the corresponding output
+ * - INVALID_ARGUMENT if one of the input arguments to prepareModel is
+ * invalid
+ * - MISSED_DEADLINE_* if the deadline was not met
+ * @param outputShapes A list of shape information of model output operands.
+ * The index into "outputShapes" corresponds to the index of the output
+ * operand in the Request outputs vector. outputShapes must be empty
+ * unless the status is either NONE or OUTPUT_INSUFFICIENT_SIZE.
+ * @param Timing Duration of execution. Unless MeasureTiming::YES was passed
+ * when launching the execution and status is NONE, all times must be
+ * reported as UINT64_MAX. A driver may choose to report any time as
+ * UINT64_MAX, indicating that particular measurement is not available.
+ */
+ Return<void> notify_1_3(V1_3::ErrorStatus status,
+ const hidl_vec<V1_2::OutputShape>& outputShapes,
+ const V1_2::Timing& timing) override;
+
+ /**
+ * ExecutionCallback::wait blocks until notify* has been called on the
+ * callback object.
+ */
+ void wait() const;
+
+ /**
+ * Retrieves the error status returned from the asynchronous task launched
+ * by one of the IPreparedModel::execute* methods. If
+ * IPreparedModel::execute* (but not IPreparedModel::executeSynchronously*)
+ * has not finished asynchronously executing, this call will block until the
+ * asynchronous task notifies the object.
+ *
+ * @return status Error status returned from launching the asynchronous task
+ * (if the launch fails) or from the asynchronous task itself (if the
+ * launch succeeds). Must be:
+ * - NONE if the asynchronous execution was successful
+ * - DEVICE_UNAVAILABLE if driver is offline or busy
+ * - GENERAL_FAILURE if the asynchronous task resulted in an unspecified
+ * error
+ * - OUTPUT_INSUFFICIENT_SIZE if at least one output operand buffer is
+ * not large enough to store the corresponding output
+ * - INVALID_ARGUMENT if one of the input arguments to prepareModel is
+ * invalid
+ * - MISSED_DEADLINE_* if the deadline could not be met
+ */
+ V1_3::ErrorStatus getStatus() const;
+
+ /**
+ * Retrieves the error status returned from the asynchronous task launched
+ * by one of the IPreparedModel::execute* methods. If
+ * IPreparedModel::execute* (but not IPreparedModel::executeSynchronously*)
+ * has not finished asynchronously executing, this call will block until the
+ * asynchronous task notifies the object.
+ *
+ * If the asynchronous task was launched by IPreparedModel::execute, an
+ * empty vector will be returned.
+ *
+ * @return outputShapes A list of shape information of model output
+ * operands. The index into "outputShapes" corresponds to the index of
+ * the output operand in the Request outputs vector. outputShapes must
+ * be empty unless the status is either NONE or
+ * OUTPUT_INSUFFICIENT_SIZE. outputShaps may be empty if the status is
+ * NONE and all model output operands are fully-specified at execution
+ * time. outputShapes must have the same number of elements as the
+ * number of model output operands if the status is
+ * OUTPUT_INSUFFICIENT_SIZE, or if the status is NONE and the model has
+ * at least one output operand that is not fully-specified.
+ */
+ const std::vector<V1_2::OutputShape>& getOutputShapes() const;
+
+ /**
+ * Retrieves the error status returned from the asynchronous task launched
+ * by one of the IPreparedModel::execute* methods. If
+ * IPreparedModel::execute* (but not IPreparedModel::executeSynchronously*)
+ * has not finished asynchronously executing, this call will block until the
+ * asynchronous task notifies the object.
+ *
+ * If the asynchronous task was launched by IPreparedModel::execute, every
+ * time must be UINT64_MAX.
+ *
+ * @return timing Duration of the execution. Every time must be UINT64_MAX
+ * unless the status is NONE.
+ */
+ V1_2::Timing getTiming() const;
+
+ private:
+ /*
+ * ExecutionCallback::notifyInternal stores the results of the execution
+ * (status, output shapes, and timing information) in the ExecutionCallback
+ * object before any call to wait or get* return. It then enables all prior
+ * and future wait calls on the ExecutionCallback object to proceed.
+ */
+ Return<void> notifyInternal(V1_3::ErrorStatus errorStatus,
+ hidl_vec<V1_2::OutputShape> outputShapes, V1_2::Timing timing);
+
+ // members
+ mutable std::mutex mMutex;
+ mutable std::condition_variable mCondition;
+ bool mNotified GUARDED_BY(mMutex) = false;
+ V1_3::ErrorStatus mErrorStatus = V1_3::ErrorStatus::GENERAL_FAILURE;
+ std::vector<V1_2::OutputShape> mOutputShapes = {};
+ V1_2::Timing mTiming = {};
+};
+
} // namespace android::hardware::neuralnetworks::V1_3::implementation
#endif // ANDROID_HARDWARE_NEURALNETWORKS_V1_3_CALLBACKS_H
diff --git a/neuralnetworks/1.3/vts/functional/include/1.3/Utils.h b/neuralnetworks/1.3/vts/functional/include/1.3/Utils.h
new file mode 100644
index 0000000..3661b66
--- /dev/null
+++ b/neuralnetworks/1.3/vts/functional/include/1.3/Utils.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_NEURALNETWORKS_V1_3_UTILS_H
+#define ANDROID_HARDWARE_NEURALNETWORKS_V1_3_UTILS_H
+
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <iosfwd>
+
+namespace android::hardware::neuralnetworks {
+
+inline constexpr V1_3::Priority kDefaultPriority = V1_3::Priority::MEDIUM;
+
+} // namespace android::hardware::neuralnetworks
+
+namespace android::hardware::neuralnetworks::V1_3 {
+
+// pretty-print values for error messages
+::std::ostream& operator<<(::std::ostream& os, ErrorStatus errorStatus);
+
+} // namespace android::hardware::neuralnetworks::V1_3
+
+#endif // ANDROID_HARDWARE_NEURALNETWORKS_V1_3_UTILS_H
diff --git a/radio/1.5/types.hal b/radio/1.5/types.hal
index 724d014..efd2e35 100644
--- a/radio/1.5/types.hal
+++ b/radio/1.5/types.hal
@@ -16,6 +16,10 @@
package android.hardware.radio@1.5;
+import @1.0::ApnAuthType;
+import @1.0::DataProfileId;
+import @1.0::DataProfileInfoType;
+import @1.0::RadioAccessFamily;
import @1.1::EutranBands;
import @1.1::GeranBands;
import @1.1::RadioAccessNetworks;
@@ -228,6 +232,16 @@
BAND_261 = 261,
};
+enum UtranBands : @1.1::UtranBands {
+ /** TD-SCDMA bands. 3GPP TS 25.102, Table 5.2: Frequency bands */
+ BAND_A = 101,
+ BAND_B = 102,
+ BAND_C = 103,
+ BAND_D = 104,
+ BAND_E = 105,
+ BAND_F = 106,
+};
+
/**
* Overwritten from @1.2::NetworkScanRequest to update
* RadioAccessSpecifier to 1.5 version
@@ -293,13 +307,74 @@
};
/**
- * Extended from @1.4::DataProfileInfo to update ApnTypes to 1.5 version
+ * Extended from @1.4::DataProfileInfo to update ApnTypes to 1.5 version and replace mtu with
+ * mtuV4 and mtuV6. In the future, this must be extended instead of overwritten.
*/
struct DataProfileInfo {
- @1.4::DataProfileInfo base;
+
+ /** ID of the data profile. */
+ DataProfileId profileId;
+
+ /** The APN name. */
+ string apn;
+
+ /** PDP_type values. */
+ PdpProtocolType protocol;
+
+ /** PDP_type values used on roaming network. */
+ PdpProtocolType roamingProtocol;
+
+ /** APN authentication type. */
+ ApnAuthType authType;
+
+ /** The username for APN, or empty string. */
+ string user;
+
+ /** The password for APN, or empty string. */
+ string password;
+
+ /** Data profile technology type. */
+ DataProfileInfoType type;
+
+ /** The period in seconds to limit the maximum connections. */
+ int32_t maxConnsTime;
+
+ /** The maximum connections during maxConnsTime. */
+ int32_t maxConns;
+
+ /**
+ * The required wait time in seconds after a successful UE initiated disconnect of a given PDN
+ * connection before the device can send a new PDN connection request for that given PDN.
+ */
+ int32_t waitTime;
+
+ /** True to enable the profile, false to disable. */
+ bool enabled;
/** Supported APN types bitmap. See ApnTypes for the value of each bit. */
bitfield<ApnTypes> supportedApnTypesBitmap;
+
+ /** The bearer bitmap. See RadioAccessFamily for the value of each bit. */
+ bitfield<RadioAccessFamily> bearerBitmap;
+
+ /** Maximum transmission unit (MTU) size in bytes for IPv4. */
+ int32_t mtuV4;
+
+ /** Maximum transmission unit (MTU) size in bytes for IPv6. */
+ int32_t mtuV6;
+
+ /**
+ * True if this data profile was used to bring up the last default (i.e internet) data
+ * connection successfully.
+ */
+ bool preferred;
+
+ /**
+ * If true, modem must persist this data profile and profileId must not be
+ * set to DataProfileId.INVALID. If the same data profile exists, this data profile must
+ * overwrite it.
+ */
+ bool persistent;
};
/**
@@ -346,7 +421,8 @@
/**
* Overwritten from @1.4::SetupDataCallResult in order to update the addresses to 1.5
- * version. In 1.5 the type of addresses changes to vector of LinkAddress.
+ * version. In 1.5 the type of addresses changes to vector of LinkAddress, and mtu is replaced by
+ * mtuV4 and mtuV6.
*/
struct SetupDataCallResult {
/** Data call fail cause. DataCallFailCause.NONE if no error. */
@@ -400,10 +476,16 @@
vec<string> pcscf;
/**
- * MTU received from network. Value <= 0 means network has either not sent a value or sent an
- * invalid value.
+ * MTU received from network for IPv4.
+ * Value <= 0 means network has either not sent a value or sent an invalid value.
*/
- int32_t mtu;
+ int32_t mtuV4;
+
+ /**
+ * MTU received from network for IPv6.
+ * Value <= 0 means network has either not sent a value or sent an invalid value.
+ */
+ int32_t mtuV6;
};
enum Domain : int32_t {
diff --git a/radio/1.5/vts/functional/Android.bp b/radio/1.5/vts/functional/Android.bp
index 85c4f99..182985e 100644
--- a/radio/1.5/vts/functional/Android.bp
+++ b/radio/1.5/vts/functional/Android.bp
@@ -34,6 +34,7 @@
"android.hardware.radio@1.0",
"android.hardware.radio.config@1.0",
"android.hardware.radio.config@1.1",
+ "android.hardware.radio.config@1.3",
],
header_libs: ["radio.util.header@1.0"],
test_suites: ["general-tests"]
diff --git a/radio/1.5/vts/functional/radio_hidl_hal_api.cpp b/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
index 77d9a02..01258cf 100644
--- a/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
@@ -833,23 +833,24 @@
android::hardware::radio::V1_5::DataProfileInfo dataProfileInfo;
memset(&dataProfileInfo, 0, sizeof(dataProfileInfo));
- dataProfileInfo.base.profileId = DataProfileId::DEFAULT;
- dataProfileInfo.base.apn = hidl_string("internet");
- dataProfileInfo.base.protocol = PdpProtocolType::IP;
- dataProfileInfo.base.roamingProtocol = PdpProtocolType::IP;
- dataProfileInfo.base.authType = ApnAuthType::NO_PAP_NO_CHAP;
- dataProfileInfo.base.user = hidl_string("username");
- dataProfileInfo.base.password = hidl_string("password");
- dataProfileInfo.base.type = DataProfileInfoType::THREE_GPP;
- dataProfileInfo.base.maxConnsTime = 300;
- dataProfileInfo.base.maxConns = 20;
- dataProfileInfo.base.waitTime = 0;
- dataProfileInfo.base.enabled = true;
+ dataProfileInfo.profileId = DataProfileId::DEFAULT;
+ dataProfileInfo.apn = hidl_string("internet");
+ dataProfileInfo.protocol = PdpProtocolType::IP;
+ dataProfileInfo.roamingProtocol = PdpProtocolType::IP;
+ dataProfileInfo.authType = ApnAuthType::NO_PAP_NO_CHAP;
+ dataProfileInfo.user = hidl_string("username");
+ dataProfileInfo.password = hidl_string("password");
+ dataProfileInfo.type = DataProfileInfoType::THREE_GPP;
+ dataProfileInfo.maxConnsTime = 300;
+ dataProfileInfo.maxConns = 20;
+ dataProfileInfo.waitTime = 0;
+ dataProfileInfo.enabled = true;
dataProfileInfo.supportedApnTypesBitmap = 320;
- dataProfileInfo.base.bearerBitmap = 161543;
- dataProfileInfo.base.mtu = 0;
- dataProfileInfo.base.preferred = true;
- dataProfileInfo.base.persistent = false;
+ dataProfileInfo.bearerBitmap = 161543;
+ dataProfileInfo.mtuV4 = 0;
+ dataProfileInfo.mtuV6 = 0;
+ dataProfileInfo.preferred = true;
+ dataProfileInfo.persistent = false;
bool roamingAllowed = false;
@@ -884,23 +885,24 @@
// Create a dataProfileInfo
android::hardware::radio::V1_5::DataProfileInfo dataProfileInfo;
memset(&dataProfileInfo, 0, sizeof(dataProfileInfo));
- dataProfileInfo.base.profileId = DataProfileId::DEFAULT;
- dataProfileInfo.base.apn = hidl_string("internet");
- dataProfileInfo.base.protocol = PdpProtocolType::IPV4V6;
- dataProfileInfo.base.roamingProtocol = PdpProtocolType::IPV4V6;
- dataProfileInfo.base.authType = ApnAuthType::NO_PAP_NO_CHAP;
- dataProfileInfo.base.user = hidl_string("username");
- dataProfileInfo.base.password = hidl_string("password");
- dataProfileInfo.base.type = DataProfileInfoType::THREE_GPP;
- dataProfileInfo.base.maxConnsTime = 300;
- dataProfileInfo.base.maxConns = 20;
- dataProfileInfo.base.waitTime = 0;
- dataProfileInfo.base.enabled = true;
+ dataProfileInfo.profileId = DataProfileId::DEFAULT;
+ dataProfileInfo.apn = hidl_string("internet");
+ dataProfileInfo.protocol = PdpProtocolType::IPV4V6;
+ dataProfileInfo.roamingProtocol = PdpProtocolType::IPV4V6;
+ dataProfileInfo.authType = ApnAuthType::NO_PAP_NO_CHAP;
+ dataProfileInfo.user = hidl_string("username");
+ dataProfileInfo.password = hidl_string("password");
+ dataProfileInfo.type = DataProfileInfoType::THREE_GPP;
+ dataProfileInfo.maxConnsTime = 300;
+ dataProfileInfo.maxConns = 20;
+ dataProfileInfo.waitTime = 0;
+ dataProfileInfo.enabled = true;
dataProfileInfo.supportedApnTypesBitmap = 320;
- dataProfileInfo.base.bearerBitmap = 161543;
- dataProfileInfo.base.mtu = 0;
- dataProfileInfo.base.preferred = true;
- dataProfileInfo.base.persistent = false;
+ dataProfileInfo.bearerBitmap = 161543;
+ dataProfileInfo.mtuV4 = 0;
+ dataProfileInfo.mtuV6 = 0;
+ dataProfileInfo.preferred = true;
+ dataProfileInfo.persistent = false;
radio_v1_5->setInitialAttachApn_1_5(serial, dataProfileInfo);
@@ -923,23 +925,24 @@
// Create a dataProfileInfo
android::hardware::radio::V1_5::DataProfileInfo dataProfileInfo;
memset(&dataProfileInfo, 0, sizeof(dataProfileInfo));
- dataProfileInfo.base.profileId = DataProfileId::DEFAULT;
- dataProfileInfo.base.apn = hidl_string("internet");
- dataProfileInfo.base.protocol = PdpProtocolType::IPV4V6;
- dataProfileInfo.base.roamingProtocol = PdpProtocolType::IPV4V6;
- dataProfileInfo.base.authType = ApnAuthType::NO_PAP_NO_CHAP;
- dataProfileInfo.base.user = hidl_string("username");
- dataProfileInfo.base.password = hidl_string("password");
- dataProfileInfo.base.type = DataProfileInfoType::THREE_GPP;
- dataProfileInfo.base.maxConnsTime = 300;
- dataProfileInfo.base.maxConns = 20;
- dataProfileInfo.base.waitTime = 0;
- dataProfileInfo.base.enabled = true;
+ dataProfileInfo.profileId = DataProfileId::DEFAULT;
+ dataProfileInfo.apn = hidl_string("internet");
+ dataProfileInfo.protocol = PdpProtocolType::IPV4V6;
+ dataProfileInfo.roamingProtocol = PdpProtocolType::IPV4V6;
+ dataProfileInfo.authType = ApnAuthType::NO_PAP_NO_CHAP;
+ dataProfileInfo.user = hidl_string("username");
+ dataProfileInfo.password = hidl_string("password");
+ dataProfileInfo.type = DataProfileInfoType::THREE_GPP;
+ dataProfileInfo.maxConnsTime = 300;
+ dataProfileInfo.maxConns = 20;
+ dataProfileInfo.waitTime = 0;
+ dataProfileInfo.enabled = true;
dataProfileInfo.supportedApnTypesBitmap = 320;
- dataProfileInfo.base.bearerBitmap = 161543;
- dataProfileInfo.base.mtu = 0;
- dataProfileInfo.base.preferred = true;
- dataProfileInfo.base.persistent = true;
+ dataProfileInfo.bearerBitmap = 161543;
+ dataProfileInfo.mtuV4 = 0;
+ dataProfileInfo.mtuV6 = 0;
+ dataProfileInfo.preferred = true;
+ dataProfileInfo.persistent = true;
// Create a dataProfileInfoList
android::hardware::hidl_vec<android::hardware::radio::V1_5::DataProfileInfo>
diff --git a/radio/1.5/vts/functional/radio_hidl_hal_test.cpp b/radio/1.5/vts/functional/radio_hidl_hal_test.cpp
index a5d236d..c29ebf9 100644
--- a/radio/1.5/vts/functional/radio_hidl_hal_test.cpp
+++ b/radio/1.5/vts/functional/radio_hidl_hal_test.cpp
@@ -47,9 +47,9 @@
EXPECT_EQ(serial, radioRsp_v1_5->rspInfo.serial);
EXPECT_EQ(RadioError::NONE, radioRsp_v1_5->rspInfo.error);
- sp<::android::hardware::radio::config::V1_1::IRadioConfig> radioConfig =
+ sp<::android::hardware::radio::config::V1_3::IRadioConfig> radioConfig =
::testing::VtsHalHidlTargetTestBase::getService<
- ::android::hardware::radio::config::V1_1::IRadioConfig>();
+ ::android::hardware::radio::config::V1_3::IRadioConfig>();
/* Enforce Vts tesing with RadioConfig is existed. */
ASSERT_NE(nullptr, radioConfig.get());
diff --git a/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h b/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h
index 217caf5..c2a7d8a 100644
--- a/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h
+++ b/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h
@@ -22,7 +22,7 @@
#include <condition_variable>
#include <mutex>
-#include <android/hardware/radio/config/1.1/IRadioConfig.h>
+#include <android/hardware/radio/config/1.3/IRadioConfig.h>
#include <android/hardware/radio/1.5/IRadio.h>
#include <android/hardware/radio/1.5/IRadioIndication.h>
diff --git a/radio/config/1.3/Android.bp b/radio/config/1.3/Android.bp
index 88de666..7360270 100644
--- a/radio/config/1.3/Android.bp
+++ b/radio/config/1.3/Android.bp
@@ -17,6 +17,9 @@
"android.hardware.radio.config@1.1",
"android.hardware.radio.config@1.2",
"android.hardware.radio@1.0",
+ "android.hardware.radio@1.1",
+ "android.hardware.radio@1.4",
+ "android.hardware.radio@1.5",
"android.hidl.base@1.0",
],
gen_java: true,
diff --git a/radio/config/1.3/IRadioConfig.hal b/radio/config/1.3/IRadioConfig.hal
index a0ce6e0..d01f54b 100644
--- a/radio/config/1.3/IRadioConfig.hal
+++ b/radio/config/1.3/IRadioConfig.hal
@@ -27,5 +27,13 @@
* serial to different methods), multiple responses (one for each method call) must still be served.
*/
interface IRadioConfig extends @1.1::IRadioConfig {
-
+ /**
+ * Request current phone capability.
+ *
+ * @param serial Serial number of request.
+ *
+ * Response callback is IRadioResponse.getPhoneCapabilityResponse_1_3() which
+ * will return <@1.3::PhoneCapability>.
+ */
+ oneway getPhoneCapability_1_3(int32_t serial);
};
diff --git a/radio/config/1.3/IRadioConfigResponse.hal b/radio/config/1.3/IRadioConfigResponse.hal
index 9c4c971..e13aa1e 100644
--- a/radio/config/1.3/IRadioConfigResponse.hal
+++ b/radio/config/1.3/IRadioConfigResponse.hal
@@ -16,11 +16,22 @@
package android.hardware.radio.config@1.3;
+import android.hardware.radio@1.0::RadioResponseInfo;
import @1.2::IRadioConfigResponse;
+import @1.3::PhoneCapability;
/**
* Interface declaring response functions to solicited radio config requests.
*/
interface IRadioConfigResponse extends @1.2::IRadioConfigResponse {
-
+ /**
+ * @param info Response info struct containing response type, serial no. and error
+ * @param phoneCapability <@1.3::PhoneCapability> it defines modem's capability for example
+ * how many logical modems it has, how many data connections it supports.
+ *
+ * Valid errors returned:
+ * RadioError:NONE
+ * RadioError:RADIO_NOT_AVAILABLE
+ */
+ oneway getPhoneCapabilityResponse_1_3(RadioResponseInfo info, PhoneCapability phoneCapability);
};
diff --git a/radio/config/1.3/default/RadioConfig.cpp b/radio/config/1.3/default/RadioConfig.cpp
index c28119c..01e98f1 100644
--- a/radio/config/1.3/default/RadioConfig.cpp
+++ b/radio/config/1.3/default/RadioConfig.cpp
@@ -24,7 +24,6 @@
namespace implementation {
using namespace ::android::hardware::radio::V1_0;
-using namespace ::android::hardware::radio::config;
// Methods from ::android::hardware::radio::config::V1_0::IRadioConfig follow.
Return<void> RadioConfig::setResponseFunctions(
@@ -105,6 +104,14 @@
return Void();
}
+// Methods from ::android::hardware::radio::config::V1_3::IRadioConfig follow.
+Return<void> RadioConfig::getPhoneCapability_1_3(int32_t /* serial */) {
+ V1_3::PhoneCapability phoneCapability;
+ RadioResponseInfo info;
+ mRadioConfigResponseV1_3->getPhoneCapabilityResponse_1_3(info, phoneCapability);
+ return Void();
+}
+
} // namespace implementation
} // namespace V1_3
} // namespace config
diff --git a/radio/config/1.3/default/RadioConfig.h b/radio/config/1.3/default/RadioConfig.h
index 00585e6..57ff368 100644
--- a/radio/config/1.3/default/RadioConfig.h
+++ b/radio/config/1.3/default/RadioConfig.h
@@ -62,6 +62,9 @@
Return<void> setPreferredDataModem(int32_t serial, uint8_t modemId);
Return<void> setModemsConfig(int32_t serial, const V1_1::ModemsConfig& modemsConfig);
Return<void> getModemsConfig(int32_t serial);
+
+ // Methods from ::android::hardware::radio::config::V1_3::IRadioConfig follow.
+ Return<void> getPhoneCapability_1_3(int32_t serial);
};
} // namespace implementation
diff --git a/radio/config/1.3/default/RadioConfigIndication.cpp b/radio/config/1.3/default/RadioConfigIndication.cpp
index eb77a48..608fa1c 100644
--- a/radio/config/1.3/default/RadioConfigIndication.cpp
+++ b/radio/config/1.3/default/RadioConfigIndication.cpp
@@ -23,10 +23,6 @@
namespace V1_3 {
namespace implementation {
-using namespace ::android::hardware::radio::V1_0;
-using namespace ::android::hardware::radio::config::V1_0;
-using namespace ::android::hardware::radio::config::V1_2;
-
// Methods from ::android::hardware::radio::config::V1_0::IRadioConfigIndication follow.
Return<void> RadioConfigIndication::simSlotsStatusChanged(
RadioIndicationType /* type */, const hidl_vec<V1_0::SimSlotStatus>& /* slotStatus */) {
diff --git a/radio/config/1.3/default/RadioConfigIndication.h b/radio/config/1.3/default/RadioConfigIndication.h
index 3697492..c92446c 100644
--- a/radio/config/1.3/default/RadioConfigIndication.h
+++ b/radio/config/1.3/default/RadioConfigIndication.h
@@ -31,10 +31,6 @@
using namespace ::android::hardware::radio::V1_0;
using namespace ::android::hardware::radio::config;
-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;
diff --git a/radio/config/1.3/default/RadioConfigResponse.cpp b/radio/config/1.3/default/RadioConfigResponse.cpp
index 48e81da..1d48a13 100644
--- a/radio/config/1.3/default/RadioConfigResponse.cpp
+++ b/radio/config/1.3/default/RadioConfigResponse.cpp
@@ -23,11 +23,6 @@
namespace V1_3 {
namespace implementation {
-using namespace ::android::hardware::radio::V1_0;
-using namespace ::android::hardware::radio::config::V1_0;
-using namespace ::android::hardware::radio::config::V1_1;
-using namespace ::android::hardware::radio::config::V1_2;
-
// Methods from ::android::hardware::radio::config::V1_0::IRadioConfigResponse follow.
Return<void> RadioConfigResponse::getSimSlotsStatusResponse(
const RadioResponseInfo& /* info */,
@@ -73,6 +68,13 @@
return Void();
}
+// Methods from ::android::hardware::radio::config::V1_3::IRadioConfigResponse follow.
+Return<void> RadioConfigResponse::getPhoneCapabilityResponse_1_3(
+ const RadioResponseInfo& /* info */, const V1_3::PhoneCapability& /* phoneCapability */) {
+ // TODO implement
+ return Void();
+}
+
} // namespace implementation
} // namespace V1_3
} // namespace config
diff --git a/radio/config/1.3/default/RadioConfigResponse.h b/radio/config/1.3/default/RadioConfigResponse.h
index 0f0033f..dc169bb 100644
--- a/radio/config/1.3/default/RadioConfigResponse.h
+++ b/radio/config/1.3/default/RadioConfigResponse.h
@@ -28,43 +28,36 @@
namespace V1_3 {
namespace implementation {
-using ::android::sp;
-using ::android::hardware::hidl_array;
-using ::android::hardware::hidl_memory;
-using ::android::hardware::hidl_string;
+using namespace ::android::hardware::radio::config;
+
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
-using ::android::hardware::Void;
+
+using ::android::hardware::radio::V1_0::RadioResponseInfo;
struct RadioConfigResponse : public IRadioConfigResponse {
// Methods from ::android::hardware::radio::config::V1_0::IRadioConfigResponse follow.
Return<void> getSimSlotsStatusResponse(
- const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
- const hidl_vec<::android::hardware::radio::config::V1_0::SimSlotStatus>& slotStatus)
- override;
- Return<void> setSimSlotsMappingResponse(
- const ::android::hardware::radio::V1_0::RadioResponseInfo& info) override;
+ const RadioResponseInfo& info,
+ const hidl_vec<V1_0::SimSlotStatus>& slotStatus) override;
+ Return<void> setSimSlotsMappingResponse(const RadioResponseInfo& info) override;
// Methods from ::android::hardware::radio::config::V1_1::IRadioConfigResponse follow.
- Return<void> getPhoneCapabilityResponse(
- const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
- const ::android::hardware::radio::config::V1_1::PhoneCapability& phoneCapability)
- override;
- Return<void> setPreferredDataModemResponse(
- const ::android::hardware::radio::V1_0::RadioResponseInfo& info) override;
- Return<void> setModemsConfigResponse(
- const ::android::hardware::radio::V1_0::RadioResponseInfo& info) override;
- Return<void> getModemsConfigResponse(
- const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
- const ::android::hardware::radio::config::V1_1::ModemsConfig& modemsConfig) override;
+ Return<void> getPhoneCapabilityResponse(const RadioResponseInfo& info,
+ const V1_1::PhoneCapability& phoneCapability) override;
+ Return<void> setPreferredDataModemResponse(const RadioResponseInfo& info) override;
+ Return<void> setModemsConfigResponse(const RadioResponseInfo& info) override;
+ Return<void> getModemsConfigResponse(const RadioResponseInfo& info,
+ const V1_1::ModemsConfig& modemsConfig) override;
// Methods from ::android::hardware::radio::config::V1_2::IRadioConfigResponse follow.
Return<void> getSimSlotsStatusResponse_1_2(
- const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
- const hidl_vec<::android::hardware::radio::config::V1_2::SimSlotStatus>& slotStatus)
- override;
+ const RadioResponseInfo& info,
+ const hidl_vec<V1_2::SimSlotStatus>& slotStatus) override;
- // Methods from ::android::hidl::base::V1_0::IBase follow.
+ // Methods from ::android::hardware::radio::config::V1_3::IRadioConfigResponse follow.
+ Return<void> getPhoneCapabilityResponse_1_3(
+ const RadioResponseInfo& info, const V1_3::PhoneCapability& phoneCapability) override;
};
} // namespace implementation
diff --git a/radio/config/1.3/types.hal b/radio/config/1.3/types.hal
index 866002a..3414534 100644
--- a/radio/config/1.3/types.hal
+++ b/radio/config/1.3/types.hal
@@ -15,3 +15,181 @@
*/
package android.hardware.radio.config@1.3;
+
+import android.hardware.radio@1.1::GeranBands;
+import android.hardware.radio@1.1::UtranBands;
+import android.hardware.radio@1.1::EutranBands;
+import android.hardware.radio@1.4::RadioAccessFamily;
+import android.hardware.radio@1.5::NgranBands;
+
+/** Type for the SIM slot. */
+enum SlotType : int32_t {
+ /** Slot type for UICC/pSIM (physical SIM). */
+ UICC = 1,
+ /** Slot type for iUICC/iSIM (integrated SIM). */
+ IUICC = 2,
+ /** Slot type for eUICC/eSIM (embedded SIM). */
+ EUICC = 3,
+ /** Slot type for soft SIM (no physical SIM). */
+ SOFT_SIM = 4,
+};
+
+/** A field in PhoneCapability that holds information about the SIM slot. */
+struct SimSlotCapability {
+ /** Corresponds to physicalSlotId in Radio@1.2::CardStatus. */
+ uint32_t physicalSlotId;
+
+ /** Type of slot. */
+ SlotType slotType;
+};
+
+/** Bitmask of features that can be supported by a single modem. */
+enum ModemFeatures : int32_t {
+ /** 3GPP2 capability. */
+ THREE_GPP2_REG = 1 << 0,
+ /** 3GPP capability. */
+ THREE_GPP_REG = 1 << 1,
+ /** CDMA 2000 with EHRPD capability. */
+ CDMA2000_EHRPD_REG = 1 << 2,
+ /** GSM capability. */
+ GERAN_REG = 1 << 3,
+ /** UMTS capability. */
+ UTRAN_REG = 1 << 4,
+ /** LTE capability. */
+ EUTRAN_REG = 1 << 5,
+ /** 5G capability. */
+ NGRAN_REG = 1 << 6,
+ /** Dual Connectivity capability. */
+ EN_DC_REG = 1 << 7,
+ /** VoLTE capability (IMS registered). */
+ PS_VOICE_REG = 1 << 8,
+ /** CS voice call capability. */
+ CS_VOICE_SESSION = 1 << 9,
+ /** Internet connection capability. */
+ INTERACTIVE_DATA_SESSION = 1 << 10,
+ /** Dedicated bearer capability. */
+ DEDICATED_BEARER = 1 << 11,
+ /** Network scanning capability. */
+ NETWORK_SCAN = 1 << 12,
+ /** CDMA capability for SIM associated with modem. */
+ CSIM = 1 << 13,
+};
+
+struct ConcurrentModemFeatures {
+ /**
+ * A vector of concurrently supportable modem features across all modems.
+ * Each entry in the vector is a bitfield of ModemFeatures that can be used
+ * concurrently with the other ModemFeatures in that list.
+ * Each bitfield must be the full set of features for a single modem.
+ *
+ * On a Dual-SIM device, each entry will be a vector of length 2.
+ * The examples below depict the modemFeatures for four Dual-SIM setups:
+ * 1. Only one modem can PS attach (IMS registered).
+ * {
+ * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
+ * CS_VOICE_SESSION | INTERACTIVE_DATA_SESSION | DEDICATED_BEARER),
+ * (GERAN_REG | UTRAN_REG)
+ * }
+ * or
+ * {
+ * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
+ * INTERACTIVE_DATA_SESSION),
+ * (GERAN_REG | UTRAN_REG | CS_VOICE_SESSION)
+ * }
+ * 2. Both modems can PS attach (dual VoLTE).
+ * {
+ * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
+ * CS_VOICE_SESSION | INTERACTIVE_DATA_SESSION | DEDICATED_BEARER),
+ * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG)
+ * }
+ * 3. Both modems can maintain an Internet connection, but they share
+ * one RF hardware.
+ * {
+ * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
+ * CS_VOICE_SESSION | INTERACTIVE_DATA_SESSION | DEDICATED_BEARER),
+ * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
+ * INTERACTIVE_DATA_SESSION)
+ * }
+ * 4. Both modems can maintain an Internet connection, and they have
+ * their own RF hardware.
+ * {
+ * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
+ * CS_VOICE_SESSION | INTERACTIVE_DATA_SESSION | DEDICATED_BEARER),
+ * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
+ * INTERACTIVE_DATA_SESSION | DEDICATED_BEARER)
+ * }
+ */
+ vec<bitfield<ModemFeatures>> modemFeatures;
+};
+
+/**
+ * Overwritten from @1.1::PhoneCapability to add new capabilities and deprecate
+ * maxActiveData, maxActiveInternetData, isInternetLingeringSupported, logicalModemList.
+ * Replaces RadioConfig@1.1::ModemInfo and should replace Radio@1.4::RadioCapabilities
+ * in the next major version upgrade. In the future, this should be extended instead of overwritten.
+ */
+struct PhoneCapability {
+ /**
+ * 3GPP UE category for UTRAN downlink direction.
+ * 25.306 Table 5.1a
+ */
+ uint8_t utranUeCategoryDl;
+ /**
+ * 3GPP UE category for UTRAN uplink direction.
+ * 25.306 Table 5.1g
+ */
+ uint8_t utranUeCategoryUl;
+ /**
+ * 3GPP UE category for EUTRAN downlink direction.
+ * 25.306 Table 4.1a
+ */
+ uint8_t eutranUeCategoryDl;
+ /**
+ * 3GPP UE category for EUTRAN uplink direction.
+ * 25.306 Table 4.1a-2
+ */
+ uint8_t eutranUeCategoryUl;
+
+ /**
+ * Length of grace period for switching between logical modems, in milliseconds.
+ * Used only when the number of logical modems is greater than the number of
+ * Internet connections the device can support, otherwise must be 0.
+ */
+ uint64_t psDataConnectionLingerTimeMillis;
+
+ vec<GeranBands> geranBands;
+ vec<UtranBands> utranBands;
+ vec<EutranBands> eutranBands;
+ vec<NgranBands> ngranBands;
+
+ /**
+ * 32-bit bitmap of supported Radio@1.4::RadioAccessFamily types.
+ * Note that RadioAccessFamily is actually the radio access technologies, so it should be
+ * renamed in the next major version upgrade.
+ */
+ bitfield<RadioAccessFamily> supportedRats;
+
+ /**
+ * List of unique logical modem UUIDs from Radio@1.4::RadioCapabilities.
+ * A UUID is typically "com.xxxx.lmX" where X is the logical modem ID.
+ * Must be equal to the number of logical modems in the device.
+ * Radio@1.2::RadioConst::MAX_UUID_LENGTH is the max length of each UUID.
+ */
+ vec<string> logicalModemUuids;
+
+ /**
+ * List of SIM slot capabilities. The order of physical slot IDs must correspond to
+ * the order of modems in logicalModemUuids.
+ */
+ vec<SimSlotCapability> simSlotCapabilities;
+
+ /**
+ * A vector of all sets of concurrently supportable modem feature sets. The order of modems
+ * in modemFeatures must correspond to the order of modems in logicalModemUuids.
+ * Each entry in concurrentFeatureSupport is independent of others in the list
+ * and represents a set of concurrently supportable features across all modems.
+ * Each entry in ConcurrentModemFeatures::modemFeatures is a bitfield of
+ * concurrently supported ModemFeatures for one modem.
+ */
+ vec<ConcurrentModemFeatures> concurrentFeatureSupport;
+};
diff --git a/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp b/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp
index 07e9ede..7f90023 100644
--- a/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp
+++ b/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp
@@ -17,3 +17,34 @@
#include <radio_config_hidl_hal_utils.h>
#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
+
+/*
+ * Test IRadioConfig.getPhoneCapability_1_3()
+ */
+TEST_P(RadioConfigHidlTest, getPhoneCapability_1_3) {
+ serial = GetRandomSerialNumber();
+ Return<void> res = radioConfig->getPhoneCapability_1_3(serial);
+ ASSERT_OK(res);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(RadioResponseType::SOLICITED, radioConfigRsp->rspInfo.type);
+ EXPECT_EQ(serial, radioConfigRsp->rspInfo.serial);
+ ALOGI("getPhoneCapability_1_3, rspInfo.error = %s\n",
+ toString(radioConfigRsp->rspInfo.error).c_str());
+
+ ASSERT_TRUE(CheckAnyOfErrors(
+ radioConfigRsp->rspInfo.error,
+ {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE, RadioError::INTERNAL_ERR}));
+
+ if (radioConfigRsp->rspInfo.error == RadioError ::NONE) {
+ int numModems = radioConfigRsp->phoneCap_1_3.logicalModemUuids.size();
+ EXPECT_GE(numModems, 0);
+ // length of simSlotCapabilities should be equal to length of logicalModemUuids.
+ EXPECT_EQ(numModems, radioConfigRsp->phoneCap_1_3.simSlotCapabilities.size());
+ // length of modemFeatures in each ConcurrentModemFeatures should be
+ // equal to length of logicalModemUuids.
+ for (V1_3::ConcurrentModemFeatures cmf :
+ radioConfigRsp->phoneCap_1_3.concurrentFeatureSupport) {
+ EXPECT_EQ(numModems, cmf.modemFeatures.size());
+ }
+ }
+}
diff --git a/radio/config/1.3/vts/functional/radio_config_hidl_hal_test.cpp b/radio/config/1.3/vts/functional/radio_config_hidl_hal_test.cpp
index dbb4bf4..cd48b25 100644
--- a/radio/config/1.3/vts/functional/radio_config_hidl_hal_test.cpp
+++ b/radio/config/1.3/vts/functional/radio_config_hidl_hal_test.cpp
@@ -17,7 +17,7 @@
#include <radio_config_hidl_hal_utils.h>
void RadioConfigHidlTest::SetUp() {
- radioConfig = ::android::hardware::radio::config::V1_3::IRadioConfig::getService(GetParam());
+ radioConfig = V1_3::IRadioConfig::getService(GetParam());
ASSERT_NE(nullptr, radioConfig.get());
radioConfigRsp = new (std::nothrow) RadioConfigResponse(*this);
diff --git a/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h b/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h
index 9b78c04..b21c7c0 100644
--- a/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h
+++ b/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h
@@ -28,19 +28,18 @@
#include <gtest/gtest.h>
#include <hidl/GtestPrinter.h>
#include <hidl/ServiceManagement.h>
+#include <log/log.h>
#include "vts_test_util.h"
-using namespace ::android::hardware::radio::config::V1_1;
-using namespace ::android::hardware::radio::config::V1_2;
-using namespace ::android::hardware::radio::config::V1_3;
+using namespace ::android::hardware::radio::config;
using ::android::sp;
-using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
+using ::android::hardware::radio::V1_0::RadioIndicationType;
using ::android::hardware::radio::V1_0::RadioResponseInfo;
using ::android::hardware::radio::V1_0::RadioResponseType;
@@ -49,43 +48,46 @@
class RadioConfigHidlTest;
/* Callback class for radio config response */
-class RadioConfigResponse : public ::android::hardware::radio::config::V1_3::IRadioConfigResponse {
+class RadioConfigResponse : public V1_3::IRadioConfigResponse {
protected:
RadioConfigHidlTest& parent;
public:
RadioResponseInfo rspInfo;
- PhoneCapability phoneCap;
+ V1_1::PhoneCapability phoneCap_1_1;
+ V1_3::PhoneCapability phoneCap_1_3;
RadioConfigResponse(RadioConfigHidlTest& parent);
virtual ~RadioConfigResponse() = default;
/* 1.0 Api */
- Return<void> getSimSlotsStatusResponse(
- const RadioResponseInfo& info,
- const hidl_vec<::android::hardware::radio::config::V1_0::SimSlotStatus>& slotStatus);
+ Return<void> getSimSlotsStatusResponse(const RadioResponseInfo& info,
+ const hidl_vec<V1_0::SimSlotStatus>& slotStatus);
Return<void> setSimSlotsMappingResponse(const RadioResponseInfo& info);
/* 1.1 Api */
Return<void> getPhoneCapabilityResponse(const RadioResponseInfo& info,
- const PhoneCapability& phoneCapability);
+ const V1_1::PhoneCapability& phoneCapability);
Return<void> setPreferredDataModemResponse(const RadioResponseInfo& info);
Return<void> getModemsConfigResponse(const RadioResponseInfo& info,
- const ModemsConfig& mConfig);
+ const V1_1::ModemsConfig& mConfig);
Return<void> setModemsConfigResponse(const RadioResponseInfo& info);
/* 1.2 Api */
Return<void> getSimSlotsStatusResponse_1_2(const RadioResponseInfo& info,
- const hidl_vec<SimSlotStatus>& slotStatus);
+ const hidl_vec<V1_2::SimSlotStatus>& slotStatus);
+
+ /* 1.3 Api */
+ Return<void> getPhoneCapabilityResponse_1_3(const RadioResponseInfo& info,
+ const V1_3::PhoneCapability& phoneCapability);
};
/* Callback class for radio config indication */
-class RadioConfigIndication
- : public ::android::hardware::radio::config::V1_3::IRadioConfigIndication {
+class RadioConfigIndication : public V1_3::IRadioConfigIndication {
protected:
RadioConfigHidlTest& parent;
@@ -94,9 +96,8 @@
virtual ~RadioConfigIndication() = default;
/* 1.2 Api */
- Return<void> simSlotsStatusChanged_1_2(
- ::android::hardware::radio::V1_0::RadioIndicationType type,
- const hidl_vec<SimSlotStatus>& slotStatus);
+ Return<void> simSlotsStatusChanged_1_2(RadioIndicationType type,
+ const hidl_vec<V1_2::SimSlotStatus>& slotStatus);
};
// The main test class for Radio config HIDL.
@@ -121,7 +122,7 @@
int serial;
/* radio config service handle */
- sp<::android::hardware::radio::config::V1_3::IRadioConfig> radioConfig;
+ sp<V1_3::IRadioConfig> radioConfig;
/* radio config response handle */
sp<RadioConfigResponse> radioConfigRsp;
diff --git a/radio/config/1.3/vts/functional/radio_config_response.cpp b/radio/config/1.3/vts/functional/radio_config_response.cpp
index 1ca960e..22098d3 100644
--- a/radio/config/1.3/vts/functional/radio_config_response.cpp
+++ b/radio/config/1.3/vts/functional/radio_config_response.cpp
@@ -16,17 +16,18 @@
#include <radio_config_hidl_hal_utils.h>
-using ::android::hardware::radio::V1_0::RadioResponseInfo;
+using namespace ::android::hardware::radio::config;
-// SimSlotStatus slotStatus;
+using ::android::hardware::hidl_vec;
+
+using ::android::hardware::radio::V1_0::RadioResponseInfo;
RadioConfigResponse::RadioConfigResponse(RadioConfigHidlTest& parent) : parent(parent) {}
/* 1.0 Apis */
Return<void> RadioConfigResponse::getSimSlotsStatusResponse(
const RadioResponseInfo& /* info */,
- const ::android::hardware::hidl_vec<
- ::android::hardware::radio::config::V1_0::SimSlotStatus>& /* slotStatus */) {
+ const hidl_vec<V1_0::SimSlotStatus>& /* slotStatus */) {
return Void();
}
@@ -36,9 +37,9 @@
/* 1.1 Apis */
Return<void> RadioConfigResponse::getPhoneCapabilityResponse(
- const RadioResponseInfo& info, const PhoneCapability& phoneCapability) {
+ const RadioResponseInfo& info, const V1_1::PhoneCapability& phoneCapability) {
rspInfo = info;
- phoneCap = phoneCapability;
+ phoneCap_1_1 = phoneCapability;
parent.notify(info.serial);
return Void();
}
@@ -49,7 +50,7 @@
}
Return<void> RadioConfigResponse::getModemsConfigResponse(const RadioResponseInfo& /* info */,
- const ModemsConfig& /* mConfig */) {
+ const V1_1::ModemsConfig& /* mConfig */) {
return Void();
}
@@ -60,6 +61,15 @@
/* 1.2 Apis */
Return<void> RadioConfigResponse::getSimSlotsStatusResponse_1_2(
const RadioResponseInfo& /* info */,
- const ::android::hardware::hidl_vec<SimSlotStatus>& /* slotStatus */) {
+ const hidl_vec<V1_2::SimSlotStatus>& /* slotStatus */) {
return Void();
-}
\ No newline at end of file
+}
+
+/* 1.3 Apis */
+Return<void> RadioConfigResponse::getPhoneCapabilityResponse_1_3(
+ const RadioResponseInfo& info, const V1_3::PhoneCapability& phoneCapability) {
+ rspInfo = info;
+ phoneCap_1_3 = phoneCapability;
+ parent.notify(info.serial);
+ return Void();
+}
diff --git a/tv/tuner/1.0/types.hal b/tv/tuner/1.0/types.hal
index 3afe37d..2eaab36 100644
--- a/tv/tuner/1.0/types.hal
+++ b/tv/tuner/1.0/types.hal
@@ -1131,9 +1131,14 @@
@export
enum FrontendAnalogType : uint32_t {
UNDEFINED = 0,
- PAL = 1 << 0,
- SECAM = 1 << 1,
- NTSC = 1 << 2,
+ AUTO = 1 << 0,
+ PAL = 1 << 1,
+ PAL_M = 1 << 2,
+ PAL_N = 1 << 3,
+ PAL_60 = 1 << 4,
+ NTSC = 1 << 5,
+ NTSC_443 = 1 << 6,
+ SECAM = 1 << 7,
};
/**
@@ -1142,23 +1147,24 @@
@export
enum FrontendAnalogSifStandard : uint32_t {
UNDEFINED = 0,
- BG = 1 << 0,
- BG_A2 = 1 << 1,
- BG_NICAM = 1 << 2,
- I = 1 << 3,
- DK = 1 << 4,
- DK1 = 1 << 5,
- DK2 = 1 << 6,
- DK3 = 1 << 7,
- DK_NICAM = 1 << 8,
- L = 1 << 9,
- M = 1 << 10,
- M_BTSC = 1 << 11,
- M_A2 = 1 << 12,
- M_EIA_J = 1 << 13,
- I_NICAM = 1 << 14,
- L_NICAM = 1 << 15,
- L_PRIME = 1 << 16,
+ AUTO = 1 << 0,
+ BG = 1 << 1,
+ BG_A2 = 1 << 2,
+ BG_NICAM = 1 << 3,
+ I = 1 << 4,
+ DK = 1 << 5,
+ DK1_A2 = 1 << 6,
+ DK2_A2 = 1 << 7,
+ DK3_A2 = 1 << 8,
+ DK_NICAM = 1 << 9,
+ L = 1 << 10,
+ M = 1 << 11,
+ M_BTSC = 1 << 12,
+ M_A2 = 1 << 13,
+ M_EIAJ = 1 << 14,
+ I_NICAM = 1 << 15,
+ L_NICAM = 1 << 16,
+ L_PRIME = 1 << 17,
};
/**