Merge "Add new vibrator effects."
diff --git a/authsecret/1.0/IAuthSecret.hal b/authsecret/1.0/IAuthSecret.hal
index d2cb5da..6b573b3 100644
--- a/authsecret/1.0/IAuthSecret.hal
+++ b/authsecret/1.0/IAuthSecret.hal
@@ -24,25 +24,23 @@
  */
 interface IAuthSecret {
     /**
-     * When the primary user correctly enters their credential, this method is
-     * passed a secret derived from that credential to prove that their
-     * credential is known.
+     * When the primary user is unlocked, this method is passed a secret to
+     * prove that is has been successfully unlocked. The primary user can either
+     * be unlocked by a person entering their credential or by another party
+     * using an escrow token e.g. a device administrator.
      *
      * The first time this is called, the secret must be used to provision state
-     * that depends on the primary user's credential. The same secret is passed
-     * on each call until a factory reset after which there must be a new
-     * secret.
+     * that depends on the primary user's secret. The same secret must be passed
+     * on each call until the next factory reset.
      *
-     * The secret must be at lesat 16 bytes.
+     * Upon factory reset, any dependence on the secret must be removed as that
+     * secret is now lost and must never be derived again. A new secret must be
+     * created for the new primary user which must be used to newly provision
+     * state the first time this method is called after factory reset.
+     *
+     * The secret must be at least 16 bytes.
      *
      * @param secret blob derived from the primary user's credential.
      */
     primaryUserCredential(vec<uint8_t> secret);
-
-    /**
-     * Called from recovery during factory reset. The secret is now lost and can
-     * no longer be derived. Any data linked to the secret must be destroyed and
-     * any dependence on the secret must be removed.
-     */
-    factoryReset();
 };
diff --git a/authsecret/1.0/default/AuthSecret.cpp b/authsecret/1.0/default/AuthSecret.cpp
index 46a3ec1..f9271e9 100644
--- a/authsecret/1.0/default/AuthSecret.cpp
+++ b/authsecret/1.0/default/AuthSecret.cpp
@@ -29,16 +29,12 @@
     return Void();
 }
 
-Return<void> AuthSecret::factoryReset() {
-    // Clear all dependency on the secret.
-    //
-    // With the example of updating a security module, the stored value must be
-    // cleared so that the new primary user enrolled as the approver of updates.
-    //
-    // This implementation does nothing as there is no dependence on the secret.
-
-    return Void();
-}
+// Note: on factory reset, clear all dependency on the secret.
+//
+// With the example of updating a security module, the stored value must be
+// cleared so that the new primary user enrolled as the approver of updates.
+//
+// This implementation does nothing as there is no dependence on the secret.
 
 }  // namespace implementation
 }  // namespace V1_0
diff --git a/authsecret/1.0/default/AuthSecret.h b/authsecret/1.0/default/AuthSecret.h
index edb49b8..387fa67 100644
--- a/authsecret/1.0/default/AuthSecret.h
+++ b/authsecret/1.0/default/AuthSecret.h
@@ -22,7 +22,6 @@
 struct AuthSecret : public IAuthSecret {
     // Methods from ::android::hardware::authsecret::V1_0::IAuthSecret follow.
     Return<void> primaryUserCredential(const hidl_vec<uint8_t>& secret) override;
-    Return<void> factoryReset() override;
 
     // Methods from ::android::hidl::base::V1_0::IBase follow.
 };
diff --git a/authsecret/1.0/vts/functional/VtsHalAuthSecretV1_0TargetTest.cpp b/authsecret/1.0/vts/functional/VtsHalAuthSecretV1_0TargetTest.cpp
index b0cbd91..a610a75 100644
--- a/authsecret/1.0/vts/functional/VtsHalAuthSecretV1_0TargetTest.cpp
+++ b/authsecret/1.0/vts/functional/VtsHalAuthSecretV1_0TargetTest.cpp
@@ -30,68 +30,42 @@
     virtual void SetUp() override {
         authsecret = ::testing::VtsHalHidlTargetTestBase::getService<IAuthSecret>();
         ASSERT_NE(authsecret, nullptr);
-        authsecret->factoryReset();
+
+        // All tests must enroll the correct secret first as this cannot be changed
+        // without a factory reset and the order of tests could change.
+        authsecret->primaryUserCredential(CORRECT_SECRET);
     }
 
     sp<IAuthSecret> authsecret;
+    hidl_vec<uint8_t> CORRECT_SECRET{61, 93, 124, 240, 5, 0, 7, 201, 9, 129, 11, 12, 0, 14, 0, 16};
+    hidl_vec<uint8_t> WRONG_SECRET{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
 };
 
 /* Provision the primary user with a secret. */
 TEST_F(AuthSecretHidlTest, provisionPrimaryUserCredential) {
-    hidl_vec<uint8_t> secret{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
-    authsecret->primaryUserCredential(secret);
-}
-
-/* Provision the primary user with a large secret. */
-TEST_F(AuthSecretHidlTest, provisionPrimaryUserCredentialWithLargeSecret) {
-    hidl_vec<uint8_t> secret{89,  233, 52,  29,  130, 210, 229, 170, 124, 102, 56,  238, 198,
-                             199, 246, 152, 185, 123, 155, 215, 29,  252, 30,  70,  118, 29,
-                             149, 36,  222, 203, 163, 7,   72,  56,  247, 19,  198, 76,  71,
-                             37,  120, 201, 220, 70,  150, 18,  23,  22,  236, 57,  184, 86,
-                             190, 122, 210, 207, 74,  51,  222, 157, 74,  196, 86,  208};
-    authsecret->primaryUserCredential(secret);
+    // Secret provisioned by SetUp()
 }
 
 /* Provision the primary user with a secret and pass the secret again. */
 TEST_F(AuthSecretHidlTest, provisionPrimaryUserCredentialAndPassAgain) {
-    hidl_vec<uint8_t> secret{64, 2, 3, 0, 5, 6, 7, 172, 9, 10, 11, 255, 13, 14, 15, 83};
-    authsecret->primaryUserCredential(secret);
-    authsecret->primaryUserCredential(secret);
+    // Secret provisioned by SetUp()
+    authsecret->primaryUserCredential(CORRECT_SECRET);
 }
 
 /* Provision the primary user with a secret and pass the secret again repeatedly. */
 TEST_F(AuthSecretHidlTest, provisionPrimaryUserCredentialAndPassAgainMultipleTimes) {
-    hidl_vec<uint8_t> secret{1, 2, 34, 4, 5, 6, 7, 8, 9, 105, 11, 12, 13, 184, 15, 16};
-    authsecret->primaryUserCredential(secret);
+    // Secret provisioned by SetUp()
     constexpr int N = 5;
     for (int i = 0; i < N; ++i) {
-        authsecret->primaryUserCredential(secret);
+        authsecret->primaryUserCredential(CORRECT_SECRET);
     }
 }
 
-/* Factory reset before provisioning the primary user with a secret. */
-TEST_F(AuthSecretHidlTest, factoryResetWithoutProvisioningPrimaryUserCredential) {
-    authsecret->factoryReset();
-}
-
-/* Provision the primary user with a secret then factory reset. */
-TEST_F(AuthSecretHidlTest, provisionPrimaryUserCredentialAndFactoryReset) {
-    hidl_vec<uint8_t> secret{1, 24, 124, 240, 5, 6, 7, 8, 9, 13, 11, 12, 189, 14, 195, 16};
-    authsecret->primaryUserCredential(secret);
-    authsecret->factoryReset();
-}
-
-/* Provision the primary differently after factory reset. */
-TEST_F(AuthSecretHidlTest, provisionPrimaryUserCredentialDifferentlyAfterFactoryReset) {
-    {
-        hidl_vec<uint8_t> secret1{19, 0, 65, 20, 65, 12, 7, 8, 9, 13, 29, 12, 189, 32, 195, 16};
-        authsecret->primaryUserCredential(secret1);
-    }
-
-    authsecret->factoryReset();
-
-    {
-        hidl_vec<uint8_t> secret2{61, 93, 124, 240, 5, 0, 7, 201, 9, 129, 11, 12, 0, 14, 0, 16};
-        authsecret->primaryUserCredential(secret2);
-    }
+/* Provision the primary user with a secret and then pass the wrong secret. This
+ * should never happen and is an framework bug if it does. As the secret is
+ * wrong, the HAL implementation may not be able to function correctly but it
+ * should fail gracefully. */
+TEST_F(AuthSecretHidlTest, provisionPrimaryUserCredentialAndWrongSecret) {
+    // Secret provisioned by SetUp()
+    authsecret->primaryUserCredential(WRONG_SECRET);
 }
diff --git a/automotive/vehicle/2.0/Android.bp b/automotive/vehicle/2.0/Android.bp
index a13359e..a0d20f3 100644
--- a/automotive/vehicle/2.0/Android.bp
+++ b/automotive/vehicle/2.0/Android.bp
@@ -56,6 +56,7 @@
         "VehiclePropertyChangeMode",
         "VehiclePropertyGroup",
         "VehiclePropertyOperation",
+        "VehiclePropertyStatus",
         "VehiclePropertyType",
         "VehicleRadioConstants",
         "VehicleTurnSignal",
diff --git a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/SubscriptionManager.h b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/SubscriptionManager.h
index 8e9089d..6086c01 100644
--- a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/SubscriptionManager.h
+++ b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/SubscriptionManager.h
@@ -49,7 +49,7 @@
     }
 
     void addOrUpdateSubscription(const SubscribeOptions &opts);
-    bool isSubscribed(int32_t propId, int32_t areaId, SubscribeFlags flags);
+    bool isSubscribed(int32_t propId, SubscribeFlags flags);
     std::vector<int32_t> getSubscribedProperties() const;
 
 private:
@@ -87,8 +87,7 @@
     /**
      * Constructs SubscriptionManager
      *
-     * @param onPropertyUnsubscribed - this callback function will be called when there are no
-     *                                    more client subscribed to particular property.
+     * @param onPropertyUnsubscribed - called when no more clients are subscribed to the property.
      */
     SubscriptionManager(const OnPropertyUnsubscribed& onPropertyUnsubscribed)
             : mOnPropertyUnsubscribed(onPropertyUnsubscribed),
@@ -115,9 +114,7 @@
             const std::vector<recyclable_ptr<VehiclePropValue>>& propValues,
             SubscribeFlags flags) const;
 
