Merge changes I2f5187bf,Icb79e1e0,I833894d3,I54dcaa61,I47a810f2

* changes:
  Test that provisioned keys can be used with KeyMint
  Add tests with corrupted MAC on keypair
  Add more EEK variant tests and related fixes
  Commonize MacedPublicKey and ProtectedData checks
  Tweak IRemotelyProvisionedComponent AIDL docs
diff --git a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
index 657b42d..0b3098b 100644
--- a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
@@ -580,12 +580,19 @@
         // Starting / resuming of streams is asynchronous at HAL level.
         // Sometimes HAL doesn't have enough information until the audio data actually gets
         // consumed by the hardware.
-        do {
+        bool timedOut = false;
+        res = Result::INVALID_STATE;
+        for (android::base::Timer elapsed;
+             res != Result::OK && !writer.hasError() &&
+             !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
+            usleep(kWriteDurationUs);
             ASSERT_OK(stream->getPresentationPosition(returnIn(res, framesInitial, ts)));
             ASSERT_RESULT(okOrInvalidState, res);
-        } while (res != Result::OK);
+        }
+        ASSERT_FALSE(writer.hasError());
+        ASSERT_FALSE(timedOut);
+
         uint64_t frames = framesInitial;
-        bool timedOut = false;
         for (android::base::Timer elapsed;
              frames <= framesInitial && !writer.hasError() &&
              !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
@@ -666,11 +673,18 @@
                 allParams.begin(), allParams.end(), std::back_inserter(pcmParams), [](auto cfg) {
                     const auto& flags = std::get<PARAM_FLAGS>(cfg);
                     return xsd::isLinearPcm(std::get<PARAM_CONFIG>(cfg).base.format)
-                           // MMAP NOIRQ profiles use different reading protocol.
+                           // MMAP NOIRQ profiles use different reading protocol,
+                           // reading h/w hotword might require Soundtrigger to be active.
                            &&
-                           std::find(flags.begin(), flags.end(),
-                                     toString(xsd::AudioInOutFlag::AUDIO_INPUT_FLAG_MMAP_NOIRQ)) ==
-                                   flags.end() &&
+                           std::find_if(
+                                   flags.begin(), flags.end(),
+                                   [](const auto& flag) {
+                                       return flag == toString(
+                                                              xsd::AudioInOutFlag::
+                                                                      AUDIO_INPUT_FLAG_MMAP_NOIRQ) ||
+                                              flag == toString(xsd::AudioInOutFlag::
+                                                                       AUDIO_INPUT_FLAG_HW_HOTWORD);
+                                   }) == flags.end() &&
                            !getCachedPolicyConfig()
                                     .getAttachedSourceDeviceForMixPort(
                                             std::get<PARAM_DEVICE_NAME>(
@@ -690,6 +704,15 @@
         InputStreamTest::TearDown();
     }
 
+    bool canQueryCapturePosition() const {
+        auto maybeSourceAddress = getCachedPolicyConfig().getSourceDeviceForMixPort(
+                getDeviceName(), getMixPortName());
+        // Returning 'true' when no source is found so the test can fail later with a more clear
+        // problem description.
+        return !maybeSourceAddress.has_value() ||
+               !xsd::isTelephonyDevice(maybeSourceAddress.value().deviceType);
+    }
+
     void createPatchIfNeeded() {
         auto maybeSourceAddress = getCachedPolicyConfig().getSourceDeviceForMixPort(
                 getDeviceName(), getMixPortName());
@@ -714,6 +737,7 @@
             EXPECT_OK(stream->setDevices({maybeSourceAddress.value()}));
         }
     }
+
     void releasePatchIfNeeded() {
         if (areAudioPatchesSupported()) {
             if (mHasPatch) {
@@ -724,7 +748,42 @@
             EXPECT_OK(stream->setDevices({address}));
         }
     }
-    const std::string& getMixPortName() const { return std::get<PARAM_PORT_NAME>(GetParam()); }
+
+    void waitForCapturePositionAdvance(StreamReader& reader, uint64_t* firstPosition = nullptr,
+                                       uint64_t* lastPosition = nullptr) {
+        static constexpr int kReadDurationUs = 50 * 1000;
+        static constexpr std::chrono::milliseconds kPositionChangeTimeout{10000};
+        uint64_t framesInitial, ts;
+        // Starting / resuming of streams is asynchronous at HAL level.
+        // Sometimes HAL doesn't have enough information until the audio data actually has been
+        // produced by the hardware. Legacy HALs might return NOT_SUPPORTED when they actually
+        // mean INVALID_STATE.
+        bool timedOut = false;
+        res = Result::INVALID_STATE;
+        for (android::base::Timer elapsed;
+             res != Result::OK && !reader.hasError() &&
+             !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
+            usleep(kReadDurationUs);
+            ASSERT_OK(stream->getCapturePosition(returnIn(res, framesInitial, ts)));
+            ASSERT_RESULT(okOrInvalidStateOrNotSupported, res);
+        }
+        ASSERT_FALSE(reader.hasError());
+        ASSERT_FALSE(timedOut);
+
+        uint64_t frames = framesInitial;
+        for (android::base::Timer elapsed;
+             frames <= framesInitial && !reader.hasError() &&
+             !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
+            usleep(kReadDurationUs);
+            ASSERT_OK(stream->getCapturePosition(returnIn(res, frames, ts)));
+            ASSERT_RESULT(Result::OK, res);
+        }
+        EXPECT_FALSE(timedOut);
+        EXPECT_FALSE(reader.hasError());
+        EXPECT_GT(frames, framesInitial);
+        if (firstPosition) *firstPosition = framesInitial;
+        if (lastPosition) *lastPosition = frames;
+    }
 
   private:
     AudioPatchHandle mPatchHandle = {};
@@ -740,47 +799,36 @@
 
 TEST_P(PcmOnlyConfigInputStreamTest, CapturePositionAdvancesWithReads) {
     doc::test("Check that the capture position advances with reads");
+    if (!canQueryCapturePosition()) {
+        GTEST_SKIP() << "Capture position retrieval is not possible";
+    }
 
     ASSERT_NO_FATAL_FAILURE(createPatchIfNeeded());
     StreamReader reader(stream.get(), stream->getBufferSize());
     ASSERT_TRUE(reader.start());
     EXPECT_TRUE(reader.waitForAtLeastOneCycle());
-
-    uint64_t framesInitial, ts;
-    ASSERT_OK(stream->getCapturePosition(returnIn(res, framesInitial, ts)));
-    ASSERT_RESULT(Result::OK, res);
-
-    EXPECT_TRUE(reader.waitForAtLeastOneCycle());
-
-    uint64_t frames;
-    ASSERT_OK(stream->getCapturePosition(returnIn(res, frames, ts)));
-    ASSERT_RESULT(Result::OK, res);
-    EXPECT_GT(frames, framesInitial);
-
-    reader.stop();
-    releasePatchIfNeeded();
+    ASSERT_NO_FATAL_FAILURE(waitForCapturePositionAdvance(reader));
 }
 
 TEST_P(PcmOnlyConfigInputStreamTest, CapturePositionPreservedOnStandby) {
     doc::test("Check that the capture position does not reset on standby");
+    if (!canQueryCapturePosition()) {
+        GTEST_SKIP() << "Capture position retrieval is not possible";
+    }
 
     ASSERT_NO_FATAL_FAILURE(createPatchIfNeeded());
     StreamReader reader(stream.get(), stream->getBufferSize());
     ASSERT_TRUE(reader.start());
     EXPECT_TRUE(reader.waitForAtLeastOneCycle());
 
-    uint64_t framesInitial, ts;
-    ASSERT_OK(stream->getCapturePosition(returnIn(res, framesInitial, ts)));
-    ASSERT_RESULT(Result::OK, res);
-
+    uint64_t framesInitial;
+    ASSERT_NO_FATAL_FAILURE(waitForCapturePositionAdvance(reader, nullptr, &framesInitial));
     reader.pause();
     ASSERT_OK(stream->standby());
     reader.resume();
-    EXPECT_FALSE(reader.hasError());
 
     uint64_t frames;
-    ASSERT_OK(stream->getCapturePosition(returnIn(res, frames, ts)));
-    ASSERT_RESULT(Result::OK, res);
+    ASSERT_NO_FATAL_FAILURE(waitForCapturePositionAdvance(reader, &frames, nullptr));
     EXPECT_GT(frames, framesInitial);
 
     reader.stop();
diff --git a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
index ae1467d..aa7fd8e 100644
--- a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
+++ b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
@@ -1194,7 +1194,17 @@
 #if MAJOR_VERSION <= 6
         address.device = AudioDevice::IN_DEFAULT;
 #elif MAJOR_VERSION >= 7
-        address.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT);
+        auto maybeSourceAddress = getCachedPolicyConfig().getSourceDeviceForMixPort(
+                getDeviceName(), getMixPortName());
+        if (maybeSourceAddress.has_value() &&
+            !xsd::isTelephonyDevice(maybeSourceAddress.value().deviceType)) {
+            address = maybeSourceAddress.value();
+            auto& metadata = initMetadata.tracks[0];
+            metadata.source = toString(xsd::AudioSource::AUDIO_SOURCE_UNPROCESSED);
+            metadata.channelMask = getConfig().base.channelMask;
+        } else {
+            address.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT);
+        }
 #endif
         const AudioConfig& config = getConfig();
         auto flags = getInputFlags();
@@ -1212,7 +1222,8 @@
 #elif MAJOR_VERSION >= 4 && MAJOR_VERSION <= 6
      const SinkMetadata initMetadata = {{ {.source = AudioSource::DEFAULT, .gain = 1 } }};
 #elif MAJOR_VERSION >= 7
-     const SinkMetadata initMetadata = {
+     const std::string& getMixPortName() const { return std::get<PARAM_PORT_NAME>(GetParam()); }
+     SinkMetadata initMetadata = {
              {{.source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT),
                .gain = 1,
                .tags = {},
diff --git a/audio/core/all-versions/vts/functional/tests/streamworker_tests.cpp b/audio/core/all-versions/vts/functional/tests/streamworker_tests.cpp
index 75116af..925fd33 100644
--- a/audio/core/all-versions/vts/functional/tests/streamworker_tests.cpp
+++ b/audio/core/all-versions/vts/functional/tests/streamworker_tests.cpp
@@ -33,12 +33,6 @@
     // Use nullptr to test error reporting from the worker thread.
     explicit TestWorker(TestStream* stream) : mStream(stream) {}
 
-    void ensureWorkerCycled() {
-        const size_t cyclesBefore = mWorkerCycles;
-        while (mWorkerCycles == cyclesBefore && !hasError()) {
-            sched_yield();
-        }
-    }
     size_t getWorkerCycles() const { return mWorkerCycles; }
     bool hasWorkerCycleCalled() const { return mWorkerCycles != 0; }
     bool hasNoWorkerCycleCalled(useconds_t usec) {
@@ -131,21 +125,21 @@
 
 TEST_P(StreamWorkerTest, Start) {
     ASSERT_TRUE(worker.start());
-    worker.ensureWorkerCycled();
+    worker.waitForAtLeastOneCycle();
     EXPECT_FALSE(worker.hasError());
 }
 
 TEST_P(StreamWorkerTest, WorkerError) {
     ASSERT_TRUE(worker.start());
     stream.error = true;
-    worker.ensureWorkerCycled();
+    worker.waitForAtLeastOneCycle();
     EXPECT_TRUE(worker.hasError());
     EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
 }
 
 TEST_P(StreamWorkerTest, PauseResume) {
     ASSERT_TRUE(worker.start());
-    worker.ensureWorkerCycled();
+    worker.waitForAtLeastOneCycle();
     EXPECT_FALSE(worker.hasError());
     worker.pause();
     EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
@@ -159,7 +153,7 @@
 
 TEST_P(StreamWorkerTest, StopPaused) {
     ASSERT_TRUE(worker.start());
-    worker.ensureWorkerCycled();
+    worker.waitForAtLeastOneCycle();
     EXPECT_FALSE(worker.hasError());
     worker.pause();
     worker.stop();
@@ -169,7 +163,7 @@
 TEST_P(StreamWorkerTest, PauseAfterErrorIgnored) {
     ASSERT_TRUE(worker.start());
     stream.error = true;
-    worker.ensureWorkerCycled();
+    worker.waitForAtLeastOneCycle();
     EXPECT_TRUE(worker.hasError());
     worker.pause();
     EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
@@ -179,7 +173,7 @@
 TEST_P(StreamWorkerTest, ResumeAfterErrorIgnored) {
     ASSERT_TRUE(worker.start());
     stream.error = true;
-    worker.ensureWorkerCycled();
+    worker.waitForAtLeastOneCycle();
     EXPECT_TRUE(worker.hasError());
     worker.resume();
     EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
@@ -188,14 +182,14 @@
 
 TEST_P(StreamWorkerTest, WorkerErrorOnResume) {
     ASSERT_TRUE(worker.start());
-    worker.ensureWorkerCycled();
+    worker.waitForAtLeastOneCycle();
     EXPECT_FALSE(worker.hasError());
     worker.pause();
     EXPECT_FALSE(worker.hasError());
     stream.error = true;
     EXPECT_FALSE(worker.hasError());
     worker.resume();
-    worker.ensureWorkerCycled();
+    worker.waitForAtLeastOneCycle();
     EXPECT_TRUE(worker.hasError());
     EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
 }
diff --git a/audio/effect/all-versions/default/util/EffectUtils.cpp b/audio/effect/all-versions/default/util/EffectUtils.cpp
index b4382dc..1156d21 100644
--- a/audio/effect/all-versions/default/util/EffectUtils.cpp
+++ b/audio/effect/all-versions/default/util/EffectUtils.cpp
@@ -25,8 +25,6 @@
 
 #include "util/EffectUtils.h"
 
-#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
-
 using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
 using ::android::hardware::audio::common::CPP_VERSION::implementation::UuidUtils;
 using ::android::hardware::audio::common::utils::EnumBitfield;
@@ -154,6 +152,29 @@
     return result;
 }
 
+template <std::size_t N>
+inline hidl_string charBufferFromHal(const char (&halBuf)[N]) {
+    // Even if the original field contains a non-terminated string, hidl_string
+    // adds a NUL terminator.
+    return hidl_string(halBuf, strnlen(halBuf, N));
+}
+
+template <std::size_t N>
+inline status_t charBufferToHal(const hidl_string& str, char (&halBuf)[N], const char* fieldName) {
+    static_assert(N > 0);
+    const size_t halBufChars = N - 1;  // Reserve one character for terminating NUL.
+    status_t result = NO_ERROR;
+    size_t strSize = str.size();
+    if (strSize > halBufChars) {
+        ALOGE("%s is too long: %zu (%zu max)", fieldName, strSize, halBufChars);
+        strSize = halBufChars;
+        result = BAD_VALUE;
+    }
+    strncpy(halBuf, str.c_str(), strSize);
+    halBuf[strSize] = '\0';
+    return result;
+}
+
 status_t EffectUtils::effectDescriptorFromHal(const effect_descriptor_t& halDescriptor,
                                               EffectDescriptor* descriptor) {
     UuidUtils::uuidFromHal(halDescriptor.type, &descriptor->type);
@@ -166,9 +187,8 @@
     memcpy(descriptor->implementor.data(), halDescriptor.implementor,
            descriptor->implementor.size());
 #else
-    descriptor->name = hidl_string(halDescriptor.name, ARRAY_SIZE(halDescriptor.name));
-    descriptor->implementor =
-            hidl_string(halDescriptor.implementor, ARRAY_SIZE(halDescriptor.implementor));
+    descriptor->name = charBufferFromHal(halDescriptor.name);
+    descriptor->implementor = charBufferFromHal(halDescriptor.implementor);
 #endif
     return NO_ERROR;
 }
@@ -186,25 +206,11 @@
     memcpy(halDescriptor->implementor, descriptor.implementor.data(),
            descriptor.implementor.size());
 #else
-    // According to 'dumpEffectDescriptor' 'name' and 'implementor' must be NUL-terminated.
-    size_t nameSize = descriptor.name.size();
-    if (nameSize >= ARRAY_SIZE(halDescriptor->name)) {
-        ALOGE("effect name is too long: %zu (%zu max)", nameSize,
-              ARRAY_SIZE(halDescriptor->name) - 1);
-        nameSize = ARRAY_SIZE(halDescriptor->name) - 1;
-        result = BAD_VALUE;
-    }
-    strncpy(halDescriptor->name, descriptor.name.c_str(), nameSize);
-    halDescriptor->name[nameSize] = '\0';
-    size_t implementorSize = descriptor.implementor.size();
-    if (implementorSize >= ARRAY_SIZE(halDescriptor->implementor)) {
-        ALOGE("effect implementor is too long: %zu (%zu max)", implementorSize,
-              ARRAY_SIZE(halDescriptor->implementor) - 1);
-        implementorSize = ARRAY_SIZE(halDescriptor->implementor) - 1;
-        result = BAD_VALUE;
-    }
-    strncpy(halDescriptor->implementor, descriptor.implementor.c_str(), implementorSize);
-    halDescriptor->implementor[implementorSize] = '\0';
+    // According to 'dumpEffectDescriptor', 'name' and 'implementor' must be NUL-terminated.
+    CONVERT_CHECKED(charBufferToHal(descriptor.name, halDescriptor->name, "effect name"), result);
+    CONVERT_CHECKED(charBufferToHal(descriptor.implementor, halDescriptor->implementor,
+                                    "effect implementor"),
+                    result);
 #endif
     return result;
 }
diff --git a/audio/effect/all-versions/default/util/tests/effectutils_tests.cpp b/audio/effect/all-versions/default/util/tests/effectutils_tests.cpp
index f3651de..d021fa0 100644
--- a/audio/effect/all-versions/default/util/tests/effectutils_tests.cpp
+++ b/audio/effect/all-versions/default/util/tests/effectutils_tests.cpp
@@ -154,3 +154,20 @@
     EXPECT_EQ(NO_ERROR, EffectUtils::effectDescriptorFromHal(halDesc, &descBack));
     EXPECT_EQ(desc, descBack);
 }
+
+TEST(EffectUtils, ConvertNameAndImplementor) {
+    for (size_t i = 0; i < EFFECT_STRING_LEN_MAX; ++i) {
+        effect_descriptor_t halDesc{};
+        for (size_t c = 0; c < i; ++c) {  // '<' to accommodate NUL terminator.
+            halDesc.name[c] = halDesc.implementor[c] = 'A' + static_cast<char>(c);
+        }
+        EffectDescriptor desc;
+        EXPECT_EQ(NO_ERROR, EffectUtils::effectDescriptorFromHal(halDesc, &desc));
+        effect_descriptor_t halDescBack;
+        EXPECT_EQ(NO_ERROR, EffectUtils::effectDescriptorToHal(desc, &halDescBack));
+        EXPECT_EQ(i, strlen(halDescBack.name));
+        EXPECT_EQ(i, strlen(halDescBack.implementor));
+        EXPECT_EQ(0, strcmp(halDesc.name, halDescBack.name));
+        EXPECT_EQ(0, strcmp(halDesc.implementor, halDescBack.implementor));
+    }
+}
diff --git a/identity/TEST_MAPPING b/identity/TEST_MAPPING
new file mode 100644
index 0000000..f35f4b7
--- /dev/null
+++ b/identity/TEST_MAPPING
@@ -0,0 +1,13 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsIdentityTestCases"
+    },
+    {
+      "name": "VtsHalIdentityTargetTest"
+    },
+    {
+      "name": "android.hardware.identity-support-lib-test"
+    }
+  ]
+}
diff --git a/power/stats/aidl/Android.bp b/power/stats/aidl/Android.bp
index 454c69a..0dbf9b4 100644
--- a/power/stats/aidl/Android.bp
+++ b/power/stats/aidl/Android.bp
@@ -41,4 +41,5 @@
             enabled: true,
         },
     },
+    host_supported: true,
 }
diff --git a/security/keymint/aidl/OWNERS b/security/keymint/aidl/OWNERS
index 5c79db8..a93b171 100644
--- a/security/keymint/aidl/OWNERS
+++ b/security/keymint/aidl/OWNERS
@@ -1,3 +1,4 @@
+jbires@google.com
 jdanis@google.com
 seleneh@google.com
 swillden@google.com
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
index c589ca1..f3c5477 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
@@ -53,13 +53,36 @@
 
     /**
      * If the generated/imported key is an asymmetric key, `certificateChain` will contain a chain
-     * of one or more certificates.  If the key parameters provided to the generate/import method
-     * contains Tag::ATTESTATION_CHALLENGE the first certificate will contain an attestation
-     * extension, and will be signed by a factory-installed attestation key and followed by a chain
-     * of certificates leading to an authoritative root.  If there is no attestation challenge, only
-     * one certificate will be returned, and it will be self-signed or contain a fake signature,
-     * depending on whether the key has KeyPurpose::SIGN.  If the generated key is symmetric,
-     * certificateChain will be empty.
+     * of one or more certificates.
+     *
+     * There are a few variations in what is contained in `certificateChain`, depending on whether
+     * the caller requested attestation, whether they provided an attestation key (via the
+     * `attestationKey` parameter of `generateKey()`, `importKey()` or `importWrappedKey()`), and in
+     * the non-attestaion case, whether the key can self-sign.
+     *
+     * 1.  Attestation with factory key.  If Tag::ATTESTATION_CHALLENGE is provided and the
+     *     `attestationKey` parameter on the generate/import call is null, the returned certificate
+     *     chain must contain an attestation certificate signed with a factory-provisioned
+     *     attestation key, and the full certificate chain for that factory-provisioned attestation
+     *     key.
+     *
+     * 2.  Attestation with caller-provided key.  If Tag::ATTESTATION_CHALLENGE is provided and the
+     *     `attestationKey` parameter on the generat/import call is non-null and contains the key
+     *     blob of a key with KeyPurpose::ATTEST_KEY, the returned certificate chain must contain
+     *     only an attestation certificate signed with the specified key.  The caller must know the
+     *     certificate chain for the provided key.
+     *
+     * 3.  Non-attestation with signing key.  If Tag::ATTESTATION_CHALLENGE is not provided and the
+     *     generated/imported key has KeyPurpose::SIGN, then the returned certificate chain must
+     *     contain only a single self-signed certificate with no attestation extension.
+     *
+     * 4.  Non-attestation with non-signing key.  If TAG::ATTESTATION_CHALLENGE is not provided and
+     *     the generated/imported key does not have KeyPurpose::SIGN, then the returned certificate
+     *     chain must contain only a single certificate with an empty signature and no attestation
+     *     extension.
+     *
+     * 5.  Symmetric key.  If the generated/imported key is symmetric, the certificate chain must be
+     *     empty.
      */
     Certificate[] certificateChain;
 }
