Merge "Revert "android.hardware.bluetooth@1.0-impl: Updated to vendor_available:true""
diff --git a/audio/common/all-versions/test/utility/Android.bp b/audio/common/all-versions/test/utility/Android.bp
index 1602d25..757f8a8 100644
--- a/audio/common/all-versions/test/utility/Android.bp
+++ b/audio/common/all-versions/test/utility/Android.bp
@@ -25,7 +25,7 @@
cc_library_static {
name: "android.hardware.audio.common.test.utility",
- defaults : ["hidl_defaults"],
+ defaults: ["hidl_defaults"],
srcs: ["src/ValidateXml.cpp"],
cflags: [
"-O0",
@@ -34,7 +34,34 @@
],
local_include_dirs: ["include/utility"],
export_include_dirs: ["include"],
- shared_libs: ["libxml2", "liblog"],
+ shared_libs: [
+ "libxml2",
+ "liblog",
+ ],
static_libs: ["libgtest"],
export_static_lib_headers: ["libgtest"],
}
+
+// Note: this isn't a VTS test, but rather a unit test
+// to verify correctness of test utilities.
+cc_test {
+ name: "android.hardware.audio.common.test.utility_tests",
+ host_supported: true,
+ local_include_dirs: ["include/utility"],
+ srcs: [
+ "src/ValidateXml.cpp",
+ "tests/utility_tests.cpp",
+ ],
+ cflags: [
+ "-Werror",
+ "-Wall",
+ "-g",
+ ],
+ shared_libs: [
+ "libbase",
+ "libxml2",
+ "liblog",
+ ],
+ static_libs: ["libgtest"],
+ test_suites: ["general-tests"],
+}
diff --git a/audio/common/all-versions/test/utility/TEST_MAPPING b/audio/common/all-versions/test/utility/TEST_MAPPING
new file mode 100644
index 0000000..0bc1871
--- /dev/null
+++ b/audio/common/all-versions/test/utility/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "android.hardware.audio.common.test.utility_tests"
+ }
+ ]
+}
diff --git a/audio/common/all-versions/test/utility/src/ValidateXml.cpp b/audio/common/all-versions/test/utility/src/ValidateXml.cpp
index a866104..f111c01 100644
--- a/audio/common/all-versions/test/utility/src/ValidateXml.cpp
+++ b/audio/common/all-versions/test/utility/src/ValidateXml.cpp
@@ -112,7 +112,8 @@
return ::testing::AssertionFailure() << "Failed to parse xml\n" << context();
}
- if (xmlXIncludeProcess(doc.get()) == -1) {
+ // Process 'include' directives w/o modifying elements loaded from included files.
+ if (xmlXIncludeProcessFlags(doc.get(), XML_PARSE_NOBASEFIX) == -1) {
return ::testing::AssertionFailure() << "Failed to resolve xincludes in xml\n" << context();
}
diff --git a/audio/common/all-versions/test/utility/tests/utility_tests.cpp b/audio/common/all-versions/test/utility/tests/utility_tests.cpp
new file mode 100644
index 0000000..c523066
--- /dev/null
+++ b/audio/common/all-versions/test/utility/tests/utility_tests.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+
+#include <ValidateXml.h>
+
+using ::android::hardware::audio::common::test::utility::validateXml;
+
+const char* XSD_SOURCE =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<xs:schema version=\"2.0\""
+ " elementFormDefault=\"qualified\""
+ " attributeFormDefault=\"unqualified\""
+ " xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
+ " <xs:element name=\"audioPolicyConfiguration\">"
+ " <xs:complexType>"
+ " <xs:sequence>"
+ " <xs:element name=\"modules\">"
+ " <xs:complexType>"
+ " <xs:sequence>"
+ " <xs:element name=\"module\" maxOccurs=\"unbounded\">"
+ " <xs:complexType>"
+ " <xs:attribute name=\"name\" type=\"xs:string\" use=\"required\"/>"
+ " </xs:complexType>"
+ " </xs:element>"
+ " </xs:sequence>"
+ " </xs:complexType>"
+ " </xs:element>"
+ " </xs:sequence>"
+ " </xs:complexType>"
+ " </xs:element>"
+ "</xs:schema>";
+
+const char* INVALID_XML_SOURCE =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<audioPolicyKonfiguration />";
+
+const char* VALID_XML_SOURCE =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<audioPolicyConfiguration>"
+ " <modules>"
+ " <module name=\"aaa\" />"
+ " %s"
+ " </modules>"
+ "</audioPolicyConfiguration>";
+
+const char* MODULE_SOURCE = "<module name=\"bbb\" />";
+
+const char* XI_INCLUDE = "<xi:include xmlns:xi=\"http://www.w3.org/2001/XInclude\" href=\"%s\" />";
+
+const char* XML_INCLUDED_SOURCE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>%s";
+
+namespace {
+
+std::string substitute(const char* fmt, const char* param) {
+ std::string buffer(static_cast<size_t>(strlen(fmt) + strlen(param)), '\0');
+ snprintf(buffer.data(), buffer.size(), fmt, param);
+ return buffer;
+}
+
+std::string substitute(const char* fmt, const std::string& s) {
+ return substitute(fmt, s.c_str());
+}
+
+} // namespace
+
+TEST(ValidateXml, InvalidXml) {
+ TemporaryFile xml;
+ ASSERT_TRUE(android::base::WriteStringToFile(INVALID_XML_SOURCE, xml.path)) << strerror(errno);
+ TemporaryFile xsd;
+ ASSERT_TRUE(android::base::WriteStringToFile(XSD_SOURCE, xsd.path)) << strerror(errno);
+ EXPECT_FALSE(validateXml("xml", "xsd", xml.path, xsd.path));
+}
+
+TEST(ValidateXml, ValidXml) {
+ TemporaryFile xml;
+ ASSERT_TRUE(
+ android::base::WriteStringToFile(substitute(VALID_XML_SOURCE, MODULE_SOURCE), xml.path))
+ << strerror(errno);
+ TemporaryFile xsd;
+ ASSERT_TRUE(android::base::WriteStringToFile(XSD_SOURCE, xsd.path)) << strerror(errno);
+ EXPECT_TRUE(validateXml("xml", "xsd", xml.path, xsd.path));
+}
+
+TEST(ValidateXml, IncludeAbsolutePath) {
+ TemporaryFile xmlInclude;
+ ASSERT_TRUE(android::base::WriteStringToFile(substitute(XML_INCLUDED_SOURCE, MODULE_SOURCE),
+ xmlInclude.path))
+ << strerror(errno);
+ TemporaryFile xml;
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ substitute(VALID_XML_SOURCE, substitute(XI_INCLUDE, xmlInclude.path)), xml.path))
+ << strerror(errno);
+ TemporaryFile xsd;
+ ASSERT_TRUE(android::base::WriteStringToFile(XSD_SOURCE, xsd.path)) << strerror(errno);
+ EXPECT_TRUE(validateXml("xml", "xsd", xml.path, xsd.path));
+}
+
+TEST(ValidateXml, IncludeSameDirRelativePath) {
+ TemporaryFile xmlInclude;
+ ASSERT_TRUE(android::base::WriteStringToFile(substitute(XML_INCLUDED_SOURCE, MODULE_SOURCE),
+ xmlInclude.path))
+ << strerror(errno);
+ TemporaryFile xml;
+ ASSERT_EQ(android::base::Dirname(xml.path), android::base::Dirname(xmlInclude.path));
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ substitute(VALID_XML_SOURCE,
+ substitute(XI_INCLUDE, android::base::Basename(xmlInclude.path))),
+ xml.path))
+ << strerror(errno);
+ TemporaryFile xsd;
+ ASSERT_TRUE(android::base::WriteStringToFile(XSD_SOURCE, xsd.path)) << strerror(errno);
+ EXPECT_TRUE(validateXml("xml", "xsd", xml.path, xsd.path));
+}
+
+TEST(ValidateXml, IncludeSubdirRelativePath) {
+ TemporaryDir xmlIncludeDir;
+ TemporaryFile xmlInclude(xmlIncludeDir.path);
+ ASSERT_TRUE(android::base::WriteStringToFile(substitute(XML_INCLUDED_SOURCE, MODULE_SOURCE),
+ xmlInclude.path))
+ << strerror(errno);
+ TemporaryFile xml;
+ ASSERT_EQ(android::base::Dirname(xml.path), android::base::Dirname(xmlIncludeDir.path));
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ substitute(VALID_XML_SOURCE,
+ substitute(XI_INCLUDE, android::base::Basename(xmlIncludeDir.path) + "/" +
+ android::base::Basename(xmlInclude.path))),
+ xml.path))
+ << strerror(errno);
+ TemporaryFile xsd;
+ ASSERT_TRUE(android::base::WriteStringToFile(XSD_SOURCE, xsd.path)) << strerror(errno);
+ EXPECT_TRUE(validateXml("xml", "xsd", xml.path, xsd.path));
+}
+
+TEST(ValidateXml, IncludeParentDirRelativePath) {
+ // An XML file from a subdirectory includes a file from the parent directory using '..' syntax.
+ TemporaryFile xmlInclude;
+ ASSERT_TRUE(android::base::WriteStringToFile(substitute(XML_INCLUDED_SOURCE, MODULE_SOURCE),
+ xmlInclude.path))
+ << strerror(errno);
+ TemporaryDir xmlIncludeDir;
+ TemporaryFile xmlParentInclude(xmlIncludeDir.path);
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ substitute(XML_INCLUDED_SOURCE,
+ substitute(XI_INCLUDE, "../" + android::base::Basename(xmlInclude.path))),
+ xmlParentInclude.path))
+ << strerror(errno);
+ TemporaryFile xml;
+ ASSERT_EQ(android::base::Dirname(xml.path), android::base::Dirname(xmlInclude.path));
+ ASSERT_EQ(android::base::Dirname(xml.path), android::base::Dirname(xmlIncludeDir.path));
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ substitute(
+ VALID_XML_SOURCE,
+ substitute(XI_INCLUDE, android::base::Basename(xmlIncludeDir.path) + "/" +
+ android::base::Basename(xmlParentInclude.path))),
+ xml.path))
+ << strerror(errno);
+ TemporaryFile xsd;
+ ASSERT_TRUE(android::base::WriteStringToFile(XSD_SOURCE, xsd.path)) << strerror(errno);
+ EXPECT_TRUE(validateXml("xml", "xsd", xml.path, xsd.path));
+}
diff --git a/memtrack/aidl/android/hardware/memtrack/IMemtrack.aidl b/memtrack/aidl/android/hardware/memtrack/IMemtrack.aidl
index e78d4d7..13c3389 100644
--- a/memtrack/aidl/android/hardware/memtrack/IMemtrack.aidl
+++ b/memtrack/aidl/android/hardware/memtrack/IMemtrack.aidl
@@ -31,12 +31,14 @@
* accounting for stride, bit depth, rounding up to page size, etc.
*
* The following getMemory() categories are important for memory accounting in
- * `dumpsys meminfo` and should be reported as described below:
+ * Android frameworks (e.g. `dumpsys meminfo`) and should be reported as described
+ * below:
*
* - MemtrackType::GRAPHICS and MemtrackRecord::FLAG_SMAPS_UNACCOUNTED
- * This should report the PSS of all DMA buffers mapped by the process
- * with the specified PID. This PSS can be calculated using ReadDmaBufPss()
- * form libdmabufinfo.
+ * This should report the PSS of all CPU-Mapped DMA-BUFs (buffers mapped into
+ * the process address space) and all GPU-Mapped DMA-BUFs (buffers mapped into
+ * the GPU device address space on behalf of the process), removing any overlap
+ * between the CPU-mapped and GPU-mapped sets.
*
* - MemtrackType::GL and MemtrackRecord::FLAG_SMAPS_UNACCOUNTED
* This category should report all GPU private allocations for the specified
@@ -46,6 +48,10 @@
* Any other memory not accounted for in /proc/<pid>/smaps if any, otherwise
* this should return 0.
*
+ * SMAPS_UNACCOUNTED memory should also include memory that is mapped with
+ * VM_PFNMAP flag set. For these mappings PSS and RSS are reported as 0 in smaps.
+ * Such mappings have no backing page structs from which PSS/RSS can be calculated.
+ *
* Constructor for the interface should be used to perform memtrack management
* setup actions and must be called once before any calls to getMemory().
*/
diff --git a/neuralnetworks/aidl/utils/src/Device.cpp b/neuralnetworks/aidl/utils/src/Device.cpp
index 0fd453b..e80de0b 100644
--- a/neuralnetworks/aidl/utils/src/Device.cpp
+++ b/neuralnetworks/aidl/utils/src/Device.cpp
@@ -119,7 +119,7 @@
<< numberOfCacheFiles.numDataCache << " vs " << nn::kMaxNumberOfCacheFiles
<< ")";
}
- return std::make_pair(numberOfCacheFiles.numDataCache, numberOfCacheFiles.numModelCache);
+ return std::make_pair(numberOfCacheFiles.numModelCache, numberOfCacheFiles.numDataCache);
}
} // namespace
diff --git a/neuralnetworks/aidl/utils/test/DeviceTest.cpp b/neuralnetworks/aidl/utils/test/DeviceTest.cpp
index e53b0a8..f121aca 100644
--- a/neuralnetworks/aidl/utils/test/DeviceTest.cpp
+++ b/neuralnetworks/aidl/utils/test/DeviceTest.cpp
@@ -58,7 +58,7 @@
const std::shared_ptr<BnDevice> kInvalidDevice;
constexpr PerformanceInfo kNoPerformanceInfo = {.execTime = std::numeric_limits<float>::max(),
.powerUsage = std::numeric_limits<float>::max()};
-constexpr NumberOfCacheFiles kNumberOfCacheFiles = {.numModelCache = nn::kMaxNumberOfCacheFiles,
+constexpr NumberOfCacheFiles kNumberOfCacheFiles = {.numModelCache = nn::kMaxNumberOfCacheFiles - 1,
.numDataCache = nn::kMaxNumberOfCacheFiles};
constexpr auto makeStatusOk = [] { return ndk::ScopedAStatus::ok(); };
@@ -300,6 +300,21 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
+TEST(DeviceTest, getNumberOfCacheFilesNeeded) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(1);
+
+ // run test
+ const auto result = Device::create(kName, mockDevice);
+
+ // verify result
+ ASSERT_TRUE(result.has_value());
+ constexpr auto kNumberOfCacheFilesPair = std::make_pair<uint32_t, uint32_t>(
+ kNumberOfCacheFiles.numModelCache, kNumberOfCacheFiles.numDataCache);
+ EXPECT_EQ(result.value()->getNumberOfCacheFilesNeeded(), kNumberOfCacheFilesPair);
+}
+
TEST(DeviceTest, getNumberOfCacheFilesNeededError) {
// setup call
const auto mockDevice = createMockDevice();
diff --git a/security/keymint/aidl/android/hardware/security/keymint/DeviceInfo.aidl b/security/keymint/aidl/android/hardware/security/keymint/DeviceInfo.aidl
index 32d69cd..b0761bf 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/DeviceInfo.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/DeviceInfo.aidl
@@ -44,6 +44,12 @@
* ? "vendor_patch_level" : uint, // YYYYMMDD
* "version" : 1, // The CDDL schema version.
* "security_level" : "tee" / "strongbox"
+ * "att_id_state": "locked" / "open", // Attestation IDs State. If "locked", this
+ * // indicates a device's attestable IDs are
+ * // factory-locked and immutable. If "open",
+ * // this indicates the device is still in a
+ * // provisionable state and the attestable IDs
+ * // are not yet frozen.
* }
*/
byte[] deviceInfo;
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
index 88b2a26..1849723 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -275,6 +275,10 @@
* must return ErrorCode::INVALID_ARGUMENT. The values 3 and 65537 must be supported. It is
* recommended to support all prime values up to 2^64.
*
+ * o Tag::CERTIFICATE_NOT_BEFORE and Tag::CERTIFICATE_NOT_AFTER specify the valid date range for
+ * the returned X.509 certificate holding the public key. If omitted, generateKey must return
+ * ErrorCode::MISSING_NOT_BEFORE or ErrorCode::MISSING_NOT_AFTER.
+ *
* The following parameters are not necessary to generate a usable RSA key, but generateKey must
* not return an error if they are omitted:
*
@@ -295,6 +299,10 @@
* Tag::EC_CURVE must be provided to generate an ECDSA key. If it is not provided, generateKey
* must return ErrorCode::UNSUPPORTED_KEY_SIZE. TEE IKeyMintDevice implementations must support
* all curves. StrongBox implementations must support P_256.
+
+ * Tag::CERTIFICATE_NOT_BEFORE and Tag::CERTIFICATE_NOT_AFTER must be provided to specify the
+ * valid date range for the returned X.509 certificate holding the public key. If omitted,
+ * generateKey must return ErrorCode::MISSING_NOT_BEFORE or ErrorCode::MISSING_NOT_AFTER.
*
* == AES Keys ==
*
diff --git a/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl b/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl
index 31dbb28..24cdbc1 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl
@@ -158,20 +158,7 @@
* payload: bstr .cbor BccPayload
* ]
*
- * VerifiedDeviceInfo = {
- * ? "brand" : tstr,
- * ? "manufacturer" : tstr,
- * ? "product" : tstr,
- * ? "model" : tstr,
- * ? "board" : tstr,
- * ? "device" : tstr,
- * ? "vb_state" : "green" / "yellow" / "orange",
- * ? "bootloader_state" : "locked" / "unlocked",
- * ? "os_version" : tstr,
- * ? "system_patch_level" : uint, // YYYYMMDD
- * ? "boot_patch_level" : uint, // YYYYMMDD
- * ? "vendor_patch_level" : uint, // YYYYMMDD
- * }
+ * VerifiedDeviceInfo = DeviceInfo // See DeviceInfo.aidl
*
* PubKeyX25519 = { // COSE_Key
* 1 : 1, // Key type : Octet Key Pair
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
index 972ce2e..b28ebcb 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
@@ -286,7 +286,7 @@
*
* Must be hardware-enforced.
*
- * TODO(b/191458710): find out if this tag is still supported.
+ * TODO(b/191738660): Remove in KeyMint V2. Currently only used for FDE.
*/
MIN_SECONDS_BETWEEN_OPS = TagType.UINT | 403,
@@ -477,12 +477,12 @@
/**
* Tag::TRUSTED_CONFIRMATION_REQUIRED is only applicable to keys with KeyPurpose SIGN, and
- * specifies that this key must not be usable unless the user provides confirmation of the data
- * to be signed. Confirmation is proven to keyMint via an approval token. See
- * CONFIRMATION_TOKEN, as well as the ConfirmationUI HAL.
+ * specifies that this key must not be usable unless the user provides confirmation of the data
+ * to be signed. Confirmation is proven to keyMint via an approval token. See the authToken
+ * parameter of begin(), as well as the ConfirmationUI HAL.
*
* If an attempt to use a key with this tag does not have a cryptographically valid
- * CONFIRMATION_TOKEN provided to finish() or if the data provided to update()/finish() does not
+ * token provided to finish() or if the data provided to update()/finish() does not
* match the data described in the token, keyMint must return NO_USER_CONFIRMATION.
*
* Must be hardware-enforced.
@@ -491,9 +491,11 @@
/**
* Tag::UNLOCKED_DEVICE_REQUIRED specifies that the key may only be used when the device is
- * unlocked.
+ * unlocked, as reported to KeyMint via authToken operation parameter and the
+ * IKeyMintDevice::deviceLocked() method
*
- * Must be software-enforced.
+ * Must be hardware-enforced (but is also keystore-enforced on a per-user basis: see the
+ * deviceLocked() documentation).
*/
UNLOCKED_DEVICE_REQUIRED = TagType.BOOL | 509,
@@ -825,11 +827,22 @@
/**
* DEVICE_UNIQUE_ATTESTATION is an argument to IKeyMintDevice::attested key generation/import
* operations. It indicates that attestation using a device-unique key is requested, rather
- * than a batch key. When a device-unique key is used, the returned chain should contain two
- * certificates:
+ * than a batch key. When a device-unique key is used, the returned chain should contain two or
+ * three certificates.
+ *
+ * In case the chain contains two certificates, they should be:
* * The attestation certificate, containing the attestation extension, as described in
- KeyCreationResult.aidl.
+ * KeyCreationResult.aidl.
* * A self-signed root certificate, signed by the device-unique key.
+ *
+ * In case the chain contains three certificates, they should be:
+ * * The attestation certificate, containing the attestation extension, as described in
+ * KeyCreationResult.aidl, signed by the device-unique key.
+ * * An intermediate certificate, containing the public portion of the device-unique key.
+ * * A self-signed root certificate, signed by a dedicated key, certifying the
+ * intermediate. Ideally, the dedicated key would be the same for all StrongBox
+ * instances of the same manufacturer to ease validation.
+ *
* No additional chained certificates are provided. Only SecurityLevel::STRONGBOX
* IKeyMintDevices may support device-unique attestations. SecurityLevel::TRUSTED_ENVIRONMENT
* IKeyMintDevices must return ErrorCode::INVALID_ARGUMENT if they receive
@@ -864,8 +877,9 @@
*
* STORAGE_KEY is used to denote that a key generated or imported is a key used for storage
* encryption. Keys of this type can either be generated or imported or secure imported using
- * keyMint. exportKey() can be used to re-wrap storage key with a per-boot ephemeral key
- * wrapped key once the key characteristics are enforced.
+ * keyMint. The convertStorageKeyToEphemeral() method of IKeyMintDevice can be used to re-wrap
+ * storage key with a per-boot ephemeral key wrapped key once the key characteristics are
+ * enforced.
*
* Keys with this tag cannot be used for any operation within keyMint.
* ErrorCode::INVALID_OPERATION is returned when a key with Tag::STORAGE_KEY is provided to
@@ -875,7 +889,7 @@
/**
* OBSOLETE: Do not use. See IKeyMintOperation.updateAad instead.
- * TODO: Delete when keystore1 is deleted.
+ * TODO(b/191738660): Remove in KeyMint v2.
*/
ASSOCIATED_DATA = TagType.BYTES | 1000,
@@ -914,11 +928,10 @@
RESET_SINCE_ID_ROTATION = TagType.BOOL | 1004,
/**
- * Tag::CONFIRMATION_TOKEN is used to deliver a cryptographic token proving that the user
- * confirmed a signing request. The content is a full-length HMAC-SHA256 value. See the
- * ConfirmationUI HAL for details of token computation.
+ * OBSOLETE: Do not use. See the authToken parameter for IKeyMintDevice::begin and for
+ * IKeyMintOperation methods instead.
*
- * Must never appear in KeyCharacteristics.
+ * TODO(b/191738660): Delete when keystore1 is deleted.
*/
CONFIRMATION_TOKEN = TagType.BYTES | 1005,
diff --git a/security/keymint/aidl/vts/functional/Android.bp b/security/keymint/aidl/vts/functional/Android.bp
index ff08ce6..386029f 100644
--- a/security/keymint/aidl/vts/functional/Android.bp
+++ b/security/keymint/aidl/vts/functional/Android.bp
@@ -23,16 +23,11 @@
default_applicable_licenses: ["hardware_interfaces_license"],
}
-cc_test {
- name: "VtsAidlKeyMintTargetTest",
+cc_defaults {
+ name: "keymint_vts_defaults",
defaults: [
- "VtsHalTargetTestDefaults",
"use_libaidlvintf_gtest_helper_static",
- ],
- srcs: [
- "AttestKeyTest.cpp",
- "DeviceUniqueAttestationTest.cpp",
- "KeyMintTest.cpp",
+ "VtsHalTargetTestDefaults",
],
shared_libs: [
"libbinder_ndk",
@@ -43,9 +38,24 @@
"android.hardware.security.secureclock-V1-ndk_platform",
"libcppbor_external",
"libcppcose_rkp",
+ "libjsoncpp",
"libkeymint",
"libkeymint_remote_prov_support",
"libkeymint_support",
+ ],
+}
+
+cc_test {
+ name: "VtsAidlKeyMintTargetTest",
+ defaults: [
+ "keymint_vts_defaults",
+ ],
+ srcs: [
+ "AttestKeyTest.cpp",
+ "DeviceUniqueAttestationTest.cpp",
+ "KeyMintTest.cpp",
+ ],
+ static_libs: [
"libkeymint_vts_test_utils",
],
test_suites: [
@@ -57,8 +67,7 @@
cc_test_library {
name: "libkeymint_vts_test_utils",
defaults: [
- "VtsHalTargetTestDefaults",
- "use_libaidlvintf_gtest_helper_static",
+ "keymint_vts_defaults",
],
srcs: [
"KeyMintAidlTestBase.cpp",
@@ -66,45 +75,22 @@
export_include_dirs: [
".",
],
- shared_libs: [
- "libbinder_ndk",
- "libcrypto",
- ],
static_libs: [
- "android.hardware.security.keymint-V1-ndk_platform",
- "android.hardware.security.secureclock-V1-ndk_platform",
- "libcppbor_external",
- "libcppcose_rkp",
"libgmock_ndk",
- "libkeymint",
- "libkeymint_remote_prov_support",
- "libkeymint_support",
],
}
cc_test {
name: "VtsHalRemotelyProvisionedComponentTargetTest",
defaults: [
- "VtsHalTargetTestDefaults",
- "use_libaidlvintf_gtest_helper_static",
+ "keymint_vts_defaults",
],
srcs: [
"VtsRemotelyProvisionedComponentTests.cpp",
],
- shared_libs: [
- "libbinder_ndk",
- "libcrypto",
- ],
static_libs: [
- "android.hardware.security.keymint-V1-ndk_platform",
- "android.hardware.security.secureclock-V1-ndk_platform",
- "libcppbor_external",
- "libcppcose_rkp",
"libgmock_ndk",
"libkeymaster_portable",
- "libkeymint",
- "libkeymint_support",
- "libkeymint_remote_prov_support",
"libkeymint_vts_test_utils",
"libpuresoftkeymasterdevice",
],
diff --git a/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp b/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
index a3ed3ad..d7abf07 100644
--- a/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
+++ b/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
@@ -40,11 +40,16 @@
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
- // The device-unique attestation chain should contain exactly two certificates:
+ // The device-unique attestation chain should contain exactly three certificates:
// * The leaf with the attestation extension.
- // * A self-signed root, signed using the device-unique key.
- ASSERT_EQ(cert_chain_.size(), 2);
- EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+ // * An intermediate, signing the leaf using the device-unique key.
+ // * A self-signed root, signed using some authority's key, certifying
+ // the device-unique key.
+ const size_t chain_length = cert_chain_.size();
+ ASSERT_TRUE(chain_length == 2 || chain_length == 3);
+ // TODO(b/191361618): Once StrongBox implementations use a correctly-issued
+ // certificate chain, do not skip issuers matching.
+ EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_, /* strict_issuer_check= */ false));
AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
EXPECT_TRUE(verify_attestation_record("challenge", "foo", sw_enforced, hw_enforced,
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index 5359b3b..2032411 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -1493,7 +1493,8 @@
return authList;
}
-AssertionResult ChainSignaturesAreValid(const vector<Certificate>& chain) {
+AssertionResult ChainSignaturesAreValid(const vector<Certificate>& chain,
+ bool strict_issuer_check) {
std::stringstream cert_data;
for (size_t i = 0; i < chain.size(); ++i) {
@@ -1520,7 +1521,7 @@
string cert_issuer = x509NameToStr(X509_get_issuer_name(key_cert.get()));
string signer_subj = x509NameToStr(X509_get_subject_name(signing_cert.get()));
- if (cert_issuer != signer_subj) {
+ if (cert_issuer != signer_subj && strict_issuer_check) {
return AssertionFailure() << "Cert " << i << " has wrong issuer.\n"
<< " Signer subject is " << signer_subj
<< " Issuer subject is " << cert_issuer << endl
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
index d592d36..ec3fcf6 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -349,7 +349,8 @@
AuthorizationSet HwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
AuthorizationSet SwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
-::testing::AssertionResult ChainSignaturesAreValid(const vector<Certificate>& chain);
+::testing::AssertionResult ChainSignaturesAreValid(const vector<Certificate>& chain,
+ bool strict_issuer_check = true);
#define INSTANTIATE_KEYMINT_AIDL_TEST(name) \
INSTANTIATE_TEST_SUITE_P(PerInstance, name, \
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index d41d270..caac346 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -71,6 +71,12 @@
bool check_patchLevels = false;
+// The maximum number of times we'll attempt to verify that corruption
+// of an ecrypted blob results in an error. Retries are necessary as there
+// is a small (roughly 1/256) chance that corrupting ciphertext still results
+// in valid PKCS7 padding.
+constexpr size_t kMaxPaddingCorruptionRetries = 8;
+
template <TagType tag_type, Tag tag, typename ValueT>
bool contains(const vector<KeyParameter>& set, TypedTag<tag_type, tag> ttag,
ValueT expected_value) {
@@ -4376,11 +4382,22 @@
string ciphertext = EncryptMessage(message, params);
EXPECT_EQ(16U, ciphertext.size());
EXPECT_NE(ciphertext, message);
- ++ciphertext[ciphertext.size() / 2];
- EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
- string plaintext;
- EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &plaintext));
+ for (size_t i = 0; i < kMaxPaddingCorruptionRetries; ++i) {
+ ++ciphertext[ciphertext.size() / 2];
+
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+ string plaintext;
+ ErrorCode error = Finish(message, &plaintext);
+ if (error == ErrorCode::INVALID_INPUT_LENGTH) {
+ // This is the expected error, we can exit the test now.
+ return;
+ } else {
+ // Very small chance we got valid decryption, so try again.
+ ASSERT_EQ(error, ErrorCode::OK);
+ }
+ }
+ FAIL() << "Corrupt ciphertext should have failed to decrypt by now.";
}
vector<uint8_t> CopyIv(const AuthorizationSet& set) {
@@ -5343,15 +5360,27 @@
string ciphertext = EncryptMessage(message, BlockMode::ECB, PaddingMode::PKCS7);
EXPECT_EQ(8U, ciphertext.size());
EXPECT_NE(ciphertext, message);
- ++ciphertext[ciphertext.size() / 2];
AuthorizationSetBuilder begin_params;
begin_params.push_back(TAG_BLOCK_MODE, BlockMode::ECB);
begin_params.push_back(TAG_PADDING, PaddingMode::PKCS7);
- EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params));
- string plaintext;
- EXPECT_EQ(ErrorCode::OK, Update(ciphertext, &plaintext));
- EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, Finish(&plaintext));
+
+ for (size_t i = 0; i < kMaxPaddingCorruptionRetries; ++i) {
+ ++ciphertext[ciphertext.size() / 2];
+
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params));
+ string plaintext;
+ EXPECT_EQ(ErrorCode::OK, Update(ciphertext, &plaintext));
+ ErrorCode error = Finish(&plaintext);
+ if (error == ErrorCode::INVALID_ARGUMENT) {
+ // This is the expected error, we can exit the test now.
+ return;
+ } else {
+ // Very small chance we got valid decryption, so try again.
+ ASSERT_EQ(error, ErrorCode::OK);
+ }
+ }
+ FAIL() << "Corrupt ciphertext should have failed to decrypt by now.";
}
struct TripleDesTestVector {
@@ -5679,16 +5708,27 @@
string ciphertext = EncryptMessage(message, BlockMode::CBC, PaddingMode::PKCS7, &iv);
EXPECT_EQ(8U, ciphertext.size());
EXPECT_NE(ciphertext, message);
- ++ciphertext[ciphertext.size() / 2];
auto begin_params = AuthorizationSetBuilder()
.BlockMode(BlockMode::CBC)
.Padding(PaddingMode::PKCS7)
.Authorization(TAG_NONCE, iv);
- EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params));
- string plaintext;
- EXPECT_EQ(ErrorCode::OK, Update(ciphertext, &plaintext));
- EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, Finish(&plaintext));
+
+ for (size_t i = 0; i < kMaxPaddingCorruptionRetries; ++i) {
+ ++ciphertext[ciphertext.size() / 2];
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params));
+ string plaintext;
+ EXPECT_EQ(ErrorCode::OK, Update(ciphertext, &plaintext));
+ ErrorCode error = Finish(&plaintext);
+ if (error == ErrorCode::INVALID_ARGUMENT) {
+ // This is the expected error, we can exit the test now.
+ return;
+ } else {
+ // Very small chance we got valid decryption, so try again.
+ ASSERT_EQ(error, ErrorCode::OK);
+ }
+ }
+ FAIL() << "Corrupt ciphertext should have failed to decrypt by now.";
}
/*
diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
index a177317..32765ad 100644
--- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
+++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
@@ -29,6 +29,7 @@
#include <openssl/ec_key.h>
#include <openssl/x509.h>
#include <remote_prov/remote_prov_utils.h>
+#include <vector>
#include "KeyMintAidlTestBase.h"
@@ -297,7 +298,8 @@
}
void checkProtectedData(const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign,
- const bytevec& keysToSignMac, const ProtectedData& protectedData) {
+ const bytevec& keysToSignMac, const ProtectedData& protectedData,
+ std::vector<BccEntryData>* bccOutput = nullptr) {
auto [parsedProtectedData, _, protDataErrMsg] = cppbor::parse(protectedData.protectedData);
ASSERT_TRUE(parsedProtectedData) << protDataErrMsg;
ASSERT_TRUE(parsedProtectedData->asArray());
@@ -335,8 +337,7 @@
ASSERT_TRUE(deviceInfoMap->asMap());
auto& signingKey = bccContents->back().pubKey;
- auto macKey = verifyAndParseCoseSign1(/* ignore_signature = */ false, signedMac->asArray(),
- signingKey,
+ auto macKey = verifyAndParseCoseSign1(signedMac->asArray(), signingKey,
cppbor::Array() // SignedMacAad
.add(challenge_)
.add(std::move(deviceInfoMap))
@@ -355,6 +356,10 @@
auto macPayload = verifyAndParseCoseMac0(&coseMac0, *macKey);
ASSERT_TRUE(macPayload) << macPayload.message();
+
+ if (bccOutput) {
+ *bccOutput = std::move(*bccContents);
+ }
}
bytevec eekId_;
@@ -388,6 +393,48 @@
}
/**
+ * Ensure that test mode outputs a unique BCC root key every time we request a
+ * certificate request. Else, it's possible that the test mode API could be used
+ * to fingerprint devices. Only the GEEK should be allowed to decrypt the same
+ * device public key multiple times.
+ */
+TEST_P(CertificateRequestTest, NewKeyPerCallInTestMode) {
+ constexpr bool testMode = true;
+ constexpr size_t eekLength = 2;
+
+ generateEek(eekLength);
+
+ bytevec keysToSignMac;
+ DeviceInfo deviceInfo;
+ ProtectedData protectedData;
+ auto status = provisionable_->generateCertificateRequest(
+ testMode, {} /* keysToSign */, eekChain_.chain, challenge_, &deviceInfo, &protectedData,
+ &keysToSignMac);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ std::vector<BccEntryData> firstBcc;
+ checkProtectedData(deviceInfo, /*keysToSign=*/cppbor::Array(), keysToSignMac, protectedData,
+ &firstBcc);
+
+ status = provisionable_->generateCertificateRequest(testMode, {} /* keysToSign */,
+ eekChain_.chain, challenge_, &deviceInfo,
+ &protectedData, &keysToSignMac);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ std::vector<BccEntryData> secondBcc;
+ checkProtectedData(deviceInfo, /*keysToSign=*/cppbor::Array(), keysToSignMac, protectedData,
+ &secondBcc);
+
+ // Verify that none of the keys in the first BCC are repeated in the second one.
+ for (const auto& i : firstBcc) {
+ for (auto& j : secondBcc) {
+ ASSERT_THAT(i.pubKey, testing::Not(testing::ElementsAreArray(j.pubKey)))
+ << "Found a repeated pubkey in two generateCertificateRequest test mode calls";
+ }
+ }
+}
+
+/**
* Generate an empty certificate request in prod mode. Generation will fail because we don't have a
* valid GEEK.
*
diff --git a/security/keymint/support/Android.bp b/security/keymint/support/Android.bp
index 718133a..9e218b6 100644
--- a/security/keymint/support/Android.bp
+++ b/security/keymint/support/Android.bp
@@ -57,8 +57,28 @@
"include",
],
shared_libs: [
+ "libbase",
"libcppbor_external",
"libcppcose_rkp",
"libcrypto",
+ "libjsoncpp",
+ ],
+}
+
+cc_test {
+ name: "libkeymint_remote_prov_support_test",
+ srcs: ["remote_prov_utils_test.cpp"],
+ static_libs: [
+ "libgmock",
+ "libgtest_main",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcppbor_external",
+ "libcppcose_rkp",
+ "libcrypto",
+ "libjsoncpp",
+ "libkeymaster_portable",
+ "libkeymint_remote_prov_support",
],
}
diff --git a/security/keymint/support/include/remote_prov/remote_prov_utils.h b/security/keymint/support/include/remote_prov/remote_prov_utils.h
index e4261f3..406b7a9 100644
--- a/security/keymint/support/include/remote_prov/remote_prov_utils.h
+++ b/security/keymint/support/include/remote_prov/remote_prov_utils.h
@@ -27,6 +27,31 @@
extern bytevec kTestMacKey;
+// The Google root key for the Endpoint Encryption Key chain, encoded as COSE_Sign1
+inline constexpr uint8_t kCoseEncodedRootCert[] = {
+ 0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x58, 0x2a, 0xa4, 0x01, 0x01, 0x03, 0x27, 0x20, 0x06,
+ 0x21, 0x58, 0x20, 0x99, 0xb9, 0xee, 0xdd, 0x5e, 0xe4, 0x52, 0xf6, 0x85, 0xc6, 0x4c, 0x62,
+ 0xdc, 0x3e, 0x61, 0xab, 0x57, 0x48, 0x7d, 0x75, 0x37, 0x29, 0xad, 0x76, 0x80, 0x32, 0xd2,
+ 0xb3, 0xcb, 0x63, 0x58, 0xd9, 0x58, 0x40, 0x1e, 0x22, 0x08, 0x4b, 0xa4, 0xb7, 0xa4, 0xc8,
+ 0xd7, 0x4e, 0x03, 0x0e, 0xfe, 0xb8, 0xaf, 0x14, 0x4c, 0xa7, 0x3b, 0x6f, 0xa5, 0xcd, 0xdc,
+ 0xda, 0x79, 0xc6, 0x2b, 0x64, 0xfe, 0x99, 0x39, 0xaf, 0x76, 0xe7, 0x80, 0xfa, 0x66, 0x00,
+ 0x85, 0x0d, 0x07, 0x98, 0x2a, 0xac, 0x91, 0x5c, 0xa7, 0x25, 0x14, 0x49, 0x06, 0x34, 0x75,
+ 0xca, 0x8a, 0x27, 0x7a, 0xd9, 0xe3, 0x5a, 0x49, 0xeb, 0x02, 0x03};
+
+// The Google Endpoint Encryption Key certificate, encoded as COSE_Sign1
+inline constexpr uint8_t kCoseEncodedGeekCert[] = {
+ 0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x58, 0x4e, 0xa5, 0x01, 0x01, 0x02, 0x58, 0x20,
+ 0xd0, 0xae, 0xc1, 0x15, 0xca, 0x2a, 0xcf, 0x73, 0xae, 0x6b, 0xcc, 0xcb, 0xd1, 0x96,
+ 0x1d, 0x65, 0xe8, 0xb1, 0xdd, 0xd7, 0x4a, 0x1a, 0x37, 0xb9, 0x43, 0x3a, 0x97, 0xd5,
+ 0x99, 0xdf, 0x98, 0x08, 0x03, 0x38, 0x18, 0x20, 0x04, 0x21, 0x58, 0x20, 0xbe, 0x85,
+ 0xe7, 0x46, 0xc4, 0xa3, 0x42, 0x5a, 0x40, 0xd9, 0x36, 0x3a, 0xa6, 0x15, 0xd0, 0x2c,
+ 0x58, 0x7e, 0x3d, 0xdc, 0x33, 0x02, 0x32, 0xd2, 0xfc, 0x5e, 0x1e, 0x87, 0x25, 0x5f,
+ 0x72, 0x60, 0x58, 0x40, 0x9b, 0xcf, 0x90, 0xe2, 0x2e, 0x4b, 0xab, 0xd1, 0x18, 0xb1,
+ 0x0e, 0x8e, 0x5d, 0x20, 0x27, 0x4b, 0x84, 0x58, 0xfe, 0xfc, 0x32, 0x90, 0x7e, 0x72,
+ 0x05, 0x83, 0xbc, 0xd7, 0x82, 0xbe, 0xfa, 0x64, 0x78, 0x2d, 0x54, 0x10, 0x4b, 0xc0,
+ 0x31, 0xbf, 0x6b, 0xe8, 0x1e, 0x35, 0xe2, 0xf0, 0x2d, 0xce, 0x6c, 0x2f, 0x4f, 0xf2,
+ 0xf5, 0x4f, 0xa5, 0xd4, 0x83, 0xad, 0x96, 0xa2, 0xf1, 0x87, 0x58, 0x04};
+
/**
* Generates random bytes.
*/
@@ -44,6 +69,11 @@
*/
ErrMsgOr<EekChain> generateEekChain(size_t length, const bytevec& eekId);
+/**
+ * Returns the CBOR-encoded, production Google Endpoint Encryption Key chain.
+ */
+bytevec getProdEekChain();
+
struct BccEntryData {
bytevec pubKey;
};
@@ -57,4 +87,26 @@
*/
ErrMsgOr<std::vector<BccEntryData>> validateBcc(const cppbor::Array* bcc);
+struct JsonOutput {
+ static JsonOutput Ok(std::string json) { return {std::move(json), ""}; }
+ static JsonOutput Error(std::string error) { return {"", std::move(error)}; }
+
+ std::string output;
+ std::string error; // if non-empty, this describes what went wrong
+};
+
+/**
+ * Take a given certificate request and output a JSON blob containing both the
+ * build fingerprint and certificate request. This data may be serialized, then
+ * later uploaded to the remote provisioning service. The input csr is not
+ * validated, only encoded.
+ *
+ * Output format:
+ * {
+ * "build_fingerprint": <string>
+ * "csr": <base64 CBOR CSR>
+ * }
+ */
+JsonOutput jsonEncodeCsrWithBuild(const cppbor::Array& csr);
+
} // namespace aidl::android::hardware::security::keymint::remote_prov
diff --git a/security/keymint/support/remote_prov_utils.cpp b/security/keymint/support/remote_prov_utils.cpp
index da10eb2..0cbee51 100644
--- a/security/keymint/support/remote_prov_utils.cpp
+++ b/security/keymint/support/remote_prov_utils.cpp
@@ -14,11 +14,15 @@
* limitations under the License.
*/
-#include <remote_prov/remote_prov_utils.h>
+#include <iterator>
+#include <tuple>
-#include <openssl/rand.h>
-
+#include <android-base/properties.h>
#include <cppbor.h>
+#include <json/json.h>
+#include <openssl/base64.h>
+#include <openssl/rand.h>
+#include <remote_prov/remote_prov_utils.h>
namespace aidl::android::hardware::security::keymint::remote_prov {
@@ -31,6 +35,10 @@
}
ErrMsgOr<EekChain> generateEekChain(size_t length, const bytevec& eekId) {
+ if (length < 2) {
+ return "EEK chain must contain at least 2 certs.";
+ }
+
auto eekChain = cppbor::Array();
bytevec prev_priv_key;
@@ -78,7 +86,19 @@
return EekChain{eekChain.encode(), pub_key, priv_key};
}
-ErrMsgOr<bytevec> verifyAndParseCoseSign1Cwt(bool ignoreSignature, const cppbor::Array* coseSign1,
+bytevec getProdEekChain() {
+ bytevec prodEek;
+ prodEek.reserve(1 + sizeof(kCoseEncodedRootCert) + sizeof(kCoseEncodedGeekCert));
+
+ // In CBOR encoding, 0x82 indicates an array of two items
+ prodEek.push_back(0x82);
+ prodEek.insert(prodEek.end(), std::begin(kCoseEncodedRootCert), std::end(kCoseEncodedRootCert));
+ prodEek.insert(prodEek.end(), std::begin(kCoseEncodedGeekCert), std::end(kCoseEncodedGeekCert));
+
+ return prodEek;
+}
+
+ErrMsgOr<bytevec> verifyAndParseCoseSign1Cwt(const cppbor::Array* coseSign1,
const bytevec& signingCoseKey, const bytevec& aad) {
if (!coseSign1 || coseSign1->size() != kCoseSign1EntryCount) {
return "Invalid COSE_Sign1";
@@ -115,27 +135,22 @@
auto serializedKey = parsedPayload->asMap()->get(-4670552)->clone();
if (!serializedKey || !serializedKey->asBstr()) return "Could not find key entry";
- if (!ignoreSignature) {
- bool selfSigned = signingCoseKey.empty();
- auto key = CoseKey::parseEd25519(selfSigned ? serializedKey->asBstr()->value()
- : signingCoseKey);
- if (!key) return "Bad signing key: " + key.moveMessage();
+ bool selfSigned = signingCoseKey.empty();
+ auto key =
+ CoseKey::parseEd25519(selfSigned ? serializedKey->asBstr()->value() : signingCoseKey);
+ if (!key) return "Bad signing key: " + key.moveMessage();
- bytevec signatureInput = cppbor::Array()
- .add("Signature1")
- .add(*protectedParams)
- .add(aad)
- .add(*payload)
- .encode();
+ bytevec signatureInput =
+ cppbor::Array().add("Signature1").add(*protectedParams).add(aad).add(*payload).encode();
- if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(),
- key->getBstrValue(CoseKey::PUBKEY_X)->data())) {
- return "Signature verification failed";
- }
+ if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(),
+ key->getBstrValue(CoseKey::PUBKEY_X)->data())) {
+ return "Signature verification failed";
}
return serializedKey->asBstr()->value();
}
+
ErrMsgOr<std::vector<BccEntryData>> validateBcc(const cppbor::Array* bcc) {
if (!bcc || bcc->size() == 0) return "Invalid BCC";
@@ -148,8 +163,7 @@
if (!entry || entry->size() != kCoseSign1EntryCount) {
return "Invalid BCC entry " + std::to_string(i) + ": " + prettyPrint(entry);
}
- auto payload = verifyAndParseCoseSign1Cwt(false /* ignoreSignature */, entry,
- std::move(prevKey), bytevec{} /* AAD */);
+ auto payload = verifyAndParseCoseSign1Cwt(entry, std::move(prevKey), bytevec{} /* AAD */);
if (!payload) {
return "Failed to verify entry " + std::to_string(i) + ": " + payload.moveMessage();
}
@@ -168,4 +182,36 @@
return result;
}
+JsonOutput jsonEncodeCsrWithBuild(const cppbor::Array& csr) {
+ const std::string kFingerprintProp = "ro.build.fingerprint";
+
+ if (!::android::base::WaitForPropertyCreation(kFingerprintProp)) {
+ return JsonOutput::Error("Unable to read build fingerprint");
+ }
+
+ bytevec csrCbor = csr.encode();
+ size_t base64Length;
+ int rc = EVP_EncodedLength(&base64Length, csrCbor.size());
+ if (!rc) {
+ return JsonOutput::Error("Error getting base64 length. Size overflow?");
+ }
+
+ std::vector<char> base64(base64Length);
+ rc = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(base64.data()), csrCbor.data(), csrCbor.size());
+ ++rc; // Account for NUL, which BoringSSL does not for some reason.
+ if (rc != base64Length) {
+ return JsonOutput::Error("Error writing base64. Expected " + std::to_string(base64Length) +
+ " bytes to be written, but " + std::to_string(rc) +
+ " bytes were actually written.");
+ }
+
+ Json::Value json(Json::objectValue);
+ json["build_fingerprint"] = ::android::base::GetProperty(kFingerprintProp, /*default=*/"");
+ json["csr"] = base64.data(); // Boring writes a NUL-terminated c-string
+
+ Json::StreamWriterBuilder factory;
+ factory["indentation"] = ""; // disable pretty formatting
+ return JsonOutput::Ok(Json::writeString(factory, json));
+}
+
} // namespace aidl::android::hardware::security::keymint::remote_prov
diff --git a/security/keymint/support/remote_prov_utils_test.cpp b/security/keymint/support/remote_prov_utils_test.cpp
new file mode 100644
index 0000000..8697c51
--- /dev/null
+++ b/security/keymint/support/remote_prov_utils_test.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/properties.h>
+#include <cppbor_parse.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <keymaster/android_keymaster_utils.h>
+#include <keymaster/logger.h>
+#include <keymaster/remote_provisioning_utils.h>
+#include <openssl/curve25519.h>
+#include <remote_prov/remote_prov_utils.h>
+#include <cstdint>
+#include "cppbor.h"
+#include "keymaster/cppcose/cppcose.h"
+
+namespace aidl::android::hardware::security::keymint::remote_prov {
+namespace {
+
+using ::keymaster::KeymasterBlob;
+using ::keymaster::validateAndExtractEekPubAndId;
+using ::testing::ElementsAreArray;
+
+TEST(RemoteProvUtilsTest, GenerateEekChainInvalidLength) {
+ ASSERT_FALSE(generateEekChain(1, /*eekId=*/{}));
+}
+
+TEST(RemoteProvUtilsTest, GenerateEekChain) {
+ bytevec kTestEekId = {'t', 'e', 's', 't', 'I', 'd', 0};
+ for (size_t length : {2, 3, 31}) {
+ auto get_eek_result = generateEekChain(length, kTestEekId);
+ ASSERT_TRUE(get_eek_result) << get_eek_result.message();
+
+ auto& [chain, pubkey, privkey] = *get_eek_result;
+
+ auto validation_result = validateAndExtractEekPubAndId(
+ /*testMode=*/true, KeymasterBlob(chain.data(), chain.size()));
+ ASSERT_TRUE(validation_result.isOk());
+
+ auto& [eekPub, eekId] = *validation_result;
+ EXPECT_THAT(eekId, ElementsAreArray(kTestEekId));
+ EXPECT_THAT(eekPub, ElementsAreArray(pubkey));
+ }
+}
+
+TEST(RemoteProvUtilsTest, GetProdEekChain) {
+ auto chain = getProdEekChain();
+
+ auto validation_result = validateAndExtractEekPubAndId(
+ /*testMode=*/false, KeymasterBlob(chain.data(), chain.size()));
+ ASSERT_TRUE(validation_result.isOk()) << "Error: " << validation_result.moveError();
+
+ auto& [eekPub, eekId] = *validation_result;
+
+ auto [geekCert, ignoredNewPos, error] =
+ cppbor::parse(kCoseEncodedGeekCert, sizeof(kCoseEncodedGeekCert));
+ ASSERT_NE(geekCert, nullptr) << "Error: " << error;
+ ASSERT_NE(geekCert->asArray(), nullptr);
+
+ auto& encodedGeekCoseKey = geekCert->asArray()->get(kCoseSign1Payload);
+ ASSERT_NE(encodedGeekCoseKey, nullptr);
+ ASSERT_NE(encodedGeekCoseKey->asBstr(), nullptr);
+
+ auto geek = CoseKey::parse(encodedGeekCoseKey->asBstr()->value());
+ ASSERT_TRUE(geek) << "Error: " << geek.message();
+
+ const std::vector<uint8_t> empty;
+ EXPECT_THAT(eekId, ElementsAreArray(geek->getBstrValue(CoseKey::KEY_ID).value_or(empty)));
+ EXPECT_THAT(eekPub, ElementsAreArray(geek->getBstrValue(CoseKey::PUBKEY_X).value_or(empty)));
+}
+
+TEST(RemoteProvUtilsTest, JsonEncodeCsr) {
+ cppbor::Array array;
+ array.add(1);
+
+ auto [json, error] = jsonEncodeCsrWithBuild(array);
+
+ ASSERT_TRUE(error.empty()) << error;
+
+ std::string expected = R"({"build_fingerprint":")" +
+ ::android::base::GetProperty("ro.build.fingerprint", /*default=*/"") +
+ R"(","csr":"gQE="})";
+
+ ASSERT_EQ(json, expected);
+}
+
+} // namespace
+} // namespace aidl::android::hardware::security::keymint::remote_prov
diff --git a/security/sharedsecret/aidl/vts/functional/SharedSecretAidlTest.cpp b/security/sharedsecret/aidl/vts/functional/SharedSecretAidlTest.cpp
index 919f882..51938ba 100644
--- a/security/sharedsecret/aidl/vts/functional/SharedSecretAidlTest.cpp
+++ b/security/sharedsecret/aidl/vts/functional/SharedSecretAidlTest.cpp
@@ -268,10 +268,16 @@
<< "Shared secret service that provided tweaked param should fail to compute "
"shared secret";
} else {
- EXPECT_EQ(ErrorCode::OK, responses[i].error) << "Others should succeed";
- EXPECT_NE(correct_response, responses[i].sharing_check)
- << "Others should calculate a different shared secret, due to the tweaked "
- "nonce.";
+ // Other services *may* succeed, or may notice the invalid size for the nonce.
+ // However, if another service completes the computation, it should get the 'wrong'
+ // answer.
+ if (responses[i].error == ErrorCode::OK) {
+ EXPECT_NE(correct_response, responses[i].sharing_check)
+ << "Others should calculate a different shared secret, due to the tweaked "
+ "nonce.";
+ } else {
+ EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error);
+ }
}
}
}
@@ -348,10 +354,16 @@
<< "Shared secret service that provided tweaked param should fail to compute "
"shared secret";
} else {
- EXPECT_EQ(ErrorCode::OK, responses[i].error) << "Others should succeed";
- EXPECT_NE(correct_response, responses[i].sharing_check)
- << "Others should calculate a different shared secret, due to the tweaked "
- "nonce.";
+ // Other services *may* succeed, or may notice the invalid size for the seed.
+ // However, if another service completes the computation, it should get the 'wrong'
+ // answer.
+ if (responses[i].error == ErrorCode::OK) {
+ EXPECT_NE(correct_response, responses[i].sharing_check)
+ << "Others should calculate a different shared secret, due to the tweaked "
+ "seed.";
+ } else {
+ EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error);
+ }
}
}
}
diff --git a/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp b/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp
index ad4ef12..ea9bcb5 100644
--- a/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp
+++ b/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp
@@ -469,7 +469,7 @@
}
}
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OffloadControlHidlTestBase);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OffloadControlTestV1_0_HalNotStarted);
INSTANTIATE_TEST_CASE_P(
PerInstance, OffloadControlTestV1_0_HalNotStarted,
testing::Combine(testing::ValuesIn(android::hardware::getAllHalInstanceNames(
@@ -478,7 +478,7 @@
IOffloadControl::descriptor))),
android::hardware::PrintInstanceTupleNameToString<>);
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OffloadControlHidlTest);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OffloadControlTestV1_0_HalStarted);
INSTANTIATE_TEST_CASE_P(
PerInstance, OffloadControlTestV1_0_HalStarted,
testing::Combine(testing::ValuesIn(android::hardware::getAllHalInstanceNames(
diff --git a/tv/cec/1.0/default/Android.bp b/tv/cec/1.0/default/Android.bp
index fc4298d..b4053df 100644
--- a/tv/cec/1.0/default/Android.bp
+++ b/tv/cec/1.0/default/Android.bp
@@ -12,12 +12,16 @@
defaults: ["hidl_defaults"],
vendor: true,
relative_install_path: "hw",
- srcs: ["HdmiCec.cpp"],
+ srcs: [
+ "HdmiCec.cpp",
+ "HdmiCecDefault.cpp",
+ ],
shared_libs: [
"libhidlbase",
"liblog",
"libbase",
+ "libcutils",
"libutils",
"libhardware",
"android.hardware.tv.cec@1.0",
diff --git a/tv/cec/1.0/default/HdmiCec.cpp b/tv/cec/1.0/default/HdmiCec.cpp
index 171bdfe..74de785 100644
--- a/tv/cec/1.0/default/HdmiCec.cpp
+++ b/tv/cec/1.0/default/HdmiCec.cpp
@@ -20,6 +20,7 @@
#include <hardware/hardware.h>
#include <hardware/hdmi_cec.h>
#include "HdmiCec.h"
+#include "HdmiCecDefault.h"
namespace android {
namespace hardware {
@@ -390,6 +391,15 @@
return mDevice->is_connected(mDevice, portId) > 0;
}
+IHdmiCec* getHdmiCecDefault() {
+ HdmiCecDefault* hdmiCecDefault = new HdmiCecDefault();
+ Result result = hdmiCecDefault->init();
+ if (result == Result::SUCCESS) {
+ return hdmiCecDefault;
+ }
+ LOG(ERROR) << "Failed to load default HAL.";
+ return nullptr;
+}
IHdmiCec* HIDL_FETCH_IHdmiCec(const char* hal) {
hdmi_cec_device_t* hdmi_cec_device;
@@ -410,7 +420,7 @@
return new HdmiCec(hdmi_cec_device);
} else {
LOG(ERROR) << "Passthrough failed to load legacy HAL.";
- return nullptr;
+ return getHdmiCecDefault();
}
}
diff --git a/tv/cec/1.0/default/HdmiCecDefault.cpp b/tv/cec/1.0/default/HdmiCecDefault.cpp
new file mode 100644
index 0000000..299bcf0
--- /dev/null
+++ b/tv/cec/1.0/default/HdmiCecDefault.cpp
@@ -0,0 +1,461 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.tv.cec@1.0-impl"
+#include <android-base/logging.h>
+
+#include <cutils/properties.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/cec.h>
+#include <linux/ioctl.h>
+#include <poll.h>
+#include <pthread.h>
+#include <sys/eventfd.h>
+#include <algorithm>
+
+#include "HdmiCecDefault.h"
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace cec {
+namespace V1_0 {
+namespace implementation {
+
+// When set to false, all the CEC commands are discarded. True by default after initialization.
+bool mCecEnabled;
+/*
+ * When set to false, HAL does not wake up the system upon receiving <Image View On> or
+ * <Text View On>. True by default after initialization.
+ */
+bool mWakeupEnabled;
+
+int mCecFd;
+int mExitFd;
+pthread_t mEventThread;
+sp<IHdmiCecCallback> mCallback;
+
+HdmiCecDefault::HdmiCecDefault() {
+ mCecFd = -1;
+ mExitFd = -1;
+ mCecEnabled = false;
+ mWakeupEnabled = false;
+ mCallback = nullptr;
+}
+
+HdmiCecDefault::~HdmiCecDefault() {
+ release();
+}
+
+// Methods from ::android::hardware::tv::cec::V1_0::IHdmiCec follow.
+Return<Result> HdmiCecDefault::addLogicalAddress(CecLogicalAddress addr) {
+ if (addr < CecLogicalAddress::TV || addr >= CecLogicalAddress::BROADCAST) {
+ LOG(ERROR) << "Add logical address failed, Invalid address";
+ return Result::FAILURE_INVALID_ARGS;
+ }
+
+ struct cec_log_addrs cecLogAddrs;
+ int ret = ioctl(mCecFd, CEC_ADAP_G_LOG_ADDRS, &cecLogAddrs);
+ if (ret) {
+ LOG(ERROR) << "Add logical address failed, Error = " << strerror(errno);
+ return Result::FAILURE_BUSY;
+ }
+
+ cecLogAddrs.cec_version = getCecVersion();
+ cecLogAddrs.vendor_id = getVendorId();
+
+ unsigned int logAddrType = CEC_LOG_ADDR_TYPE_UNREGISTERED;
+ unsigned int allDevTypes = 0;
+ unsigned int primDevType = 0xff;
+ switch (addr) {
+ case CecLogicalAddress::TV:
+ primDevType = CEC_OP_PRIM_DEVTYPE_TV;
+ logAddrType = CEC_LOG_ADDR_TYPE_TV;
+ allDevTypes = CEC_OP_ALL_DEVTYPE_TV;
+ break;
+ case CecLogicalAddress::RECORDER_1:
+ case CecLogicalAddress::RECORDER_2:
+ case CecLogicalAddress::RECORDER_3:
+ primDevType = CEC_OP_PRIM_DEVTYPE_RECORD;
+ logAddrType = CEC_LOG_ADDR_TYPE_RECORD;
+ allDevTypes = CEC_OP_ALL_DEVTYPE_RECORD;
+ break;
+ case CecLogicalAddress::TUNER_1:
+ case CecLogicalAddress::TUNER_2:
+ case CecLogicalAddress::TUNER_3:
+ case CecLogicalAddress::TUNER_4:
+ primDevType = CEC_OP_PRIM_DEVTYPE_TUNER;
+ logAddrType = CEC_LOG_ADDR_TYPE_TUNER;
+ allDevTypes = CEC_OP_ALL_DEVTYPE_TUNER;
+ break;
+ case CecLogicalAddress::PLAYBACK_1:
+ case CecLogicalAddress::PLAYBACK_2:
+ case CecLogicalAddress::PLAYBACK_3:
+ primDevType = CEC_OP_PRIM_DEVTYPE_PLAYBACK;
+ logAddrType = CEC_LOG_ADDR_TYPE_PLAYBACK;
+ allDevTypes = CEC_OP_ALL_DEVTYPE_PLAYBACK;
+ cecLogAddrs.flags |= CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU;
+ break;
+ case CecLogicalAddress::AUDIO_SYSTEM:
+ primDevType = CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM;
+ logAddrType = CEC_LOG_ADDR_TYPE_AUDIOSYSTEM;
+ allDevTypes = CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM;
+ break;
+ case CecLogicalAddress::FREE_USE:
+ primDevType = CEC_OP_PRIM_DEVTYPE_PROCESSOR;
+ logAddrType = CEC_LOG_ADDR_TYPE_SPECIFIC;
+ allDevTypes = CEC_OP_ALL_DEVTYPE_SWITCH;
+ break;
+ case CecLogicalAddress::UNREGISTERED:
+ cecLogAddrs.flags |= CEC_LOG_ADDRS_FL_ALLOW_UNREG_FALLBACK;
+ break;
+ }
+
+ int logAddrIndex = cecLogAddrs.num_log_addrs;
+
+ cecLogAddrs.num_log_addrs += 1;
+ cecLogAddrs.log_addr[logAddrIndex] = static_cast<cec_logical_address_t>(addr);
+ cecLogAddrs.log_addr_type[logAddrIndex] = logAddrType;
+ cecLogAddrs.primary_device_type[logAddrIndex] = primDevType;
+ cecLogAddrs.all_device_types[logAddrIndex] = allDevTypes;
+ cecLogAddrs.features[logAddrIndex][0] = 0;
+ cecLogAddrs.features[logAddrIndex][1] = 0;
+
+ ret = ioctl(mCecFd, CEC_ADAP_S_LOG_ADDRS, &cecLogAddrs);
+ if (ret) {
+ LOG(ERROR) << "Add logical address failed, Error = " << strerror(errno);
+ return Result::FAILURE_BUSY;
+ }
+ return Result::SUCCESS;
+}
+
+Return<void> HdmiCecDefault::clearLogicalAddress() {
+ struct cec_log_addrs cecLogAddrs;
+ memset(&cecLogAddrs, 0, sizeof(cecLogAddrs));
+ int ret = ioctl(mCecFd, CEC_ADAP_S_LOG_ADDRS, &cecLogAddrs);
+ if (ret) {
+ LOG(ERROR) << "Clear logical Address failed, Error = " << strerror(errno);
+ }
+ return Void();
+}
+
+Return<void> HdmiCecDefault::getPhysicalAddress(getPhysicalAddress_cb callback) {
+ uint16_t addr;
+ int ret = ioctl(mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
+ if (ret) {
+ LOG(ERROR) << "Get physical address failed, Error = " << strerror(errno);
+ callback(Result::FAILURE_INVALID_STATE, addr);
+ return Void();
+ }
+ callback(Result::SUCCESS, addr);
+ return Void();
+}
+
+Return<SendMessageResult> HdmiCecDefault::sendMessage(const CecMessage& message) {
+ if (!mCecEnabled) {
+ return SendMessageResult::FAIL;
+ }
+
+ struct cec_msg cecMsg;
+ memset(&cecMsg, 0, sizeof(cec_msg));
+
+ int initiator = static_cast<cec_logical_address_t>(message.initiator);
+ int destination = static_cast<cec_logical_address_t>(message.destination);
+
+ cecMsg.msg[0] = (initiator << 4) | destination;
+ for (size_t i = 0; i < message.body.size(); ++i) {
+ cecMsg.msg[i + 1] = message.body[i];
+ }
+ cecMsg.len = message.body.size() + 1;
+
+ int ret = ioctl(mCecFd, CEC_TRANSMIT, &cecMsg);
+
+ if (ret) {
+ LOG(ERROR) << "Send message failed, Error = " << strerror(errno);
+ return SendMessageResult::FAIL;
+ }
+
+ if (cecMsg.tx_status != CEC_TX_STATUS_OK) {
+ LOG(ERROR) << "Send message tx_status = " << cecMsg.tx_status;
+ }
+
+ switch (cecMsg.tx_status) {
+ case CEC_TX_STATUS_OK:
+ return SendMessageResult::SUCCESS;
+ case CEC_TX_STATUS_ARB_LOST:
+ return SendMessageResult::BUSY;
+ case CEC_TX_STATUS_NACK:
+ return SendMessageResult::NACK;
+ default:
+ return SendMessageResult::FAIL;
+ }
+}
+
+Return<void> HdmiCecDefault::setCallback(const sp<IHdmiCecCallback>& callback) {
+ if (mCallback != nullptr) {
+ mCallback->unlinkToDeath(this);
+ mCallback = nullptr;
+ }
+
+ if (callback != nullptr) {
+ mCallback = callback;
+ mCallback->linkToDeath(this, 0 /*cookie*/);
+ }
+ return Void();
+}
+
+Return<int32_t> HdmiCecDefault::getCecVersion() {
+ return property_get_int32("ro.hdmi.cec_version", CEC_OP_CEC_VERSION_1_4);
+}
+
+Return<uint32_t> HdmiCecDefault::getVendorId() {
+ return property_get_int32("ro.hdmi.vendor_id", 0x000c03 /* HDMI LLC vendor ID */);
+}
+
+Return<void> HdmiCecDefault::getPortInfo(getPortInfo_cb callback) {
+ uint16_t addr;
+ int ret = ioctl(mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
+ if (ret) {
+ LOG(ERROR) << "Get port info failed, Error = " << strerror(errno);
+ }
+
+ unsigned int type = property_get_int32("ro.hdmi.device_type", CEC_DEVICE_PLAYBACK);
+ hidl_vec<HdmiPortInfo> portInfos(1);
+ portInfos[0] = {.type = (type == CEC_DEVICE_TV ? HdmiPortType::INPUT : HdmiPortType::OUTPUT),
+ .portId = 1,
+ .cecSupported = true,
+ .arcSupported = false,
+ .physicalAddress = addr};
+ callback(portInfos);
+ return Void();
+}
+
+Return<void> HdmiCecDefault::setOption(OptionKey key, bool value) {
+ switch (key) {
+ case OptionKey::ENABLE_CEC:
+ LOG(DEBUG) << "setOption: Enable CEC: " << value;
+ mCecEnabled = value;
+ break;
+ case OptionKey::WAKEUP:
+ LOG(DEBUG) << "setOption: WAKEUP: " << value;
+ mWakeupEnabled = value;
+ break;
+ default:
+ break;
+ }
+ return Void();
+}
+
+Return<void> HdmiCecDefault::setLanguage(const hidl_string& /*language*/) {
+ return Void();
+}
+
+Return<void> HdmiCecDefault::enableAudioReturnChannel(int32_t /*portId*/, bool /*enable*/) {
+ return Void();
+}
+
+Return<bool> HdmiCecDefault::isConnected(int32_t /*portId*/) {
+ uint16_t addr;
+ int ret = ioctl(mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
+ if (ret) {
+ LOG(ERROR) << "Is connected failed, Error = " << strerror(errno);
+ return false;
+ }
+ if (addr == CEC_PHYS_ADDR_INVALID) {
+ return false;
+ }
+ return true;
+}
+
+// Initialise the cec file descriptor
+Return<Result> HdmiCecDefault::init() {
+ const char* path = "/dev/cec0";
+ mCecFd = open(path, O_RDWR);
+ if (mCecFd < 0) {
+ LOG(ERROR) << "Failed to open " << path << ", Error = " << strerror(errno);
+ return Result::FAILURE_NOT_SUPPORTED;
+ }
+ mExitFd = eventfd(0, EFD_NONBLOCK);
+ if (mExitFd < 0) {
+ LOG(ERROR) << "Failed to open eventfd, Error = " << strerror(errno);
+ release();
+ return Result::FAILURE_NOT_SUPPORTED;
+ }
+
+ // Ensure the CEC device supports required capabilities
+ struct cec_caps caps = {};
+ int ret = ioctl(mCecFd, CEC_ADAP_G_CAPS, &caps);
+ if (ret) {
+ LOG(ERROR) << "Unable to query cec adapter capabilities, Error = " << strerror(errno);
+ release();
+ return Result::FAILURE_NOT_SUPPORTED;
+ }
+
+ if (!(caps.capabilities & (CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT | CEC_CAP_PASSTHROUGH))) {
+ LOG(ERROR) << "Wrong cec adapter capabilities " << caps.capabilities;
+ release();
+ return Result::FAILURE_NOT_SUPPORTED;
+ }
+
+ uint32_t mode = CEC_MODE_INITIATOR | CEC_MODE_EXCL_FOLLOWER_PASSTHRU;
+ ret = ioctl(mCecFd, CEC_S_MODE, &mode);
+ if (ret) {
+ LOG(ERROR) << "Unable to set initiator mode, Error = " << strerror(errno);
+ release();
+ return Result::FAILURE_NOT_SUPPORTED;
+ }
+
+ /* thread loop for receiving cec messages and hotplug events*/
+ if (pthread_create(&mEventThread, NULL, event_thread, NULL)) {
+ LOG(ERROR) << "Can't create event thread: " << strerror(errno);
+ release();
+ return Result::FAILURE_NOT_SUPPORTED;
+ }
+
+ mCecEnabled = true;
+ mWakeupEnabled = true;
+ return Result::SUCCESS;
+}
+
+Return<void> HdmiCecDefault::release() {
+ if (mExitFd > 0) {
+ uint64_t tmp = 1;
+ write(mExitFd, &tmp, sizeof(tmp));
+ pthread_join(mEventThread, NULL);
+ }
+ if (mExitFd > 0) {
+ close(mExitFd);
+ }
+ if (mCecFd > 0) {
+ close(mCecFd);
+ }
+ mCecEnabled = false;
+ mWakeupEnabled = false;
+ setCallback(nullptr);
+ return Void();
+}
+
+void* HdmiCecDefault::event_thread(void*) {
+ struct pollfd ufds[3] = {
+ {mCecFd, POLLIN, 0},
+ {mCecFd, POLLERR, 0},
+ {mExitFd, POLLIN, 0},
+ };
+
+ while (1) {
+ ufds[0].revents = 0;
+ ufds[1].revents = 0;
+ ufds[2].revents = 0;
+
+ int ret = poll(ufds, /* size(ufds) = */ 3, /* timeout = */ -1);
+
+ if (ret <= 0) {
+ continue;
+ }
+
+ if (ufds[2].revents == POLLIN) { /* Exit */
+ break;
+ }
+
+ if (ufds[1].revents == POLLERR) { /* CEC Event */
+ struct cec_event ev;
+ ret = ioctl(mCecFd, CEC_DQEVENT, &ev);
+
+ if (!mCecEnabled) {
+ continue;
+ }
+
+ if (ret) {
+ LOG(ERROR) << "CEC_DQEVENT failed, Error = " << strerror(errno);
+ continue;
+ }
+
+ if (ev.event == CEC_EVENT_STATE_CHANGE) {
+ if (mCallback != nullptr) {
+ HotplugEvent hotplugEvent{
+ .connected = (ev.state_change.phys_addr != CEC_PHYS_ADDR_INVALID),
+ .portId = 1};
+ mCallback->onHotplugEvent(hotplugEvent);
+ } else {
+ LOG(ERROR) << "No event callback for hotplug";
+ }
+ }
+ }
+
+ if (ufds[0].revents == POLLIN) { /* CEC Driver */
+ struct cec_msg msg = {};
+ ret = ioctl(mCecFd, CEC_RECEIVE, &msg);
+
+ if (!mCecEnabled) {
+ continue;
+ }
+
+ if (ret) {
+ LOG(ERROR) << "CEC_RECEIVE failed, Error = " << strerror(errno);
+ continue;
+ }
+
+ if (msg.rx_status != CEC_RX_STATUS_OK) {
+ LOG(ERROR) << "msg rx_status = " << msg.rx_status;
+ continue;
+ }
+
+ if (!mWakeupEnabled && isWakeupMessage(msg)) {
+ LOG(DEBUG) << "Filter wakeup message";
+ continue;
+ }
+
+ if (mCallback != nullptr) {
+ size_t length = std::min(msg.len - 1, (uint32_t)MaxLength::MESSAGE_BODY);
+ CecMessage cecMessage{
+ .initiator = static_cast<CecLogicalAddress>(msg.msg[0] >> 4),
+ .destination = static_cast<CecLogicalAddress>(msg.msg[0] & 0xf),
+ };
+ cecMessage.body.resize(length);
+ for (size_t i = 0; i < length; ++i) {
+ cecMessage.body[i] = static_cast<uint8_t>(msg.msg[i + 1]);
+ }
+ mCallback->onCecMessage(cecMessage);
+ } else {
+ LOG(ERROR) << "no event callback for message";
+ }
+ }
+ }
+ return NULL;
+}
+
+int HdmiCecDefault::getOpcode(struct cec_msg message) {
+ return (static_cast<uint8_t>(message.msg[1]) & 0xff);
+}
+
+bool HdmiCecDefault::isWakeupMessage(struct cec_msg message) {
+ int opcode = getOpcode(message);
+ switch (opcode) {
+ case CEC_MESSAGE_TEXT_VIEW_ON:
+ case CEC_MESSAGE_IMAGE_VIEW_ON:
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace cec
+} // namespace tv
+} // namespace hardware
+} // namespace android
diff --git a/tv/cec/1.0/default/HdmiCecDefault.h b/tv/cec/1.0/default/HdmiCecDefault.h
new file mode 100644
index 0000000..c1bb2c7
--- /dev/null
+++ b/tv/cec/1.0/default/HdmiCecDefault.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/hardware/tv/cec/1.0/IHdmiCec.h>
+#include <hardware/hdmi_cec.h>
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace cec {
+namespace V1_0 {
+namespace implementation {
+
+struct HdmiCecDefault : public IHdmiCec, public hidl_death_recipient {
+ HdmiCecDefault();
+ ~HdmiCecDefault();
+ // Methods from ::android::hardware::tv::cec::V1_0::IHdmiCec follow.
+ Return<Result> addLogicalAddress(CecLogicalAddress addr) override;
+ Return<void> clearLogicalAddress() override;
+ Return<void> getPhysicalAddress(getPhysicalAddress_cb _hidl_cb) override;
+ Return<SendMessageResult> sendMessage(const CecMessage& message) override;
+ Return<void> setCallback(const sp<IHdmiCecCallback>& callback) override;
+ Return<int32_t> getCecVersion() override;
+ Return<uint32_t> getVendorId() override;
+ Return<void> getPortInfo(getPortInfo_cb _hidl_cb) override;
+ Return<void> setOption(OptionKey key, bool value) override;
+ Return<void> setLanguage(const hidl_string& language) override;
+ Return<void> enableAudioReturnChannel(int32_t portId, bool enable) override;
+ Return<bool> isConnected(int32_t portId) override;
+
+ virtual void serviceDied(uint64_t, const wp<::android::hidl::base::V1_0::IBase>&) {
+ setCallback(nullptr);
+ }
+
+ Return<Result> init();
+ Return<void> release();
+ static void* event_thread(void*);
+ static int getOpcode(struct cec_msg message);
+ static bool isWakeupMessage(struct cec_msg message);
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace cec
+} // namespace tv
+} // namespace hardware
+} // namespace android
diff --git a/vibrator/aidl/default/Vibrator.cpp b/vibrator/aidl/default/Vibrator.cpp
index c446afd..322833b 100644
--- a/vibrator/aidl/default/Vibrator.cpp
+++ b/vibrator/aidl/default/Vibrator.cpp
@@ -125,6 +125,11 @@
ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
int32_t* durationMs) {
+ std::vector<CompositePrimitive> supported;
+ getSupportedPrimitives(&supported);
+ if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
if (primitive != CompositePrimitive::NOOP) {
*durationMs = 100;
} else {