-    std::list<sp<HalClient>> getSubscribedClients(int32_t propId,
-                                                  int32_t area,
-                                                  SubscribeFlags flags) const;
+    std::list<sp<HalClient>> getSubscribedClients(int32_t propId, SubscribeFlags flags) const;
     /**
      * If there are no clients subscribed to given properties than callback function provided
      * in the constructor will be called.
@@ -125,10 +122,9 @@
     void unsubscribe(ClientId clientId, int32_t propId);
 private:
     std::list<sp<HalClient>> getSubscribedClientsLocked(int32_t propId,
-                                                        int32_t area,
                                                         SubscribeFlags flags) const;
 
-    bool updateHalEventSubscriptionLocked(const SubscribeOptions &opts, SubscribeOptions* out);
+    bool updateHalEventSubscriptionLocked(const SubscribeOptions& opts, SubscribeOptions* out);
 
     void addClientToPropMapLocked(int32_t propId, const sp<HalClient>& client);
 
diff --git a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHal.h b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHal.h
index 8203a1e..fd28483 100644
--- a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHal.h
+++ b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHal.h
@@ -48,17 +48,14 @@
 
     /**
      * Subscribe to HAL property events. This method might be called multiple
-     * times for the same vehicle property to update subscribed areas or sample
-     * rate.
+     * times for the same vehicle property to update sample rate.
      *
      * @param property to subscribe
-     * @param areas a bitwise vehicle areas or 0 for all supported areas
      * @param sampleRate sample rate in Hz for properties that support sample
      *                   rate, e.g. for properties with
      *                   VehiclePropertyChangeMode::CONTINUOUS
      */
     virtual StatusCode subscribe(int32_t property,
-                                 int32_t areas,
                                  float sampleRate) = 0;
 
     /**
diff --git a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleObjectPool.h b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleObjectPool.h
index 05c649b..359bb6d 100644
--- a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleObjectPool.h
+++ b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleObjectPool.h
@@ -191,9 +191,8 @@
     VehiclePropValuePool& operator=(VehiclePropValuePool&) = delete;
 private:
     bool isDisposable(VehiclePropertyType type, size_t vecSize) const {
-        return vecSize > mMaxRecyclableVectorSize ||
-               VehiclePropertyType::STRING == type ||
-               VehiclePropertyType::COMPLEX == type;
+        return vecSize > mMaxRecyclableVectorSize || VehiclePropertyType::STRING == type ||
+               VehiclePropertyType::MIXED == type;
     }
 
     RecyclableType obtainDisposable(VehiclePropertyType valueType,
diff --git a/automotive/vehicle/2.0/default/common/src/SubscriptionManager.cpp b/automotive/vehicle/2.0/default/common/src/SubscriptionManager.cpp
index 74f0a5f..a7d5f50 100644
--- a/automotive/vehicle/2.0/default/common/src/SubscriptionManager.cpp
+++ b/automotive/vehicle/2.0/default/common/src/SubscriptionManager.cpp
@@ -34,23 +34,12 @@
 bool mergeSubscribeOptions(const SubscribeOptions &oldOpts,
                            const SubscribeOptions &newOpts,
                            SubscribeOptions *outResult) {
-
-    int32_t updatedAreas = oldOpts.vehicleAreas;
-    if (updatedAreas != kAllSupportedAreas) {
-        updatedAreas = newOpts.vehicleAreas != kAllSupportedAreas
-            ? updatedAreas | newOpts.vehicleAreas
-            : kAllSupportedAreas;
-    }
-
     float updatedRate = std::max(oldOpts.sampleRate, newOpts.sampleRate);
     SubscribeFlags updatedFlags = SubscribeFlags(oldOpts.flags | newOpts.flags);
 
-    bool updated = updatedRate > oldOpts.sampleRate
-                   || updatedAreas != oldOpts.vehicleAreas
-                   || updatedFlags != oldOpts.flags;
+    bool updated = (updatedRate > oldOpts.sampleRate) || (updatedFlags != oldOpts.flags);
     if (updated) {
         *outResult = oldOpts;
-        outResult->vehicleAreas = updatedAreas;
         outResult->sampleRate = updatedRate;
         outResult->flags = updatedFlags;
     }
@@ -75,15 +64,13 @@
 }
 
 bool HalClient::isSubscribed(int32_t propId,
-                             int32_t areaId,
                              SubscribeFlags flags) {
     auto it = mSubscriptions.find(propId);
     if (it == mSubscriptions.end()) {
         return false;
     }
     const SubscribeOptions& opts = it->second;
-    bool res = (opts.flags & flags)
-           && (opts.vehicleAreas == 0 || areaId == 0 || opts.vehicleAreas & areaId);
+    bool res = (opts.flags & flags);
     return res;
 }
 
@@ -139,8 +126,7 @@
         MuxGuard g(mLock);
         for (const auto& propValue: propValues) {
             VehiclePropValue* v = propValue.get();
-            auto clients = getSubscribedClientsLocked(
-                v->prop, v->areaId, flags);
+            auto clients = getSubscribedClientsLocked(v->prop, flags);
             for (const auto& client : clients) {
                 clientValuesMap[client].push_back(v);
             }
@@ -158,21 +144,21 @@
     return clientValues;
 }
 
-std::list<sp<HalClient>> SubscriptionManager::getSubscribedClients(
-    int32_t propId, int32_t area, SubscribeFlags flags) const {
+std::list<sp<HalClient>> SubscriptionManager::getSubscribedClients(int32_t propId,
+                                                                   SubscribeFlags flags) const {
     MuxGuard g(mLock);
-    return getSubscribedClientsLocked(propId, area, flags);
+    return getSubscribedClientsLocked(propId, flags);
 }
 
 std::list<sp<HalClient>> SubscriptionManager::getSubscribedClientsLocked(
-        int32_t propId, int32_t area, SubscribeFlags flags) const {
+    int32_t propId, SubscribeFlags flags) const {
     std::list<sp<HalClient>> subscribedClients;
 
     sp<HalClientVector> propClients = getClientsForPropertyLocked(propId);
     if (propClients.get() != nullptr) {
         for (size_t i = 0; i < propClients->size(); i++) {
             const auto& client = propClients->itemAt(i);
-            if (client->isSubscribed(propId, area, flags)) {
+            if (client->isSubscribed(propId, flags)) {
                 subscribedClients.push_back(client);
             }
         }
diff --git a/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp b/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp
index ae543bb..1918421 100644
--- a/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp
+++ b/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp
@@ -142,15 +142,6 @@
             return StatusCode::INVALID_ARG;
         }
 
-        int32_t areas = isGlobalProp(prop) ? 0 : ops.vehicleAreas;
-        if (areas != 0 && ((areas & config->supportedAreas) != areas)) {
-            ALOGE("Failed to subscribe property 0x%x. Requested areas 0x%x are "
-                  "out of supported range of 0x%x", prop, ops.vehicleAreas,
-                  config->supportedAreas);
-            return StatusCode::INVALID_ARG;
-        }
-
-        ops.vehicleAreas = areas;
         ops.sampleRate = checkSampleRate(*config, ops.sampleRate);
     }
 
@@ -164,7 +155,7 @@
     }
 
     for (auto opt : updatedOptions) {
-        mHal->subscribe(opt.propId, opt.vehicleAreas, opt.sampleRate);
+        mHal->subscribe(opt.propId, opt.sampleRate);
     }
 
     return StatusCode::OK;
@@ -224,8 +215,8 @@
 void VehicleHalManager::onHalPropertySetError(StatusCode errorCode,
                                               int32_t property,
                                               int32_t areaId) {
-    const auto& clients = mSubscriptionManager.getSubscribedClients(
-            property, 0, SubscribeFlags::HAL_EVENT);
+    const auto& clients =
+        mSubscriptionManager.getSubscribedClients(property, SubscribeFlags::HAL_EVENT);
 
     for (auto client : clients) {
         client->getCallback()->onPropertySetError(errorCode, property, areaId);
@@ -326,8 +317,7 @@
 }
 
 void VehicleHalManager::handlePropertySetEvent(const VehiclePropValue& value) {
-    auto clients = mSubscriptionManager.getSubscribedClients(
-            value.prop, value.areaId, SubscribeFlags::SET_CALL);
+    auto clients = mSubscriptionManager.getSubscribedClients(value.prop, SubscribeFlags::SET_CALL);
     for (auto client : clients) {
         client->getCallback()->onPropertySet(value);
     }
diff --git a/automotive/vehicle/2.0/default/common/src/VehicleObjectPool.cpp b/automotive/vehicle/2.0/default/common/src/VehicleObjectPool.cpp
index ac1245a..3f98a94 100644
--- a/automotive/vehicle/2.0/default/common/src/VehicleObjectPool.cpp
+++ b/automotive/vehicle/2.0/default/common/src/VehicleObjectPool.cpp
@@ -82,7 +82,7 @@
 }
 
 VehiclePropValuePool::RecyclableType VehiclePropValuePool::obtainComplex() {
-    return obtain(VehiclePropertyType::COMPLEX);
+    return obtain(VehiclePropertyType::MIXED);
 }
 
 VehiclePropValuePool::RecyclableType VehiclePropValuePool::obtainRecylable(
@@ -138,18 +138,14 @@
 }
 
 bool VehiclePropValuePool::InternalPool::check(VehiclePropValue::RawValue* v) {
-    return check(&v->int32Values,
-                 (VehiclePropertyType::INT32 == mPropType
-                  || VehiclePropertyType::INT32_VEC == mPropType
-                  || VehiclePropertyType::BOOLEAN == mPropType))
-           && check(&v->floatValues,
-                    (VehiclePropertyType::FLOAT == mPropType
-                     || VehiclePropertyType::FLOAT_VEC == mPropType))
-           && check(&v->int64Values,
-                    VehiclePropertyType::INT64 == mPropType)
-           && check(&v->bytes,
-                    VehiclePropertyType::BYTES == mPropType)
-           && v->stringValue.size() == 0;
+    return check(&v->int32Values, (VehiclePropertyType::INT32 == mPropType ||
+                                   VehiclePropertyType::INT32_VEC == mPropType ||
+                                   VehiclePropertyType::BOOLEAN == mPropType)) &&
+           check(&v->floatValues, (VehiclePropertyType::FLOAT == mPropType ||
+                                   VehiclePropertyType::FLOAT_VEC == mPropType)) &&
+           check(&v->int64Values, (VehiclePropertyType::INT64 == mPropType ||
+                                   VehiclePropertyType::INT64_VEC == mPropType)) &&
+           check(&v->bytes, VehiclePropertyType::BYTES == mPropType) && v->stringValue.size() == 0;
 }
 
 VehiclePropValue* VehiclePropValuePool::InternalPool::createObject() {
diff --git a/automotive/vehicle/2.0/default/common/src/VehicleUtils.cpp b/automotive/vehicle/2.0/default/common/src/VehicleUtils.cpp
index 9146fa1..34a6380 100644
--- a/automotive/vehicle/2.0/default/common/src/VehicleUtils.cpp
+++ b/automotive/vehicle/2.0/default/common/src/VehicleUtils.cpp
@@ -42,13 +42,14 @@
             val->value.floatValues.resize(vecSize);
             break;
         case VehiclePropertyType::INT64:
+        case VehiclePropertyType::INT64_VEC:
             val->value.int64Values.resize(vecSize);
             break;
         case VehiclePropertyType::BYTES:
             val->value.bytes.resize(vecSize);
             break;
         case VehiclePropertyType::STRING:
-        case VehiclePropertyType::COMPLEX:
+        case VehiclePropertyType::MIXED:
             break; // Valid, but nothing to do.
         default:
             ALOGE("createVehiclePropValue: unknown type: %d", type);
@@ -68,6 +69,7 @@
         case VehiclePropertyType::FLOAT_VEC:
             return value.floatValues.size();
         case VehiclePropertyType::INT64:
+        case VehiclePropertyType::INT64_VEC:
             return value.int64Values.size();
         case VehiclePropertyType::BYTES:
             return value.bytes.size();
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
index 71601a0..18e8c40 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
@@ -54,10 +54,8 @@
  *   floatValues[1] - dispersion defines min and max range relative to initial value
  *   floatValues[2] - increment, with every timer tick the value will be incremented by this amount
  */
-const int32_t kGenerateFakeDataControllingProperty = 0x0666
-        | VehiclePropertyGroup::VENDOR
-        | VehicleArea::GLOBAL
-        | VehiclePropertyType::COMPLEX;
+const int32_t kGenerateFakeDataControllingProperty =
+    0x0666 | VehiclePropertyGroup::VENDOR | VehicleArea::GLOBAL | VehiclePropertyType::MIXED;
 
 const int32_t kHvacPowerProperties[] = {
     toInt(VehicleProperty::HVAC_FAN_SPEED),
@@ -238,7 +236,8 @@
              .prop = toInt(VehicleProperty::HVAC_POWER_ON),
              .access = VehiclePropertyAccess::READ_WRITE,
              .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-             .supportedAreas = toInt(VehicleAreaZone::ROW_1),
+             .areaConfigs = {VehicleAreaConfig{
+                 .areaId = (VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT)}},
              // TODO(bryaneyler): Ideally, this is generated dynamically from
              // kHvacPowerProperties.
              .configString = "0x12400500,0x12400501"  // HVAC_FAN_SPEED,HVAC_FAN_DIRECTION
@@ -249,51 +248,52 @@
         .config = {.prop = toInt(VehicleProperty::HVAC_DEFROSTER),
                    .access = VehiclePropertyAccess::READ_WRITE,
                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                   .supportedAreas =
-                       VehicleAreaWindow::FRONT_WINDSHIELD | VehicleAreaWindow::REAR_WINDSHIELD},
+                   .areaConfigs =
+                       {VehicleAreaConfig{.areaId = toInt(VehicleAreaWindow::FRONT_WINDSHIELD)},
+                        VehicleAreaConfig{.areaId = toInt(VehicleAreaWindow::REAR_WINDSHIELD)}}},
         .initialValue = {.int32Values = {0}}  // Will be used for all areas.
     },
 
     {.config = {.prop = toInt(VehicleProperty::HVAC_RECIRC_ON),
                 .access = VehiclePropertyAccess::READ_WRITE,
                 .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                .supportedAreas = toInt(VehicleAreaZone::ROW_1)},
+                .areaConfigs = {VehicleAreaConfig{
+                    .areaId = (VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT)}}},
      .initialValue = {.int32Values = {1}}},
 
     {.config = {.prop = toInt(VehicleProperty::HVAC_AC_ON),
                 .access = VehiclePropertyAccess::READ_WRITE,
                 .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                .supportedAreas = toInt(VehicleAreaZone::ROW_1)},
+                .areaConfigs = {VehicleAreaConfig{
+                    .areaId = (VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT)}}},
      .initialValue = {.int32Values = {1}}},
 
     {.config = {.prop = toInt(VehicleProperty::HVAC_AUTO_ON),
                 .access = VehiclePropertyAccess::READ_WRITE,
                 .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                .supportedAreas = toInt(VehicleAreaZone::ROW_1)},
+                .areaConfigs = {VehicleAreaConfig{
+                    .areaId = (VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT)}}},
      .initialValue = {.int32Values = {1}}},
 
     {.config = {.prop = toInt(VehicleProperty::HVAC_FAN_SPEED),
                 .access = VehiclePropertyAccess::READ_WRITE,
                 .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                .supportedAreas = toInt(VehicleAreaZone::ROW_1),
-                .areaConfigs = {VehicleAreaConfig{.areaId = toInt(VehicleAreaZone::ROW_1),
-                                                  .minInt32Value = 1,
-                                                  .maxInt32Value = 7}}},
+                .areaConfigs = {VehicleAreaConfig{
+                    .areaId = (VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT),
+                    .minInt32Value = 1,
+                    .maxInt32Value = 7}}},
      .initialValue = {.int32Values = {3}}},
 