diff --git a/tv/cec/1.0/vts/functional/Android.bp b/tv/cec/1.0/vts/functional/Android.bp
new file mode 100644
index 0000000..9a2c714
--- /dev/null
+++ b/tv/cec/1.0/vts/functional/Android.bp
@@ -0,0 +1,38 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+    name: "VtsHalTvCecV1_0TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: ["VtsHalTvCecV1_0TargetTest.cpp"],
+    static_libs: [
+        "android.hardware.tv.cec@1.0",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+    disable_framework: true,
+}
diff --git a/tv/cec/1.0/vts/functional/README.md b/tv/cec/1.0/vts/functional/README.md
new file mode 100644
index 0000000..aecd6a6
--- /dev/null
+++ b/tv/cec/1.0/vts/functional/README.md
@@ -0,0 +1,30 @@
+# CEC VTS testing for Android TV devices
+
+Validate HDMI CEC VTS (android.hardware.tv.cec@1.0) functionality.
+
+### Setup:
+
+Running these CEC VTS tests requires an Android playback, TV or audio device connected to the host machine.
+
+![drawing](setup.png)
+
+### Building
+
+From the Android root folder, after choosing the lunch combo, use `make vts` to build VTS.
+
+### Automation
+
+On the host machine, ensure that the [software requirements](https://codelabs.developers.google.com/codelabs/android-lab/#2) for python SDK are met.
+
+Given the setup described above you can run tests with any of the following commands:
+
+1. Using vts-tradefed :
+```
+cd $ANDROID_BUILD_TOP/out/host/linux-x86/vts/android-vts/tools
+./vts-tradefed run commandAndExit vts -m VtsHalTvCecV1_0TargetTest
+```
+2. Using atest
+```
+atest VtsHalTvCecV1_0TargetTest
+```
+Note : atest internally handles building as well. To update the test use '-c' (clear cache) option
diff --git a/tv/cec/1.0/vts/functional/VtsHalTvCecV1_0TargetTest.cpp b/tv/cec/1.0/vts/functional/VtsHalTvCecV1_0TargetTest.cpp
new file mode 100644
index 0000000..7b42689
--- /dev/null
+++ b/tv/cec/1.0/vts/functional/VtsHalTvCecV1_0TargetTest.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "HdmiCec_hal_test"
+#include <android-base/logging.h>
+
+#include <android/hardware/tv/cec/1.0/IHdmiCec.h>
+#include <android/hardware/tv/cec/1.0/types.h>
+#include <utils/Log.h>
+#include <sstream>
+#include <vector>
+
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+using ::android::sp;
+using ::android::hardware::hidl_death_recipient;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::tv::cec::V1_0::CecDeviceType;
+using ::android::hardware::tv::cec::V1_0::CecLogicalAddress;
+using ::android::hardware::tv::cec::V1_0::CecMessage;
+using ::android::hardware::tv::cec::V1_0::HdmiPortInfo;
+using ::android::hardware::tv::cec::V1_0::HdmiPortType;
+using ::android::hardware::tv::cec::V1_0::IHdmiCec;
+using ::android::hardware::tv::cec::V1_0::OptionKey;
+using ::android::hardware::tv::cec::V1_0::Result;
+using ::android::hardware::tv::cec::V1_0::SendMessageResult;
+
+#define CEC_VERSION 0x05
+#define INCORRECT_VENDOR_ID 0x00
+#define TV_PHYSICAL_ADDRESS 0x0000
+
+// The main test class for TV CEC HAL.
+class HdmiCecTest : public ::testing::TestWithParam<std::string> {
+  public:
+    void SetUp() override {
+        hdmiCec = IHdmiCec::getService(GetParam());
+        ASSERT_NE(hdmiCec, nullptr);
+        ALOGI("%s: getService() for hdmiCec is %s", __func__,
+              hdmiCec->isRemote() ? "remote" : "local");
+
+        hdmiCec_death_recipient = new HdmiCecDeathRecipient();
+        ASSERT_NE(hdmiCec_death_recipient, nullptr);
+        ASSERT_TRUE(hdmiCec->linkToDeath(hdmiCec_death_recipient, 0).isOk());
+    }
+
+    std::vector<int> getDeviceTypes() {
+        std::vector<int> deviceTypes;
+        FILE* p = popen("getprop ro.hdmi.device_type", "re");
+        if (p) {
+            char* line = NULL;
+            size_t len = 0;
+            if (getline(&line, &len, p) > 0) {
+                std::istringstream stream(line);
+                std::string number{};
+                while (std::getline(stream, number, ',')) {
+                    deviceTypes.push_back(stoi(number));
+                }
+            }
+            pclose(p);
+        }
+        return deviceTypes;
+    }
+
+    bool hasDeviceType(CecDeviceType type) {
+        std::vector<int> deviceTypes = getDeviceTypes();
+        for (auto deviceType = deviceTypes.begin(); deviceType != deviceTypes.end(); ++deviceType) {
+            if (*deviceType == (int)type) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    class HdmiCecDeathRecipient : public hidl_death_recipient {
+      public:
+        void serviceDied(uint64_t /*cookie*/,
+                         const android::wp<::android::hidl::base::V1_0::IBase>& /*who*/) override {
+            FAIL();
+        }
+    };
+
+    sp<IHdmiCec> hdmiCec;
+    sp<HdmiCecDeathRecipient> hdmiCec_death_recipient;
+};
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HdmiCecTest);
+INSTANTIATE_TEST_SUITE_P(
+        PerInstance, HdmiCecTest,
+        testing::ValuesIn(android::hardware::getAllHalInstanceNames(IHdmiCec::descriptor)),
+        android::hardware::PrintInstanceNameToString);
+
+TEST_P(HdmiCecTest, ClearAddLogicalAddress) {
+    hdmiCec->clearLogicalAddress();
+    Return<Result> ret = hdmiCec->addLogicalAddress(CecLogicalAddress::PLAYBACK_3);
+    EXPECT_EQ(ret, Result::SUCCESS);
+}
+
+TEST_P(HdmiCecTest, PhysicalAddress) {
+    Result result;
+    uint16_t addr;
+    Return<void> ret = hdmiCec->getPhysicalAddress([&result, &addr](Result res, uint16_t paddr) {
+        result = res;
+        addr = paddr;
+    });
+    EXPECT_TRUE(ret.isOk());
+    EXPECT_EQ(result, Result::SUCCESS);
+    if (!hasDeviceType(CecDeviceType::TV)) {
+        EXPECT_NE(addr, TV_PHYSICAL_ADDRESS);
+    }
+}
+
+TEST_P(HdmiCecTest, SendMessage) {
+    CecMessage message;
+    message.initiator = CecLogicalAddress::PLAYBACK_1;
+    message.destination = CecLogicalAddress::BROADCAST;
+    message.body.resize(1);
+    message.body[0] = 131;
+    SendMessageResult ret = hdmiCec->sendMessage(message);
+    EXPECT_EQ(ret, SendMessageResult::SUCCESS);
+}
+
+TEST_P(HdmiCecTest, CecVersion) {
+    Return<int32_t> ret = hdmiCec->getCecVersion();
+    EXPECT_GE(ret, CEC_VERSION);
+}
+
+TEST_P(HdmiCecTest, VendorId) {
+    Return<uint32_t> ret = hdmiCec->getVendorId();
+    EXPECT_NE(ret, INCORRECT_VENDOR_ID);
+}
+
+TEST_P(HdmiCecTest, GetPortInfo) {
+    hidl_vec<HdmiPortInfo> ports;
+    Return<void> ret =
+            hdmiCec->getPortInfo([&ports](hidl_vec<HdmiPortInfo> list) { ports = list; });
+    EXPECT_TRUE(ret.isOk());
+    bool cecSupportedOnDevice = false;
+    for (size_t i = 0; i < ports.size(); ++i) {
+        EXPECT_TRUE((ports[i].type == HdmiPortType::OUTPUT) ||
+                    (ports[i].type == HdmiPortType::INPUT));
+        if (ports[i].portId == 0) {
+            ALOGW("%s: Port id should start from 1", __func__);
+        }
+        cecSupportedOnDevice = cecSupportedOnDevice | ports[i].cecSupported;
+    }
+    EXPECT_NE(cecSupportedOnDevice, false) << "At least one port should support CEC";
+}
+
+TEST_P(HdmiCecTest, SetOption) {
+    Return<void> ret;
+    ret = hdmiCec->setOption(OptionKey::WAKEUP, false);
+    EXPECT_TRUE(ret.isOk());
+    ret = hdmiCec->setOption(OptionKey::ENABLE_CEC, false);
+    EXPECT_TRUE(ret.isOk());
+    ret = hdmiCec->setOption(OptionKey::SYSTEM_CEC_CONTROL, true);
+    EXPECT_TRUE(ret.isOk());
+    // Restore option keys to their default values
+    ret = hdmiCec->setOption(OptionKey::WAKEUP, true);
+    EXPECT_TRUE(ret.isOk());
+    ret = hdmiCec->setOption(OptionKey::ENABLE_CEC, true);
+    EXPECT_TRUE(ret.isOk());
+    ret = hdmiCec->setOption(OptionKey::SYSTEM_CEC_CONTROL, false);
+    EXPECT_TRUE(ret.isOk());
+}
+
+TEST_P(HdmiCecTest, SetLanguage) {
+    Return<void> ret = hdmiCec->setLanguage("eng");
+    EXPECT_TRUE(ret.isOk());
+}
+
+TEST_P(HdmiCecTest, EnableAudioReturnChannel) {
+    hidl_vec<HdmiPortInfo> ports;
+    Return<void> ret =
+            hdmiCec->getPortInfo([&ports](hidl_vec<HdmiPortInfo> list) { ports = list; });
+    EXPECT_TRUE(ret.isOk());
+    for (size_t i = 0; i < ports.size(); ++i) {
+        if (ports[i].arcSupported) {
+            ret = hdmiCec->enableAudioReturnChannel(ports[i].portId, true);
+            EXPECT_TRUE(ret.isOk());
+        }
+    }
+}
+
+TEST_P(HdmiCecTest, IsConnected) {
+    hidl_vec<HdmiPortInfo> ports;
+    Return<void> ret =
+            hdmiCec->getPortInfo([&ports](hidl_vec<HdmiPortInfo> list) { ports = list; });
+    EXPECT_TRUE(ret.isOk());
+    for (size_t i = 0; i < ports.size(); ++i) {
+        Return<bool> ret = hdmiCec->isConnected(ports[i].portId);
+        EXPECT_TRUE(ret.isOk());
+    }
+}
diff --git a/tv/cec/1.0/vts/functional/setup.png b/tv/cec/1.0/vts/functional/setup.png
new file mode 100644
index 0000000..a64b86c
--- /dev/null
+++ b/tv/cec/1.0/vts/functional/setup.png
Binary files differ