-    {.config =
-         {
-             .prop = toInt(VehicleProperty::HVAC_FAN_DIRECTION),
-             .access = VehiclePropertyAccess::READ_WRITE,
-             .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-             .supportedAreas = toInt(VehicleAreaZone::ROW_1),
-         },
+    {.config = {.prop = toInt(VehicleProperty::HVAC_FAN_DIRECTION),
+                .access = VehiclePropertyAccess::READ_WRITE,
+                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+                .areaConfigs = {VehicleAreaConfig{
+                    .areaId = (VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT)}}},
      .initialValue = {.int32Values = {toInt(VehicleHvacFanDirection::FACE)}}},
 
     {.config = {.prop = toInt(VehicleProperty::HVAC_TEMPERATURE_SET),
                 .access = VehiclePropertyAccess::READ_WRITE,
                 .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-                .supportedAreas = VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT,
                 .areaConfigs = {VehicleAreaConfig{
                                     .areaId = toInt(VehicleAreaZone::ROW_1_LEFT),
                                     .minFloatValue = 16,
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
index 6bc0522..16d2b0b 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
@@ -138,8 +138,9 @@
             return status;
         }
     } else if (mHvacPowerProps.count(propValue.prop)) {
-        auto hvacPowerOn = mPropStore->readValueOrNull(toInt(VehicleProperty::HVAC_POWER_ON),
-                                                      toInt(VehicleAreaZone::ROW_1));
+        auto hvacPowerOn = mPropStore->readValueOrNull(
+            toInt(VehicleProperty::HVAC_POWER_ON),
+            (VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT));
 
         if (hvacPowerOn && hvacPowerOn->value.int32Values.size() == 1
                 && hvacPowerOn->value.int32Values[0] == 0) {
@@ -177,7 +178,7 @@
 void EmulatedVehicleHal::onCreate() {
     for (auto& it : kVehicleProperties) {
         VehiclePropConfig cfg = it.config;
-        int32_t supportedAreas = cfg.supportedAreas;
+        int32_t numAreas = cfg.areaConfigs.size();
 
         if (isDiagnosticProperty(cfg)) {
             // do not write an initial empty value for the diagnostic properties
@@ -185,22 +186,26 @@
             continue;
         }
 
-        //  A global property will have supportedAreas = 0
+        // A global property will have only a single area
         if (isGlobalProp(cfg.prop)) {
-            supportedAreas = 0;
+            numAreas = 1;
         }
 
-        // This loop is a do-while so it executes at least once to handle global properties
-        do {
-            int32_t curArea = supportedAreas;
-            supportedAreas &= supportedAreas - 1;  // Clear the right-most bit of supportedAreas.
-            curArea ^= supportedAreas;  // Set curArea to the previously cleared bit.
+        for (int i = 0; i < numAreas; i++) {
+            int32_t curArea;
+
+            if (isGlobalProp(cfg.prop)) {
+                curArea = 0;
+            } else {
+                curArea = cfg.areaConfigs[i].areaId;
+            }
 
             // Create a separate instance for each individual zone
             VehiclePropValue prop = {
                 .prop = cfg.prop,
                 .areaId = curArea,
             };
+
             if (it.initialAreaValues.size() > 0) {
                 auto valueForAreaIt = it.initialAreaValues.find(curArea);
                 if (valueForAreaIt != it.initialAreaValues.end()) {
@@ -213,8 +218,7 @@
                 prop.value = it.initialValue;
             }
             mPropStore->writeValue(prop);
-
-        } while (supportedAreas != 0);
+        }
     }
     initObd2LiveFrame(*mPropStore->getConfigOrDie(OBD2_LIVE_FRAME));
     initObd2FreezeFrame(*mPropStore->getConfigOrDie(OBD2_FREEZE_FRAME));
@@ -246,8 +250,7 @@
     }
 }
 
-StatusCode EmulatedVehicleHal::subscribe(int32_t property, int32_t,
-                                        float sampleRate) {
+StatusCode EmulatedVehicleHal::subscribe(int32_t property, float sampleRate) {
     ALOGI("%s propId: 0x%x, sampleRate: %f", __func__, property, sampleRate);
 
     if (isContinuousProperty(property)) {
@@ -389,7 +392,7 @@
 }
 
 void EmulatedVehicleHal::initObd2LiveFrame(const VehiclePropConfig& propConfig) {
-    auto liveObd2Frame = createVehiclePropValue(VehiclePropertyType::COMPLEX, 0);
+    auto liveObd2Frame = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
     auto sensorStore = fillDefaultObd2Frame(static_cast<size_t>(propConfig.configArray[0]),
                                             static_cast<size_t>(propConfig.configArray[1]));
     sensorStore->fillPropValue("", liveObd2Frame.get());
@@ -406,7 +409,7 @@
                                                   "P0102"
                                                   "P0123"};
     for (auto&& dtc : sampleDtcs) {
-        auto freezeFrame = createVehiclePropValue(VehiclePropertyType::COMPLEX, 0);
+        auto freezeFrame = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
         sensorStore->fillPropValue(dtc, freezeFrame.get());
         freezeFrame->prop = OBD2_FREEZE_FRAME;
 
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
index 99d7edb..62fc126 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
@@ -53,7 +53,7 @@
     VehiclePropValuePtr get(const VehiclePropValue& requestedPropValue,
                             StatusCode* outStatus) override;
     StatusCode set(const VehiclePropValue& propValue) override;
-    StatusCode subscribe(int32_t property, int32_t areas, float sampleRate) override;
+    StatusCode subscribe(int32_t property, float sampleRate) override;
     StatusCode unsubscribe(int32_t property) override;
 
     //  Methods from EmulatedVehicleHalIface
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp
index 38cb743..fca8e9e 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp
@@ -234,10 +234,6 @@
     protoCfg->set_change_mode(toInt(cfg.changeMode));
     protoCfg->set_value_type(toInt(getPropType(cfg.prop)));
 
-    if (!isGlobalProp(cfg.prop)) {
-        protoCfg->set_supported_areas(cfg.supportedAreas);
-    }
-
     for (auto& configElement : cfg.configArray) {
         protoCfg->add_config_array(configElement);
     }
@@ -251,9 +247,10 @@
         case VehiclePropertyType::STRING:
         case VehiclePropertyType::BOOLEAN:
         case VehiclePropertyType::INT32_VEC:
+        case VehiclePropertyType::INT64_VEC:
         case VehiclePropertyType::FLOAT_VEC:
         case VehiclePropertyType::BYTES:
-        case VehiclePropertyType::COMPLEX:
+        case VehiclePropertyType::MIXED:
             // Do nothing.  These types don't have min/max values
             break;
         case VehiclePropertyType::INT64:
diff --git a/automotive/vehicle/2.0/default/tests/SubscriptionManager_test.cpp b/automotive/vehicle/2.0/default/tests/SubscriptionManager_test.cpp
index 5688dd6..4865e9e 100644
--- a/automotive/vehicle/2.0/default/tests/SubscriptionManager_test.cpp
+++ b/automotive/vehicle/2.0/default/tests/SubscriptionManager_test.cpp
@@ -51,11 +51,7 @@
     }
 
     hidl_vec<SubscribeOptions> subscrToProp1 = {
-        SubscribeOptions {
-            .propId = PROP1,
-            .vehicleAreas = toInt(VehicleAreaZone::ROW_1_LEFT),
-            .flags = SubscribeFlags::HAL_EVENT
-        },
+        SubscribeOptions{.propId = PROP1, .flags = SubscribeFlags::HAL_EVENT},
     };
 
     hidl_vec<SubscribeOptions> subscrToProp2 = {
@@ -66,15 +62,8 @@
     };
 
     hidl_vec<SubscribeOptions> subscrToProp1and2 = {
-        SubscribeOptions {
-            .propId = PROP1,
-            .vehicleAreas = toInt(VehicleAreaZone::ROW_1_LEFT),
-            .flags = SubscribeFlags::HAL_EVENT
-        },
-        SubscribeOptions {
-            .propId = PROP2,
-            .flags = SubscribeFlags::HAL_EVENT
-        },
+        SubscribeOptions{.propId = PROP1, .flags = SubscribeFlags::HAL_EVENT},
+        SubscribeOptions{.propId = PROP2, .flags = SubscribeFlags::HAL_EVENT},
     };
 
     static std::list<sp<IVehicleCallback>> extractCallbacks(
@@ -87,14 +76,11 @@
     }
 
     std::list<sp<HalClient>> clientsToProp1() {
-        return manager.getSubscribedClients(PROP1,
-                                            toInt(VehicleAreaZone::ROW_1_LEFT),
-                                            SubscribeFlags::DEFAULT);
+        return manager.getSubscribedClients(PROP1, SubscribeFlags::DEFAULT);
     }
 
     std::list<sp<HalClient>> clientsToProp2() {
-        return manager.getSubscribedClients(PROP2, 0,
-                                            SubscribeFlags::DEFAULT);
+        return manager.getSubscribedClients(PROP2, SubscribeFlags::DEFAULT);
     }
 
     void onPropertyUnsubscribed(int propertyId) {
@@ -126,7 +112,6 @@
 
     auto clients = manager.getSubscribedClients(
             PROP1,
-            toInt(VehicleAreaZone::ROW_1_LEFT),
             SubscribeFlags::HAL_EVENT);
 
     ASSERT_ALL_EXISTS({cb1, cb2}, extractCallbacks(clients));
@@ -137,24 +122,14 @@
     ASSERT_EQ(StatusCode::OK,
               manager.addOrUpdateSubscription(1, cb1, subscrToProp1, &updatedOptions));
 
-    // Wrong zone
-    auto clients = manager.getSubscribedClients(
-            PROP1,
-            toInt(VehicleAreaZone::ROW_2_LEFT),
-            SubscribeFlags::HAL_EVENT);
-    ASSERT_TRUE(clients.empty());
-
     // Wrong prop
-    clients = manager.getSubscribedClients(
-            toInt(VehicleProperty::AP_POWER_BOOTUP_REASON),
-            toInt(VehicleAreaZone::ROW_1_LEFT),
-            SubscribeFlags::HAL_EVENT);
+    auto clients = manager.getSubscribedClients(toInt(VehicleProperty::AP_POWER_BOOTUP_REASON),
+                                                SubscribeFlags::HAL_EVENT);
     ASSERT_TRUE(clients.empty());
 
     // Wrong flag
     clients = manager.getSubscribedClients(
             PROP1,
-            toInt(VehicleAreaZone::ROW_1_LEFT),
             SubscribeFlags::SET_CALL);
     ASSERT_TRUE(clients.empty());
 }
@@ -166,7 +141,6 @@
 
     auto clients = manager.getSubscribedClients(
             PROP1,
-            toInt(VehicleAreaZone::ROW_1_LEFT),
             SubscribeFlags::DEFAULT);
     ASSERT_EQ((size_t) 1, clients.size());
     ASSERT_EQ(cb1, clients.front()->getCallback());
@@ -176,18 +150,15 @@
     ASSERT_EQ(StatusCode::OK, manager.addOrUpdateSubscription(1, cb1, {
         SubscribeOptions {
                 .propId = PROP1,
-                .vehicleAreas = toInt(VehicleAreaZone::ROW_2),
                 .flags = SubscribeFlags::DEFAULT
             }
         }, &updatedOptions));
 
     clients = manager.getSubscribedClients(PROP1,
-                                           toInt(VehicleAreaZone::ROW_1_LEFT),
                                            SubscribeFlags::DEFAULT);
     ASSERT_ALL_EXISTS({cb1}, extractCallbacks(clients));
 
     clients = manager.getSubscribedClients(PROP1,
-                                           toInt(VehicleAreaZone::ROW_2),
                                            SubscribeFlags::DEFAULT);
     ASSERT_ALL_EXISTS({cb1}, extractCallbacks(clients));
 }
diff --git a/automotive/vehicle/2.0/default/tests/VehicleHalManager_test.cpp b/automotive/vehicle/2.0/default/tests/VehicleHalManager_test.cpp
index 4864d5d..5b195db 100644
--- a/automotive/vehicle/2.0/default/tests/VehicleHalManager_test.cpp
+++ b/automotive/vehicle/2.0/default/tests/VehicleHalManager_test.cpp
@@ -106,7 +106,6 @@
     }
 
     StatusCode subscribe(int32_t /* property */,
-                         int32_t /* areas */,
                          float /* sampleRate */) override {
         return StatusCode::OK;
     }
@@ -286,6 +285,7 @@
 
     cb->reset();
     VehiclePropValue actualValue(*subscribedValue.get());
+    actualValue.status = VehiclePropertyStatus::AVAILABLE;
     hal->sendPropEvent(std::move(subscribedValue));
 
     ASSERT_TRUE(cb->waitForExpectedEvents(1)) << "Events received: "
diff --git a/automotive/vehicle/2.0/default/tests/VehicleHalTestUtils.h b/automotive/vehicle/2.0/default/tests/VehicleHalTestUtils.h
index 2a06417..3cabcf2 100644
--- a/automotive/vehicle/2.0/default/tests/VehicleHalTestUtils.h
+++ b/automotive/vehicle/2.0/default/tests/VehicleHalTestUtils.h
@@ -29,10 +29,8 @@
 namespace vehicle {
 namespace V2_0 {
 
-constexpr int32_t kCustomComplexProperty = 0xbeef
-        | VehiclePropertyGroup::VENDOR
-        | VehiclePropertyType::COMPLEX
-        | VehicleArea::GLOBAL;
+constexpr int32_t kCustomComplexProperty =
+    0xbeef | VehiclePropertyGroup::VENDOR | VehiclePropertyType::MIXED | VehicleArea::GLOBAL;
 
 const VehiclePropConfig kVehicleProperties[] = {
     {
@@ -46,8 +44,6 @@
         .prop = toInt(VehicleProperty::HVAC_FAN_SPEED),
         .access = VehiclePropertyAccess::READ_WRITE,
         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
-        .supportedAreas = static_cast<int32_t>(
-            VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT),
         .areaConfigs = {
             VehicleAreaConfig {
                 .areaId = toInt(VehicleAreaZone::ROW_1_LEFT),
@@ -66,8 +62,6 @@
         .prop = toInt(VehicleProperty::HVAC_SEAT_TEMPERATURE),
         .access = VehiclePropertyAccess::WRITE,
         .changeMode = VehiclePropertyChangeMode::ON_SET,
-        .supportedAreas = static_cast<int32_t>(
-            VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT),
         .areaConfigs = {
             VehicleAreaConfig {
                 .areaId = toInt(VehicleAreaZone::ROW_1_LEFT),
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index d88479a..f673d1a 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -27,6 +27,7 @@
     INT32          = 0x00400000,
     INT32_VEC      = 0x00410000,
     INT64          = 0x00500000,
+    INT64_VEC      = 0x00510000,
     FLOAT          = 0x00600000,
     FLOAT_VEC      = 0x00610000,
     BYTES          = 0x00700000,
@@ -35,7 +36,7 @@
      * Any combination of scalar or vector types. The exact format must be
      * provided in the description of the property.
      */
-    COMPLEX        = 0x00e00000,
+    MIXED          = 0x00e00000,
 
     MASK           = 0x00ff0000
 };
@@ -315,7 +316,7 @@
     WHEEL_TICK = (
       0x0306
       | VehiclePropertyGroup:SYSTEM
-      | VehiclePropertyType:COMPLEX
+      | VehiclePropertyType:MIXED
       | VehicleArea:GLOBAL),
 
 
@@ -1673,7 +1674,7 @@
     /**
      * Vehicle Maps Service (VMS) message
      *
-     * This property uses COMPLEX data to communicate vms messages.
+     * This property uses MIXED data to communicate vms messages.
      *
      * Its contents are to be interpreted as follows:
      * the indices defined in VmsMessageIntegerValuesIndex are to be used to
@@ -1689,7 +1690,7 @@
     VEHICLE_MAP_SERVICE = (
         0x0C00
         | VehiclePropertyGroup:SYSTEM
-        | VehiclePropertyType:COMPLEX
+        | VehiclePropertyType:MIXED
         | VehicleArea:GLOBAL),
 
     /**
@@ -1736,7 +1737,7 @@
     OBD2_LIVE_FRAME = (
       0x0D00
       | VehiclePropertyGroup:SYSTEM
-      | VehiclePropertyType:COMPLEX
+      | VehiclePropertyType:MIXED
       | VehicleArea:GLOBAL),
 
     /**
@@ -1766,7 +1767,7 @@
     OBD2_FREEZE_FRAME = (
       0x0D01
       | VehiclePropertyGroup:SYSTEM
-      | VehiclePropertyType:COMPLEX
+      | VehiclePropertyType:MIXED
       | VehicleArea:GLOBAL),
 
     /**
@@ -1787,7 +1788,7 @@
     OBD2_FREEZE_FRAME_INFO = (
       0x0D02
       | VehiclePropertyGroup:SYSTEM
-      | VehiclePropertyType:COMPLEX
+      | VehiclePropertyType:MIXED
       | VehicleArea:GLOBAL),
 
     /**
@@ -1813,7 +1814,7 @@
     OBD2_FREEZE_FRAME_CLEAR = (
       0x0D03
       | VehiclePropertyGroup:SYSTEM
-      | VehiclePropertyType:COMPLEX
+      | VehiclePropertyType:MIXED
       | VehicleArea:GLOBAL),
 };
 
@@ -2169,6 +2170,21 @@
 };
 
 /**
+ * Property status is a dynamic value that may change based on the vehicle state.
+ */
+enum VehiclePropertyStatus : int32_t {
+    /** Property is available and behaving normally */
+    AVAILABLE   = 0x00,
+    /**
+     * Property is not available, for read and/or write.  This is a transient state, as the
+     *  property is expected to be available at a later time.
+     */
+    UNAVAILABLE = 0x01,
+    /** There is an error with this property. */
+    ERROR       = 0x02,
+};
+
+/**
  * Car states.
  *
  * The driving states determine what features of the UI will be accessible.
@@ -2212,20 +2228,15 @@
   ROW_1_LEFT = 0x00000001,
   ROW_1_CENTER = 0x00000002,
   ROW_1_RIGHT = 0x00000004,
-  ROW_1 = 0x00000008,
   ROW_2_LEFT = 0x00000010,
   ROW_2_CENTER = 0x00000020,
   ROW_2_RIGHT = 0x00000040,
-  ROW_2 = 0x00000080,
   ROW_3_LEFT = 0x00000100,
   ROW_3_CENTER = 0x00000200,
   ROW_3_RIGHT = 0x00000400,
-  ROW_3 = 0x00000800,
   ROW_4_LEFT = 0x00001000,
   ROW_4_CENTER = 0x00002000,
   ROW_4_RIGHT = 0x00004000,
-  ROW_4 = 0x00008000,
-  WHOLE_CABIN = 0x80000000,
 };
 
 /**
@@ -2313,13 +2324,6 @@
     VehiclePropertyChangeMode changeMode;
 
     /**
-     * Some of the properties may have associated areas (for example, some hvac
-     * properties are associated with VehicleAreaZone), in these
-     * cases the config may contain an ORed value for the associated areas.
-     */
-    int32_t supportedAreas;
-
-    /**
      * Contains per-area configuration.
      */
     vec<VehicleAreaConfig> areaConfigs;
@@ -2372,6 +2376,9 @@
      */
     int32_t areaId;
 
+    /** Status of the property */
+    VehiclePropertyStatus status;
+
     /**
      * Contains value for a single property. Depending on property data type of
      * this property (VehiclePropetyType) one field of this structure must be filled in.
@@ -2484,12 +2491,6 @@
     int32_t propId;
 
     /**
-     * Area ids - this must be a bit mask of areas to subscribe or 0 to subscribe
-     * to all areas.
-     */
-    int32_t vehicleAreas;
-
-    /**
      * Sample rate in Hz.
      *
      * Must be provided for properties with
diff --git a/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerTestUtils.h b/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerTestUtils.h
index 29b9de3..00d9d8c 100644
--- a/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerTestUtils.h
+++ b/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerTestUtils.h
@@ -58,10 +58,12 @@
   std::string dumpDebugInfo();
   std::unique_ptr<ComposerClient> createClient();
 
+ protected:
+  sp<IComposer> mComposer;
+
  private:
   void init();
 
-  sp<IComposer> mComposer;
   std::unordered_set<IComposer::Capability> mCapabilities;
 };
 
diff --git a/graphics/composer/2.2/Android.bp b/graphics/composer/2.2/Android.bp
new file mode 100644
index 0000000..633a208
--- /dev/null
+++ b/graphics/composer/2.2/Android.bp
@@ -0,0 +1,20 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.graphics.composer@2.2",
+    root: "android.hardware",
+    vndk: {
+        enabled: true,
+    },
+    srcs: [
+        "IComposer.hal",
+        "IComposerClient.hal",
+    ],
+    interfaces: [
+        "android.hardware.graphics.common@1.0",
+        "android.hardware.graphics.composer@2.1",
+        "android.hidl.base@1.0",
+    ],
+    gen_java: false,
+}
+
diff --git a/graphics/composer/2.2/IComposer.hal b/graphics/composer/2.2/IComposer.hal
new file mode 100644
index 0000000..05052c8
--- /dev/null
+++ b/graphics/composer/2.2/IComposer.hal
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.graphics.composer@2.2;
+
+import @2.1::IComposer;
+
+interface IComposer extends @2.1::IComposer {
+
+    /* createClient from @2.1::IComposer must return an instance of @2.2::IComposerClient */
+
+};
diff --git a/graphics/composer/2.2/IComposerClient.hal b/graphics/composer/2.2/IComposerClient.hal
new file mode 100644
index 0000000..dcd9c8d
--- /dev/null
+++ b/graphics/composer/2.2/IComposerClient.hal
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.graphics.composer@2.2;
+
+import android.hardware.graphics.common@1.0::PixelFormat;
+import android.hardware.graphics.common@1.0::Dataspace;
+import @2.1::IComposerClient;
+import @2.1::Display;
+import @2.1::Error;
+
+interface IComposerClient extends @2.1::IComposerClient {
+
+    enum PowerMode : @2.1::IComposerClient.PowerMode {
+        /**
+         * The display is configured as in ON but may stop applying display
+         * updates from the client. This is effectively a hint to the device
+         * that drawing to the display has been suspended and that the the
+         * device must remain on and continue displaying its current contents
+         * indefinitely until the power mode changes.
+         *
+         * This mode may also be used as a signal to enable hardware-based
+         * functionality to take over the display and manage it autonomously
+         * to implement a low power always-on display.
+         */
+        ON_SUSPEND = 4
+    };
+
+    /**
+     * Following enums define keys for metadata defined by SMPTE ST 2086:2014
+     * and CTA 861.3.
+     */
+    enum PerFrameMetadataKey : int32_t {
+        /** SMPTE ST 2084:2014.
+         * Coordinates defined in CIE 1931 xy chromaticity space
+         */
+        /** SMPTE ST 2084:2014 */
+        DISPLAY_RED_PRIMARY_X,
+        /** SMPTE ST 2084:2014 */
+        DISPLAY_RED_PRIMARY_Y,
+        /** SMPTE ST 2084:2014 */
+        DISPLAY_GREEN_PRIMARY_X,
+        /** SMPTE ST 2084:2014 */
+        DISPLAY_GREEN_PRIMARY_Y,
+        /** SMPTE ST 2084:2014 */
+        DISPLAY_BLUE_PRIMARY_X,
+        /** SMPTE ST 2084:2014 */
+        DISPLAY_BLUE_PRIMARY_Y,
+        /** SMPTE ST 2084:2014 */
+        WHITE_POINT_X,
+        /** SMPTE ST 2084:2014 */
+        WHITE_POINT_Y,
+        /** SMPTE ST 2084:2014.
+         * Units: nits
+         * max as defined by ST 2048: 10,000 nits
+         */
+        MAX_LUMINANCE,
+        /** SMPTE ST 2084:2014 */
+        MIN_LUMINANCE,
+        /** CTA 861.3 */
+        MAX_CONTENT_LIGHT_LEVEL,
+        /** CTA 861.3 */
+        MAX_FRAME_AVERAGE_LIGHT_LEVEL,
+    };
+
+    struct PerFrameMetadata {
+        PerFrameMetadataKey key;
+        float value;
+    };
+
+    struct FloatColor {
+        float r;
+        float g;
+        float b;
+        float a;
+    };
+
+    enum Command : @2.1::IComposerClient.Command {
+        /**
+         * setPerFrameMetadata(Display display, vec<PerFrameMetadata> data)
+         * Sets the PerFrameMetadata for the display. This metadata must be used
+         * by the implementation to better tone map content to that display.
+         *
+         * This is a method that may be called every frame. Thus it's
+         * implemented using buffered transport.
+         * SET_PER_FRAME_METADATA is the command used by the buffered transport
+         * mechanism.
+         */
+        SET_PER_FRAME_METADATA = 0x207 << @2.1::IComposerClient.Command:OPCODE_SHIFT,
+
+        /**
+         * SET_LAYER_COLOR has this pseudo prototype
+         *
+         *   setLayerColor(FloatColor color);
+         *
+         * Sets the color of the given layer. If the composition type of the layer
+         * is not Composition::SOLID_COLOR, this call must succeed and have no
+         * other effect.
+         *
+         * @param color is the new color using float type.
+         */
+        SET_LAYER_FLOAT_COLOR = 0x40c << @2.1::IComposerClient.Command:OPCODE_SHIFT,
+    };
+
+    /**
+     * Returns the PerFrameMetadataKeys that are supported by this device.
+     *
+     * @param display is the display on which to create the layer.
+     * @return keys is the vector of PerFrameMetadataKey keys that are
+     *        supported by this device.
+     * @return error is NONE upon success. Otherwise,
+     *         UNSUPPORTED if not supported on underlying HAL
+     */
+    getPerFrameMetadataKeys(Display display)
+        generates (Error error,
+                   vec<PerFrameMetadataKey> keys);
+
+    /**
+     * getReadbackBufferAttributes
+     * Returns the format which should be used when allocating a buffer for use by
+     * device readback as well as the dataspace in which its contents should be
+     * interpreted.
+     *
+     * The width and height of this buffer must be those of the currently-active
+     * display configuration, and the usage flags must consist of the following:
+     *   BufferUsage::CPU_READ | BufferUsage::GPU_TEXTURE |
+     *   BufferUsage::COMPOSER_OUTPUT
+     *
+     * The format and dataspace provided must be sufficient such that if a
+     * correctly-configured buffer is passed into setReadbackBuffer, filled by
+     * the device, and then displayed by the client as a full-screen buffer, the
+     * output of the display remains the same (subject to the note about protected
+     * content in the description of setReadbackBuffer).
+     *
+     * Parameters:
+     * @param display - the display on which to create the layer.
+     *
+     * @return format - the format the client should use when allocating a device
+     *       readback buffer
+     * @return dataspace - the dataspace to use when interpreting the
+     *       contents of a device readback buffer
+     * @return error is NONE upon success. Otherwise,
+     *         BAD_DISPLAY when an invalid display handle was passed in.
+     *         UNSUPPORTED if not supported on underlying HAL
+     *
+     * See also:
+     *   setReadbackBuffer
+     *   getReadbackBufferFence
+     */
+    getReadbackBufferAttributes(Display display)
+        generates (Error error,
+                   PixelFormat format,
+                   Dataspace dataspace);
+
+    /**
+     * getReadbackBufferFence
+     * Returns an acquire sync fence file descriptor which must signal when the
+     * buffer provided to setReadbackBuffer has been filled by the device and is
+     * safe for the client to read.
+     *
+     * If it is already safe to read from this buffer, -1 may be returned instead.
+     * The client takes ownership of this file descriptor and is responsible for
+     * closing it when it is no longer needed.
+     *
+     * This function must be called immediately after the composition cycle being
+     * captured into the readback buffer. The complete ordering of a readback buffer
+     * capture is as follows:
+     *
+     *   getReadbackBufferAttributes
+     *   // Readback buffer is allocated
+     *   // Many frames may pass
+     *
+     *   setReadbackBuffer
+     *   validateDisplay
+     *   presentDisplay
+     *   getReadbackBufferFence
+     *   // Implicitly wait on the acquire fence before accessing the buffer
+     *
+     * Parameters:
+     * @param display - the display on which to create the layer.
+     *
+     * @return acquireFence - a sync fence file descriptor as described above; pointer
+     *       must be non-NULL
+     * @return error - is HWC2_ERROR_NONE or one of the following errors:
+     *         BAD_DISPLAY - an invalid display handle was passed in
+     *         UNSUPPORTED if not supported on underlying HAL
+     *
+     * See also:
+     *   getReadbackBufferAttributes
+     *   setReadbackBuffer
+     */
+    getReadbackBufferFence(Display display)
+                generates (Error error,
+                           handle acquireFence);
+
+    /**
+     * setReadbackBuffer
+     * Sets the readback buffer to be filled with the contents of the next
+     * composition performed for this display (i.e., the contents present at the
+     * time of the next validateDisplay/presentDisplay cycle).
+     *
+     * This buffer must have been allocated as described in
+     * getReadbackBufferAttributes and is in the dataspace provided by the same.
+     *
+     * If there is hardware protected content on the display at the time of the next
+     * composition, the area of the readback buffer covered by such content must be
+     * completely black. Any areas of the buffer not covered by such content may
+     * optionally be black as well.
+     *
+     * The release fence file descriptor provided works identically to the one
+     * described for setOutputBuffer.
+     *
+     * This function must not be called between any call to validateDisplay and a
+     * subsequent call to presentDisplay.
+     *
+     * Parameters:
+     * @param display - the display on which to create the layer.
+     * @param buffer - the new readback buffer
+     * @param releaseFence - a sync fence file descriptor as described in setOutputBuffer
+     *
+     * @return error - is HWC2_ERROR_NONE or one of the following errors:
+     *   HWC2_ERROR_BAD_DISPLAY - an invalid display handle was passed in
+     *   HWC2_ERROR_BAD_PARAMETER - the new readback buffer handle was invalid
+     *
+     * See also:
+     *   getReadbackBufferAttributes
+     *   getReadbackBufferFence
+     */
+    setReadbackBuffer(Display display, handle buffer, handle releaseFence) generates (Error error);
+
+    /**
+     * setPowerMode_2_2
+     * Sets the power mode of the given display. The transition must be
+     * complete when this function returns. It is valid to call this function
+     * multiple times with the same power mode.
+     *
+     * All displays must support PowerMode::ON and PowerMode::OFF.  Whether a
+     * display supports PowerMode::DOZE or PowerMode::DOZE_SUSPEND may be
+     * queried using getDozeSupport.
+     *
+     * @param display is the display to which the power mode is set.
+     * @param mode is the new power mode.
+     * @return error is NONE upon success. Otherwise,
+     *         BAD_DISPLAY when an invalid display handle was passed in.
+     *         BAD_PARAMETER when mode was not a valid power mode.
+     *         UNSUPPORTED when mode is not supported on this display.
+     */
+    setPowerMode_2_2(Display display, PowerMode mode) generates (Error error);
+
+};
diff --git a/graphics/composer/2.2/utils/OWNERS b/graphics/composer/2.2/utils/OWNERS
new file mode 100644
index 0000000..1beb074
--- /dev/null
+++ b/graphics/composer/2.2/utils/OWNERS
@@ -0,0 +1,8 @@
+# Graphics team
+courtneygo@google.com
+olv@google.com
+stoza@google.com
+
+# VTS team
+yim@google.com
+zhuoyao@google.com
diff --git a/graphics/composer/2.2/utils/command-buffer/Android.bp b/graphics/composer/2.2/utils/command-buffer/Android.bp
new file mode 100644
index 0000000..efaabd4
--- /dev/null
+++ b/graphics/composer/2.2/utils/command-buffer/Android.bp
@@ -0,0 +1,13 @@
+cc_library_headers {
+    name: "android.hardware.graphics.composer@2.2-command-buffer",
+    defaults: ["hidl_defaults"],
+    vendor_available: true,
+    shared_libs: [
+        "android.hardware.graphics.composer@2.1",
+        "android.hardware.graphics.composer@2.2",
+    ],
+    header_libs: [
+        "android.hardware.graphics.composer@2.1-command-buffer",
+    ],
+    export_include_dirs: ["include"],
+}
diff --git a/graphics/composer/2.2/utils/command-buffer/include/composer-command-buffer/2.2/ComposerCommandBuffer.h b/graphics/composer/2.2/utils/command-buffer/include/composer-command-buffer/2.2/ComposerCommandBuffer.h
new file mode 100644
index 0000000..c803d3c
--- /dev/null
+++ b/graphics/composer/2.2/utils/command-buffer/include/composer-command-buffer/2.2/ComposerCommandBuffer.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#ifndef LOG_TAG
+#warn "ComposerCommandBuffer.h included without LOG_TAG"
+#endif
+
+#undef LOG_NDEBUG
+#define LOG_NDEBUG 0
+
+#include <algorithm>
+#include <limits>
+#include <memory>
+#include <vector>
+
+#include <inttypes.h>
+#include <string.h>
+
+#include <android/hardware/graphics/composer/2.2/IComposer.h>
+#include <android/hardware/graphics/composer/2.2/IComposerClient.h>
+#include <fmq/MessageQueue.h>
+#include <log/log.h>
+#include <sync/sync.h>
+
+#include <composer-command-buffer/2.1/ComposerCommandBuffer.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_2 {
+
+using android::hardware::MessageQueue;
+using android::hardware::graphics::common::V1_0::ColorTransform;
+using android::hardware::graphics::common::V1_0::Dataspace;
+using android::hardware::graphics::common::V1_0::Transform;
+using android::hardware::graphics::composer::V2_1::Config;
+using android::hardware::graphics::composer::V2_1::Display;
+using android::hardware::graphics::composer::V2_1::Error;
+using android::hardware::graphics::composer::V2_1::IComposerCallback;
+using android::hardware::graphics::composer::V2_1::Layer;
+using android::hardware::graphics::composer::V2_2::IComposerClient;
+
+using CommandQueueType = MessageQueue<uint32_t, kSynchronizedReadWrite>;
+
+// This class helps build a command queue.  Note that all sizes/lengths are in
+// units of uint32_t's.
+class CommandWriterBase : public V2_1::CommandWriterBase {
+   public:
+    CommandWriterBase(uint32_t initialMaxSize) : V2_1::CommandWriterBase(initialMaxSize) {}
+
+    static constexpr uint16_t kSetLayerFloatColorLength = 4;
+    void setLayerFloatColor(IComposerClient::FloatColor color) {
+        beginCommand_2_2(IComposerClient::Command::SET_LAYER_FLOAT_COLOR,
+                         kSetLayerFloatColorLength);
+        writeFloatColor(color);
+        endCommand();
+    }
+
+    void setPerFrameMetadata(const hidl_vec<IComposerClient::PerFrameMetadata>& metadataVec) {
+        beginCommand_2_2(IComposerClient::Command::SET_PER_FRAME_METADATA, metadataVec.size() * 2);
+        for (const auto& metadata : metadataVec) {
+            writeSigned(static_cast<int32_t>(metadata.key));
+            writeFloat(metadata.value);
+        }
+        endCommand();
+    }
+
+   protected:
+    void beginCommand_2_2(IComposerClient::Command command, uint16_t length) {
+        V2_1::CommandWriterBase::beginCommand(
+            static_cast<V2_1::IComposerClient::Command>(static_cast<int32_t>(command)), length);
+    }
+
+    void writeFloatColor(const IComposerClient::FloatColor& color) {
+        writeFloat(color.r);
+        writeFloat(color.g);
+        writeFloat(color.b);
+        writeFloat(color.a);
+    }
+};
+
+// This class helps parse a command queue.  Note that all sizes/lengths are in
+// units of uint32_t's.
+class CommandReaderBase : public V2_1::CommandReaderBase {
+   public:
+    CommandReaderBase() : V2_1::CommandReaderBase(){};
+
+   protected:
+    IComposerClient::FloatColor readFloatColor() {
+        float r = readFloat();
+        float g = readFloat();
+        float b = readFloat();
+        float a = readFloat();
+        return IComposerClient::FloatColor{r, g, b, a};
+    }
+};
+
+}  // namespace V2_2
+}  // namespace composer
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
diff --git a/graphics/composer/2.2/vts/functional/Android.bp b/graphics/composer/2.2/vts/functional/Android.bp
new file mode 100644
index 0000000..0325a6c
--- /dev/null
+++ b/graphics/composer/2.2/vts/functional/Android.bp
@@ -0,0 +1,74 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_static {
+    name: "libVtsHalGraphicsComposerTestUtils@2.2",
+    defaults: ["hidl_defaults"],
+    srcs: [
+        "VtsHalGraphicsComposerTestUtils.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.graphics.composer@2.1",
+        "android.hardware.graphics.composer@2.2",
+        "libfmq",
+        "libsync",
+    ],
+    static_libs: [
+        "libVtsHalGraphicsComposerTestUtils",
+        "VtsHalHidlTargetTestBase",
+    ],
+    header_libs: [
+        "android.hardware.graphics.composer@2.1-command-buffer",
+        "android.hardware.graphics.composer@2.2-command-buffer",
+    ],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-O0",
+        "-g",
+        "-DLOG_TAG=\"GraphicsComposerTestUtils@2.2\"",
+    ],
+    export_include_dirs: ["include"],
+}
+
+cc_test {
+    name: "VtsHalGraphicsComposerV2_2TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: ["VtsHalGraphicsComposerV2_2TargetTest.cpp"],
+
+    // TODO(b/64437680): Assume these libs are always available on the device.
+    shared_libs: [
+        "libfmq",
+	"libhidltransport",
+        "libsync",
+    ],
+    static_libs: [
+        "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.composer@2.2",
+        "android.hardware.graphics.composer@2.1",
+        "android.hardware.graphics.mapper@2.0",
+        "android.hardware.graphics.mapper@2.1",
+        "libVtsHalGraphicsComposerTestUtils",
+        "libVtsHalGraphicsComposerTestUtils@2.2",
+        "libVtsHalGraphicsMapperTestUtils",
+        "libnativehelper",
+    ],
+    header_libs: [
+        "android.hardware.graphics.composer@2.1-command-buffer",
+        "android.hardware.graphics.composer@2.2-command-buffer",
+    ],
+}
diff --git a/graphics/composer/2.2/vts/functional/OWNERS b/graphics/composer/2.2/vts/functional/OWNERS
new file mode 100644
index 0000000..1beb074
--- /dev/null
+++ b/graphics/composer/2.2/vts/functional/OWNERS
@@ -0,0 +1,8 @@
+# Graphics team
+courtneygo@google.com
+olv@google.com
+stoza@google.com
+
+# VTS team
+yim@google.com
+zhuoyao@google.com
diff --git a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerTestUtils.cpp b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerTestUtils.cpp
new file mode 100644
index 0000000..00946ce
--- /dev/null
+++ b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerTestUtils.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 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 <VtsHalHidlTargetTestBase.h>
+#include <hidl/HidlTransportUtils.h>
+
+#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
+#include "2.2/VtsHalGraphicsComposerTestUtils.h"
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_2 {
+namespace tests {
+
+using android::hardware::graphics::composer::V2_2::IComposerClient;
+using android::hardware::details::getDescriptor;
+using android::hardware::details::canCastInterface;
+
+std::unique_ptr<ComposerClient_v2_2> Composer_v2_2::createClient_v2_2() {
+    std::unique_ptr<ComposerClient_v2_2> client;
+    mComposer->createClient([&](const auto& tmpError, const auto& tmpClient) {
+        ASSERT_EQ(Error::NONE, tmpError) << "failed to create client";
+        ALOGV("tmpClient is a %s", getDescriptor(&(*tmpClient)).c_str());
+        ASSERT_TRUE(canCastInterface(
+            &(*tmpClient), "android.hardware.graphics.composer@2.2::IComposerClient", false))
+            << "Cannot create 2.2 IComposerClient";
+        client = std::make_unique<ComposerClient_v2_2>(IComposerClient::castFrom(tmpClient, true));
+    });
+
+    return client;
+}
+
+std::vector<IComposerClient::PerFrameMetadataKey> ComposerClient_v2_2::getPerFrameMetadataKeys(
+    Display display) {
+    std::vector<IComposerClient::PerFrameMetadataKey> keys;
+    mClient_v2_2->getPerFrameMetadataKeys(display, [&](const auto& tmpError, const auto& tmpKeys) {
+        ASSERT_EQ(Error::NONE, tmpError) << "failed to get HDR metadata keys";
+        keys = tmpKeys;
+    });
+
+    return keys;
+}
+
+void ComposerClient_v2_2::execute_v2_2(V2_1::tests::TestCommandReader* reader,
+                                       V2_2::CommandWriterBase* writer) {
+    bool queueChanged = false;
+    uint32_t commandLength = 0;
+    hidl_vec<hidl_handle> commandHandles;
+    ASSERT_TRUE(writer->writeQueue(&queueChanged, &commandLength, &commandHandles));
+
+    if (queueChanged) {
+        auto ret = mClient_v2_2->setInputCommandQueue(*writer->getMQDescriptor());
+        ASSERT_EQ(Error::NONE, static_cast<Error>(ret));
+        return;
+    }
+
+    mClient_v2_2->executeCommands(commandLength, commandHandles,
+                                  [&](const auto& tmpError, const auto& tmpOutQueueChanged,
+                                      const auto& tmpOutLength, const auto& tmpOutHandles) {
+                                      ASSERT_EQ(Error::NONE, tmpError);
+
+                                      if (tmpOutQueueChanged) {
+                                          mClient_v2_2->getOutputCommandQueue(
+                                              [&](const auto& tmpError, const auto& tmpDescriptor) {
+                                                  ASSERT_EQ(Error::NONE, tmpError);
+                                                  reader->setMQDescriptor(tmpDescriptor);
+                                              });
+                                      }
+
+                                      ASSERT_TRUE(reader->readQueue(tmpOutLength, tmpOutHandles));
+                                      reader->parse();
+                                  });
+}
+
+void ComposerClient_v2_2::setPowerMode_2_2(Display display, V2_2::IComposerClient::PowerMode mode) {
+    Error error = mClient_v2_2->setPowerMode_2_2(display, mode);
+    ASSERT_TRUE(error == Error::NONE || error == Error::UNSUPPORTED) << "failed to set power mode";
+}
+
+void ComposerClient_v2_2::setReadbackBuffer(Display display, const native_handle_t* buffer,
+                                            int32_t /* releaseFence */) {
+    // Ignoring fence, HIDL doesn't care
+    Error error = mClient_v2_2->setReadbackBuffer(display, buffer, nullptr);
+    ASSERT_EQ(Error::NONE, error) << "failed to setReadbackBuffer";
+}
+
+void ComposerClient_v2_2::getReadbackBufferAttributes(Display display, PixelFormat* outPixelFormat,
+                                                      Dataspace* outDataspace) {
+    mClient_v2_2->getReadbackBufferAttributes(
+        display,
+        [&](const auto& tmpError, const auto& tmpOutPixelFormat, const auto& tmpOutDataspace) {
+            ASSERT_EQ(Error::NONE, tmpError) << "failed to get readback buffer attributes";
+            *outPixelFormat = tmpOutPixelFormat;
+            *outDataspace = tmpOutDataspace;
+        });
+}
+
+void ComposerClient_v2_2::getReadbackBufferFence(Display display, int32_t* outFence) {
+    hidl_handle handle;
+    mClient_v2_2->getReadbackBufferFence(display, [&](const auto& tmpError, const auto& tmpHandle) {
+        ASSERT_EQ(Error::NONE, tmpError) << "failed to get readback fence";
+        handle = tmpHandle;
+    });
+    *outFence = 0;
+}
+
+}  // namespace tests
+}  // namespace V2_2
+}  // namespace composer
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
diff --git a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2TargetTest.cpp b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2TargetTest.cpp
new file mode 100644
index 0000000..8b44d61
--- /dev/null
+++ b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2TargetTest.cpp
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2018 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 "graphics_composer_hidl_hal_test@2.2"
+
+#include <android-base/logging.h>
+#include <android/hardware/graphics/mapper/2.1/IMapper.h>
+#include <sync/sync.h>
+#include "2.2/VtsHalGraphicsComposerTestUtils.h"
+#include "GraphicsComposerCallback.h"
+#include "TestCommandReader.h"
+#include "VtsHalGraphicsComposerTestUtils.h"
+#include "VtsHalGraphicsMapperTestUtils.h"
+
+#include <VtsHalHidlTargetTestBase.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_2 {
+namespace tests {
+namespace {
+
+using android::hardware::graphics::common::V1_0::BufferUsage;
+using android::hardware::graphics::common::V1_0::ColorMode;
+using android::hardware::graphics::common::V1_0::ColorTransform;
+using android::hardware::graphics::common::V1_0::Dataspace;
+using android::hardware::graphics::common::V1_0::PixelFormat;
+using android::hardware::graphics::common::V1_0::Transform;
+using android::hardware::graphics::composer::V2_2::IComposerClient;
+using android::hardware::graphics::mapper::V2_0::IMapper;
+using android::hardware::graphics::mapper::V2_0::tests::Gralloc;
+using GrallocError = android::hardware::graphics::mapper::V2_0::Error;
+
+// Test environment for graphics.composer
+class GraphicsComposerHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
+   public:
+    // get the test environment singleton
+    static GraphicsComposerHidlEnvironment* Instance() {
+        static GraphicsComposerHidlEnvironment* instance = new GraphicsComposerHidlEnvironment;
+        return instance;
+    }
+
+    virtual void registerTestServices() override { registerTestService<IComposer>(); }
+
+   private:
+    GraphicsComposerHidlEnvironment() {}
+
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(GraphicsComposerHidlEnvironment);
+};
+
+class GraphicsComposerHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+   protected:
+    void SetUp() override {
+        ASSERT_NO_FATAL_FAILURE(
+            mComposer = std::make_unique<Composer_v2_2>(
+                GraphicsComposerHidlEnvironment::Instance()->getServiceName<IComposer>()));
+        ASSERT_NO_FATAL_FAILURE(mComposerClient = mComposer->createClient_v2_2());
+
+        mComposerCallback = new V2_1::tests::GraphicsComposerCallback;
+        mComposerClient->registerCallback(mComposerCallback);
+
+        // assume the first display is primary and is never removed
+        mPrimaryDisplay = waitForFirstDisplay();
+
+        // explicitly disable vsync
+        mComposerClient->setVsyncEnabled(mPrimaryDisplay, false);
+        mComposerCallback->setVsyncAllowed(false);
+    }
+
+    void TearDown() override {
+        if (mComposerCallback != nullptr) {
+            EXPECT_EQ(0, mComposerCallback->getInvalidHotplugCount());
+            EXPECT_EQ(0, mComposerCallback->getInvalidRefreshCount());
+            EXPECT_EQ(0, mComposerCallback->getInvalidVsyncCount());
+        }
+    }
+
+    // use the slot count usually set by SF
+    static constexpr uint32_t kBufferSlotCount = 64;
+
+    std::unique_ptr<Composer_v2_2> mComposer;
+    std::unique_ptr<ComposerClient_v2_2> mComposerClient;
+    sp<V2_1::tests::GraphicsComposerCallback> mComposerCallback;
+    // the first display and is assumed never to be removed
+    Display mPrimaryDisplay;
+
+   private:
+    Display waitForFirstDisplay() {
+        while (true) {
+            std::vector<Display> displays = mComposerCallback->getDisplays();
+            if (displays.empty()) {
+                usleep(5 * 1000);
+                continue;
+            }
+
+            return displays[0];
+        }
+    }
+};
+
+// Tests for IComposerClient::Command.
+class GraphicsComposerHidlCommandTest : public GraphicsComposerHidlTest {
+   protected:
+    void SetUp() override {
+        ASSERT_NO_FATAL_FAILURE(GraphicsComposerHidlTest::SetUp());
+
+        ASSERT_NO_FATAL_FAILURE(mGralloc = std::make_unique<Gralloc>());
+
+        mWriter = std::make_unique<V2_2::CommandWriterBase>(1024);
+        mReader = std::make_unique<V2_1::tests::TestCommandReader>();
+    }
+
+    void TearDown() override { ASSERT_NO_FATAL_FAILURE(GraphicsComposerHidlTest::TearDown()); }
+
+    const native_handle_t* allocate() {
+        IMapper::BufferDescriptorInfo info{};
+        info.width = 64;
+        info.height = 64;
+        info.layerCount = 1;
+        info.format = PixelFormat::RGBA_8888;
+        info.usage =
+            static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN);
+
+        return mGralloc->allocate(info);
+    }
+
+    void execute() { mComposerClient->execute_v2_2(mReader.get(), mWriter.get()); }
+
+    std::unique_ptr<V2_2::CommandWriterBase> mWriter;
+    std::unique_ptr<V2_1::tests::TestCommandReader> mReader;
+
+   private:
+    std::unique_ptr<Gralloc> mGralloc;
+};
+
+/**
+ * Test IComposerClient::Command::SET_PER_FRAME_METADATA.
+ */
+TEST_F(GraphicsComposerHidlCommandTest, SET_PER_FRAME_METADATA) {
+    Layer layer;
+    ASSERT_NO_FATAL_FAILURE(layer =
+                                mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));
+
+    mWriter->selectDisplay(mPrimaryDisplay);
+    mWriter->selectLayer(layer);
+
+    /**
+     * DISPLAY_P3 is a color space that uses the DCI_P3 primaries,
+     * the D65 white point and the SRGB transfer functions.
+     * Rendering Intent: Colorimetric
+     * Primaries:
+     *                  x       y
+     *  green           0.265   0.690
+     *  blue            0.150   0.060
+     *  red             0.680   0.320
+     *  white (D65)     0.3127  0.3290
+     */
+
+    std::vector<IComposerClient::PerFrameMetadata> hidlMetadata;
+    hidlMetadata.push_back({IComposerClient::PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X, 0.680});
+    hidlMetadata.push_back({IComposerClient::PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y, 0.320});
+    hidlMetadata.push_back({IComposerClient::PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X, 0.265});
+    hidlMetadata.push_back({IComposerClient::PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y, 0.690});
+    hidlMetadata.push_back({IComposerClient::PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X, 0.150});
+    hidlMetadata.push_back({IComposerClient::PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y, 0.060});
+    hidlMetadata.push_back({IComposerClient::PerFrameMetadataKey::WHITE_POINT_X, 0.3127});
+    hidlMetadata.push_back({IComposerClient::PerFrameMetadataKey::WHITE_POINT_Y, 0.3290});
+    hidlMetadata.push_back({IComposerClient::PerFrameMetadataKey::MAX_LUMINANCE, 100.0});
+    hidlMetadata.push_back({IComposerClient::PerFrameMetadataKey::MIN_LUMINANCE, 0.1});
+    hidlMetadata.push_back({IComposerClient::PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL, 78.0});
+    hidlMetadata.push_back(
+        {IComposerClient::PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL, 62.0});
+    mWriter->setPerFrameMetadata(hidlMetadata);
+    execute();
+}
+
+/**
+ * Test IComposerClient::getPerFrameMetadataKeys.
+ */
+TEST_F(GraphicsComposerHidlTest, GetPerFrameMetadataKeys) {
+    mComposerClient->getPerFrameMetadataKeys(mPrimaryDisplay);
+}
+/**
+ * Test IComposerClient::setPowerMode_2_2.
+ */
+TEST_F(GraphicsComposerHidlTest, setPowerMode_2_2) {
+    std::vector<IComposerClient::PowerMode> modes;
+    modes.push_back(IComposerClient::PowerMode::OFF);
+    modes.push_back(IComposerClient::PowerMode::ON_SUSPEND);
+    modes.push_back(IComposerClient::PowerMode::ON);
+
+    for (auto mode : modes) {
+        mComposerClient->setPowerMode_2_2(mPrimaryDisplay, mode);
+    }
+}
+
+TEST_F(GraphicsComposerHidlTest, setReadbackBuffer) {
+    mComposerClient->setReadbackBuffer(mPrimaryDisplay, nullptr, -1);
+}
+
+TEST_F(GraphicsComposerHidlTest, getReadbackBufferFence) {
+    int32_t fence;
+    mComposerClient->getReadbackBufferFence(mPrimaryDisplay, &fence);
+}
+
+TEST_F(GraphicsComposerHidlTest, getReadbackBufferAttributes) {
+    PixelFormat pixelFormat;
+    Dataspace dataspace;
+    mComposerClient->getReadbackBufferAttributes(mPrimaryDisplay, &pixelFormat, &dataspace);
+}
+
+/**
+ * Test IComposerClient::Command::SET_LAYER_FLOAT_COLOR.
+ */
+TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_FLOAT_COLOR) {
+    V2_1::Layer layer;
+    ASSERT_NO_FATAL_FAILURE(layer =
+                                mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));
+
+    mWriter->selectDisplay(mPrimaryDisplay);
+    mWriter->selectLayer(layer);
+    mWriter->setLayerFloatColor(IComposerClient::FloatColor{1.0, 1.0, 1.0, 1.0});
+    mWriter->setLayerFloatColor(IComposerClient::FloatColor{0.0, 0.0, 0.0, 0.0});
+}
+
+}  // namespace
+}  // namespace tests
+}  // namespace V2_2
+}  // namespace composer
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
+
+int main(int argc, char** argv) {
+    using android::hardware::graphics::composer::V2_2::tests::GraphicsComposerHidlEnvironment;
+    ::testing::AddGlobalTestEnvironment(GraphicsComposerHidlEnvironment::Instance());
+    ::testing::InitGoogleTest(&argc, argv);
+    GraphicsComposerHidlEnvironment::Instance()->init(&argc, argv);
+    int status = RUN_ALL_TESTS();
+    return status;
+}
diff --git a/graphics/composer/2.2/vts/functional/include/2.2/VtsHalGraphicsComposerTestUtils.h b/graphics/composer/2.2/vts/functional/include/2.2/VtsHalGraphicsComposerTestUtils.h
new file mode 100644
index 0000000..c5756ed
--- /dev/null
+++ b/graphics/composer/2.2/vts/functional/include/2.2/VtsHalGraphicsComposerTestUtils.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include <VtsHalGraphicsComposerTestUtils.h>
+#include <VtsHalHidlTargetTestBase.h>
+#include <android/hardware/graphics/composer/2.2/IComposer.h>
+#include <android/hardware/graphics/composer/2.2/IComposerClient.h>
+#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_2 {
+namespace tests {
+
+using android::hardware::graphics::common::V1_0::ColorMode;
+using android::hardware::graphics::common::V1_0::Dataspace;
+using android::hardware::graphics::common::V1_0::Hdr;
+using android::hardware::graphics::common::V1_0::PixelFormat;
+using android::hardware::graphics::composer::V2_2::IComposer;
+using android::hardware::graphics::composer::V2_2::IComposerClient;
+
+class ComposerClient_v2_2;
+
+// Only thing I need for Composer_v2_2 is to create a v2_2 ComposerClient
+// Everything else is the same
+class Composer_v2_2 : public V2_1::tests::Composer {
+   public:
+    Composer_v2_2() : V2_1::tests::Composer(){};
+    explicit Composer_v2_2(const std::string& name) : V2_1::tests::Composer(name){};
+
+    std::unique_ptr<ComposerClient_v2_2> createClient_v2_2();
+};
+
+// A wrapper to IComposerClient.
+class ComposerClient_v2_2
+    : public android::hardware::graphics::composer::V2_1::tests::ComposerClient {
+   public:
+    ComposerClient_v2_2(const sp<IComposerClient>& client)
+        : V2_1::tests::ComposerClient(client), mClient_v2_2(client){};
+
+    void execute_v2_2(V2_1::tests::TestCommandReader* reader, V2_2::CommandWriterBase* writer);
+
+    std::vector<IComposerClient::PerFrameMetadataKey> getPerFrameMetadataKeys(Display display);
+
+    void setPowerMode_2_2(Display display, V2_2::IComposerClient::PowerMode mode);
+    void setReadbackBuffer(Display display, const native_handle_t* buffer, int32_t releaseFence);
+    void getReadbackBufferAttributes(Display display, PixelFormat* outPixelFormat,
+                                     Dataspace* outDataspace);
+    void getReadbackBufferFence(Display display, int32_t* outFence);
+
+   private:
+    sp<V2_2::IComposerClient> mClient_v2_2;
+};
+
+}  // namespace tests
+}  // namespace V2_2
+}  // namespace composer
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
diff --git a/graphics/mapper/2.1/IMapper.hal b/graphics/mapper/2.1/IMapper.hal
index a23656d..6047e96 100644
--- a/graphics/mapper/2.1/IMapper.hal
+++ b/graphics/mapper/2.1/IMapper.hal
@@ -16,10 +16,47 @@
 
 package android.hardware.graphics.mapper@2.1;
 
-import android.hardware.graphics.mapper@2.0::Error;
-import android.hardware.graphics.mapper@2.0::IMapper;
+import android.hardware.graphics.common@1.1::BufferUsage;
+import android.hardware.graphics.common@1.1::PixelFormat;
+import @2.0::BufferDescriptor;
+import @2.0::Error;
+import @2.0::IMapper;
 
-interface IMapper extends android.hardware.graphics.mapper@2.0::IMapper {
+interface IMapper extends @2.0::IMapper {
+    /**
+     * This is the same as @2.0::IMapper::BufferDescriptorInfo except that it
+     * accepts @1.1::PixelFormat and @1.1::BufferUsage.
+     */
+    struct BufferDescriptorInfo {
+        /**
+         * The width specifies how many columns of pixels must be in the
+         * allocated buffer, but does not necessarily represent the offset in
+         * columns between the same column in adjacent rows. The rows may be
+         * padded.
+         */
+        uint32_t width;
+
+       /**
+        * The height specifies how many rows of pixels must be in the
+        * allocated buffer.
+        */
+        uint32_t height;
+
+       /**
+        * The number of image layers that must be in the allocated buffer.
+        */
+        uint32_t layerCount;
+
+        /** Buffer pixel format. */
+        PixelFormat format;
+
+        /**
+         * Buffer usage mask; valid flags can be found in the definition of
+         * BufferUsage.
+         */
+        bitfield<BufferUsage> usage;
+    };
+
     /**
      * Validate that the buffer can be safely accessed by a caller who assumes
      * the specified descriptorInfo and stride. This must at least validate
@@ -58,4 +95,30 @@
             generates (Error error,
                        uint32_t numFds,
                        uint32_t numInts);
+
+    /**
+     * This is the same as @2.0::IMapper::createDescriptor except that it
+     * accepts @2.1::IMapper::BufferDescriptorInfo.
+     *
+     * Creates a buffer descriptor. The descriptor can be used with IAllocator
+     * to allocate buffers.
+     *
+     * Since the buffer descriptor fully describes a buffer, any device
+     * dependent or device independent checks must be performed here whenever
+     * possible. Specifically, when layered buffers are not supported, this
+     * function must return UNSUPPORTED if layerCount is great than 1.
+     *
+     * @param descriptorInfo specifies the attributes of the descriptor.
+     * @return error is NONE upon success. Otherwise,
+     *                  BAD_VALUE when any of the specified attributes is
+     *                            invalid or conflicting.
+     *                  NO_RESOURCES when the creation cannot be fullfilled at
+     *                               this time.
+     *                  UNSUPPORTED when any of the specified attributes is
+     *                              not supported.
+     * @return descriptor is the newly created buffer descriptor.
+     */
+    createDescriptor_2_1(BufferDescriptorInfo descriptorInfo)
+              generates (Error error,
+                         BufferDescriptor descriptor);
 };
diff --git a/graphics/mapper/2.1/vts/functional/VtsHalGraphicsMapperV2_1TargetTest.cpp b/graphics/mapper/2.1/vts/functional/VtsHalGraphicsMapperV2_1TargetTest.cpp
index 4067c8d..88b96ae 100644
--- a/graphics/mapper/2.1/vts/functional/VtsHalGraphicsMapperV2_1TargetTest.cpp
+++ b/graphics/mapper/2.1/vts/functional/VtsHalGraphicsMapperV2_1TargetTest.cpp
@@ -30,10 +30,27 @@
 namespace tests {
 namespace {
 
+using android::hardware::graphics::mapper::V2_0::BufferDescriptor;
 using android::hardware::graphics::mapper::V2_0::Error;
 
-using android::hardware::graphics::common::V1_0::BufferUsage;
-using android::hardware::graphics::common::V1_0::PixelFormat;
+using android::hardware::graphics::common::V1_1::BufferUsage;
+using android::hardware::graphics::common::V1_1::PixelFormat;
+
+// abuse VTS to check binary compatibility between BufferDescriptorInfos
+using OldBufferDescriptorInfo =
+    android::hardware::graphics::mapper::V2_0::IMapper::BufferDescriptorInfo;
+static_assert(sizeof(OldBufferDescriptorInfo) == sizeof(IMapper::BufferDescriptorInfo) &&
+                  offsetof(OldBufferDescriptorInfo, width) ==
+                      offsetof(IMapper::BufferDescriptorInfo, width) &&
+                  offsetof(OldBufferDescriptorInfo, height) ==
+                      offsetof(IMapper::BufferDescriptorInfo, height) &&
+                  offsetof(OldBufferDescriptorInfo, layerCount) ==
+                      offsetof(IMapper::BufferDescriptorInfo, layerCount) &&
+                  offsetof(OldBufferDescriptorInfo, format) ==
+                      offsetof(IMapper::BufferDescriptorInfo, format) &&
+                  offsetof(OldBufferDescriptorInfo, usage) ==
+                      offsetof(IMapper::BufferDescriptorInfo, usage),
+              "");
 
 class Gralloc : public V2_0::tests::Gralloc {
    public:
@@ -72,6 +89,32 @@
         });
     }
 
+    BufferDescriptor createDescriptor(const IMapper::BufferDescriptorInfo& descriptorInfo) {
+        BufferDescriptor descriptor;
+        mMapper->createDescriptor_2_1(
+            descriptorInfo, [&](const auto& tmpError, const auto& tmpDescriptor) {
+                ASSERT_EQ(Error::NONE, tmpError) << "failed to create descriptor";
+                descriptor = tmpDescriptor;
+            });
+
+        return descriptor;
+    }
+
+    const native_handle_t* allocate(const IMapper::BufferDescriptorInfo& descriptorInfo,
+                                    bool import, uint32_t* outStride = nullptr) {
+        BufferDescriptor descriptor = createDescriptor(descriptorInfo);
+        if (::testing::Test::HasFatalFailure()) {
+            return nullptr;
+        }
+
+        auto buffers = V2_0::tests::Gralloc::allocate(descriptor, 1, import, outStride);
+        if (::testing::Test::HasFatalFailure()) {
+            return nullptr;
+        }
+
+        return buffers[0];
+    }
+
    private:
     void init() {
         mMapper = IMapper::castFrom(V2_0::tests::Gralloc::getMapper());
@@ -229,6 +272,24 @@
     native_handle_delete(rawBufferHandle);
 }
 
+/**
+ * Test IMapper::createDescriptor with valid descriptor info.
+ */
+TEST_F(GraphicsMapperHidlTest, CreateDescriptor_2_1Basic) {
+    ASSERT_NO_FATAL_FAILURE(mGralloc->createDescriptor(mDummyDescriptorInfo));
+}
+
+/**
+ * Test IMapper::createDescriptor with invalid descriptor info.
+ */
+TEST_F(GraphicsMapperHidlTest, CreateDescriptor_2_1Negative) {
+    auto info = mDummyDescriptorInfo;
+    info.width = 0;
+    mGralloc->getMapper()->createDescriptor_2_1(info, [&](const auto& tmpError, const auto&) {
+        EXPECT_EQ(Error::BAD_VALUE, tmpError) << "createDescriptor did not fail with BAD_VALUE";
+    });
+}
+
 }  // namespace
 }  // namespace tests
 }  // namespace V2_1
@@ -238,7 +299,10 @@
 }  // namespace android
 
 int main(int argc, char** argv) {
+    using android::hardware::graphics::mapper::V2_0::tests::GraphicsMapperHidlEnvironment;
+    ::testing::AddGlobalTestEnvironment(GraphicsMapperHidlEnvironment::Instance());
     ::testing::InitGoogleTest(&argc, argv);
+    GraphicsMapperHidlEnvironment::Instance()->init(&argc, argv);
 
     int status = RUN_ALL_TESTS();
     LOG(INFO) << "Test result = " << status;
diff --git a/radio/1.0/vts/functional/radio_hidl_hal_voice.cpp b/radio/1.0/vts/functional/radio_hidl_hal_voice.cpp
index b3d5648..d0320b9 100644
--- a/radio/1.0/vts/functional/radio_hidl_hal_voice.cpp
+++ b/radio/1.0/vts/functional/radio_hidl_hal_voice.cpp
@@ -489,4 +489,4 @@
                                       RadioError::MODEM_ERR, RadioError::OPERATION_NOT_ALLOWED},
                                      CHECK_GENERAL_ERROR));
     }
-}
\ No newline at end of file
+}
diff --git a/wifi/1.2/Android.bp b/wifi/1.2/Android.bp
index 100b36b..f41324e 100644
--- a/wifi/1.2/Android.bp
+++ b/wifi/1.2/Android.bp
@@ -10,6 +10,7 @@
         "types.hal",
         "IWifi.hal",
         "IWifiChip.hal",
+        "IWifiChipEventCallback.hal",
         "IWifiNanIface.hal",
         "IWifiNanIfaceEventCallback.hal",
     ],
diff --git a/wifi/1.2/IWifiChip.hal b/wifi/1.2/IWifiChip.hal
index 72cbf81..d336a33 100644
--- a/wifi/1.2/IWifiChip.hal
+++ b/wifi/1.2/IWifiChip.hal
@@ -16,7 +16,9 @@
 
 package android.hardware.wifi@1.2;
 
+import @1.0::WifiStatus;
 import @1.1::IWifiChip;
+import IWifiChipEventCallback;
 
 /**
  * Interface that represents a chip that must be configured as a single unit.
@@ -24,4 +26,18 @@
  * to perform operations like NAN, RTT, etc.
  */
 interface IWifiChip extends @1.1::IWifiChip {
+    /**
+     * Requests notifications of significant events on this chip. Multiple calls
+     * to this must register multiple callbacks each of which must receive all
+     * events.
+     *
+     * @param callback An instance of the |IWifiChipEventCallback| HIDL interface
+     *        object.
+     * @return status WifiStatus of the operation.
+     *         Possible status codes:
+     *         |WifiStatusCode.SUCCESS|,
+     *         |WifiStatusCode.ERROR_WIFI_CHIP_INVALID|
+     */
+    registerEventCallback_1_2(IWifiChipEventCallback callback)
+        generates (WifiStatus status);
 };
diff --git a/wifi/1.2/IWifiChipEventCallback.hal b/wifi/1.2/IWifiChipEventCallback.hal
new file mode 100644
index 0000000..5d2e7e9
--- /dev/null
+++ b/wifi/1.2/IWifiChipEventCallback.hal
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.wifi@1.2;
+
+import @1.0::IWifiChipEventCallback;
+import @1.0::WifiBand;
+
+/**
+ * Wifi chip event callbacks.
+ */
+interface IWifiChipEventCallback extends @1.0::IWifiChipEventCallback {
+    /**
+     * Struct describing the state of each iface operating on the radio chain
+     * (hardware MAC) on the device.
+     */
+    struct IfaceInfo {
+        /** Name of the interface (For ex: "wlan0"). */
+        string name;
+        /** Wifi channel on which this interface is operating. */
+        uint32_t channel;
+    };
+
+    /**
+     * Struct describing the state of each hardware radio chain (hardware MAC)
+     * on the device.
+     */
+    struct RadioModeInfo {
+        /**
+         * Identifier for this radio chain. This is vendor dependent & used
+         * only for debugging purposes.
+         */
+        uint32_t radioId;
+        /**
+         * List of bands on which this radio chain is operating.
+         * Can be one of:
+         * a) WifiBand.BAND_24GHZ => 2.4Ghz.
+         * b) WifiBand.BAND_5GHZ => 5Ghz.
+         * c) WifiBand.BAND_24GHZ_5GHZ = 2.4Ghz + 5Ghz (Radio is time sharing
+         * across the 2 bands).
+         */
+        WifiBand bandInfo;
+        /** List of interfaces on this radio chain (hardware MAC). */
+        vec<IfaceInfo> ifaceInfos;
+    };
+
+    /**
+     * Asynchronous callback indicating a radio mode change.
+     * Radio mode change could be a result of:
+     * a) Bringing up concurrent interfaces (For ex: STA + AP).
+     * b) Change in operating band of one of the concurrent interfaces (For ex:
+     * STA connection moved from 2.4G to 5G)
+     *
+     * @param radioModeInfos List of RadioModeInfo structures for each
+     * radio chain (hardware MAC) on the device.
+     */
+    oneway onRadioModeChange(vec<RadioModeInfo> radioModeInfos);
+};
diff --git a/wifi/1.2/default/Android.mk b/wifi/1.2/default/Android.mk
index 8d0262b..978cf63 100644
--- a/wifi/1.2/default/Android.mk
+++ b/wifi/1.2/default/Android.mk
@@ -94,6 +94,7 @@
 LOCAL_MODULE := android.hardware.wifi@1.0-service-tests
 LOCAL_PROPRIETARY_MODULE := true
 LOCAL_SRC_FILES := \
+    tests/hidl_struct_util_unit_tests.cpp \
     tests/main.cpp \
     tests/mock_wifi_feature_flags.cpp \
     tests/mock_wifi_legacy_hal.cpp \
diff --git a/wifi/1.2/default/hidl_struct_util.cpp b/wifi/1.2/default/hidl_struct_util.cpp
index f87828c..b1c609e 100644
--- a/wifi/1.2/default/hidl_struct_util.cpp
+++ b/wifi/1.2/default/hidl_struct_util.cpp
@@ -266,6 +266,57 @@
     CHECK(false);
 }
 
+bool convertLegacyWifiMacInfoToHidl(
+    const legacy_hal::WifiMacInfo& legacy_mac_info,
+    IWifiChipEventCallback::RadioModeInfo* hidl_radio_mode_info) {
+    if (!hidl_radio_mode_info) {
+        return false;
+    }
+    *hidl_radio_mode_info = {};
+
+    hidl_radio_mode_info->radioId = legacy_mac_info.wlan_mac_id;
+    // Convert from bitmask of bands in the legacy HAL to enum value in
+    // the HIDL interface.
+    if (legacy_mac_info.mac_band & legacy_hal::WLAN_MAC_2_4_BAND &&
+        legacy_mac_info.mac_band & legacy_hal::WLAN_MAC_5_0_BAND) {
+        hidl_radio_mode_info->bandInfo = WifiBand::BAND_24GHZ_5GHZ;
+    } else if (legacy_mac_info.mac_band & legacy_hal::WLAN_MAC_2_4_BAND) {
+        hidl_radio_mode_info->bandInfo = WifiBand::BAND_24GHZ;
+    } else if (legacy_mac_info.mac_band & legacy_hal::WLAN_MAC_5_0_BAND) {
+        hidl_radio_mode_info->bandInfo = WifiBand::BAND_5GHZ;
+    } else {
+        hidl_radio_mode_info->bandInfo = WifiBand::BAND_UNSPECIFIED;
+    }
+    std::vector<IWifiChipEventCallback::IfaceInfo> iface_info_vec;
+    for (const auto& legacy_iface_info : legacy_mac_info.iface_infos) {
+        IWifiChipEventCallback::IfaceInfo iface_info;
+        iface_info.name = legacy_iface_info.name;
+        iface_info.channel = legacy_iface_info.channel;
+        iface_info_vec.push_back(iface_info);
+    }
+    hidl_radio_mode_info->ifaceInfos = iface_info_vec;
+    return true;
+}
+
+bool convertLegacyWifiMacInfosToHidl(
+    const std::vector<legacy_hal::WifiMacInfo>& legacy_mac_infos,
+    std::vector<IWifiChipEventCallback::RadioModeInfo>* hidl_radio_mode_infos) {
+    if (!hidl_radio_mode_infos) {
+        return false;
+    }
+    *hidl_radio_mode_infos = {};
+
+    for (const auto& legacy_mac_info : legacy_mac_infos) {
+        IWifiChipEventCallback::RadioModeInfo hidl_radio_mode_info;
+        if (!convertLegacyWifiMacInfoToHidl(legacy_mac_info,
+                                            &hidl_radio_mode_info)) {
+            return false;
+        }
+        hidl_radio_mode_infos->push_back(hidl_radio_mode_info);
+    }
+    return true;
+}
+
 bool convertLegacyFeaturesToHidlStaCapabilities(
     uint32_t legacy_feature_set, uint32_t legacy_logger_feature_set,
     uint32_t* hidl_caps) {
diff --git a/wifi/1.2/default/hidl_struct_util.h b/wifi/1.2/default/hidl_struct_util.h
index 1208afd..ce4bb81 100644
--- a/wifi/1.2/default/hidl_struct_util.h
+++ b/wifi/1.2/default/hidl_struct_util.h
@@ -22,6 +22,7 @@
 #include <android/hardware/wifi/1.0/IWifiChip.h>
 #include <android/hardware/wifi/1.0/types.h>
 #include <android/hardware/wifi/1.1/IWifiChip.h>
+#include <android/hardware/wifi/1.2/IWifiChipEventCallback.h>
 #include <android/hardware/wifi/1.2/types.h>
 
 #include "wifi_legacy_hal.h"
@@ -55,6 +56,9 @@
     WifiDebugHostWakeReasonStats* hidl_stats);
 legacy_hal::wifi_power_scenario convertHidlTxPowerScenarioToLegacy(
     V1_1::IWifiChip::TxPowerScenario hidl_scenario);
+bool convertLegacyWifiMacInfosToHidl(
+    const std::vector<legacy_hal::WifiMacInfo>& legacy_mac_infos,
+    std::vector<IWifiChipEventCallback::RadioModeInfo>* hidl_radio_mode_infos);
 
 // STA iface conversion methods.
 bool convertLegacyFeaturesToHidlStaCapabilities(
diff --git a/wifi/1.2/default/tests/hidl_struct_util_unit_tests.cpp b/wifi/1.2/default/tests/hidl_struct_util_unit_tests.cpp
new file mode 100644
index 0000000..1d6e9e4
--- /dev/null
+++ b/wifi/1.2/default/tests/hidl_struct_util_unit_tests.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <gmock/gmock.h>
+
+#undef NAN
+#include "hidl_struct_util.h"
+
+using testing::Test;
+
+namespace {
+constexpr uint32_t kMacId1 = 1;
+constexpr uint32_t kMacId2 = 2;
+constexpr uint32_t kIfaceChannel1 = 3;
+constexpr uint32_t kIfaceChannel2 = 5;
+constexpr char kIfaceName1[] = "wlan0";
+constexpr char kIfaceName2[] = "wlan1";
+}  // namespace
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace V1_2 {
+namespace implementation {
+using namespace android::hardware::wifi::V1_0;
+
+class HidlStructUtilTest : public Test {};
+
+TEST_F(HidlStructUtilTest, CanConvertLegacyWifiMacInfosToHidlWithOneMac) {
+    std::vector<legacy_hal::WifiMacInfo> legacy_mac_infos;
+    legacy_hal::WifiMacInfo legacy_mac_info1 = {
+        .wlan_mac_id = kMacId1,
+        .mac_band =
+            legacy_hal::WLAN_MAC_5_0_BAND | legacy_hal::WLAN_MAC_2_4_BAND};
+    legacy_hal::WifiIfaceInfo legacy_iface_info1 = {.name = kIfaceName1,
+                                                    .channel = kIfaceChannel1};
+    legacy_hal::WifiIfaceInfo legacy_iface_info2 = {.name = kIfaceName2,
+                                                    .channel = kIfaceChannel2};
+    legacy_mac_info1.iface_infos.push_back(legacy_iface_info1);
+    legacy_mac_info1.iface_infos.push_back(legacy_iface_info2);
+    legacy_mac_infos.push_back(legacy_mac_info1);
+
+    std::vector<IWifiChipEventCallback::RadioModeInfo> hidl_radio_mode_infos;
+    ASSERT_TRUE(hidl_struct_util::convertLegacyWifiMacInfosToHidl(
+        legacy_mac_infos, &hidl_radio_mode_infos));
+
+    ASSERT_EQ(1u, hidl_radio_mode_infos.size());
+    auto hidl_radio_mode_info1 = hidl_radio_mode_infos[0];
+    EXPECT_EQ(legacy_mac_info1.wlan_mac_id, hidl_radio_mode_info1.radioId);
+    EXPECT_EQ(WifiBand::BAND_24GHZ_5GHZ, hidl_radio_mode_info1.bandInfo);
+    ASSERT_EQ(2u, hidl_radio_mode_info1.ifaceInfos.size());
+    auto hidl_iface_info1 = hidl_radio_mode_info1.ifaceInfos[0];
+    EXPECT_EQ(legacy_iface_info1.name, hidl_iface_info1.name);
+    EXPECT_EQ(static_cast<uint32_t>(legacy_iface_info1.channel),
+              hidl_iface_info1.channel);
+    auto hidl_iface_info2 = hidl_radio_mode_info1.ifaceInfos[1];
+    EXPECT_EQ(legacy_iface_info2.name, hidl_iface_info2.name);
+    EXPECT_EQ(static_cast<uint32_t>(legacy_iface_info2.channel),
+              hidl_iface_info2.channel);
+}
+
+TEST_F(HidlStructUtilTest, CanConvertLegacyWifiMacInfosToHidlWithTwoMac) {
+    std::vector<legacy_hal::WifiMacInfo> legacy_mac_infos;
+    legacy_hal::WifiMacInfo legacy_mac_info1 = {
+        .wlan_mac_id = kMacId1, .mac_band = legacy_hal::WLAN_MAC_5_0_BAND};
+    legacy_hal::WifiIfaceInfo legacy_iface_info1 = {.name = kIfaceName1,
+                                                    .channel = kIfaceChannel1};
+    legacy_hal::WifiMacInfo legacy_mac_info2 = {
+        .wlan_mac_id = kMacId2, .mac_band = legacy_hal::WLAN_MAC_2_4_BAND};
+    legacy_hal::WifiIfaceInfo legacy_iface_info2 = {.name = kIfaceName2,
+                                                    .channel = kIfaceChannel2};
+    legacy_mac_info1.iface_infos.push_back(legacy_iface_info1);
+    legacy_mac_infos.push_back(legacy_mac_info1);
+    legacy_mac_info2.iface_infos.push_back(legacy_iface_info2);
+    legacy_mac_infos.push_back(legacy_mac_info2);
+
+    std::vector<IWifiChipEventCallback::RadioModeInfo> hidl_radio_mode_infos;
+    ASSERT_TRUE(hidl_struct_util::convertLegacyWifiMacInfosToHidl(
+        legacy_mac_infos, &hidl_radio_mode_infos));
+
+    ASSERT_EQ(2u, hidl_radio_mode_infos.size());
+
+    // Find mac info 1.
+    const auto hidl_radio_mode_info1 = std::find_if(
+        hidl_radio_mode_infos.begin(), hidl_radio_mode_infos.end(),
+        [&legacy_mac_info1](const IWifiChipEventCallback::RadioModeInfo& x) {
+            return x.radioId == legacy_mac_info1.wlan_mac_id;
+        });
+    ASSERT_NE(hidl_radio_mode_infos.end(), hidl_radio_mode_info1);
+    EXPECT_EQ(WifiBand::BAND_5GHZ, hidl_radio_mode_info1->bandInfo);
+    ASSERT_EQ(1u, hidl_radio_mode_info1->ifaceInfos.size());
+    auto hidl_iface_info1 = hidl_radio_mode_info1->ifaceInfos[0];
+    EXPECT_EQ(legacy_iface_info1.name, hidl_iface_info1.name);
+    EXPECT_EQ(static_cast<uint32_t>(legacy_iface_info1.channel),
+              hidl_iface_info1.channel);
+
+    // Find mac info 2.
+    const auto hidl_radio_mode_info2 = std::find_if(
+        hidl_radio_mode_infos.begin(), hidl_radio_mode_infos.end(),
+        [&legacy_mac_info2](const IWifiChipEventCallback::RadioModeInfo& x) {
+            return x.radioId == legacy_mac_info2.wlan_mac_id;
+        });
+    ASSERT_NE(hidl_radio_mode_infos.end(), hidl_radio_mode_info2);
+    EXPECT_EQ(WifiBand::BAND_24GHZ, hidl_radio_mode_info2->bandInfo);
+    ASSERT_EQ(1u, hidl_radio_mode_info2->ifaceInfos.size());
+    auto hidl_iface_info2 = hidl_radio_mode_info2->ifaceInfos[0];
+    EXPECT_EQ(legacy_iface_info2.name, hidl_iface_info2.name);
+    EXPECT_EQ(static_cast<uint32_t>(legacy_iface_info2.channel),
+              hidl_iface_info2.channel);
+}
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
diff --git a/wifi/1.2/default/wifi.cpp b/wifi/1.2/default/wifi.cpp
index d6106b4..79f921f 100644
--- a/wifi/1.2/default/wifi.cpp
+++ b/wifi/1.2/default/wifi.cpp
@@ -80,6 +80,9 @@
 Return<void> Wifi::debug(const hidl_handle& handle,
                          const hidl_vec<hidl_string>&) {
     LOG(INFO) << "-----------Debug is called----------------";
+    if (!chip_.get()) {
+        return Void();
+    }
     return chip_->debug(handle, {});
 }
 
diff --git a/wifi/1.2/default/wifi_chip.cpp b/wifi/1.2/default/wifi_chip.cpp
index bc3404a..38301e9 100644
--- a/wifi/1.2/default/wifi_chip.cpp
+++ b/wifi/1.2/default/wifi_chip.cpp
@@ -306,7 +306,7 @@
 }
 
 Return<void> WifiChip::registerEventCallback(
-    const sp<IWifiChipEventCallback>& event_callback,
+    const sp<V1_0::IWifiChipEventCallback>& event_callback,
     registerEventCallback_cb hidl_status_cb) {
     return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
                            &WifiChip::registerEventCallbackInternal,
@@ -520,6 +520,14 @@
                            hidl_status_cb);
 }
 
+Return<void> WifiChip::registerEventCallback_1_2(
+    const sp<IWifiChipEventCallback>& event_callback,
+    registerEventCallback_cb hidl_status_cb) {
+    return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+                           &WifiChip::registerEventCallbackInternal_1_2,
+                           hidl_status_cb, event_callback);
+}
+
 Return<void> WifiChip::debug(const hidl_handle& handle,
                              const hidl_vec<hidl_string>&) {
     if (handle != nullptr && handle->numFds >= 1) {
@@ -556,11 +564,9 @@
 }
 
 WifiStatus WifiChip::registerEventCallbackInternal(
-    const sp<IWifiChipEventCallback>& event_callback) {
-    if (!event_cb_handler_.addCallback(event_callback)) {
-        return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
-    }
-    return createWifiStatus(WifiStatusCode::SUCCESS);
+    const sp<V1_0::IWifiChipEventCallback>& /* event_callback */) {
+    // Deprecated support for this callback.
+    return createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED);
 }
 
 std::pair<WifiStatus, uint32_t> WifiChip::getCapabilitiesInternal() {
@@ -997,6 +1003,14 @@
     return createWifiStatusFromLegacyError(legacy_status);
 }
 
+WifiStatus WifiChip::registerEventCallbackInternal_1_2(
+    const sp<IWifiChipEventCallback>& event_callback) {
+    if (!event_cb_handler_.addCallback(event_callback)) {
+        return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
+    }
+    return createWifiStatus(WifiStatusCode::SUCCESS);
+}
+
 WifiStatus WifiChip::handleChipConfiguration(
     /* NONNULL */ std::unique_lock<std::recursive_mutex>* lock,
     ChipModeId mode_id) {
@@ -1030,6 +1044,13 @@
                    << legacyErrorToString(legacy_status);
         return createWifiStatusFromLegacyError(legacy_status);
     }
+    // Every time the HAL is restarted, we need to register the
+    // radio mode change callback.
+    WifiStatus status = registerRadioModeChangeCallback();
+    if (status.code != WifiStatusCode::SUCCESS) {
+        // This probably is not a critical failure?
+        LOG(ERROR) << "Failed to register radio mode change callback";
+    }
     return createWifiStatus(WifiStatusCode::SUCCESS);
 }
 
@@ -1073,6 +1094,36 @@
     return createWifiStatusFromLegacyError(legacy_status);
 }
 
+WifiStatus WifiChip::registerRadioModeChangeCallback() {
+    android::wp<WifiChip> weak_ptr_this(this);
+    const auto& on_radio_mode_change_callback =
+        [weak_ptr_this](const std::vector<legacy_hal::WifiMacInfo>& mac_infos) {
+            const auto shared_ptr_this = weak_ptr_this.promote();
+            if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+                LOG(ERROR) << "Callback invoked on an invalid object";
+                return;
+            }
+            std::vector<IWifiChipEventCallback::RadioModeInfo>
+                hidl_radio_mode_infos;
+            if (!hidl_struct_util::convertLegacyWifiMacInfosToHidl(
+                    mac_infos, &hidl_radio_mode_infos)) {
+                LOG(ERROR) << "Error converting wifi mac info";
+                return;
+            }
+            for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+                if (!callback->onRadioModeChange(hidl_radio_mode_infos)
+                         .isOk()) {
+                    LOG(ERROR) << "Failed to invoke onRadioModeChange"
+                               << " callback on: " << toString(callback);
+                }
+            }
+        };
+    legacy_hal::wifi_error legacy_status =
+        legacy_hal_.lock()->registerRadioModeChangeCallbackHandler(
+            getWlan0IfaceName(), on_radio_mode_change_callback);
+    return createWifiStatusFromLegacyError(legacy_status);
+}
+
 void WifiChip::populateModes() {
     // The chip combination supported for current devices is fixed.
     // They can be one of the following based on device features:
diff --git a/wifi/1.2/default/wifi_chip.h b/wifi/1.2/default/wifi_chip.h
index 4ffa8da..24a5486 100644
--- a/wifi/1.2/default/wifi_chip.h
+++ b/wifi/1.2/default/wifi_chip.h
@@ -74,7 +74,7 @@
     // HIDL methods exposed.
     Return<void> getId(getId_cb hidl_status_cb) override;
     Return<void> registerEventCallback(
-        const sp<IWifiChipEventCallback>& event_callback,
+        const sp<V1_0::IWifiChipEventCallback>& event_callback,
         registerEventCallback_cb hidl_status_cb) override;
     Return<void> getCapabilities(getCapabilities_cb hidl_status_cb) override;
     Return<void> getAvailableModes(
@@ -138,6 +138,9 @@
         resetTxPowerScenario_cb hidl_status_cb) override;
     Return<void> debug(const hidl_handle& handle,
                        const hidl_vec<hidl_string>& options) override;
+    Return<void> registerEventCallback_1_2(
+        const sp<IWifiChipEventCallback>& event_callback,
+        registerEventCallback_1_2_cb hidl_status_cb) override;
 
    private:
     void invalidateAndRemoveAllIfaces();
@@ -145,7 +148,7 @@
     // Corresponding worker functions for the HIDL methods.
     std::pair<WifiStatus, ChipId> getIdInternal();
     WifiStatus registerEventCallbackInternal(
-        const sp<IWifiChipEventCallback>& event_callback);
+        const sp<V1_0::IWifiChipEventCallback>& event_callback);
     std::pair<WifiStatus, uint32_t> getCapabilitiesInternal();
     std::pair<WifiStatus, std::vector<ChipMode>> getAvailableModesInternal();
     WifiStatus configureChipInternal(
@@ -192,10 +195,13 @@
     WifiStatus enableDebugErrorAlertsInternal(bool enable);
     WifiStatus selectTxPowerScenarioInternal(TxPowerScenario scenario);
     WifiStatus resetTxPowerScenarioInternal();
+    WifiStatus registerEventCallbackInternal_1_2(
+        const sp<IWifiChipEventCallback>& event_callback);
 
     WifiStatus handleChipConfiguration(
         std::unique_lock<std::recursive_mutex>* lock, ChipModeId mode_id);
     WifiStatus registerDebugRingBufferCallback();
+    WifiStatus registerRadioModeChangeCallback();
 
     void populateModes();
     std::vector<IWifiChip::ChipIfaceCombination>
diff --git a/wifi/1.2/default/wifi_legacy_hal.cpp b/wifi/1.2/default/wifi_legacy_hal.cpp
index 9abe514..5f40d50 100644
--- a/wifi/1.2/default/wifi_legacy_hal.cpp
+++ b/wifi/1.2/default/wifi_legacy_hal.cpp
@@ -148,6 +148,17 @@
     }
 }
 
+// Callback to be invoked for radio mode change indication.
+std::function<void(wifi_request_id, uint32_t, wifi_mac_info*)>
+    on_radio_mode_change_internal_callback;
+void onAsyncRadioModeChange(wifi_request_id id, uint32_t num_macs,
+                            wifi_mac_info* mac_infos) {
+    const auto lock = hidl_sync_util::acquireGlobalLock();
+    if (on_radio_mode_change_internal_callback) {
+        on_radio_mode_change_internal_callback(id, num_macs, mac_infos);
+    }
+}
+
 // Callback to be invoked for rtt results results.
 std::function<void(wifi_request_id, unsigned num_results,
                    wifi_rtt_result* rtt_results[])>
@@ -937,6 +948,41 @@
         0, getIfaceHandle(iface_name));
 }
 
+wifi_error WifiLegacyHal::registerRadioModeChangeCallbackHandler(
+    const std::string& iface_name,
+    const on_radio_mode_change_callback& on_user_change_callback) {
+    if (on_radio_mode_change_internal_callback) {
+        return WIFI_ERROR_NOT_AVAILABLE;
+    }
+    on_radio_mode_change_internal_callback = [on_user_change_callback](
+                                                 wifi_request_id /* id */,
+                                                 uint32_t num_macs,
+                                                 wifi_mac_info* mac_infos_arr) {
+        if (num_macs > 0 && mac_infos_arr) {
+            std::vector<WifiMacInfo> mac_infos_vec;
+            for (uint32_t i = 0; i < num_macs; i++) {
+                WifiMacInfo mac_info;
+                mac_info.wlan_mac_id = mac_infos_arr[i].wlan_mac_id;
+                mac_info.mac_band = mac_infos_arr[i].mac_band;
+                for (int32_t j = 0; j < mac_infos_arr[i].num_iface; j++) {
+                    WifiIfaceInfo iface_info;
+                    iface_info.name = mac_infos_arr[i].iface_info[j].iface_name;
+                    iface_info.channel = mac_infos_arr[i].iface_info[j].channel;
+                    mac_info.iface_infos.push_back(iface_info);
+                }
+                mac_infos_vec.push_back(mac_info);
+            }
+            on_user_change_callback(mac_infos_vec);
+        }
+    };
+    wifi_error status = global_func_table_.wifi_set_radio_mode_change_handler(
+        0, getIfaceHandle(iface_name), {onAsyncRadioModeChange});
+    if (status != WIFI_SUCCESS) {
+        on_radio_mode_change_internal_callback = nullptr;
+    }
+    return status;
+}
+
 wifi_error WifiLegacyHal::startRttRangeRequest(
     const std::string& iface_name, wifi_request_id id,
     const std::vector<wifi_rtt_config>& rtt_configs,
@@ -1324,6 +1370,7 @@
     on_rssi_threshold_breached_internal_callback = nullptr;
     on_ring_buffer_data_internal_callback = nullptr;
     on_error_alert_internal_callback = nullptr;
+    on_radio_mode_change_internal_callback = nullptr;
     on_rtt_results_internal_callback = nullptr;
     on_nan_notify_response_user_callback = nullptr;
     on_nan_event_publish_terminated_user_callback = nullptr;
diff --git a/wifi/1.2/default/wifi_legacy_hal.h b/wifi/1.2/default/wifi_legacy_hal.h
index da88f6b..bd68bcd 100644
--- a/wifi/1.2/default/wifi_legacy_hal.h
+++ b/wifi/1.2/default/wifi_legacy_hal.h
@@ -132,6 +132,26 @@
 // Callback for alerts.
 using on_error_alert_callback =
     std::function<void(int32_t, const std::vector<uint8_t>&)>;
+
+// Struct for the mac info from the legacy HAL. This is a cleaner version
+// of the |wifi_mac_info| & |wifi_iface_info|.
+typedef struct {
+    std::string name;
+    wifi_channel channel;
+} WifiIfaceInfo;
+
+typedef struct {
+    uint32_t wlan_mac_id;
+    /* BIT MASK of BIT(WLAN_MAC*) as represented by wlan_mac_band */
+    uint32_t mac_band;
+    /* Represents the connected Wi-Fi interfaces associated with each MAC */
+    std::vector<WifiIfaceInfo> iface_infos;
+} WifiMacInfo;
+
+// Callback for radio mode change
+using on_radio_mode_change_callback =
+    std::function<void(const std::vector<WifiMacInfo>&)>;
+
 /**
  * Class that encapsulates all legacy HAL interactions.
  * This class manages the lifetime of the event loop thread used by legacy HAL.
@@ -253,6 +273,10 @@
         const on_error_alert_callback& on_alert_callback);
     wifi_error deregisterErrorAlertCallbackHandler(
         const std::string& iface_name);
+    // Radio mode functions.
+    wifi_error registerRadioModeChangeCallbackHandler(
+        const std::string& iface_name,
+        const on_radio_mode_change_callback& on_user_change_callback);
     // RTT functions.
     wifi_error startRttRangeRequest(
         const std::string& iface_name, wifi_request_id id,
diff --git a/wifi/1.2/default/wifi_legacy_hal_stubs.cpp b/wifi/1.2/default/wifi_legacy_hal_stubs.cpp
index 28972cb..2ee2aa2 100644
--- a/wifi/1.2/default/wifi_legacy_hal_stubs.cpp
+++ b/wifi/1.2/default/wifi_legacy_hal_stubs.cpp
@@ -134,6 +134,7 @@
     populateStubFor(&hal_fn->wifi_configure_roaming);
     populateStubFor(&hal_fn->wifi_select_tx_power_scenario);
     populateStubFor(&hal_fn->wifi_reset_tx_power_scenario);
+    populateStubFor(&hal_fn->wifi_set_radio_mode_change_handler);
     return true;
 }
 }  // namespace legacy_hal