Merge changes Ibf80a618,I0a2b90bc

* changes:
  Use IComposerClient::EX_UNSUPPORTED
  Check createComposer().isOk() in test setup
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h b/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h
index 49b33d5..0f0ccf1 100644
--- a/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h
+++ b/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h
@@ -67,24 +67,30 @@
 }
 
 inline const ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig* getAreaConfig(
-        const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& propValue,
+        int32_t propId, int32_t areaId,
         const ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig& config) {
     if (config.areaConfigs.size() == 0) {
         return nullptr;
     }
 
-    if (isGlobalProp(propValue.prop)) {
+    if (isGlobalProp(propId)) {
         return &(config.areaConfigs[0]);
     }
 
     for (const auto& c : config.areaConfigs) {
-        if (c.areaId == propValue.areaId) {
+        if (c.areaId == areaId) {
             return &c;
         }
     }
     return nullptr;
 }
 
+inline const ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig* getAreaConfig(
+        const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& propValue,
+        const ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig& config) {
+    return getAreaConfig(propValue.prop, propValue.areaId, config);
+}
+
 inline std::unique_ptr<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>
 createVehiclePropValueVec(::aidl::android::hardware::automotive::vehicle::VehiclePropertyType type,
                           size_t vecSize) {
diff --git a/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp b/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp
index 1a79230..c1fa896 100644
--- a/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp
+++ b/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp
@@ -21,9 +21,11 @@
 
 #include <VehicleHalTypes.h>
 #include <VehicleUtils.h>
-#include <android-base/format.h>
+#include <android-base/stringprintf.h>
 #include <math/HashCombine.h>
 
+#include <inttypes.h>
+
 namespace android {
 namespace hardware {
 namespace automotive {
@@ -36,13 +38,14 @@
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
 using ::android::base::Error;
 using ::android::base::Result;
+using ::android::base::StringPrintf;
 
 bool VehiclePropertyStore::RecordId::operator==(const VehiclePropertyStore::RecordId& other) const {
     return area == other.area && token == other.token;
 }
 
 std::string VehiclePropertyStore::RecordId::toString() const {
-    return ::fmt::format("RecordID{{.areaId={:d}, .token={:d}}}", area, token);
+    return StringPrintf("RecordID{{.areaId=% " PRId32 ", .token=%" PRId64 "}", area, token);
 }
 
 size_t VehiclePropertyStore::RecordIdHash::operator()(RecordId const& recordId) const {
diff --git a/automotive/vehicle/aidl/impl/vhal/include/ConnectedClient.h b/automotive/vehicle/aidl/impl/vhal/include/ConnectedClient.h
index d8516b1..833707a 100644
--- a/automotive/vehicle/aidl/impl/vhal/include/ConnectedClient.h
+++ b/automotive/vehicle/aidl/impl/vhal/include/ConnectedClient.h
@@ -19,6 +19,7 @@
 
 #include "PendingRequestPool.h"
 
+#include <IVehicleHardware.h>
 #include <VehicleHalTypes.h>
 
 #include <aidl/android/hardware/automotive/vehicle/IVehicleCallback.h>
@@ -41,24 +42,24 @@
 // This class is thread-safe.
 class ConnectedClient {
   public:
-    ConnectedClient(
-            std::shared_ptr<PendingRequestPool> requestPool,
-            std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>
-                    callback);
+    using CallbackType =
+            std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>;
+
+    ConnectedClient(std::shared_ptr<PendingRequestPool> requestPool, CallbackType callback);
 
     virtual ~ConnectedClient() = default;
 
     // Gets the unique ID for this client.
     const void* id();
 
-    // Add client requests. The requests would be registered as pending requests until
+    // Adds client requests. The requests would be registered as pending requests until
     // {@code tryFinishRequests} is called for them.
     // Returns {@code INVALID_ARG} error if any of the requestIds are duplicate with one of the
     // pending request IDs or {@code TRY_AGAIN} error if the pending request pool is full and could
     // no longer add requests.
     ::android::base::Result<void> addRequests(const std::unordered_set<int64_t>& requestIds);
 
-    // Mark the requests as finished. Returns a list of request IDs that was pending and has been
+    // Marks the requests as finished. Returns a list of request IDs that was pending and has been
     // finished. It must be a set of the requested request IDs.
     std::unordered_set<int64_t> tryFinishRequests(const std::unordered_set<int64_t>& requestIds);
 
@@ -67,8 +68,7 @@
     virtual std::shared_ptr<const PendingRequestPool::TimeoutCallbackFunc> getTimeoutCallback() = 0;
 
     const std::shared_ptr<PendingRequestPool> mRequestPool;
-    const std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>
-            mCallback;
+    const CallbackType mCallback;
 };
 
 // A class to represent a client that calls {@code IVehicle.setValues} or {@code
@@ -76,10 +76,7 @@
 template <class ResultType, class ResultsType>
 class GetSetValuesClient final : public ConnectedClient {
   public:
-    GetSetValuesClient(
-            std::shared_ptr<PendingRequestPool> requestPool,
-            std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>
-                    callback);
+    GetSetValuesClient(std::shared_ptr<PendingRequestPool> requestPool, CallbackType callback);
 
     // Sends the results to this client.
     void sendResults(const std::vector<ResultType>& results);
@@ -104,15 +101,17 @@
 // A class to represent a client that calls {@code IVehicle.subscribe}.
 class SubscriptionClient final : public ConnectedClient {
   public:
-    SubscriptionClient(
-            std::shared_ptr<PendingRequestPool> requestPool,
-            std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>
-                    callback);
+    SubscriptionClient(std::shared_ptr<PendingRequestPool> requestPool, CallbackType callback);
 
     // Gets the callback to be called when the request for this client has finished.
-    std::shared_ptr<const std::function<
-            void(std::vector<::aidl::android::hardware::automotive::vehicle::GetValueResult>)>>
-    getResultCallback();
+    std::shared_ptr<const IVehicleHardware::GetValuesCallback> getResultCallback();
+
+    // Marshals the updated values into largeParcelable and sents it through {@code onPropertyEvent}
+    // callback.
+    static void sendUpdatedValues(
+            CallbackType callback,
+            std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>&&
+                    updatedValues);
 
   protected:
     // Gets the callback to be called when the request for this client has timeout.
@@ -121,14 +120,11 @@
   private:
     // The following members are only initialized during construction.
     std::shared_ptr<const PendingRequestPool::TimeoutCallbackFunc> mTimeoutCallback;
-    std::shared_ptr<const std::function<void(
-            std::vector<::aidl::android::hardware::automotive::vehicle::GetValueResult>)>>
-            mResultCallback;
+    std::shared_ptr<const IVehicleHardware::GetValuesCallback> mResultCallback;
+    std::shared_ptr<const IVehicleHardware::PropertyChangeCallback> mPropertyChangeCallback;
 
     static void onGetValueResults(
-            const void* clientId,
-            std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>
-                    callback,
+            const void* clientId, CallbackType callback,
             std::shared_ptr<PendingRequestPool> requestPool,
             std::vector<::aidl::android::hardware::automotive::vehicle::GetValueResult> results);
 };
diff --git a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
index b0423a3..62b2627 100644
--- a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
+++ b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
@@ -93,11 +93,68 @@
             GetSetValuesClient<::aidl::android::hardware::automotive::vehicle::SetValueResult,
                                ::aidl::android::hardware::automotive::vehicle::SetValueResults>;
 
+    // A thread safe class to maintain an increasing request ID for each subscribe client. This
+    // class is safe to pass to async callbacks.
+    class SubscribeIdByClient {
+      public:
+        int64_t getId(const CallbackType& callback);
+
+      private:
+        std::mutex mLock;
+        std::unordered_map<const AIBinder*, int64_t> mIds GUARDED_BY(mLock);
+    };
+
+    // A thread safe class to store all subscribe clients. This class is safe to pass to async
+    // callbacks.
+    class SubscriptionClients {
+      public:
+        SubscriptionClients(std::shared_ptr<PendingRequestPool> pool) : mPendingRequestPool(pool) {}
+
+        std::shared_ptr<SubscriptionClient> getClient(const CallbackType& callback);
+
+        void removeClient(const AIBinder* clientId);
+
+        size_t countClients();
+
+      private:
+        std::mutex mLock;
+        std::unordered_map<const AIBinder*, std::shared_ptr<SubscriptionClient>> mClients
+                GUARDED_BY(mLock);
+        // PendingRequestPool is thread-safe.
+        std::shared_ptr<PendingRequestPool> mPendingRequestPool;
+    };
+
+    // A wrapper for linkToDeath to enable stubbing for test.
+    class ILinkToDeath {
+      public:
+        virtual ~ILinkToDeath() = default;
+
+        virtual binder_status_t linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient,
+                                            void* cookie) = 0;
+    };
+
+    // A real implementation for ILinkToDeath.
+    class AIBinderLinkToDeathImpl final : public ILinkToDeath {
+      public:
+        binder_status_t linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient,
+                                    void* cookie) override;
+    };
+
+    // OnBinderDiedContext is a type used as a cookie passed deathRecipient. The deathRecipient's
+    // onBinderDied function takes only a cookie as input and we have to store all the contexts
+    // as the cookie.
+    struct OnBinderDiedContext {
+        DefaultVehicleHal* vhal;
+        const AIBinder* clientId;
+    };
+
     // The default timeout of get or set value requests is 30s.
     // TODO(b/214605968): define TIMEOUT_IN_NANO in IVehicle and allow getValues/setValues/subscribe
     // to specify custom timeouts.
     static constexpr int64_t TIMEOUT_IN_NANO = 30'000'000'000;
-    const std::unique_ptr<IVehicleHardware> mVehicleHardware;
+    // heart beat event interval: 3s
+    static constexpr int64_t HEART_BEAT_INTERVAL_IN_NANO = 3'000'000'000;
+    const std::shared_ptr<IVehicleHardware> mVehicleHardware;
 
     // mConfigsByPropId and mConfigFile are only modified during initialization, so no need to
     // lock guard them.
@@ -108,22 +165,24 @@
     // PendingRequestPool is thread-safe.
     std::shared_ptr<PendingRequestPool> mPendingRequestPool;
     // SubscriptionManager is thread-safe.
-    std::unique_ptr<SubscriptionManager> mSubscriptionManager;
+    std::shared_ptr<SubscriptionManager> mSubscriptionManager;
 
     std::mutex mLock;
-    std::unordered_map<CallbackType, std::shared_ptr<GetValuesClient>> mGetValuesClients
+    std::unordered_map<const AIBinder*, std::unique_ptr<OnBinderDiedContext>> mOnBinderDiedContexts
             GUARDED_BY(mLock);
-    std::unordered_map<CallbackType, std::shared_ptr<SetValuesClient>> mSetValuesClients
+    std::unordered_map<const AIBinder*, std::shared_ptr<GetValuesClient>> mGetValuesClients
             GUARDED_BY(mLock);
-    std::unordered_map<CallbackType, std::shared_ptr<SubscriptionClient>> mSubscriptionClients
+    std::unordered_map<const AIBinder*, std::shared_ptr<SetValuesClient>> mSetValuesClients
             GUARDED_BY(mLock);
-    // An increasing request ID we keep for subscribe clients.
-    std::unordered_map<CallbackType, int64_t> mSubscribeIdByClient GUARDED_BY(mLock);
+    // SubscriptionClients is thread-safe.
+    std::shared_ptr<SubscriptionClients> mSubscriptionClients;
+    // mLinkToDeathImpl is only going to be changed in test.
+    std::unique_ptr<ILinkToDeath> mLinkToDeathImpl;
 
-    template <class T>
-    std::shared_ptr<T> getOrCreateClient(
-            std::unordered_map<CallbackType, std::shared_ptr<T>>* clients,
-            const CallbackType& callback) REQUIRES(mLock);
+    // RecurrentTimer is thread-safe.
+    RecurrentTimer mRecurrentTimer;
+
+    ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
 
     ::android::base::Result<void> checkProperty(
             const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& propValue);
@@ -136,13 +195,55 @@
             const std::vector<::aidl::android::hardware::automotive::vehicle::SetValueRequest>&
                     requests);
 
-    void getValueFromHardwareCallCallback(
-            const CallbackType& callback,
+    ::android::base::Result<void> checkSubscribeOptions(
+            const std::vector<::aidl::android::hardware::automotive::vehicle::SubscribeOptions>&
+                    options);
+
+    ::android::base::Result<void> checkReadPermission(
+            const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value) const;
+
+    ::android::base::Result<void> checkWritePermission(
+            const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value) const;
+
+    ::android::base::Result<
+            const ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig*>
+    getConfig(int32_t propId) const;
+
+    void onBinderDiedWithContext(const AIBinder* clientId);
+
+    void onBinderUnlinkedWithContext(const AIBinder* clientId);
+
+    void monitorBinderLifeCycle(const CallbackType& callback);
+
+    template <class T>
+    static std::shared_ptr<T> getOrCreateClient(
+            std::unordered_map<const AIBinder*, std::shared_ptr<T>>* clients,
+            const CallbackType& callback, std::shared_ptr<PendingRequestPool> pendingRequestPool);
+
+    static void getValueFromHardwareCallCallback(
+            std::weak_ptr<IVehicleHardware> vehicleHardware,
+            std::shared_ptr<SubscribeIdByClient> subscribeIdByClient,
+            std::shared_ptr<SubscriptionClients> subscriptionClients, const CallbackType& callback,
             const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value);
 
+    static void onPropertyChangeEvent(
+            std::weak_ptr<SubscriptionManager> subscriptionManager,
+            const std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>&
+                    updatedValues);
+
+    static void checkHealth(std::weak_ptr<IVehicleHardware> hardware,
+                            std::weak_ptr<SubscriptionManager> subscriptionManager);
+
+    static void onBinderDied(void* cookie);
+
+    static void onBinderUnlinked(void* cookie);
+
     // Test-only
     // Set the default timeout for pending requests.
     void setTimeout(int64_t timeoutInNano);
+
+    // Test-only
+    void setLinkToDeathImpl(std::unique_ptr<ILinkToDeath> impl);
 };
 
 }  // namespace vehicle
diff --git a/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h b/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h
index 28809c6..e739c8c 100644
--- a/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h
+++ b/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h
@@ -38,6 +38,7 @@
 // A thread-safe subscription manager that manages all VHAL subscriptions.
 class SubscriptionManager final {
   public:
+    using ClientIdType = const AIBinder*;
     using CallbackType =
             std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>;
     using GetValueFunc = std::function<void(
@@ -59,24 +60,24 @@
                     options,
             bool isContinuousProperty);
 
-    // Unsubscribes from the properties for the callback.
-    // Returns error if the callback was not subscribed before or one of the given property was not
+    // Unsubscribes from the properties for the client.
+    // Returns error if the client was not subscribed before or one of the given property was not
     // subscribed. If error is returned, no property would be unsubscribed.
-    // Returns ok if all the requested properties for the callback are unsubscribed.
-    ::android::base::Result<void> unsubscribe(const CallbackType& callback,
+    // Returns ok if all the requested properties for the client are unsubscribed.
+    ::android::base::Result<void> unsubscribe(ClientIdType client,
                                               const std::vector<int32_t>& propIds);
 
-    // Unsubscribes to all the properties for the callback.
-    // Returns error if the callback was not subscribed before. If error is returned, no property
+    // Unsubscribes from all the properties for the client.
+    // Returns error if the client was not subscribed before. If error is returned, no property
     // would be unsubscribed.
-    // Returns ok if all the properties for the callback are unsubscribed.
-    ::android::base::Result<void> unsubscribe(const CallbackType& callback);
+    // Returns ok if all the properties for the client are unsubscribed.
+    ::android::base::Result<void> unsubscribe(ClientIdType client);
 
     // For a list of updated properties, returns a map that maps clients subscribing to
     // the updated properties to a list of updated values. This would only return on-change property
     // clients that should be informed for the given updated values.
     std::unordered_map<
-            std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>,
+            CallbackType,
             std::vector<const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue*>>
     getSubscribedClients(
             const std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>&
@@ -86,6 +87,9 @@
     static bool checkSampleRate(float sampleRate);
 
   private:
+    // Friend class for testing.
+    friend class DefaultVehicleHalTest;
+
     struct PropIdAreaId {
         int32_t propId;
         int32_t areaId;
@@ -131,9 +135,10 @@
     };
 
     mutable std::mutex mLock;
-    std::unordered_map<PropIdAreaId, std::unordered_set<CallbackType>, PropIdAreaIdHash>
+    std::unordered_map<PropIdAreaId, std::unordered_map<ClientIdType, CallbackType>,
+                       PropIdAreaIdHash>
             mClientsByPropIdArea GUARDED_BY(mLock);
-    std::unordered_map<CallbackType, std::unordered_map<PropIdAreaId, std::unique_ptr<Subscription>,
+    std::unordered_map<ClientIdType, std::unordered_map<PropIdAreaId, std::unique_ptr<Subscription>,
                                                         PropIdAreaIdHash>>
             mSubscriptionsByClient GUARDED_BY(mLock);
     // RecurrentTimer is thread-safe.
@@ -141,6 +146,9 @@
     const GetValueFunc mGetValue;
 
     static ::android::base::Result<int64_t> getInterval(float sampleRate);
+
+    // Checks whether the manager is empty. For testing purpose.
+    bool isEmpty();
 };
 
 }  // namespace vehicle
diff --git a/automotive/vehicle/aidl/impl/vhal/src/ConnectedClient.cpp b/automotive/vehicle/aidl/impl/vhal/src/ConnectedClient.cpp
index 7d02a05..5ccef55 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/ConnectedClient.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/ConnectedClient.cpp
@@ -258,7 +258,7 @@
             });
     auto requestPoolCopy = mRequestPool;
     const void* clientId = reinterpret_cast<const void*>(this);
-    mResultCallback = std::make_shared<const std::function<void(std::vector<GetValueResult>)>>(
+    mResultCallback = std::make_shared<const IVehicleHardware::GetValuesCallback>(
             [clientId, callback, requestPoolCopy](std::vector<GetValueResult> results) {
                 onGetValueResults(clientId, callback, requestPoolCopy, results);
             });
@@ -274,6 +274,32 @@
     return mTimeoutCallback;
 }
 
+void SubscriptionClient::sendUpdatedValues(std::shared_ptr<IVehicleCallback> callback,
+                                           std::vector<VehiclePropValue>&& updatedValues) {
+    if (updatedValues.empty()) {
+        return;
+    }
+
+    // TODO(b/205189110): Use memory pool here and fill in sharedMemoryId.
+    VehiclePropValues vehiclePropValues;
+    int32_t sharedMemoryFileCount = 0;
+    ScopedAStatus status = vectorToStableLargeParcelable(updatedValues, &vehiclePropValues);
+    if (!status.isOk()) {
+        int statusCode = status.getServiceSpecificError();
+        ALOGE("subscribe: failed to marshal result into large parcelable, error: "
+              "%s, code: %d",
+              status.getMessage(), statusCode);
+        return;
+    }
+
+    if (ScopedAStatus callbackStatus =
+                callback->onPropertyEvent(vehiclePropValues, sharedMemoryFileCount);
+        !callbackStatus.isOk()) {
+        ALOGE("subscribe: failed to call callback, error: %s, code: %d", status.getMessage(),
+              status.getServiceSpecificError());
+    }
+}
+
 void SubscriptionClient::onGetValueResults(const void* clientId,
                                            std::shared_ptr<IVehicleCallback> callback,
                                            std::shared_ptr<PendingRequestPool> requestPool,
@@ -308,27 +334,7 @@
         propValues.push_back(std::move(result.prop.value()));
     }
 
-    if (propValues.empty()) {
-        return;
-    }
-    // TODO(b/205189110): Use memory pool here and fill in sharedMemoryId.
-    VehiclePropValues vehiclePropValues;
-    int32_t sharedMemoryFileCount = 0;
-    ScopedAStatus status = vectorToStableLargeParcelable(propValues, &vehiclePropValues);
-    if (!status.isOk()) {
-        int statusCode = status.getServiceSpecificError();
-        ALOGE("failed to marshal result into large parcelable, error: "
-              "%s, code: %d",
-              status.getMessage(), statusCode);
-        return;
-    }
-
-    if (ScopedAStatus callbackStatus =
-                callback->onPropertyEvent(vehiclePropValues, sharedMemoryFileCount);
-        !callbackStatus.isOk()) {
-        ALOGE("failed to call callback, error: %s, code: %d", status.getMessage(),
-              status.getServiceSpecificError());
-    }
+    sendUpdatedValues(callback, std::move(propValues));
 }
 
 }  // namespace vehicle
diff --git a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
index 1e76eb7..3e088c5 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
@@ -23,7 +23,9 @@
 #include <VehicleUtils.h>
 
 #include <android-base/result.h>
+#include <android-base/stringprintf.h>
 #include <utils/Log.h>
+#include <utils/SystemClock.h>
 
 #include <inttypes.h>
 #include <set>
@@ -40,7 +42,6 @@
 using ::aidl::android::hardware::automotive::vehicle::GetValueRequests;
 using ::aidl::android::hardware::automotive::vehicle::GetValueResult;
 using ::aidl::android::hardware::automotive::vehicle::GetValueResults;
-using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback;
 using ::aidl::android::hardware::automotive::vehicle::SetValueRequest;
 using ::aidl::android::hardware::automotive::vehicle::SetValueRequests;
 using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
@@ -50,11 +51,18 @@
 using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfigs;
+using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyChangeMode;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
 using ::android::automotive::car_binder_lib::LargeParcelableBase;
 using ::android::base::Error;
 using ::android::base::expected;
 using ::android::base::Result;
+using ::android::base::StringPrintf;
+
+using ::ndk::ScopedAIBinder_DeathRecipient;
 using ::ndk::ScopedAStatus;
 
 std::string toString(const std::unordered_set<int64_t>& values) {
@@ -70,6 +78,29 @@
 
 }  // namespace
 
+std::shared_ptr<SubscriptionClient> DefaultVehicleHal::SubscriptionClients::getClient(
+        const CallbackType& callback) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    return getOrCreateClient(&mClients, callback, mPendingRequestPool);
+}
+
+int64_t DefaultVehicleHal::SubscribeIdByClient::getId(const CallbackType& callback) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    // This would be initialized to 0 if callback does not exist in the map.
+    int64_t subscribeId = (mIds[callback->asBinder().get()])++;
+    return subscribeId;
+}
+
+void DefaultVehicleHal::SubscriptionClients::removeClient(const AIBinder* clientId) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    mClients.erase(clientId);
+}
+
+size_t DefaultVehicleHal::SubscriptionClients::countClients() {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    return mClients.size();
+}
+
 DefaultVehicleHal::DefaultVehicleHal(std::unique_ptr<IVehicleHardware> hardware)
     : mVehicleHardware(std::move(hardware)),
       mPendingRequestPool(std::make_shared<PendingRequestPool>(TIMEOUT_IN_NANO)) {
@@ -90,51 +121,139 @@
         mConfigFile = std::move(result.value());
     }
 
-    mSubscriptionManager = std::make_unique<SubscriptionManager>(
-            [this](const CallbackType& callback, const VehiclePropValue& value) {
-                getValueFromHardwareCallCallback(callback, value);
-            });
+    mSubscriptionClients = std::make_shared<SubscriptionClients>(mPendingRequestPool);
+
+    auto subscribeIdByClient = std::make_shared<SubscribeIdByClient>();
+    // Make a weak copy of IVehicleHardware because subscriptionManager uses IVehicleHardware and
+    // IVehicleHardware uses subscriptionManager. We want to avoid cyclic reference.
+    std::weak_ptr<IVehicleHardware> hardwareCopy = mVehicleHardware;
+    SubscriptionManager::GetValueFunc getValueFunc = std::bind(
+            &DefaultVehicleHal::getValueFromHardwareCallCallback, hardwareCopy, subscribeIdByClient,
+            mSubscriptionClients, std::placeholders::_1, std::placeholders::_2);
+    mSubscriptionManager = std::make_shared<SubscriptionManager>(std::move(getValueFunc));
+
+    std::weak_ptr<SubscriptionManager> subscriptionManagerCopy = mSubscriptionManager;
+    mVehicleHardware->registerOnPropertyChangeEvent(
+            std::make_unique<IVehicleHardware::PropertyChangeCallback>(
+                    [subscriptionManagerCopy](std::vector<VehiclePropValue> updatedValues) {
+                        onPropertyChangeEvent(subscriptionManagerCopy, updatedValues);
+                    }));
+
+    // Register heartbeat event.
+    mRecurrentTimer.registerTimerCallback(
+            HEART_BEAT_INTERVAL_IN_NANO,
+            std::make_shared<std::function<void()>>([hardwareCopy, subscriptionManagerCopy]() {
+                checkHealth(hardwareCopy, subscriptionManagerCopy);
+            }));
+
+    mLinkToDeathImpl = std::make_unique<AIBinderLinkToDeathImpl>();
+    mDeathRecipient = ScopedAIBinder_DeathRecipient(
+            AIBinder_DeathRecipient_new(&DefaultVehicleHal::onBinderDied));
+    AIBinder_DeathRecipient_setOnUnlinked(mDeathRecipient.get(),
+                                          &DefaultVehicleHal::onBinderUnlinked);
 }
 
 DefaultVehicleHal::~DefaultVehicleHal() {
-    // mSubscriptionManager has reference to this, so must be destroyed before other members.
-    mSubscriptionManager.reset();
+    // Delete the deathRecipient so that onBinderDied would not be called to reference 'this'.
+    mDeathRecipient = ScopedAIBinder_DeathRecipient();
+}
+
+void DefaultVehicleHal::onPropertyChangeEvent(
+        std::weak_ptr<SubscriptionManager> subscriptionManager,
+        const std::vector<VehiclePropValue>& updatedValues) {
+    auto manager = subscriptionManager.lock();
+    if (manager == nullptr) {
+        ALOGW("the SubscriptionManager is destroyed, DefaultVehicleHal is ending");
+        return;
+    }
+    auto updatedValuesByClients = manager->getSubscribedClients(updatedValues);
+    for (const auto& [callback, valuePtrs] : updatedValuesByClients) {
+        std::vector<VehiclePropValue> values;
+        for (const VehiclePropValue* valuePtr : valuePtrs) {
+            values.push_back(*valuePtr);
+        }
+        SubscriptionClient::sendUpdatedValues(callback, std::move(values));
+    }
 }
 
 template <class T>
 std::shared_ptr<T> DefaultVehicleHal::getOrCreateClient(
-        std::unordered_map<CallbackType, std::shared_ptr<T>>* clients,
-        const CallbackType& callback) {
-    if (clients->find(callback) == clients->end()) {
-        // TODO(b/204943359): Remove client from clients when linkToDeath is implemented.
-        (*clients)[callback] = std::make_shared<T>(mPendingRequestPool, callback);
+        std::unordered_map<const AIBinder*, std::shared_ptr<T>>* clients,
+        const CallbackType& callback, std::shared_ptr<PendingRequestPool> pendingRequestPool) {
+    const AIBinder* clientId = callback->asBinder().get();
+    if (clients->find(clientId) == clients->end()) {
+        (*clients)[clientId] = std::make_shared<T>(pendingRequestPool, callback);
     }
-    return (*clients)[callback];
+    return (*clients)[clientId];
+}
+
+void DefaultVehicleHal::monitorBinderLifeCycle(const CallbackType& callback) {
+    AIBinder* clientId = callback->asBinder().get();
+    {
+        std::scoped_lock<std::mutex> lockGuard(mLock);
+        if (mOnBinderDiedContexts.find(clientId) != mOnBinderDiedContexts.end()) {
+            // Already registered.
+            return;
+        }
+    }
+
+    std::unique_ptr<OnBinderDiedContext> context = std::make_unique<OnBinderDiedContext>(
+            OnBinderDiedContext{.vhal = this, .clientId = clientId});
+    binder_status_t status = mLinkToDeathImpl->linkToDeath(clientId, mDeathRecipient.get(),
+                                                           static_cast<void*>(context.get()));
+    if (status == STATUS_OK) {
+        std::scoped_lock<std::mutex> lockGuard(mLock);
+        // Insert into a map to keep the context object alive.
+        mOnBinderDiedContexts[clientId] = std::move(context);
+    } else {
+        ALOGE("failed to call linkToDeath on client binder, status: %d", static_cast<int>(status));
+    }
+}
+
+void DefaultVehicleHal::onBinderDied(void* cookie) {
+    OnBinderDiedContext* context = reinterpret_cast<OnBinderDiedContext*>(cookie);
+    context->vhal->onBinderDiedWithContext(context->clientId);
+}
+
+void DefaultVehicleHal::onBinderDiedWithContext(const AIBinder* clientId) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    mSetValuesClients.erase(clientId);
+    mGetValuesClients.erase(clientId);
+    mSubscriptionClients->removeClient(clientId);
+    mSubscriptionManager->unsubscribe(clientId);
+}
+
+void DefaultVehicleHal::onBinderUnlinked(void* cookie) {
+    // Delete the context associated with this cookie.
+    OnBinderDiedContext* context = reinterpret_cast<OnBinderDiedContext*>(cookie);
+    context->vhal->onBinderUnlinkedWithContext(context->clientId);
+}
+
+void DefaultVehicleHal::onBinderUnlinkedWithContext(const AIBinder* clientId) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    mOnBinderDiedContexts.erase(clientId);
 }
 
 template std::shared_ptr<DefaultVehicleHal::GetValuesClient>
 DefaultVehicleHal::getOrCreateClient<DefaultVehicleHal::GetValuesClient>(
-        std::unordered_map<CallbackType, std::shared_ptr<GetValuesClient>>* clients,
-        const CallbackType& callback);
+        std::unordered_map<const AIBinder*, std::shared_ptr<GetValuesClient>>* clients,
+        const CallbackType& callback, std::shared_ptr<PendingRequestPool> pendingRequestPool);
 template std::shared_ptr<DefaultVehicleHal::SetValuesClient>
 DefaultVehicleHal::getOrCreateClient<DefaultVehicleHal::SetValuesClient>(
-        std::unordered_map<CallbackType, std::shared_ptr<SetValuesClient>>* clients,
-        const CallbackType& callback);
+        std::unordered_map<const AIBinder*, std::shared_ptr<SetValuesClient>>* clients,
+        const CallbackType& callback, std::shared_ptr<PendingRequestPool> pendingRequestPool);
 template std::shared_ptr<SubscriptionClient>
 DefaultVehicleHal::getOrCreateClient<SubscriptionClient>(
-        std::unordered_map<CallbackType, std::shared_ptr<SubscriptionClient>>* clients,
-        const CallbackType& callback);
+        std::unordered_map<const AIBinder*, std::shared_ptr<SubscriptionClient>>* clients,
+        const CallbackType& callback, std::shared_ptr<PendingRequestPool> pendingRequestPool);
 
-void DefaultVehicleHal::getValueFromHardwareCallCallback(const CallbackType& callback,
-                                                         const VehiclePropValue& value) {
-    int64_t subscribeId;
-    std::shared_ptr<SubscriptionClient> client;
-    {
-        std::scoped_lock<std::mutex> lockGuard(mLock);
-        // This is initialized to 0 if callback does not exist in the map.
-        subscribeId = (mSubscribeIdByClient[callback])++;
-        client = getOrCreateClient(&mSubscriptionClients, callback);
-    }
+void DefaultVehicleHal::getValueFromHardwareCallCallback(
+        std::weak_ptr<IVehicleHardware> vehicleHardware,
+        std::shared_ptr<SubscribeIdByClient> subscribeIdByClient,
+        std::shared_ptr<SubscriptionClients> subscriptionClients, const CallbackType& callback,
+        const VehiclePropValue& value) {
+    int64_t subscribeId = subscribeIdByClient->getId(callback);
+    auto client = subscriptionClients->getClient(callback);
     if (auto addRequestResult = client->addRequests({subscribeId}); !addRequestResult.ok()) {
         ALOGE("subscribe[%" PRId64 "]: too many pending requests, ignore the getValue request",
               subscribeId);
@@ -146,8 +265,12 @@
             .prop = value,
     }};
 
-    if (StatusCode status =
-                mVehicleHardware->getValues(client->getResultCallback(), hardwareRequests);
+    std::shared_ptr<IVehicleHardware> hardware = vehicleHardware.lock();
+    if (hardware == nullptr) {
+        ALOGW("the IVehicleHardware is destroyed, DefaultVehicleHal is ending");
+        return;
+    }
+    if (StatusCode status = hardware->getValues(client->getResultCallback(), hardwareRequests);
         status != StatusCode::OK) {
         // If the hardware returns error, finish all the pending requests for this request because
         // we never expect hardware to call callback for these requests.
@@ -174,33 +297,43 @@
     return ScopedAStatus::ok();
 }
 
-Result<void> DefaultVehicleHal::checkProperty(const VehiclePropValue& propValue) {
-    int32_t propId = propValue.prop;
+Result<const VehiclePropConfig*> DefaultVehicleHal::getConfig(int32_t propId) const {
     auto it = mConfigsByPropId.find(propId);
     if (it == mConfigsByPropId.end()) {
         return Error() << "no config for property, ID: " << propId;
     }
-    const VehiclePropConfig& config = it->second;
-    const VehicleAreaConfig* areaConfig = getAreaConfig(propValue, config);
+    return &(it->second);
+}
+
+Result<void> DefaultVehicleHal::checkProperty(const VehiclePropValue& propValue) {
+    int32_t propId = propValue.prop;
+    auto result = getConfig(propId);
+    if (!result.ok()) {
+        return result.error();
+    }
+    const VehiclePropConfig* config = result.value();
+    const VehicleAreaConfig* areaConfig = getAreaConfig(propValue, *config);
     if (!isGlobalProp(propId) && areaConfig == nullptr) {
         // Ignore areaId for global property. For non global property, check whether areaId is
         // allowed. areaId must appear in areaConfig.
         return Error() << "invalid area ID: " << propValue.areaId << " for prop ID: " << propId
                        << ", not listed in config";
     }
-    if (auto result = checkPropValue(propValue, &config); !result.ok()) {
+    if (auto result = checkPropValue(propValue, config); !result.ok()) {
         return Error() << "invalid property value: " << propValue.toString()
-                       << ", error: " << result.error().message();
+                       << ", error: " << getErrorMsg(result);
     }
     if (auto result = checkValueRange(propValue, areaConfig); !result.ok()) {
         return Error() << "property value out of range: " << propValue.toString()
-                       << ", error: " << result.error().message();
+                       << ", error: " << getErrorMsg(result);
     }
     return {};
 }
 
 ScopedAStatus DefaultVehicleHal::getValues(const CallbackType& callback,
                                            const GetValueRequests& requests) {
+    monitorBinderLifeCycle(callback);
+
     expected<LargeParcelableBase::BorrowedOwnedObject<GetValueRequests>, ScopedAStatus>
             deserializedResults = fromStableLargeParcelable(requests);
     if (!deserializedResults.ok()) {
@@ -215,24 +348,54 @@
         ALOGE("getValues: duplicate request ID");
         return toScopedAStatus(maybeRequestIds, StatusCode::INVALID_ARG);
     }
+
+    // A list of failed result we already know before sending to hardware.
+    std::vector<GetValueResult> failedResults;
+    // The list of requests that we would send to hardware.
+    std::vector<GetValueRequest> hardwareRequests;
+
+    for (const auto& request : getValueRequests) {
+        if (auto result = checkReadPermission(request.prop); !result.ok()) {
+            ALOGW("property does not support reading: %s", getErrorMsg(result).c_str());
+            failedResults.push_back(GetValueResult{
+                    .requestId = request.requestId,
+                    .status = getErrorCode(result),
+                    .prop = {},
+            });
+        } else {
+            hardwareRequests.push_back(request);
+        }
+    }
+
     // The set of request Ids that we would send to hardware.
-    std::unordered_set<int64_t> hardwareRequestIds(maybeRequestIds.value().begin(),
-                                                   maybeRequestIds.value().end());
+    std::unordered_set<int64_t> hardwareRequestIds;
+    for (const auto& request : hardwareRequests) {
+        hardwareRequestIds.insert(request.requestId);
+    }
 
     std::shared_ptr<GetValuesClient> client;
     {
         std::scoped_lock<std::mutex> lockGuard(mLock);
-        client = getOrCreateClient(&mGetValuesClients, callback);
+        client = getOrCreateClient(&mGetValuesClients, callback, mPendingRequestPool);
     }
     // Register the pending hardware requests and also check for duplicate request Ids.
     if (auto addRequestResult = client->addRequests(hardwareRequestIds); !addRequestResult.ok()) {
         ALOGE("getValues[%s]: failed to add pending requests, error: %s",
-              toString(hardwareRequestIds).c_str(), addRequestResult.error().message().c_str());
+              toString(hardwareRequestIds).c_str(), getErrorMsg(addRequestResult).c_str());
         return toScopedAStatus(addRequestResult);
     }
 
+    if (!failedResults.empty()) {
+        // First send the failed results we already know back to the client.
+        client->sendResults(failedResults);
+    }
+
+    if (hardwareRequests.empty()) {
+        return ScopedAStatus::ok();
+    }
+
     if (StatusCode status =
-                mVehicleHardware->getValues(client->getResultCallback(), getValueRequests);
+                mVehicleHardware->getValues(client->getResultCallback(), hardwareRequests);
         status != StatusCode::OK) {
         // If the hardware returns error, finish all the pending requests for this request because
         // we never expect hardware to call callback for these requests.
@@ -247,6 +410,8 @@
 
 ScopedAStatus DefaultVehicleHal::setValues(const CallbackType& callback,
                                            const SetValueRequests& requests) {
+    monitorBinderLifeCycle(callback);
+
     expected<LargeParcelableBase::BorrowedOwnedObject<SetValueRequests>, ScopedAStatus>
             deserializedResults = fromStableLargeParcelable(requests);
     if (!deserializedResults.ok()) {
@@ -269,9 +434,17 @@
 
     for (auto& request : setValueRequests) {
         int64_t requestId = request.requestId;
+        if (auto result = checkWritePermission(request.value); !result.ok()) {
+            ALOGW("property does not support writing: %s", getErrorMsg(result).c_str());
+            failedResults.push_back(SetValueResult{
+                    .requestId = requestId,
+                    .status = getErrorCode(result),
+            });
+            continue;
+        }
         if (auto result = checkProperty(request.value); !result.ok()) {
-            ALOGW("setValues[%" PRId64 "]: property not valid: %s", requestId,
-                  result.error().message().c_str());
+            ALOGW("setValues[%" PRId64 "]: property is not valid: %s", requestId,
+                  getErrorMsg(result).c_str());
             failedResults.push_back(SetValueResult{
                     .requestId = requestId,
                     .status = StatusCode::INVALID_ARG,
@@ -291,13 +464,13 @@
     std::shared_ptr<SetValuesClient> client;
     {
         std::scoped_lock<std::mutex> lockGuard(mLock);
-        client = getOrCreateClient(&mSetValuesClients, callback);
+        client = getOrCreateClient(&mSetValuesClients, callback, mPendingRequestPool);
     }
 
     // Register the pending hardware requests and also check for duplicate request Ids.
     if (auto addRequestResult = client->addRequests(hardwareRequestIds); !addRequestResult.ok()) {
         ALOGE("setValues[%s], failed to add pending requests, error: %s",
-              toString(hardwareRequestIds).c_str(), addRequestResult.error().message().c_str());
+              toString(hardwareRequestIds).c_str(), getErrorMsg(addRequestResult).c_str());
         return toScopedAStatus(addRequestResult, StatusCode::INVALID_ARG);
     }
 
@@ -306,6 +479,10 @@
         client->sendResults(failedResults);
     }
 
+    if (hardwareRequests.empty()) {
+        return ScopedAStatus::ok();
+    }
+
     if (StatusCode status =
                 mVehicleHardware->setValues(client->getResultCallback(), hardwareRequests);
         status != StatusCode::OK) {
@@ -360,13 +537,112 @@
     return vectorToStableLargeParcelable(std::move(configs), output);
 }
 
-ScopedAStatus DefaultVehicleHal::subscribe(const CallbackType&,
-                                           const std::vector<SubscribeOptions>&, int32_t) {
+Result<void> DefaultVehicleHal::checkSubscribeOptions(
+        const std::vector<SubscribeOptions>& options) {
+    for (const auto& option : options) {
+        int32_t propId = option.propId;
+        if (mConfigsByPropId.find(propId) == mConfigsByPropId.end()) {
+            return Error(toInt(StatusCode::INVALID_ARG))
+                   << StringPrintf("no config for property, ID: %" PRId32, propId);
+        }
+        const VehiclePropConfig& config = mConfigsByPropId[propId];
+
+        if (config.changeMode != VehiclePropertyChangeMode::ON_CHANGE &&
+            config.changeMode != VehiclePropertyChangeMode::CONTINUOUS) {
+            return Error(toInt(StatusCode::INVALID_ARG))
+                   << "only support subscribing to ON_CHANGE or CONTINUOUS property";
+        }
+
+        if (config.access != VehiclePropertyAccess::READ &&
+            config.access != VehiclePropertyAccess::READ_WRITE) {
+            return Error(toInt(StatusCode::ACCESS_DENIED))
+                   << StringPrintf("Property %" PRId32 " has no read access", propId);
+        }
+
+        if (config.changeMode == VehiclePropertyChangeMode::CONTINUOUS) {
+            float sampleRate = option.sampleRate;
+            float minSampleRate = config.minSampleRate;
+            float maxSampleRate = config.maxSampleRate;
+            if (sampleRate < minSampleRate || sampleRate > maxSampleRate) {
+                return Error(toInt(StatusCode::INVALID_ARG))
+                       << StringPrintf("sample rate: %f out of range, must be within %f and %f",
+                                       sampleRate, minSampleRate, maxSampleRate);
+            }
+            if (!SubscriptionManager::checkSampleRate(sampleRate)) {
+                return Error(toInt(StatusCode::INVALID_ARG))
+                       << "invalid sample rate: " << sampleRate;
+            }
+        }
+
+        if (isGlobalProp(propId)) {
+            continue;
+        }
+
+        // Non-global property.
+        for (int32_t areaId : option.areaIds) {
+            if (auto areaConfig = getAreaConfig(propId, areaId, config); areaConfig == nullptr) {
+                return Error(toInt(StatusCode::INVALID_ARG))
+                       << StringPrintf("invalid area ID: %" PRId32 " for prop ID: %" PRId32
+                                       ", not listed in config",
+                                       areaId, propId);
+            }
+        }
+    }
+    return {};
+}
+
+ScopedAStatus DefaultVehicleHal::subscribe(const CallbackType& callback,
+                                           const std::vector<SubscribeOptions>& options,
+                                           [[maybe_unused]] int32_t maxSharedMemoryFileCount) {
+    monitorBinderLifeCycle(callback);
+
+    // TODO(b/205189110): Use shared memory file count.
+    if (auto result = checkSubscribeOptions(options); !result.ok()) {
+        ALOGE("subscribe: invalid subscribe options: %s", getErrorMsg(result).c_str());
+        return toScopedAStatus(result);
+    }
+
+    std::vector<SubscribeOptions> onChangeSubscriptions;
+    std::vector<SubscribeOptions> continuousSubscriptions;
+    for (const auto& option : options) {
+        int32_t propId = option.propId;
+        // We have already validate config exists.
+        const VehiclePropConfig& config = mConfigsByPropId[propId];
+
+        SubscribeOptions optionCopy = option;
+        // If areaIds is empty, subscribe to all areas.
+        if (optionCopy.areaIds.empty() && !isGlobalProp(propId)) {
+            for (const auto& areaConfig : config.areaConfigs) {
+                optionCopy.areaIds.push_back(areaConfig.areaId);
+            }
+        }
+
+        if (isGlobalProp(propId)) {
+            optionCopy.areaIds = {0};
+        }
+
+        if (config.changeMode == VehiclePropertyChangeMode::CONTINUOUS) {
+            continuousSubscriptions.push_back(std::move(optionCopy));
+        } else {
+            onChangeSubscriptions.push_back(std::move(optionCopy));
+        }
+    }
+    // Since we have already check the sample rates, the following functions must succeed.
+    if (!onChangeSubscriptions.empty()) {
+        mSubscriptionManager->subscribe(callback, onChangeSubscriptions,
+                                        /*isContinuousProperty=*/false);
+    }
+    if (!continuousSubscriptions.empty()) {
+        mSubscriptionManager->subscribe(callback, continuousSubscriptions,
+                                        /*isContinuousProperty=*/true);
+    }
     return ScopedAStatus::ok();
 }
 
-ScopedAStatus DefaultVehicleHal::unsubscribe(const CallbackType&, const std::vector<int32_t>&) {
-    return ScopedAStatus::ok();
+ScopedAStatus DefaultVehicleHal::unsubscribe(const CallbackType& callback,
+                                             const std::vector<int32_t>& propIds) {
+    return toScopedAStatus(mSubscriptionManager->unsubscribe(callback->asBinder().get(), propIds),
+                           StatusCode::INVALID_ARG);
 }
 
 ScopedAStatus DefaultVehicleHal::returnSharedMemory(const CallbackType&, int64_t) {
@@ -378,6 +654,70 @@
     return mVehicleHardware.get();
 }
 
+Result<void> DefaultVehicleHal::checkWritePermission(const VehiclePropValue& value) const {
+    int32_t propId = value.prop;
+    auto result = getConfig(propId);
+    if (!result.ok()) {
+        return Error(toInt(StatusCode::INVALID_ARG)) << getErrorMsg(result);
+    }
+    const VehiclePropConfig* config = result.value();
+
+    if (config->access != VehiclePropertyAccess::WRITE &&
+        config->access != VehiclePropertyAccess::READ_WRITE) {
+        return Error(toInt(StatusCode::ACCESS_DENIED))
+               << StringPrintf("Property %" PRId32 " has no write access", propId);
+    }
+    return {};
+}
+
+Result<void> DefaultVehicleHal::checkReadPermission(const VehiclePropValue& value) const {
+    int32_t propId = value.prop;
+    auto result = getConfig(propId);
+    if (!result.ok()) {
+        return Error(toInt(StatusCode::INVALID_ARG)) << getErrorMsg(result);
+    }
+    const VehiclePropConfig* config = result.value();
+
+    if (config->access != VehiclePropertyAccess::READ &&
+        config->access != VehiclePropertyAccess::READ_WRITE) {
+        return Error(toInt(StatusCode::ACCESS_DENIED))
+               << StringPrintf("Property %" PRId32 " has no read access", propId);
+    }
+    return {};
+}
+
+void DefaultVehicleHal::checkHealth(std::weak_ptr<IVehicleHardware> hardware,
+                                    std::weak_ptr<SubscriptionManager> subscriptionManager) {
+    auto hardwarePtr = hardware.lock();
+    if (hardwarePtr == nullptr) {
+        ALOGW("the VehicleHardware is destroyed, DefaultVehicleHal is ending");
+        return;
+    }
+
+    StatusCode status = hardwarePtr->checkHealth();
+    if (status != StatusCode::OK) {
+        ALOGE("VHAL check health returns non-okay status");
+        return;
+    }
+    std::vector<VehiclePropValue> values = {{
+            .prop = toInt(VehicleProperty::VHAL_HEARTBEAT),
+            .areaId = 0,
+            .status = VehiclePropertyStatus::AVAILABLE,
+            .value.int64Values = {uptimeMillis()},
+    }};
+    onPropertyChangeEvent(subscriptionManager, values);
+    return;
+}
+
+binder_status_t DefaultVehicleHal::AIBinderLinkToDeathImpl::linkToDeath(
+        AIBinder* binder, AIBinder_DeathRecipient* recipient, void* cookie) {
+    return AIBinder_linkToDeath(binder, recipient, cookie);
+}
+
+void DefaultVehicleHal::setLinkToDeathImpl(std::unique_ptr<ILinkToDeath> impl) {
+    mLinkToDeathImpl = std::move(impl);
+}
+
 }  // namespace vehicle
 }  // namespace automotive
 }  // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp b/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp
index dc9a6ce..21bfba6 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp
@@ -26,7 +26,7 @@
 
 namespace {
 
-constexpr float ONE_SECOND_IN_NANO = 1000000000.;
+constexpr float ONE_SECOND_IN_NANO = 1'000'000'000.;
 
 }  // namespace
 
@@ -55,6 +55,8 @@
     std::scoped_lock<std::mutex> lockGuard(mLock);
 
     mClientsByPropIdArea.clear();
+    // RecurrentSubscription has reference to mGetValue, so it must be destroyed before mGetValue is
+    // destroyed.
     mSubscriptionsByClient.clear();
 }
 
@@ -80,7 +82,6 @@
     std::scoped_lock<std::mutex> lockGuard(mLock);
 
     std::vector<int64_t> intervals;
-
     for (const auto& option : options) {
         float sampleRate = option.sampleRate;
 
@@ -99,6 +100,7 @@
     }
 
     size_t intervalIndex = 0;
+    ClientIdType clientId = callback->asBinder().get();
     for (const auto& option : options) {
         int32_t propId = option.propId;
         const std::vector<int32_t>& areaIds = option.areaIds;
@@ -117,7 +119,7 @@
                         .prop = propId,
                         .areaId = areaId,
                 };
-                mSubscriptionsByClient[callback][propIdAreaId] =
+                mSubscriptionsByClient[clientId][propIdAreaId] =
                         std::make_unique<RecurrentSubscription>(
                                 mTimer,
                                 [this, callback, propValueRequest] {
@@ -125,24 +127,24 @@
                                 },
                                 interval);
             } else {
-                mSubscriptionsByClient[callback][propIdAreaId] =
+                mSubscriptionsByClient[clientId][propIdAreaId] =
                         std::make_unique<OnChangeSubscription>();
             }
-            mClientsByPropIdArea[propIdAreaId].insert(callback);
+            mClientsByPropIdArea[propIdAreaId][clientId] = callback;
         }
     }
     return {};
 }
 
-Result<void> SubscriptionManager::unsubscribe(const std::shared_ptr<IVehicleCallback>& callback,
+Result<void> SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdType clientId,
                                               const std::vector<int32_t>& propIds) {
     std::scoped_lock<std::mutex> lockGuard(mLock);
 
-    if (mSubscriptionsByClient.find(callback) == mSubscriptionsByClient.end()) {
+    if (mSubscriptionsByClient.find(clientId) == mSubscriptionsByClient.end()) {
         return Error() << "No property was subscribed for the callback";
     }
     std::unordered_set<int32_t> subscribedPropIds;
-    for (auto const& [propIdAreaId, _] : mSubscriptionsByClient[callback]) {
+    for (auto const& [propIdAreaId, _] : mSubscriptionsByClient[clientId]) {
         subscribedPropIds.insert(propIdAreaId.propId);
     }
 
@@ -152,13 +154,13 @@
         }
     }
 
-    auto& subscriptions = mSubscriptionsByClient[callback];
+    auto& subscriptions = mSubscriptionsByClient[clientId];
     auto it = subscriptions.begin();
     while (it != subscriptions.end()) {
         int32_t propId = it->first.propId;
         if (std::find(propIds.begin(), propIds.end(), propId) != propIds.end()) {
             auto& clients = mClientsByPropIdArea[it->first];
-            clients.erase(callback);
+            clients.erase(clientId);
             if (clients.empty()) {
                 mClientsByPropIdArea.erase(it->first);
             }
@@ -168,27 +170,27 @@
         }
     }
     if (subscriptions.empty()) {
-        mSubscriptionsByClient.erase(callback);
+        mSubscriptionsByClient.erase(clientId);
     }
     return {};
 }
 
-Result<void> SubscriptionManager::unsubscribe(const std::shared_ptr<IVehicleCallback>& callback) {
+Result<void> SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdType clientId) {
     std::scoped_lock<std::mutex> lockGuard(mLock);
 
-    if (mSubscriptionsByClient.find(callback) == mSubscriptionsByClient.end()) {
-        return Error() << "No property was subscribed for the callback";
+    if (mSubscriptionsByClient.find(clientId) == mSubscriptionsByClient.end()) {
+        return Error() << "No property was subscribed for this client";
     }
 
-    auto& subscriptions = mSubscriptionsByClient[callback];
+    auto& subscriptions = mSubscriptionsByClient[clientId];
     for (auto const& [propIdAreaId, _] : subscriptions) {
         auto& clients = mClientsByPropIdArea[propIdAreaId];
-        clients.erase(callback);
+        clients.erase(clientId);
         if (clients.empty()) {
             mClientsByPropIdArea.erase(propIdAreaId);
         }
     }
-    mSubscriptionsByClient.erase(callback);
+    mSubscriptionsByClient.erase(clientId);
     return {};
 }
 
@@ -206,8 +208,8 @@
         if (mClientsByPropIdArea.find(propIdAreaId) == mClientsByPropIdArea.end()) {
             continue;
         }
-        for (const auto& client : mClientsByPropIdArea[propIdAreaId]) {
-            if (!mSubscriptionsByClient[client][propIdAreaId]->isOnChange()) {
+        for (const auto& [clientId, client] : mClientsByPropIdArea[propIdAreaId]) {
+            if (!mSubscriptionsByClient[clientId][propIdAreaId]->isOnChange()) {
                 continue;
             }
             clients[client].push_back(&value);
@@ -216,6 +218,11 @@
     return clients;
 }
 
+bool SubscriptionManager::isEmpty() {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    return mSubscriptionsByClient.empty() && mClientsByPropIdArea.empty();
+}
+
 SubscriptionManager::RecurrentSubscription::RecurrentSubscription(
         std::shared_ptr<RecurrentTimer> timer, std::function<void()>&& action, int64_t interval)
     : mAction(std::make_shared<std::function<void()>>(action)), mTimer(timer) {
diff --git a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
index d763f03..ff355c3 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
@@ -17,6 +17,7 @@
 #include "ConnectedClient.h"
 #include "DefaultVehicleHal.h"
 #include "MockVehicleCallback.h"
+#include "MockVehicleHardware.h"
 
 #include <IVehicleHardware.h>
 #include <LargeParcelableBase.h>
@@ -27,6 +28,7 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <utils/Log.h>
+#include <utils/SystemClock.h>
 
 #include <chrono>
 #include <list>
@@ -55,10 +57,14 @@
 using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
 using ::aidl::android::hardware::automotive::vehicle::SetValueResults;
 using ::aidl::android::hardware::automotive::vehicle::StatusCode;
+using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
 using ::aidl::android::hardware::automotive::vehicle::VehicleAreaWindow;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfigs;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropErrors;
+using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyChangeMode;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValues;
 
@@ -67,208 +73,34 @@
 
 using ::ndk::ScopedAStatus;
 using ::ndk::ScopedFileDescriptor;
+using ::ndk::SpAIBinder;
 
 using ::testing::Eq;
+using ::testing::UnorderedElementsAre;
 using ::testing::UnorderedElementsAreArray;
 using ::testing::WhenSortedBy;
 
 constexpr int32_t INVALID_PROP_ID = 0;
 // VehiclePropertyGroup:SYSTEM,VehicleArea:WINDOW,VehiclePropertyType:INT32
 constexpr int32_t INT32_WINDOW_PROP = 10001 + 0x10000000 + 0x03000000 + 0x00400000;
-
-template <class T>
-std::optional<T> pop(std::list<T>& items) {
-    if (items.size() > 0) {
-        auto item = std::move(items.front());
-        items.pop_front();
-        return item;
-    }
-    return std::nullopt;
-}
+// VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
+constexpr int32_t GLOBAL_ON_CHANGE_PROP = 10002 + 0x10000000 + 0x01000000 + 0x00400000;
+// VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
+constexpr int32_t GLOBAL_CONTINUOUS_PROP = 10003 + 0x10000000 + 0x01000000 + 0x00400000;
+// VehiclePropertyGroup:SYSTEM,VehicleArea:WINDOW,VehiclePropertyType:INT32
+constexpr int32_t AREA_ON_CHANGE_PROP = 10004 + 0x10000000 + 0x03000000 + 0x00400000;
+// VehiclePropertyGroup:SYSTEM,VehicleArea:WINDOW,VehiclePropertyType:INT32
+constexpr int32_t AREA_CONTINUOUS_PROP = 10005 + 0x10000000 + 0x03000000 + 0x00400000;
+// VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
+constexpr int32_t READ_ONLY_PROP = 10006 + 0x10000000 + 0x01000000 + 0x00400000;
+// VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
+constexpr int32_t WRITE_ONLY_PROP = 10007 + 0x10000000 + 0x01000000 + 0x00400000;
 
 int32_t testInt32VecProp(size_t i) {
     // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32_VEC
     return static_cast<int32_t>(i) + 0x10000000 + 0x01000000 + 0x00410000;
 }
 
-class MockVehicleHardware final : public IVehicleHardware {
-  public:
-    ~MockVehicleHardware() {
-        std::scoped_lock<std::mutex> lockGuard(mLock);
-        for (auto& thread : mThreads) {
-            thread.join();
-        }
-    }
-
-    std::vector<VehiclePropConfig> getAllPropertyConfigs() const override {
-        std::scoped_lock<std::mutex> lockGuard(mLock);
-        return mPropertyConfigs;
-    }
-
-    StatusCode setValues(std::shared_ptr<const SetValuesCallback> callback,
-                         const std::vector<SetValueRequest>& requests) override {
-        std::scoped_lock<std::mutex> lockGuard(mLock);
-        return handleRequests(__func__, callback, requests, &mSetValueRequests,
-                              &mSetValueResponses);
-    }
-
-    StatusCode getValues(std::shared_ptr<const GetValuesCallback> callback,
-                         const std::vector<GetValueRequest>& requests) const override {
-        std::scoped_lock<std::mutex> lockGuard(mLock);
-        return handleRequests(__func__, callback, requests, &mGetValueRequests,
-                              &mGetValueResponses);
-    }
-
-    DumpResult dump(const std::vector<std::string>&) override {
-        // TODO(b/200737967): mock this.
-        return DumpResult{};
-    }
-
-    StatusCode checkHealth() override {
-        // TODO(b/200737967): mock this.
-        return StatusCode::OK;
-    }
-
-    void registerOnPropertyChangeEvent(std::unique_ptr<const PropertyChangeCallback>) override {
-        // TODO(b/200737967): mock this.
-    }
-
-    void registerOnPropertySetErrorEvent(std::unique_ptr<const PropertySetErrorCallback>) override {
-        // TODO(b/200737967): mock this.
-    }
-
-    // Test functions.
-    void setPropertyConfigs(const std::vector<VehiclePropConfig>& configs) {
-        std::scoped_lock<std::mutex> lockGuard(mLock);
-        mPropertyConfigs = configs;
-    }
-
-    void addGetValueResponses(const std::vector<GetValueResult>& responses) {
-        std::scoped_lock<std::mutex> lockGuard(mLock);
-        mGetValueResponses.push_back(responses);
-    }
-
-    void addSetValueResponses(const std::vector<SetValueResult>& responses) {
-        std::scoped_lock<std::mutex> lockGuard(mLock);
-        mSetValueResponses.push_back(responses);
-    }
-
-    std::vector<GetValueRequest> nextGetValueRequests() {
-        std::scoped_lock<std::mutex> lockGuard(mLock);
-        std::optional<std::vector<GetValueRequest>> request = pop(mGetValueRequests);
-        if (!request.has_value()) {
-            return std::vector<GetValueRequest>();
-        }
-        return std::move(request.value());
-    }
-
-    std::vector<SetValueRequest> nextSetValueRequests() {
-        std::scoped_lock<std::mutex> lockGuard(mLock);
-        std::optional<std::vector<SetValueRequest>> request = pop(mSetValueRequests);
-        if (!request.has_value()) {
-            return std::vector<SetValueRequest>();
-        }
-        return std::move(request.value());
-    }
-
-    void setStatus(const char* functionName, StatusCode status) {
-        std::scoped_lock<std::mutex> lockGuard(mLock);
-        mStatusByFunctions[functionName] = status;
-    }
-
-    void setSleepTime(int64_t timeInNano) {
-        std::scoped_lock<std::mutex> lockGuard(mLock);
-        mSleepTime = timeInNano;
-    }
-
-  private:
-    mutable std::mutex mLock;
-    std::vector<VehiclePropConfig> mPropertyConfigs GUARDED_BY(mLock);
-    mutable std::list<std::vector<GetValueRequest>> mGetValueRequests GUARDED_BY(mLock);
-    mutable std::list<std::vector<GetValueResult>> mGetValueResponses GUARDED_BY(mLock);
-    mutable std::list<std::vector<SetValueRequest>> mSetValueRequests GUARDED_BY(mLock);
-    mutable std::list<std::vector<SetValueResult>> mSetValueResponses GUARDED_BY(mLock);
-    std::unordered_map<const char*, StatusCode> mStatusByFunctions GUARDED_BY(mLock);
-    int64_t mSleepTime GUARDED_BY(mLock) = 0;
-    mutable std::vector<std::thread> mThreads GUARDED_BY(mLock);
-
-    template <class ResultType>
-    StatusCode returnResponse(
-            std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback,
-            std::list<std::vector<ResultType>>* storedResponses) const;
-
-    template <class RequestType, class ResultType>
-    StatusCode handleRequests(
-            const char* functionName,
-            std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback,
-            const std::vector<RequestType>& requests,
-            std::list<std::vector<RequestType>>* storedRequests,
-            std::list<std::vector<ResultType>>* storedResponses) const REQUIRES(mLock);
-};
-
-template <class ResultType>
-StatusCode MockVehicleHardware::returnResponse(
-        std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback,
-        std::list<std::vector<ResultType>>* storedResponses) const {
-    if (storedResponses->size() > 0) {
-        (*callback)(std::move(storedResponses->front()));
-        storedResponses->pop_front();
-        return StatusCode::OK;
-    } else {
-        ALOGE("no more response");
-        return StatusCode::INTERNAL_ERROR;
-    }
-}
-
-template StatusCode MockVehicleHardware::returnResponse<GetValueResult>(
-        std::shared_ptr<const std::function<void(std::vector<GetValueResult>)>> callback,
-        std::list<std::vector<GetValueResult>>* storedResponses) const;
-
-template StatusCode MockVehicleHardware::returnResponse<SetValueResult>(
-        std::shared_ptr<const std::function<void(std::vector<SetValueResult>)>> callback,
-        std::list<std::vector<SetValueResult>>* storedResponses) const;
-
-template <class RequestType, class ResultType>
-StatusCode MockVehicleHardware::handleRequests(
-        const char* functionName,
-        std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback,
-        const std::vector<RequestType>& requests,
-        std::list<std::vector<RequestType>>* storedRequests,
-        std::list<std::vector<ResultType>>* storedResponses) const {
-    storedRequests->push_back(requests);
-    if (auto it = mStatusByFunctions.find(functionName); it != mStatusByFunctions.end()) {
-        if (StatusCode status = it->second; status != StatusCode::OK) {
-            return status;
-        }
-    }
-
-    if (mSleepTime != 0) {
-        int64_t sleepTime = mSleepTime;
-        mThreads.emplace_back([this, callback, sleepTime, storedResponses]() {
-            std::this_thread::sleep_for(std::chrono::nanoseconds(sleepTime));
-            returnResponse(callback, storedResponses);
-        });
-        return StatusCode::OK;
-
-    } else {
-        return returnResponse(callback, storedResponses);
-    }
-}
-
-template StatusCode MockVehicleHardware::handleRequests<GetValueRequest, GetValueResult>(
-        const char* functionName,
-        std::shared_ptr<const std::function<void(std::vector<GetValueResult>)>> callback,
-        const std::vector<GetValueRequest>& requests,
-        std::list<std::vector<GetValueRequest>>* storedRequests,
-        std::list<std::vector<GetValueResult>>* storedResponses) const;
-
-template StatusCode MockVehicleHardware::handleRequests<SetValueRequest, SetValueResult>(
-        const char* functionName,
-        std::shared_ptr<const std::function<void(std::vector<SetValueResult>)>> callback,
-        const std::vector<SetValueRequest>& requests,
-        std::list<std::vector<SetValueRequest>>* storedRequests,
-        std::list<std::vector<SetValueResult>>* storedResponses) const;
-
 struct PropConfigCmp {
     bool operator()(const VehiclePropConfig& a, const VehiclePropConfig& b) const {
         return (a.prop < b.prop);
@@ -321,6 +153,62 @@
                                     .areaId = toInt(VehicleAreaWindow::ROW_1_RIGHT),
                             },
                     .expectedStatus = StatusCode::INVALID_ARG,
+            },
+            {
+                    .name = "no_write_permission",
+                    .request =
+                            {
+                                    .prop = READ_ONLY_PROP,
+                                    .value.int32Values = {0},
+                            },
+                    .expectedStatus = StatusCode::ACCESS_DENIED,
+            }};
+}
+
+struct SubscribeInvalidOptionsTestCase {
+    std::string name;
+    SubscribeOptions option;
+};
+
+std::vector<SubscribeInvalidOptionsTestCase> getSubscribeInvalidOptionsTestCases() {
+    return {{
+                    .name = "invalid_prop",
+                    .option =
+                            {
+                                    .propId = INVALID_PROP_ID,
+                            },
+            },
+            {
+                    .name = "invalid_area_ID",
+                    .option =
+                            {
+                                    .propId = AREA_ON_CHANGE_PROP,
+                                    .areaIds = {0},
+                            },
+            },
+            {
+                    .name = "invalid_sample_rate",
+                    .option =
+                            {
+                                    .propId = GLOBAL_CONTINUOUS_PROP,
+                                    .sampleRate = 0.0,
+                            },
+            },
+            {
+                    .name = "sample_rate_out_of_range",
+                    .option =
+                            {
+                                    .propId = GLOBAL_CONTINUOUS_PROP,
+                                    .sampleRate = 1000.0,
+                            },
+            },
+            {
+                    .name = "static_property",
+                    .option =
+                            {
+                                    // Default change mode is static.
+                                    .propId = testInt32VecProp(0),
+                            },
             }};
 }
 
@@ -334,6 +222,7 @@
         for (size_t i = 0; i < 10000; i++) {
             testConfigs.push_back(VehiclePropConfig{
                     .prop = testInt32VecProp(i),
+                    .access = VehiclePropertyAccess::READ_WRITE,
                     .areaConfigs =
                             {
                                     {
@@ -344,19 +233,104 @@
                             },
             });
         }
+        // A property with area config.
         testConfigs.push_back(
                 VehiclePropConfig{.prop = INT32_WINDOW_PROP,
+                                  .access = VehiclePropertyAccess::READ_WRITE,
                                   .areaConfigs = {{
                                           .areaId = toInt(VehicleAreaWindow::ROW_1_LEFT),
                                           .minInt32Value = 0,
                                           .maxInt32Value = 100,
                                   }}});
+        // A global on-change property.
+        testConfigs.push_back(VehiclePropConfig{
+                .prop = GLOBAL_ON_CHANGE_PROP,
+                .access = VehiclePropertyAccess::READ_WRITE,
+                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+        });
+        // A global continuous property.
+        testConfigs.push_back(VehiclePropConfig{
+                .prop = GLOBAL_CONTINUOUS_PROP,
+                .access = VehiclePropertyAccess::READ_WRITE,
+                .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
+                .minSampleRate = 0.0,
+                .maxSampleRate = 100.0,
+        });
+        // A per-area on-change property.
+        testConfigs.push_back(VehiclePropConfig{
+                .prop = AREA_ON_CHANGE_PROP,
+                .access = VehiclePropertyAccess::READ_WRITE,
+                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+                .areaConfigs =
+                        {
+                                {
+
+                                        .areaId = toInt(VehicleAreaWindow::ROW_1_LEFT),
+                                        .minInt32Value = 0,
+                                        .maxInt32Value = 100,
+                                },
+                                {
+                                        .areaId = toInt(VehicleAreaWindow::ROW_1_RIGHT),
+                                        .minInt32Value = 0,
+                                        .maxInt32Value = 100,
+                                },
+                        },
+        });
+        // A per-area continuous property.
+        testConfigs.push_back(VehiclePropConfig{
+                .prop = AREA_CONTINUOUS_PROP,
+                .access = VehiclePropertyAccess::READ_WRITE,
+                .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
+                .minSampleRate = 0.0,
+                .maxSampleRate = 1000.0,
+                .areaConfigs =
+                        {
+                                {
+
+                                        .areaId = toInt(VehicleAreaWindow::ROW_1_LEFT),
+                                        .minInt32Value = 0,
+                                        .maxInt32Value = 100,
+                                },
+                                {
+                                        .areaId = toInt(VehicleAreaWindow::ROW_1_RIGHT),
+                                        .minInt32Value = 0,
+                                        .maxInt32Value = 100,
+                                },
+                        },
+        });
+        // A read-only property.
+        testConfigs.push_back(VehiclePropConfig{
+                .prop = READ_ONLY_PROP,
+                .access = VehiclePropertyAccess::READ,
+                .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
+                .minSampleRate = 0.0,
+                .maxSampleRate = 1000.0,
+        });
+        // A write-only property.
+        testConfigs.push_back(VehiclePropConfig{
+                .prop = WRITE_ONLY_PROP,
+                .access = VehiclePropertyAccess::WRITE,
+                .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
+                .minSampleRate = 0.0,
+                .maxSampleRate = 1000.0,
+        });
+        // Register the heartbeat event property.
+        testConfigs.push_back(VehiclePropConfig{
+                .prop = toInt(VehicleProperty::VHAL_HEARTBEAT),
+                .access = VehiclePropertyAccess::READ,
+                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+        });
         hardware->setPropertyConfigs(testConfigs);
         mHardwarePtr = hardware.get();
         mVhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
         mVhalClient = IVehicle::fromBinder(mVhal->asBinder());
         mCallback = ndk::SharedRefBase::make<MockVehicleCallback>();
-        mCallbackClient = IVehicleCallback::fromBinder(mCallback->asBinder());
+        // Keep the local binder alive.
+        mBinder = mCallback->asBinder();
+        mCallbackClient = IVehicleCallback::fromBinder(mBinder);
+
+        // Set the linkToDeath to a fake implementation that always returns OK.
+        setTestLinkToDeathImpl();
     }
 
     void TearDown() override {
@@ -374,10 +348,36 @@
 
     void setTimeout(int64_t timeoutInNano) { mVhal->setTimeout(timeoutInNano); }
 
+    void setTestLinkToDeathImpl() {
+        mVhal->setLinkToDeathImpl(std::make_unique<TestLinkToDeathImpl>());
+    }
+
     size_t countPendingRequests() { return mVhal->mPendingRequestPool->countPendingRequests(); }
 
+    size_t countClients() {
+        std::scoped_lock<std::mutex> lockGuard(mVhal->mLock);
+        return mVhal->mGetValuesClients.size() + mVhal->mSetValuesClients.size() +
+               mVhal->mSubscriptionClients->countClients();
+    }
+
     std::shared_ptr<PendingRequestPool> getPool() { return mVhal->mPendingRequestPool; }
 
+    void onBinderDied(void* cookie) { return mVhal->onBinderDied(cookie); }
+
+    void onBinderUnlinked(void* cookie) { return mVhal->onBinderUnlinked(cookie); }
+
+    void* getOnBinderDiedContexts(AIBinder* clientId) {
+        std::scoped_lock<std::mutex> lockGuard(mVhal->mLock);
+        return mVhal->mOnBinderDiedContexts[clientId].get();
+    }
+
+    bool countOnBinderDiedContexts() {
+        std::scoped_lock<std::mutex> lockGuard(mVhal->mLock);
+        return mVhal->mOnBinderDiedContexts.size();
+    }
+
+    bool hasNoSubscriptions() { return mVhal->mSubscriptionManager->isEmpty(); }
+
     static Result<void> getValuesTestCases(size_t size, GetValueRequests& requests,
                                            std::vector<GetValueResult>& expectedResults,
                                            std::vector<GetValueRequest>& expectedHardwareRequests) {
@@ -448,17 +448,20 @@
         return {};
     }
 
-    size_t countClients() {
-        std::scoped_lock<std::mutex> lockGuard(mVhal->mLock);
-        return mVhal->mGetValuesClients.size() + mVhal->mSetValuesClients.size();
-    }
-
   private:
     std::shared_ptr<DefaultVehicleHal> mVhal;
     std::shared_ptr<IVehicle> mVhalClient;
     MockVehicleHardware* mHardwarePtr;
     std::shared_ptr<MockVehicleCallback> mCallback;
     std::shared_ptr<IVehicleCallback> mCallbackClient;
+    SpAIBinder mBinder;
+
+    class TestLinkToDeathImpl final : public DefaultVehicleHal::ILinkToDeath {
+      public:
+        binder_status_t linkToDeath(AIBinder*, AIBinder_DeathRecipient*, void*) override {
+            return STATUS_OK;
+        }
+    };
 };
 
 TEST_F(DefaultVehicleHalTest, testGetAllPropConfigsSmall) {
@@ -584,6 +587,39 @@
     ASSERT_EQ(status.getServiceSpecificError(), toInt(StatusCode::INVALID_ARG));
 }
 
+TEST_F(DefaultVehicleHalTest, testGetValuesNoReadPermission) {
+    GetValueRequests requests = {
+            .sharedMemoryFd = {},
+            .payloads =
+                    {
+                            {
+                                    .requestId = 0,
+                                    .prop =
+                                            {
+                                                    .prop = WRITE_ONLY_PROP,
+                                            },
+                            },
+                    },
+    };
+
+    auto status = getClient()->getValues(getCallbackClient(), requests);
+
+    ASSERT_TRUE(status.isOk()) << "getValue with no read permission should return okay with error "
+                                  "returned from callback"
+                               << ", error: " << status.getMessage();
+    EXPECT_TRUE(getHardware()->nextGetValueRequests().empty()) << "expect no request to hardware";
+
+    auto maybeResult = getCallback()->nextGetValueResults();
+    ASSERT_TRUE(maybeResult.has_value()) << "no results in callback";
+    EXPECT_EQ(maybeResult.value().payloads, std::vector<GetValueResult>({
+                                                    {
+                                                            .requestId = 0,
+                                                            .status = StatusCode::ACCESS_DENIED,
+                                                    },
+                                            }))
+            << "expect to get ACCESS_DENIED status if no read permission";
+}
+
 TEST_F(DefaultVehicleHalTest, testGetValuesFinishBeforeTimeout) {
     // timeout: 0.1s
     int64_t timeout = 100000000;
@@ -970,6 +1006,544 @@
     ASSERT_FALSE(status.isOk()) << "duplicate request properties in one request must fail";
 }
 
+TEST_F(DefaultVehicleHalTest, testSubscribeUnsubscribe) {
+    std::vector<SubscribeOptions> options = {
+            {
+                    .propId = GLOBAL_ON_CHANGE_PROP,
+            },
+    };
+
+    auto status = getClient()->subscribe(getCallbackClient(), options, 0);
+
+    ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage();
+
+    status = getClient()->unsubscribe(getCallbackClient(),
+                                      std::vector<int32_t>({GLOBAL_ON_CHANGE_PROP}));
+
+    ASSERT_TRUE(status.isOk()) << "unsubscribe failed: " << status.getMessage();
+}
+
+TEST_F(DefaultVehicleHalTest, testSubscribeGlobalOnChangeNormal) {
+    std::vector<SubscribeOptions> options = {
+            {
+                    .propId = GLOBAL_ON_CHANGE_PROP,
+            },
+    };
+
+    auto status = getClient()->subscribe(getCallbackClient(), options, 0);
+
+    ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage();
+
+    VehiclePropValue testValue{
+            .prop = GLOBAL_ON_CHANGE_PROP,
+            .value.int32Values = {0},
+    };
+    SetValueRequests setValueRequests = {
+            .payloads =
+                    {
+                            SetValueRequest{
+                                    .requestId = 0,
+                                    .value = testValue,
+                            },
+                    },
+    };
+    std::vector<SetValueResult> setValueResults = {{
+            .requestId = 0,
+            .status = StatusCode::OK,
+    }};
+
+    // Set the value to trigger a property change event.
+    getHardware()->addSetValueResponses(setValueResults);
+    status = getClient()->setValues(getCallbackClient(), setValueRequests);
+
+    ASSERT_TRUE(status.isOk()) << "setValues failed: " << status.getMessage();
+
+    auto maybeResults = getCallback()->nextOnPropertyEventResults();
+    ASSERT_TRUE(maybeResults.has_value()) << "no results in callback";
+    ASSERT_THAT(maybeResults.value().payloads, UnorderedElementsAre(testValue))
+            << "results mismatch, expect on change event for the updated value";
+    ASSERT_FALSE(getCallback()->nextOnPropertyEventResults().has_value())
+            << "more results than expected";
+    EXPECT_EQ(countClients(), static_cast<size_t>(1));
+}
+
+TEST_F(DefaultVehicleHalTest, testSubscribeGlobalOnchangeUnrelatedEventIgnored) {
+    std::vector<SubscribeOptions> options = {
+            {
+                    .propId = GLOBAL_ON_CHANGE_PROP,
+            },
+    };
+
+    auto status = getClient()->subscribe(getCallbackClient(), options, 0);
+
+    ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage();
+
+    VehiclePropValue testValue{
+            .prop = GLOBAL_CONTINUOUS_PROP,
+            .value.int32Values = {0},
+    };
+
+    // Set the value to trigger a property change event. This event should be ignored because we
+    // have not subscribed to it.
+    getHardware()->addSetValueResponses({{
+            .requestId = 0,
+            .status = StatusCode::OK,
+    }});
+    status = getClient()->setValues(getCallbackClient(),
+                                    {
+                                            .payloads =
+                                                    {
+                                                            SetValueRequest{
+                                                                    .requestId = 0,
+                                                                    .value = testValue,
+                                                            },
+                                                    },
+                                    });
+
+    ASSERT_TRUE(status.isOk()) << "setValues failed: " << status.getMessage();
+
+    ASSERT_FALSE(getCallback()->nextOnPropertyEventResults().has_value())
+            << "must receive no property update event if the property is not subscribed";
+}
+
+TEST_F(DefaultVehicleHalTest, testSubscribeAreaOnChange) {
+    int testAreaId = toInt(VehicleAreaWindow::ROW_1_LEFT);
+    std::vector<SubscribeOptions> options = {
+            {
+                    .propId = AREA_ON_CHANGE_PROP,
+                    .areaIds = {testAreaId},
+            },
+    };
+
+    auto status = getClient()->subscribe(getCallbackClient(), options, 0);
+
+    ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage();
+
+    VehiclePropValue testValue{
+            .prop = AREA_ON_CHANGE_PROP,
+            .areaId = testAreaId,
+            .value.int32Values = {0},
+    };
+
+    // Set the value to trigger a property change event.
+    getHardware()->addSetValueResponses({{
+            .requestId = 0,
+            .status = StatusCode::OK,
+    }});
+    status = getClient()->setValues(getCallbackClient(),
+                                    {
+                                            .payloads =
+                                                    {
+                                                            SetValueRequest{
+                                                                    .requestId = 0,
+                                                                    .value = testValue,
+                                                            },
+                                                    },
+                                    });
+
+    ASSERT_TRUE(status.isOk()) << "setValues failed: " << status.getMessage();
+
+    auto maybeResults = getCallback()->nextOnPropertyEventResults();
+    ASSERT_TRUE(maybeResults.has_value()) << "no results in callback";
+    ASSERT_THAT(maybeResults.value().payloads, UnorderedElementsAre(testValue))
+            << "results mismatch, expect on change event for the updated value";
+    ASSERT_FALSE(getCallback()->nextOnPropertyEventResults().has_value())
+            << "more results than expected";
+}
+
+TEST_F(DefaultVehicleHalTest, testSubscribeAreaOnChangeAllAreas) {
+    std::vector<SubscribeOptions> options = {
+            {
+                    .propId = AREA_ON_CHANGE_PROP,
+                    // No areaIds means subscribing to all area IDs.
+                    .areaIds = {},
+            },
+    };
+
+    auto status = getClient()->subscribe(getCallbackClient(), options, 0);
+
+    ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage();
+
+    VehiclePropValue testValue1{
+            .prop = AREA_ON_CHANGE_PROP,
+            .areaId = toInt(VehicleAreaWindow::ROW_1_LEFT),
+            .value.int32Values = {0},
+    };
+    VehiclePropValue testValue2{
+            .prop = AREA_ON_CHANGE_PROP,
+            .areaId = toInt(VehicleAreaWindow::ROW_1_RIGHT),
+            .value.int32Values = {0},
+    };
+
+    // Set the values to trigger property change events for two areas.
+    getHardware()->addSetValueResponses({{
+                                                 .requestId = 0,
+                                                 .status = StatusCode::OK,
+                                         },
+                                         {
+                                                 .requestId = 1,
+                                                 .status = StatusCode::OK,
+                                         }});
+    status = getClient()->setValues(getCallbackClient(),
+                                    {
+                                            .payloads =
+                                                    {
+                                                            SetValueRequest{
+                                                                    .requestId = 0,
+                                                                    .value = testValue1,
+                                                            },
+                                                            SetValueRequest{
+                                                                    .requestId = 1,
+                                                                    .value = testValue2,
+                                                            },
+                                                    },
+                                    });
+
+    ASSERT_TRUE(status.isOk()) << "setValues failed: " << status.getMessage();
+
+    auto maybeResults = getCallback()->nextOnPropertyEventResults();
+    ASSERT_TRUE(maybeResults.has_value()) << "no results in callback";
+    ASSERT_THAT(maybeResults.value().payloads, UnorderedElementsAre(testValue1, testValue2))
+            << "results mismatch, expect two on-change events for all updated areas";
+    ASSERT_FALSE(getCallback()->nextOnPropertyEventResults().has_value())
+            << "more results than expected";
+}
+
+TEST_F(DefaultVehicleHalTest, testSubscribeGlobalContinuous) {
+    VehiclePropValue testValue{
+            .prop = GLOBAL_CONTINUOUS_PROP,
+            .value.int32Values = {0},
+    };
+    // Set responses for all the hardware getValues requests.
+    getHardware()->setGetValueResponder(
+            [](std::shared_ptr<const IVehicleHardware::GetValuesCallback> callback,
+               const std::vector<GetValueRequest>& requests) {
+                std::vector<GetValueResult> results;
+                for (auto& request : requests) {
+                    VehiclePropValue prop = request.prop;
+                    prop.value.int32Values = {0};
+                    results.push_back({
+                            .requestId = request.requestId,
+                            .status = StatusCode::OK,
+                            .prop = prop,
+                    });
+                }
+                (*callback)(results);
+                return StatusCode::OK;
+            });
+
+    std::vector<SubscribeOptions> options = {
+            {
+                    .propId = GLOBAL_CONTINUOUS_PROP,
+                    .sampleRate = 20.0,
+            },
+    };
+
+    auto status = getClient()->subscribe(getCallbackClient(), options, 0);
+
+    ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage();
+
+    // Sleep for 1s, which should generate ~20 events.
+    std::this_thread::sleep_for(std::chrono::seconds(1));
+
+    // Should trigger about 20 times, check for at least 15 events to be safe.
+    for (size_t i = 0; i < 15; i++) {
+        auto maybeResults = getCallback()->nextOnPropertyEventResults();
+        ASSERT_TRUE(maybeResults.has_value()) << "no results in callback";
+        ASSERT_THAT(maybeResults.value().payloads, UnorderedElementsAre(testValue))
+                << "results mismatch, expect to get the updated value";
+    }
+    EXPECT_EQ(countClients(), static_cast<size_t>(1));
+}
+
+TEST_F(DefaultVehicleHalTest, testSubscribeAreaContinuous) {
+    // Set responses for all the hardware getValues requests.
+    getHardware()->setGetValueResponder(
+            [](std::shared_ptr<const IVehicleHardware::GetValuesCallback> callback,
+               const std::vector<GetValueRequest>& requests) {
+                std::vector<GetValueResult> results;
+                for (auto& request : requests) {
+                    VehiclePropValue prop = request.prop;
+                    prop.value.int32Values = {0};
+                    results.push_back({
+                            .requestId = request.requestId,
+                            .status = StatusCode::OK,
+                            .prop = prop,
+                    });
+                }
+                (*callback)(results);
+                return StatusCode::OK;
+            });
+
+    std::vector<SubscribeOptions> options = {
+            {
+                    .propId = AREA_CONTINUOUS_PROP,
+                    .sampleRate = 20.0,
+                    .areaIds = {toInt(VehicleAreaWindow::ROW_1_LEFT)},
+            },
+            {
+                    .propId = AREA_CONTINUOUS_PROP,
+                    .sampleRate = 10.0,
+                    .areaIds = {toInt(VehicleAreaWindow::ROW_1_RIGHT)},
+            },
+    };
+
+    auto status = getClient()->subscribe(getCallbackClient(), options, 0);
+
+    ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage();
+
+    // Sleep for 1s, which should generate ~20 events.
+    std::this_thread::sleep_for(std::chrono::seconds(1));
+
+    std::vector<VehiclePropValue> events;
+    while (true) {
+        auto maybeResults = getCallback()->nextOnPropertyEventResults();
+        if (!maybeResults.has_value()) {
+            break;
+        }
+        for (const auto& value : maybeResults.value().payloads) {
+            events.push_back(value);
+        }
+    }
+
+    size_t leftCount = 0;
+    size_t rightCount = 0;
+
+    for (const auto& event : events) {
+        ASSERT_EQ(event.prop, AREA_CONTINUOUS_PROP);
+        if (event.areaId == toInt(VehicleAreaWindow::ROW_1_LEFT)) {
+            leftCount++;
+            continue;
+        }
+        rightCount++;
+    }
+
+    // Should trigger about 20 times, check for at least 15 events to be safe.
+    ASSERT_GE(leftCount, static_cast<size_t>(15));
+    // Should trigger about 10 times, check for at least 5 events to be safe.
+    ASSERT_GE(rightCount, static_cast<size_t>(5));
+}
+
+TEST_F(DefaultVehicleHalTest, testUnsubscribeOnChange) {
+    std::vector<SubscribeOptions> options = {
+            {
+                    .propId = GLOBAL_ON_CHANGE_PROP,
+            },
+    };
+
+    auto status = getClient()->subscribe(getCallbackClient(), options, 0);
+
+    ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage();
+
+    status = getClient()->unsubscribe(getCallbackClient(),
+                                      std::vector<int32_t>({GLOBAL_ON_CHANGE_PROP}));
+
+    ASSERT_TRUE(status.isOk()) << "unsubscribe failed: " << status.getMessage();
+
+    VehiclePropValue testValue{
+            .prop = GLOBAL_ON_CHANGE_PROP,
+            .value.int32Values = {0},
+    };
+
+    // Set the value to trigger a property change event.
+    getHardware()->addSetValueResponses({{
+            .requestId = 0,
+            .status = StatusCode::OK,
+    }});
+    status = getClient()->setValues(getCallbackClient(),
+                                    {
+                                            .payloads =
+                                                    {
+                                                            SetValueRequest{
+                                                                    .requestId = 0,
+                                                                    .value = testValue,
+                                                            },
+                                                    },
+                                    });
+
+    ASSERT_TRUE(status.isOk()) << "setValues failed: " << status.getMessage();
+
+    ASSERT_FALSE(getCallback()->nextOnPropertyEventResults().has_value())
+            << "No property event should be generated after unsubscription";
+}
+
+TEST_F(DefaultVehicleHalTest, testUnsubscribeContinuous) {
+    VehiclePropValue testValue{
+            .prop = GLOBAL_CONTINUOUS_PROP,
+            .value.int32Values = {0},
+    };
+    // Set responses for all the hardware getValues requests.
+    getHardware()->setGetValueResponder(
+            [](std::shared_ptr<const IVehicleHardware::GetValuesCallback> callback,
+               const std::vector<GetValueRequest>& requests) {
+                std::vector<GetValueResult> results;
+                for (auto& request : requests) {
+                    VehiclePropValue prop = request.prop;
+                    prop.value.int32Values = {0};
+                    results.push_back({
+                            .requestId = request.requestId,
+                            .status = StatusCode::OK,
+                            .prop = prop,
+                    });
+                }
+                (*callback)(results);
+                return StatusCode::OK;
+            });
+
+    std::vector<SubscribeOptions> options = {
+            {
+                    .propId = GLOBAL_CONTINUOUS_PROP,
+                    .sampleRate = 20.0,
+            },
+    };
+
+    auto status = getClient()->subscribe(getCallbackClient(), options, 0);
+
+    ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage();
+
+    status = getClient()->unsubscribe(getCallbackClient(),
+                                      std::vector<int32_t>({GLOBAL_CONTINUOUS_PROP}));
+
+    ASSERT_TRUE(status.isOk()) << "unsubscribe failed: " << status.getMessage();
+
+    // Clear existing events.
+    while (getCallback()->nextOnPropertyEventResults().has_value()) {
+        // Do nothing.
+    }
+
+    // Wait for a while, make sure no new events are generated.
+    std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+    ASSERT_FALSE(getCallback()->nextOnPropertyEventResults().has_value())
+            << "No property event should be generated after unsubscription";
+}
+
+class SubscribeInvalidOptionsTest
+    : public DefaultVehicleHalTest,
+      public testing::WithParamInterface<SubscribeInvalidOptionsTestCase> {};
+
+INSTANTIATE_TEST_SUITE_P(
+        SubscribeInvalidOptionsTests, SubscribeInvalidOptionsTest,
+        ::testing::ValuesIn(getSubscribeInvalidOptionsTestCases()),
+        [](const testing::TestParamInfo<SubscribeInvalidOptionsTest::ParamType>& info) {
+            return info.param.name;
+        });
+
+TEST_P(SubscribeInvalidOptionsTest, testSubscribeInvalidOptions) {
+    std::vector<SubscribeOptions> options = {GetParam().option};
+
+    auto status = getClient()->subscribe(getCallbackClient(), options, 0);
+
+    ASSERT_FALSE(status.isOk()) << "invalid subscribe options must fail";
+    ASSERT_EQ(status.getServiceSpecificError(), toInt(StatusCode::INVALID_ARG));
+}
+
+TEST_F(DefaultVehicleHalTest, testSubscribeNoReadPermission) {
+    std::vector<SubscribeOptions> options = {{
+            .propId = WRITE_ONLY_PROP,
+    }};
+
+    auto status = getClient()->subscribe(getCallbackClient(), options, 0);
+
+    ASSERT_FALSE(status.isOk()) << "subscribe to a write-only property must fail";
+    ASSERT_EQ(status.getServiceSpecificError(), toInt(StatusCode::ACCESS_DENIED));
+}
+
+TEST_F(DefaultVehicleHalTest, testUnsubscribeFailure) {
+    auto status = getClient()->unsubscribe(getCallbackClient(),
+                                           std::vector<int32_t>({GLOBAL_ON_CHANGE_PROP}));
+
+    ASSERT_FALSE(status.isOk()) << "unsubscribe to a not-subscribed property must fail";
+    ASSERT_EQ(status.getServiceSpecificError(), toInt(StatusCode::INVALID_ARG));
+}
+
+TEST_F(DefaultVehicleHalTest, testHeartbeatEvent) {
+    std::vector<SubscribeOptions> options = {{
+            .propId = toInt(VehicleProperty::VHAL_HEARTBEAT),
+    }};
+    int64_t currentTime = uptimeMillis();
+    auto status = getClient()->subscribe(getCallbackClient(), options, 0);
+
+    ASSERT_TRUE(status.isOk()) << "unable to subscribe to heartbeat event: " << status.getMessage();
+
+    // We send out a heartbeat event every 3s, so sleep for 3s.
+    std::this_thread::sleep_for(std::chrono::seconds(3));
+
+    auto maybeResults = getCallback()->nextOnPropertyEventResults();
+    ASSERT_TRUE(maybeResults.has_value()) << "no results in callback";
+    ASSERT_EQ(maybeResults.value().payloads.size(), static_cast<size_t>(1));
+    VehiclePropValue gotValue = maybeResults.value().payloads[0];
+    ASSERT_EQ(gotValue.prop, toInt(VehicleProperty::VHAL_HEARTBEAT));
+    ASSERT_EQ(gotValue.value.int64Values.size(), static_cast<size_t>(1));
+    ASSERT_GE(gotValue.value.int64Values[0], currentTime)
+            << "expect to get the latest timestamp with the heartbeat event";
+}
+
+TEST_F(DefaultVehicleHalTest, testOnBinderDiedUnlinked) {
+    // First subscribe to a continuous property so that we register a death recipient for our
+    // client.
+    VehiclePropValue testValue{
+            .prop = GLOBAL_CONTINUOUS_PROP,
+            .value.int32Values = {0},
+    };
+    // Set responses for all the hardware getValues requests.
+    getHardware()->setGetValueResponder(
+            [](std::shared_ptr<const IVehicleHardware::GetValuesCallback> callback,
+               const std::vector<GetValueRequest>& requests) {
+                std::vector<GetValueResult> results;
+                for (auto& request : requests) {
+                    VehiclePropValue prop = request.prop;
+                    prop.value.int32Values = {0};
+                    results.push_back({
+                            .requestId = request.requestId,
+                            .status = StatusCode::OK,
+                            .prop = prop,
+                    });
+                }
+                (*callback)(results);
+                return StatusCode::OK;
+            });
+    std::vector<SubscribeOptions> options = {
+            {
+                    .propId = GLOBAL_CONTINUOUS_PROP,
+                    .sampleRate = 20.0,
+            },
+    };
+    auto status = getClient()->subscribe(getCallbackClient(), options, 0);
+    ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage();
+    // Sleep for 100ms so that the subscriptionClient gets created because we would at least try to
+    // get value once.
+    std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+    // Issue another getValue request on the same client.
+    GetValueRequests requests;
+    std::vector<GetValueResult> expectedResults;
+    std::vector<GetValueRequest> expectedHardwareRequests;
+    ASSERT_TRUE(getValuesTestCases(1, requests, expectedResults, expectedHardwareRequests).ok());
+    getHardware()->addGetValueResponses(expectedResults);
+    status = getClient()->getValues(getCallbackClient(), requests);
+    ASSERT_TRUE(status.isOk()) << "getValues failed: " << status.getMessage();
+
+    ASSERT_EQ(countOnBinderDiedContexts(), static_cast<size_t>(1))
+            << "expect one OnBinderDied context when one client is registered";
+
+    // Get the death recipient cookie for our callback that would be used in onBinderDied and
+    // onBinderUnlinked.
+    AIBinder* clientId = getCallbackClient()->asBinder().get();
+    void* context = getOnBinderDiedContexts(clientId);
+
+    onBinderDied(context);
+
+    ASSERT_EQ(countClients(), static_cast<size_t>(0))
+            << "expect all clients to be removed when binder died";
+    ASSERT_TRUE(hasNoSubscriptions()) << "expect no subscriptions when binder died";
+
+    onBinderUnlinked(context);
+
+    ASSERT_EQ(countOnBinderDiedContexts(), static_cast<size_t>(0))
+            << "expect OnBinderDied context to be deleted when binder is unlinked";
+}
+
 }  // namespace vehicle
 }  // namespace automotive
 }  // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.cpp b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.cpp
index ca366cd..5e3e03c 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.cpp
@@ -31,16 +31,6 @@
 using ::ndk::ScopedFileDescriptor;
 
 template <class T>
-std::optional<T> pop(std::list<T>& items) {
-    if (items.size() > 0) {
-        auto item = std::move(items.front());
-        items.pop_front();
-        return item;
-    }
-    return std::nullopt;
-}
-
-template <class T>
 static ScopedAStatus storeResults(const T& results, std::list<T>* storedResults) {
     T resultsCopy{
             .payloads = results.payloads,
@@ -65,8 +55,12 @@
     return storeResults(results, &mSetValueResults);
 }
 
-ScopedAStatus MockVehicleCallback::onPropertyEvent(const VehiclePropValues&, int32_t) {
-    return ScopedAStatus::ok();
+ScopedAStatus MockVehicleCallback::onPropertyEvent(const VehiclePropValues& results,
+                                                   int32_t sharedMemoryFileCount) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+
+    mSharedMemoryFileCount = sharedMemoryFileCount;
+    return storeResults(results, &mOnPropertyEventResults);
 }
 
 ScopedAStatus MockVehicleCallback::onPropertySetError(const VehiclePropErrors&) {
@@ -83,6 +77,11 @@
     return pop(mSetValueResults);
 }
 
+std::optional<VehiclePropValues> MockVehicleCallback::nextOnPropertyEventResults() {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    return pop(mOnPropertyEventResults);
+}
+
 }  // namespace vehicle
 }  // namespace automotive
 }  // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.h b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.h
index 916575a..c83164f 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.h
+++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.h
@@ -31,6 +31,16 @@
 namespace automotive {
 namespace vehicle {
 
+template <class T>
+std::optional<T> pop(std::list<T>& items) {
+    if (items.size() > 0) {
+        auto item = std::move(items.front());
+        items.pop_front();
+        return item;
+    }
+    return std::nullopt;
+}
+
 // MockVehicleCallback is a mock VehicleCallback implementation that simply stores the results.
 class MockVehicleCallback final
     : public ::aidl::android::hardware::automotive::vehicle::BnVehicleCallback {
@@ -52,6 +62,8 @@
     nextGetValueResults();
     std::optional<::aidl::android::hardware::automotive::vehicle::SetValueResults>
     nextSetValueResults();
+    std::optional<::aidl::android::hardware::automotive::vehicle::VehiclePropValues>
+    nextOnPropertyEventResults();
 
   private:
     std::mutex mLock;
@@ -59,6 +71,9 @@
             GUARDED_BY(mLock);
     std::list<::aidl::android::hardware::automotive::vehicle::SetValueResults> mSetValueResults
             GUARDED_BY(mLock);
+    std::list<::aidl::android::hardware::automotive::vehicle::VehiclePropValues>
+            mOnPropertyEventResults GUARDED_BY(mLock);
+    int32_t mSharedMemoryFileCount GUARDED_BY(mLock);
 };
 
 }  // namespace vehicle
diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp
new file mode 100644
index 0000000..eec32dd
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MockVehicleHardware.h"
+#include "MockVehicleCallback.h"
+
+#include <utils/Log.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+using ::aidl::android::hardware::automotive::vehicle::GetValueRequest;
+using ::aidl::android::hardware::automotive::vehicle::GetValueResult;
+using ::aidl::android::hardware::automotive::vehicle::SetValueRequest;
+using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
+using ::aidl::android::hardware::automotive::vehicle::StatusCode;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
+
+MockVehicleHardware::~MockVehicleHardware() {
+    std::unique_lock<std::mutex> lk(mLock);
+    mCv.wait(lk, [this] { return mThreadCount == 0; });
+}
+
+std::vector<VehiclePropConfig> MockVehicleHardware::getAllPropertyConfigs() const {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    return mPropertyConfigs;
+}
+
+StatusCode MockVehicleHardware::setValues(std::shared_ptr<const SetValuesCallback> callback,
+                                          const std::vector<SetValueRequest>& requests) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    if (StatusCode status = handleRequestsLocked(__func__, callback, requests, &mSetValueRequests,
+                                                 &mSetValueResponses);
+        status != StatusCode::OK) {
+        return status;
+    }
+    if (mPropertyChangeCallback == nullptr) {
+        return StatusCode::OK;
+    }
+    std::vector<VehiclePropValue> values;
+    for (auto& request : requests) {
+        values.push_back(request.value);
+    }
+    (*mPropertyChangeCallback)(values);
+    return StatusCode::OK;
+}
+
+StatusCode MockVehicleHardware::getValues(std::shared_ptr<const GetValuesCallback> callback,
+                                          const std::vector<GetValueRequest>& requests) const {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    if (mGetValueResponder != nullptr) {
+        return mGetValueResponder(callback, requests);
+    }
+    return handleRequestsLocked(__func__, callback, requests, &mGetValueRequests,
+                                &mGetValueResponses);
+}
+
+DumpResult MockVehicleHardware::dump(const std::vector<std::string>&) {
+    // TODO(b/200737967): mock this.
+    return DumpResult{};
+}
+
+StatusCode MockVehicleHardware::checkHealth() {
+    // TODO(b/200737967): mock this.
+    return StatusCode::OK;
+}
+
+void MockVehicleHardware::registerOnPropertyChangeEvent(
+        std::unique_ptr<const PropertyChangeCallback> callback) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    mPropertyChangeCallback = std::move(callback);
+}
+
+void MockVehicleHardware::registerOnPropertySetErrorEvent(
+        std::unique_ptr<const PropertySetErrorCallback>) {
+    // TODO(b/200737967): mock this.
+}
+
+void MockVehicleHardware::setPropertyConfigs(const std::vector<VehiclePropConfig>& configs) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    mPropertyConfigs = configs;
+}
+
+void MockVehicleHardware::addGetValueResponses(const std::vector<GetValueResult>& responses) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    mGetValueResponses.push_back(responses);
+}
+
+void MockVehicleHardware::addSetValueResponses(const std::vector<SetValueResult>& responses) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    mSetValueResponses.push_back(responses);
+}
+
+void MockVehicleHardware::setGetValueResponder(
+        std::function<StatusCode(std::shared_ptr<const GetValuesCallback>,
+                                 const std::vector<GetValueRequest>&)>&& responder) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    mGetValueResponder = responder;
+}
+
+std::vector<GetValueRequest> MockVehicleHardware::nextGetValueRequests() {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    std::optional<std::vector<GetValueRequest>> request = pop(mGetValueRequests);
+    if (!request.has_value()) {
+        return std::vector<GetValueRequest>();
+    }
+    return std::move(request.value());
+}
+
+std::vector<SetValueRequest> MockVehicleHardware::nextSetValueRequests() {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    std::optional<std::vector<SetValueRequest>> request = pop(mSetValueRequests);
+    if (!request.has_value()) {
+        return std::vector<SetValueRequest>();
+    }
+    return std::move(request.value());
+}
+
+void MockVehicleHardware::setStatus(const char* functionName, StatusCode status) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    mStatusByFunctions[functionName] = status;
+}
+
+void MockVehicleHardware::setSleepTime(int64_t timeInNano) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    mSleepTime = timeInNano;
+}
+
+template <class ResultType>
+StatusCode MockVehicleHardware::returnResponse(
+        std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback,
+        std::list<std::vector<ResultType>>* storedResponses) const {
+    if (storedResponses->size() > 0) {
+        (*callback)(std::move(storedResponses->front()));
+        storedResponses->pop_front();
+        return StatusCode::OK;
+    } else {
+        ALOGE("no more response");
+        return StatusCode::INTERNAL_ERROR;
+    }
+}
+
+template StatusCode MockVehicleHardware::returnResponse<GetValueResult>(
+        std::shared_ptr<const std::function<void(std::vector<GetValueResult>)>> callback,
+        std::list<std::vector<GetValueResult>>* storedResponses) const;
+
+template StatusCode MockVehicleHardware::returnResponse<SetValueResult>(
+        std::shared_ptr<const std::function<void(std::vector<SetValueResult>)>> callback,
+        std::list<std::vector<SetValueResult>>* storedResponses) const;
+
+template <class RequestType, class ResultType>
+StatusCode MockVehicleHardware::handleRequestsLocked(
+        const char* functionName,
+        std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback,
+        const std::vector<RequestType>& requests,
+        std::list<std::vector<RequestType>>* storedRequests,
+        std::list<std::vector<ResultType>>* storedResponses) const {
+    storedRequests->push_back(requests);
+    if (auto it = mStatusByFunctions.find(functionName); it != mStatusByFunctions.end()) {
+        if (StatusCode status = it->second; status != StatusCode::OK) {
+            return status;
+        }
+    }
+
+    if (mSleepTime != 0) {
+        int64_t sleepTime = mSleepTime;
+        mThreadCount++;
+        std::thread t([this, callback, sleepTime, storedResponses]() {
+            std::this_thread::sleep_for(std::chrono::nanoseconds(sleepTime));
+            returnResponse(callback, storedResponses);
+            mThreadCount--;
+            mCv.notify_one();
+        });
+        // Detach the thread here so we do not have to maintain the thread object. mThreadCount
+        // and mCv make sure we wait for all threads to end before we exit.
+        t.detach();
+        return StatusCode::OK;
+
+    } else {
+        return returnResponse(callback, storedResponses);
+    }
+}
+
+template StatusCode MockVehicleHardware::handleRequestsLocked<GetValueRequest, GetValueResult>(
+        const char* functionName,
+        std::shared_ptr<const std::function<void(std::vector<GetValueResult>)>> callback,
+        const std::vector<GetValueRequest>& requests,
+        std::list<std::vector<GetValueRequest>>* storedRequests,
+        std::list<std::vector<GetValueResult>>* storedResponses) const;
+
+template StatusCode MockVehicleHardware::handleRequestsLocked<SetValueRequest, SetValueResult>(
+        const char* functionName,
+        std::shared_ptr<const std::function<void(std::vector<SetValueResult>)>> callback,
+        const std::vector<SetValueRequest>& requests,
+        std::list<std::vector<SetValueRequest>>* storedRequests,
+        std::list<std::vector<SetValueResult>>* storedResponses) const;
+
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h
new file mode 100644
index 0000000..0844de1
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef android_hardware_automotive_vehicle_aidl_impl_vhal_test_MockVehicleHardware_H_
+#define android_hardware_automotive_vehicle_aidl_impl_vhal_test_MockVehicleHardware_H_
+
+#include <IVehicleHardware.h>
+#include <VehicleHalTypes.h>
+
+#include <android-base/thread_annotations.h>
+
+#include <atomic>
+#include <condition_variable>
+#include <list>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+class MockVehicleHardware final : public IVehicleHardware {
+  public:
+    ~MockVehicleHardware();
+
+    std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropConfig>
+    getAllPropertyConfigs() const override;
+    ::aidl::android::hardware::automotive::vehicle::StatusCode setValues(
+            std::shared_ptr<const SetValuesCallback> callback,
+            const std::vector<::aidl::android::hardware::automotive::vehicle::SetValueRequest>&
+                    requests) override;
+    ::aidl::android::hardware::automotive::vehicle::StatusCode getValues(
+            std::shared_ptr<const GetValuesCallback> callback,
+            const std::vector<::aidl::android::hardware::automotive::vehicle::GetValueRequest>&
+                    requests) const override;
+    DumpResult dump(const std::vector<std::string>&) override;
+    ::aidl::android::hardware::automotive::vehicle::StatusCode checkHealth() override;
+    void registerOnPropertyChangeEvent(
+            std::unique_ptr<const PropertyChangeCallback> callback) override;
+    void registerOnPropertySetErrorEvent(std::unique_ptr<const PropertySetErrorCallback>) override;
+
+    // Test functions.
+    void setPropertyConfigs(
+            const std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropConfig>&
+                    configs);
+    void addGetValueResponses(
+            const std::vector<::aidl::android::hardware::automotive::vehicle::GetValueResult>&
+                    responses);
+    void addSetValueResponses(
+            const std::vector<::aidl::android::hardware::automotive::vehicle::SetValueResult>&
+                    responses);
+    void setGetValueResponder(
+            std::function<::aidl::android::hardware::automotive::vehicle::StatusCode(
+                    std::shared_ptr<const GetValuesCallback>,
+                    const std::vector<
+                            ::aidl::android::hardware::automotive::vehicle::GetValueRequest>&)>&&
+                    responder);
+    std::vector<::aidl::android::hardware::automotive::vehicle::GetValueRequest>
+    nextGetValueRequests();
+    std::vector<::aidl::android::hardware::automotive::vehicle::SetValueRequest>
+    nextSetValueRequests();
+    void setStatus(const char* functionName,
+                   ::aidl::android::hardware::automotive::vehicle::StatusCode status);
+    void setSleepTime(int64_t timeInNano);
+
+  private:
+    mutable std::mutex mLock;
+    mutable std::condition_variable mCv;
+    mutable std::atomic<int> mThreadCount;
+    std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropConfig> mPropertyConfigs
+            GUARDED_BY(mLock);
+    mutable std::list<std::vector<::aidl::android::hardware::automotive::vehicle::GetValueRequest>>
+            mGetValueRequests GUARDED_BY(mLock);
+    mutable std::list<std::vector<::aidl::android::hardware::automotive::vehicle::GetValueResult>>
+            mGetValueResponses GUARDED_BY(mLock);
+    mutable std::list<std::vector<::aidl::android::hardware::automotive::vehicle::SetValueRequest>>
+            mSetValueRequests GUARDED_BY(mLock);
+    mutable std::list<std::vector<::aidl::android::hardware::automotive::vehicle::SetValueResult>>
+            mSetValueResponses GUARDED_BY(mLock);
+    std::unordered_map<const char*, ::aidl::android::hardware::automotive::vehicle::StatusCode>
+            mStatusByFunctions GUARDED_BY(mLock);
+    int64_t mSleepTime GUARDED_BY(mLock) = 0;
+    std::unique_ptr<const PropertyChangeCallback> mPropertyChangeCallback GUARDED_BY(mLock);
+    std::function<::aidl::android::hardware::automotive::vehicle::StatusCode(
+            std::shared_ptr<const GetValuesCallback>,
+            const std::vector<::aidl::android::hardware::automotive::vehicle::GetValueRequest>&)>
+            mGetValueResponder GUARDED_BY(mLock);
+
+    template <class ResultType>
+    ::aidl::android::hardware::automotive::vehicle::StatusCode returnResponse(
+            std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback,
+            std::list<std::vector<ResultType>>* storedResponses) const;
+    template <class RequestType, class ResultType>
+    ::aidl::android::hardware::automotive::vehicle::StatusCode handleRequestsLocked(
+            const char* functionName,
+            std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback,
+            const std::vector<RequestType>& requests,
+            std::list<std::vector<RequestType>>* storedRequests,
+            std::list<std::vector<ResultType>>* storedResponses) const REQUIRES(mLock);
+};
+
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_automotive_vehicle_aidl_impl_vhal_test_MockVehicleHardware_H_
diff --git a/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp
index fa08d6c..f81b1a2 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp
@@ -45,6 +45,7 @@
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValues;
 using ::ndk::ScopedAStatus;
+using ::ndk::SpAIBinder;
 using ::testing::ElementsAre;
 using ::testing::WhenSorted;
 
@@ -95,7 +96,9 @@
                             0);
                 });
         mCallback = ::ndk::SharedRefBase::make<PropertyCallback>();
-        mCallbackClient = IVehicleCallback::fromBinder(mCallback->asBinder());
+        // Keep the local binder alive.
+        mBinder = mCallback->asBinder();
+        mCallbackClient = IVehicleCallback::fromBinder(mBinder);
     }
 
     SubscriptionManager* getManager() { return mManager.get(); }
@@ -112,6 +115,7 @@
     std::unique_ptr<SubscriptionManager> mManager;
     std::shared_ptr<PropertyCallback> mCallback;
     std::shared_ptr<IVehicleCallback> mCallbackClient;
+    SpAIBinder mBinder;
 };
 
 TEST_F(SubscriptionManagerTest, testSubscribeGlobalContinuous) {
@@ -185,7 +189,7 @@
 
     // Theoretically trigger 10 times, but check for at least 9 times to be stable.
     EXPECT_GE(getEvents().size(), static_cast<size_t>(9));
-    EXPECT_LE(getEvents().size(), static_cast<size_t>(11));
+    EXPECT_LE(getEvents().size(), static_cast<size_t>(15));
 }
 
 TEST_F(SubscriptionManagerTest, testSubscribeMultipleAreasContinuous) {
@@ -229,7 +233,7 @@
     auto result = getManager()->subscribe(getCallbackClient(), options, true);
     ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
 
-    result = getManager()->unsubscribe(getCallbackClient());
+    result = getManager()->unsubscribe(getCallbackClient()->asBinder().get());
     ASSERT_TRUE(result.ok()) << "failed to unsubscribe: " << result.error().message();
 
     clearEvents();
@@ -257,7 +261,8 @@
     auto result = getManager()->subscribe(getCallbackClient(), options, true);
     ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
 
-    result = getManager()->unsubscribe(getCallbackClient(), std::vector<int32_t>({0}));
+    result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(),
+                                       std::vector<int32_t>({0}));
     ASSERT_TRUE(result.ok()) << "failed to unsubscribe: " << result.error().message();
 
     clearEvents();
@@ -289,7 +294,7 @@
     auto result = getManager()->subscribe(getCallbackClient(), options, true);
     ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
 
-    result = getManager()->unsubscribe(getCallbackClient());
+    result = getManager()->unsubscribe(getCallbackClient()->asBinder().get());
     ASSERT_TRUE(result.ok()) << "failed to unsubscribe: " << result.error().message();
 
     clearEvents();
@@ -315,12 +320,14 @@
     ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
 
     // Property ID: 2 was not subscribed.
-    result = getManager()->unsubscribe(getCallbackClient(), std::vector<int32_t>({0, 1, 2}));
+    result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(),
+                                       std::vector<int32_t>({0, 1, 2}));
     ASSERT_FALSE(result.ok()) << "unsubscribe an unsubscribed property must fail";
 
     // Since property 0 and property 1 was not unsubscribed successfully, we should be able to
     // unsubscribe them again.
-    result = getManager()->unsubscribe(getCallbackClient(), std::vector<int32_t>({0, 1}));
+    result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(),
+                                       std::vector<int32_t>({0, 1}));
     ASSERT_TRUE(result.ok()) << "a failed unsubscription must not unsubscribe any properties"
                              << result.error().message();
 }
@@ -343,10 +350,10 @@
             },
     };
 
-    std::shared_ptr<IVehicleCallback> client1 = IVehicleCallback::fromBinder(
-            ::ndk::SharedRefBase::make<PropertyCallback>()->asBinder());
-    std::shared_ptr<IVehicleCallback> client2 = IVehicleCallback::fromBinder(
-            ::ndk::SharedRefBase::make<PropertyCallback>()->asBinder());
+    SpAIBinder binder1 = ::ndk::SharedRefBase::make<PropertyCallback>()->asBinder();
+    std::shared_ptr<IVehicleCallback> client1 = IVehicleCallback::fromBinder(binder1);
+    SpAIBinder binder2 = ::ndk::SharedRefBase::make<PropertyCallback>()->asBinder();
+    std::shared_ptr<IVehicleCallback> client2 = IVehicleCallback::fromBinder(binder2);
     auto result = getManager()->subscribe(client1, options1, false);
     ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
     result = getManager()->subscribe(client2, options2, false);
@@ -447,7 +454,8 @@
     auto result = getManager()->subscribe(getCallbackClient(), options, false);
     ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
 
-    result = getManager()->unsubscribe(getCallbackClient(), std::vector<int32_t>({0}));
+    result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(),
+                                       std::vector<int32_t>({0}));
     ASSERT_TRUE(result.ok()) << "failed to unsubscribe: " << result.error().message();
 
     std::vector<VehiclePropValue> updatedValues = {
diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecType.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecType.aidl
index 44b434b..3a5f951 100644
--- a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecType.aidl
+++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecType.aidl
@@ -41,4 +41,5 @@
   APTX_HD = 4,
   LDAC = 5,
   LC3 = 6,
+  VENDOR = 7,
 }
diff --git a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecType.aidl b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecType.aidl
index 68c60f5..9c33081 100644
--- a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecType.aidl
+++ b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecType.aidl
@@ -26,4 +26,5 @@
     APTX_HD,
     LDAC,
     LC3,
+    VENDOR,
 }
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp b/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp
index 92cd0f5..380732f 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp
+++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp
@@ -355,6 +355,7 @@
                 kDefaultOffloadLc3Capability);
         break;
       case CodecType::UNKNOWN:
+      case CodecType::VENDOR:
         codec_capability = {};
         break;
     }
@@ -420,6 +421,7 @@
       }
       break;
     case CodecType::UNKNOWN:
+    case CodecType::VENDOR:
       break;
   }
   return false;
diff --git a/common/support/Android.bp b/common/support/Android.bp
index 718901e..12ab1f7 100644
--- a/common/support/Android.bp
+++ b/common/support/Android.bp
@@ -25,6 +25,7 @@
     apex_available: [
         "//apex_available:platform",
         "com.android.neuralnetworks",
+        "com.android.media.swcodec",
     ],
     min_sdk_version: "29",
 }
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 594f07b..1731c9c 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -292,6 +292,14 @@
             <instance>default</instance>
         </interface>
     </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.graphics.allocator</name>
+        <version>1</version>
+        <interface>
+            <name>IAllocator</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
     <hal format="hidl" optional="false">
         <name>android.hardware.graphics.composer</name>
         <version>2.1-4</version>
diff --git a/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/Setting.aidl b/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/Setting.aidl
index 41bc9ae..d998478 100644
--- a/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/Setting.aidl
+++ b/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/Setting.aidl
@@ -39,4 +39,6 @@
   WIFI_SCANNING = 3,
   AIRPLANE_MODE = 4,
   MICROPHONE = 5,
+  BT_MAIN = 6,
+  BT_SCANNING = 7,
 }
diff --git a/contexthub/aidl/android/hardware/contexthub/Setting.aidl b/contexthub/aidl/android/hardware/contexthub/Setting.aidl
index f2e55db..91d4c3f 100644
--- a/contexthub/aidl/android/hardware/contexthub/Setting.aidl
+++ b/contexthub/aidl/android/hardware/contexthub/Setting.aidl
@@ -39,4 +39,12 @@
      * by CHRE.
      */
     MICROPHONE,
+    /**
+     * The main BT toggle in the Android settings for BT connectivity.
+     */
+    BT_MAIN,
+    /**
+     * The "BT scanning" setting for location scans.
+     */
+    BT_SCANNING,
 }
diff --git a/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp b/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp
index a47f64e..f0583be 100644
--- a/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp
+++ b/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp
@@ -300,6 +300,14 @@
     testSettingChanged(Setting::MICROPHONE);
 }
 
+TEST_P(ContextHubAidl, TestOnBtMainSettingChanged) {
+    testSettingChanged(Setting::BT_MAIN);
+}
+
+TEST_P(ContextHubAidl, TestOnBtScanningSettingChanged) {
+    testSettingChanged(Setting::BT_SCANNING);
+}
+
 std::vector<std::tuple<std::string, int32_t>> generateContextHubMapping() {
     std::vector<std::tuple<std::string, int32_t>> tuples;
     auto contextHubAidlNames = android::getAidlHalInstanceNames(IContextHub::descriptor);
diff --git a/graphics/allocator/aidl/Android.bp b/graphics/allocator/aidl/Android.bp
new file mode 100644
index 0000000..ea8a599
--- /dev/null
+++ b/graphics/allocator/aidl/Android.bp
@@ -0,0 +1,40 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+    name: "android.hardware.graphics.allocator",
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+        support_system_process: true,
+    },
+    srcs: ["android/hardware/graphics/allocator/*.aidl"],
+    imports: [
+        "android.hardware.common-V2",
+    ],
+    stability: "vintf",
+    backend: {
+        cpp: {
+            enabled: false,
+        },
+        java: {
+            enabled: false,
+        },
+        ndk: {
+            apex_available: [
+                "//apex_available:platform",
+                "com.android.media.swcodec",
+            ],
+            vndk: {
+                enabled: true,
+            },
+            min_sdk_version: "29",
+        },
+    },
+}
diff --git a/graphics/allocator/aidl/aidl_api/android.hardware.graphics.allocator/current/android/hardware/graphics/allocator/AllocationError.aidl b/graphics/allocator/aidl/aidl_api/android.hardware.graphics.allocator/current/android/hardware/graphics/allocator/AllocationError.aidl
new file mode 100644
index 0000000..6e7b739
--- /dev/null
+++ b/graphics/allocator/aidl/aidl_api/android.hardware.graphics.allocator/current/android/hardware/graphics/allocator/AllocationError.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.graphics.allocator;
+@Backing(type="int") @VintfStability
+enum AllocationError {
+  BAD_DESCRIPTOR = 0,
+  NO_RESOURCES = 1,
+  UNSUPPORTED = 2,
+}
diff --git a/graphics/allocator/aidl/aidl_api/android.hardware.graphics.allocator/current/android/hardware/graphics/allocator/AllocationResult.aidl b/graphics/allocator/aidl/aidl_api/android.hardware.graphics.allocator/current/android/hardware/graphics/allocator/AllocationResult.aidl
new file mode 100644
index 0000000..73cfeb5
--- /dev/null
+++ b/graphics/allocator/aidl/aidl_api/android.hardware.graphics.allocator/current/android/hardware/graphics/allocator/AllocationResult.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.graphics.allocator;
+@VintfStability
+parcelable AllocationResult {
+  int stride;
+  android.hardware.common.NativeHandle[] buffers;
+}
diff --git a/graphics/allocator/aidl/aidl_api/android.hardware.graphics.allocator/current/android/hardware/graphics/allocator/IAllocator.aidl b/graphics/allocator/aidl/aidl_api/android.hardware.graphics.allocator/current/android/hardware/graphics/allocator/IAllocator.aidl
new file mode 100644
index 0000000..fe0b0a2
--- /dev/null
+++ b/graphics/allocator/aidl/aidl_api/android.hardware.graphics.allocator/current/android/hardware/graphics/allocator/IAllocator.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.graphics.allocator;
+@VintfStability
+interface IAllocator {
+  android.hardware.graphics.allocator.AllocationResult allocate(in byte[] descriptor, in int count);
+}
diff --git a/graphics/allocator/aidl/android/hardware/graphics/allocator/AllocationError.aidl b/graphics/allocator/aidl/android/hardware/graphics/allocator/AllocationError.aidl
new file mode 100644
index 0000000..c6b77b9
--- /dev/null
+++ b/graphics/allocator/aidl/android/hardware/graphics/allocator/AllocationError.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2022 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.allocator;
+
+@VintfStability
+@Backing(type="int")
+enum AllocationError {
+    /**
+     * Invalid BufferDescriptor.
+     */
+    BAD_DESCRIPTOR,
+
+    /**
+     * Resource unavailable.
+     */
+    NO_RESOURCES,
+
+    /**
+     * Permanent failure.
+     */
+    UNSUPPORTED
+}
\ No newline at end of file
diff --git a/graphics/allocator/aidl/android/hardware/graphics/allocator/AllocationResult.aidl b/graphics/allocator/aidl/android/hardware/graphics/allocator/AllocationResult.aidl
new file mode 100644
index 0000000..0774e25
--- /dev/null
+++ b/graphics/allocator/aidl/android/hardware/graphics/allocator/AllocationResult.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2022 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.allocator;
+
+import android.hardware.common.NativeHandle;
+
+ /**
+ * Result of an IAllocator::allocate call.
+ *
+ * @sa +ndk libnativewindow#AHardwareBuffer_Desc
+ */
+@VintfStability
+parcelable AllocationResult {
+    int stride;
+    NativeHandle[] buffers;
+}
\ No newline at end of file
diff --git a/graphics/allocator/aidl/android/hardware/graphics/allocator/IAllocator.aidl b/graphics/allocator/aidl/android/hardware/graphics/allocator/IAllocator.aidl
new file mode 100644
index 0000000..8c3ca96
--- /dev/null
+++ b/graphics/allocator/aidl/android/hardware/graphics/allocator/IAllocator.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2022 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.allocator;
+
+import android.hardware.graphics.allocator.AllocationResult;
+
+@VintfStability
+interface IAllocator {
+    /**
+     * Allocates buffers with the properties specified by the descriptor.
+     *
+     * Allocations should be optimized for usage bits provided in the
+     * descriptor.
+     *
+     * @param descriptor Properties of the buffers to allocate. This must be
+     *     obtained from IMapper::createDescriptor().
+     * @param count The number of buffers to allocate.
+     * @return An AllocationResult containing the result of an error, or
+     *         an AllocationError status
+     */
+    AllocationResult allocate(in byte[] descriptor, in int count);
+}
diff --git a/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/BufferUsage.aidl b/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/BufferUsage.aidl
index b4ef451..e1edb17 100644
--- a/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/BufferUsage.aidl
+++ b/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/BufferUsage.aidl
@@ -54,10 +54,11 @@
   RENDERSCRIPT = 1048576,
   VIDEO_DECODER = 4194304,
   SENSOR_DIRECT_DATA = 8388608,
+  GPU_DATA_BUFFER = 16777216,
   GPU_CUBE_MAP = 33554432,
   GPU_MIPMAP_COMPLETE = 67108864,
   HW_IMAGE_ENCODER = 134217728,
-  GPU_DATA_BUFFER = 16777216,
+  FRONT_BUFFER = 4294967296,
   VENDOR_MASK = -268435456,
   VENDOR_MASK_HI = -281474976710656,
 }
diff --git a/graphics/common/aidl/android/hardware/graphics/common/BufferUsage.aidl b/graphics/common/aidl/android/hardware/graphics/common/BufferUsage.aidl
index d978f46..4b5a306 100644
--- a/graphics/common/aidl/android/hardware/graphics/common/BufferUsage.aidl
+++ b/graphics/common/aidl/android/hardware/graphics/common/BufferUsage.aidl
@@ -87,6 +87,12 @@
     /** buffer is used as a sensor direct report output */
     SENSOR_DIRECT_DATA                 = 1 << 23,
 
+    /**
+     * buffer is used as as an OpenGL shader storage or uniform
+     * buffer object
+     */
+    GPU_DATA_BUFFER                    = 1 << 24,
+
     /** buffer is used as a cube map texture */
     GPU_CUBE_MAP                       = 1 << 25,
 
@@ -98,17 +104,17 @@
      */
     HW_IMAGE_ENCODER                   = 1 << 27,
 
-    /**
-     * buffer is used as as an OpenGL shader storage or uniform
-     * buffer object
-     */
-    GPU_DATA_BUFFER                    = 1 << 24,
+    /* Bits 28-31 are reserved for vendor usage */
 
-    /** bits 25-27 must be zero and are reserved for future versions */
+    /**
+    * Buffer is used for front-buffer rendering
+    */
+    FRONT_BUFFER                       = 1L << 32,
+
     /** bits 28-31 are reserved for vendor extensions */
     VENDOR_MASK                        = 0xf << 28,
 
-    /** bits 32-47 must be zero and are reserved for future versions */
+    /** bits 33-47 must be zero and are reserved for future versions */
     /** bits 48-63 are reserved for vendor extensions */
     VENDOR_MASK_HI                     = (1L * 0xffff) << 48,
 }
diff --git a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
index 2ab9c01..9371154 100644
--- a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
+++ b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
@@ -21,6 +21,7 @@
 #include <thread>
 #include <vector>
 
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
 #include <aidl/android/hardware/graphics/common/PlaneLayoutComponentType.h>
 
 #include <android-base/logging.h>
@@ -1205,6 +1206,40 @@
 }
 
 /**
+ * Test IMapper::isSupported with optional format R_8
+ */
+TEST_P(GraphicsMapperHidlTest, IsSupportedR8) {
+    auto info = mDummyDescriptorInfo;
+    info.format = static_cast<android::hardware::graphics::common::V1_2::PixelFormat>(
+            aidl::android::hardware::graphics::common::PixelFormat::R_8);
+    bool supported = false;
+
+    ASSERT_NO_FATAL_FAILURE(supported = mGralloc->isSupported(info));
+
+    if (!supported) {
+        GTEST_SUCCEED() << "R_8 is optional; unsupported so skipping allocation test";
+        return;
+    }
+
+    BufferDescriptor descriptor;
+    ASSERT_NO_FATAL_FAILURE(descriptor = mGralloc->createDescriptor(info));
+
+    constexpr uint32_t count = 1;
+    std::vector<const native_handle_t*> bufferHandles;
+    uint32_t stride;
+    ASSERT_NO_FATAL_FAILURE(bufferHandles =
+                                    mGralloc->allocate(descriptor, count, false,
+                                                       Tolerance::kToleranceStrict, &stride));
+
+    EXPECT_LE(info.width, stride) << "invalid buffer stride";
+    EXPECT_EQ(1u, bufferHandles.size());
+
+    for (auto bufferHandle : bufferHandles) {
+        mGralloc->freeBuffer(bufferHandle);
+    }
+}
+
+/**
  * Test IMapper::get(BufferId)
  */
 TEST_P(GraphicsMapperHidlTest, GetBufferId) {
diff --git a/keymaster/3.0/vts/functional/Android.bp b/keymaster/3.0/vts/functional/Android.bp
index e2ae803..39bec3f 100644
--- a/keymaster/3.0/vts/functional/Android.bp
+++ b/keymaster/3.0/vts/functional/Android.bp
@@ -38,5 +38,11 @@
         "libcrypto_static",
         "libsoftkeymasterdevice",
     ],
-    test_suites: ["general-tests", "vts"],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+    sanitize: {
+        cfi: false,
+    },
 }
diff --git a/keymaster/4.1/vts/functional/Android.bp b/keymaster/4.1/vts/functional/Android.bp
index c650bec..547ce38 100644
--- a/keymaster/4.1/vts/functional/Android.bp
+++ b/keymaster/4.1/vts/functional/Android.bp
@@ -48,4 +48,7 @@
         "general-tests",
         "vts",
     ],
+    sanitize: {
+        cfi: false,
+    },
 }
diff --git a/neuralnetworks/1.0/utils/Android.bp b/neuralnetworks/1.0/utils/Android.bp
index 31cdded..ad30e30 100644
--- a/neuralnetworks/1.0/utils/Android.bp
+++ b/neuralnetworks/1.0/utils/Android.bp
@@ -31,16 +31,11 @@
     export_include_dirs: ["include"],
     cflags: ["-Wthread-safety"],
     static_libs: [
+        "android.hardware.neuralnetworks@1.0",
         "libarect",
         "neuralnetworks_types",
         "neuralnetworks_utils_hal_common",
     ],
-    shared_libs: [
-        "android.hardware.neuralnetworks@1.0",
-    ],
-    export_static_lib_headers: [
-        "neuralnetworks_utils_hal_common",
-    ],
     target: {
         android: {
             shared_libs: ["libnativewindow"],
@@ -55,19 +50,14 @@
     static_libs: [
         "android.hardware.neuralnetworks@1.0",
         "libgmock",
-        "libneuralnetworks_common",
         "neuralnetworks_types",
         "neuralnetworks_utils_hal_common",
         "neuralnetworks_utils_hal_1_0",
     ],
     shared_libs: [
-        "android.hidl.allocator@1.0",
-        "android.hidl.memory@1.0",
         "libbase",
         "libcutils",
-        "libfmq",
         "libhidlbase",
-        "libhidlmemory",
         "liblog",
         "libutils",
     ],
diff --git a/neuralnetworks/1.1/utils/Android.bp b/neuralnetworks/1.1/utils/Android.bp
index 737ff58..4b8999f 100644
--- a/neuralnetworks/1.1/utils/Android.bp
+++ b/neuralnetworks/1.1/utils/Android.bp
@@ -31,17 +31,12 @@
     export_include_dirs: ["include"],
     cflags: ["-Wthread-safety"],
     static_libs: [
+        "android.hardware.neuralnetworks@1.0",
+        "android.hardware.neuralnetworks@1.1",
         "neuralnetworks_types",
         "neuralnetworks_utils_hal_common",
         "neuralnetworks_utils_hal_1_0",
     ],
-    shared_libs: [
-        "android.hardware.neuralnetworks@1.0",
-        "android.hardware.neuralnetworks@1.1",
-    ],
-    export_static_lib_headers: [
-        "neuralnetworks_utils_hal_common",
-    ],
 }
 
 cc_test {
@@ -52,20 +47,15 @@
         "android.hardware.neuralnetworks@1.0",
         "android.hardware.neuralnetworks@1.1",
         "libgmock",
-        "libneuralnetworks_common",
         "neuralnetworks_types",
         "neuralnetworks_utils_hal_common",
         "neuralnetworks_utils_hal_1_0",
         "neuralnetworks_utils_hal_1_1",
     ],
     shared_libs: [
-        "android.hidl.allocator@1.0",
-        "android.hidl.memory@1.0",
         "libbase",
         "libcutils",
-        "libfmq",
         "libhidlbase",
-        "libhidlmemory",
         "liblog",
         "libutils",
     ],
diff --git a/neuralnetworks/1.2/utils/Android.bp b/neuralnetworks/1.2/utils/Android.bp
index 4eefb0f..4c5f065 100644
--- a/neuralnetworks/1.2/utils/Android.bp
+++ b/neuralnetworks/1.2/utils/Android.bp
@@ -31,19 +31,14 @@
     export_include_dirs: ["include"],
     cflags: ["-Wthread-safety"],
     static_libs: [
-        "neuralnetworks_types",
-        "neuralnetworks_utils_hal_common",
-        "neuralnetworks_utils_hal_1_0",
-        "neuralnetworks_utils_hal_1_1",
-    ],
-    shared_libs: [
         "android.hardware.neuralnetworks@1.0",
         "android.hardware.neuralnetworks@1.1",
         "android.hardware.neuralnetworks@1.2",
         "libfmq",
-    ],
-    export_static_lib_headers: [
+        "neuralnetworks_types",
         "neuralnetworks_utils_hal_common",
+        "neuralnetworks_utils_hal_1_0",
+        "neuralnetworks_utils_hal_1_1",
     ],
     product_variables: {
         debuggable: { // eng and userdebug builds
@@ -71,7 +66,6 @@
         "android.hardware.neuralnetworks@1.1",
         "android.hardware.neuralnetworks@1.2",
         "libgmock",
-        "libneuralnetworks_common",
         "neuralnetworks_types",
         "neuralnetworks_utils_hal_common",
         "neuralnetworks_utils_hal_1_0",
@@ -79,13 +73,10 @@
         "neuralnetworks_utils_hal_1_2",
     ],
     shared_libs: [
-        "android.hidl.allocator@1.0",
-        "android.hidl.memory@1.0",
         "libbase",
         "libcutils",
         "libfmq",
         "libhidlbase",
-        "libhidlmemory",
         "liblog",
         "libutils",
     ],
diff --git a/neuralnetworks/1.3/utils/Android.bp b/neuralnetworks/1.3/utils/Android.bp
index 7acb4fc..c512dda 100644
--- a/neuralnetworks/1.3/utils/Android.bp
+++ b/neuralnetworks/1.3/utils/Android.bp
@@ -31,21 +31,16 @@
     export_include_dirs: ["include"],
     cflags: ["-Wthread-safety"],
     static_libs: [
-        "neuralnetworks_types",
-        "neuralnetworks_utils_hal_common",
-        "neuralnetworks_utils_hal_1_0",
-        "neuralnetworks_utils_hal_1_1",
-        "neuralnetworks_utils_hal_1_2",
-    ],
-    shared_libs: [
         "android.hardware.neuralnetworks@1.0",
         "android.hardware.neuralnetworks@1.1",
         "android.hardware.neuralnetworks@1.2",
         "android.hardware.neuralnetworks@1.3",
         "libfmq",
-    ],
-    export_static_lib_headers: [
+        "neuralnetworks_types",
         "neuralnetworks_utils_hal_common",
+        "neuralnetworks_utils_hal_1_0",
+        "neuralnetworks_utils_hal_1_1",
+        "neuralnetworks_utils_hal_1_2",
     ],
     target: {
         host: {
@@ -69,7 +64,6 @@
         "android.hardware.neuralnetworks@1.2",
         "android.hardware.neuralnetworks@1.3",
         "libgmock",
-        "libneuralnetworks_common",
         "neuralnetworks_types",
         "neuralnetworks_utils_hal_common",
         "neuralnetworks_utils_hal_1_0",
@@ -78,13 +72,10 @@
         "neuralnetworks_utils_hal_1_3",
     ],
     shared_libs: [
-        "android.hidl.allocator@1.0",
-        "android.hidl.memory@1.0",
         "libbase",
         "libcutils",
         "libfmq",
         "libhidlbase",
-        "libhidlmemory",
         "liblog",
         "libutils",
     ],
diff --git a/neuralnetworks/aidl/utils/Android.bp b/neuralnetworks/aidl/utils/Android.bp
index 3faa613..9148eac 100644
--- a/neuralnetworks/aidl/utils/Android.bp
+++ b/neuralnetworks/aidl/utils/Android.bp
@@ -111,19 +111,13 @@
     static_libs: [
         "libaidlcommonsupport",
         "libgmock",
-        "libneuralnetworks_common",
         "neuralnetworks_types",
         "neuralnetworks_utils_hal_common",
     ],
     shared_libs: [
-        "android.hidl.allocator@1.0",
         "libbase",
         "libbinder_ndk",
         "libcutils",
-        "libhidlbase",
-        "libhidlmemory",
-        "liblog",
-        "libutils",
     ],
     target: {
         android: {
diff --git a/neuralnetworks/aidl/utils/test/MockBuffer.h b/neuralnetworks/aidl/utils/test/MockBuffer.h
index f77fa86..7a05a0f 100644
--- a/neuralnetworks/aidl/utils/test/MockBuffer.h
+++ b/neuralnetworks/aidl/utils/test/MockBuffer.h
@@ -21,7 +21,6 @@
 #include <android/binder_interface_utils.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-#include <hidl/Status.h>
 
 namespace aidl::android::hardware::neuralnetworks::utils {
 
diff --git a/neuralnetworks/aidl/utils/test/MockBurst.h b/neuralnetworks/aidl/utils/test/MockBurst.h
index 4cf60b6..609bd30 100644
--- a/neuralnetworks/aidl/utils/test/MockBurst.h
+++ b/neuralnetworks/aidl/utils/test/MockBurst.h
@@ -21,7 +21,6 @@
 #include <android/binder_interface_utils.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-#include <hidl/Status.h>
 
 namespace aidl::android::hardware::neuralnetworks::utils {
 
diff --git a/neuralnetworks/aidl/utils/test/MockExecution.h b/neuralnetworks/aidl/utils/test/MockExecution.h
index 216f569..782e54f 100644
--- a/neuralnetworks/aidl/utils/test/MockExecution.h
+++ b/neuralnetworks/aidl/utils/test/MockExecution.h
@@ -21,8 +21,6 @@
 #include <android/binder_interface_utils.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-#include <hidl/HidlSupport.h>
-#include <hidl/Status.h>
 
 namespace aidl::android::hardware::neuralnetworks::utils {
 
diff --git a/neuralnetworks/aidl/utils/test/MockFencedExecutionCallback.h b/neuralnetworks/aidl/utils/test/MockFencedExecutionCallback.h
index 06f9ea2..29449bb 100644
--- a/neuralnetworks/aidl/utils/test/MockFencedExecutionCallback.h
+++ b/neuralnetworks/aidl/utils/test/MockFencedExecutionCallback.h
@@ -22,7 +22,6 @@
 #include <android/binder_interface_utils.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-#include <hidl/Status.h>
 
 namespace aidl::android::hardware::neuralnetworks::utils {
 
diff --git a/neuralnetworks/aidl/utils/test/MockPreparedModel.h b/neuralnetworks/aidl/utils/test/MockPreparedModel.h
index 318acc2..a5b3b66 100644
--- a/neuralnetworks/aidl/utils/test/MockPreparedModel.h
+++ b/neuralnetworks/aidl/utils/test/MockPreparedModel.h
@@ -22,8 +22,6 @@
 #include <android/binder_interface_utils.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-#include <hidl/HidlSupport.h>
-#include <hidl/Status.h>
 
 namespace aidl::android::hardware::neuralnetworks::utils {
 
diff --git a/neuralnetworks/utils/adapter/hidl/Android.bp b/neuralnetworks/utils/adapter/hidl/Android.bp
index d073106..6875daa 100644
--- a/neuralnetworks/utils/adapter/hidl/Android.bp
+++ b/neuralnetworks/utils/adapter/hidl/Android.bp
@@ -30,17 +30,16 @@
     local_include_dirs: ["include/nnapi/hal"],
     export_include_dirs: ["include"],
     static_libs: [
-        "neuralnetworks_types",
-        "neuralnetworks_utils_hal_1_0",
-        "neuralnetworks_utils_hal_1_1",
-        "neuralnetworks_utils_hal_1_2",
-        "neuralnetworks_utils_hal_1_3",
-    ],
-    shared_libs: [
         "android.hardware.neuralnetworks@1.0",
         "android.hardware.neuralnetworks@1.1",
         "android.hardware.neuralnetworks@1.2",
         "android.hardware.neuralnetworks@1.3",
         "libfmq",
+        "neuralnetworks_types",
+        "neuralnetworks_utils_hal_1_0",
+        "neuralnetworks_utils_hal_1_1",
+        "neuralnetworks_utils_hal_1_2",
+        "neuralnetworks_utils_hal_1_3",
+        "neuralnetworks_utils_hal_common",
     ],
 }
diff --git a/neuralnetworks/utils/common/Android.bp b/neuralnetworks/utils/common/Android.bp
index 39927a3..bfba24f 100644
--- a/neuralnetworks/utils/common/Android.bp
+++ b/neuralnetworks/utils/common/Android.bp
@@ -39,20 +39,12 @@
     srcs: ["test/*.cpp"],
     static_libs: [
         "libgmock",
-        "libneuralnetworks_common",
         "neuralnetworks_types",
         "neuralnetworks_utils_hal_common",
     ],
     shared_libs: [
-        "android.hidl.allocator@1.0",
-        "android.hidl.memory@1.0",
         "libbase",
         "libcutils",
-        "libfmq",
-        "libhidlbase",
-        "libhidlmemory",
-        "liblog",
-        "libutils",
     ],
     target: {
         android: {
diff --git a/neuralnetworks/utils/service/Android.bp b/neuralnetworks/utils/service/Android.bp
index c3272ae..452078b 100644
--- a/neuralnetworks/utils/service/Android.bp
+++ b/neuralnetworks/utils/service/Android.bp
@@ -33,6 +33,10 @@
     local_include_dirs: ["include/nnapi/hal"],
     export_include_dirs: ["include"],
     static_libs: [
+        "android.hardware.neuralnetworks@1.0",
+        "android.hardware.neuralnetworks@1.1",
+        "android.hardware.neuralnetworks@1.2",
+        "android.hardware.neuralnetworks@1.3",
         "neuralnetworks_types",
         "neuralnetworks_utils_hal_1_0",
         "neuralnetworks_utils_hal_1_1",
@@ -40,10 +44,4 @@
         "neuralnetworks_utils_hal_1_3",
         "neuralnetworks_utils_hal_common",
     ],
-    shared_libs: [
-        "android.hardware.neuralnetworks@1.0",
-        "android.hardware.neuralnetworks@1.1",
-        "android.hardware.neuralnetworks@1.2",
-        "android.hardware.neuralnetworks@1.3",
-    ],
 }
diff --git a/sensors/aidl/aidl_api/android.hardware.sensors/current/android/hardware/sensors/Event.aidl b/sensors/aidl/aidl_api/android.hardware.sensors/current/android/hardware/sensors/Event.aidl
index c92ab1a..4f49002 100644
--- a/sensors/aidl/aidl_api/android.hardware.sensors/current/android/hardware/sensors/Event.aidl
+++ b/sensors/aidl/aidl_api/android.hardware.sensors/current/android/hardware/sensors/Event.aidl
@@ -52,6 +52,9 @@
     android.hardware.sensors.AdditionalInfo additional;
     android.hardware.sensors.Event.EventPayload.Data data;
     android.hardware.sensors.Event.EventPayload.HeadTracker headTracker;
+    android.hardware.sensors.Event.EventPayload.LimitedAxesImu limitedAxesImu;
+    android.hardware.sensors.Event.EventPayload.LimitedAxesImuUncal limitedAxesImuUncal;
+    android.hardware.sensors.Event.EventPayload.Heading heading;
     @FixedSize @VintfStability
     parcelable Vec4 {
       float x;
@@ -86,11 +89,37 @@
       int discontinuityCount;
     }
     @FixedSize @VintfStability
+    parcelable LimitedAxesImu {
+      float x;
+      float y;
+      float z;
+      float xSupported;
+      float ySupported;
+      float zSupported;
+    }
+    @FixedSize @VintfStability
+    parcelable LimitedAxesImuUncal {
+      float x;
+      float y;
+      float z;
+      float xBias;
+      float yBias;
+      float zBias;
+      float xSupported;
+      float ySupported;
+      float zSupported;
+    }
+    @FixedSize @VintfStability
     parcelable HeartRate {
       float bpm;
       android.hardware.sensors.SensorStatus status;
     }
     @FixedSize @VintfStability
+    parcelable Heading {
+      float heading;
+      float accuracy;
+    }
+    @FixedSize @VintfStability
     parcelable MetaData {
       android.hardware.sensors.Event.EventPayload.MetaData.MetaDataEventType what;
       @Backing(type="int") @VintfStability
diff --git a/sensors/aidl/aidl_api/android.hardware.sensors/current/android/hardware/sensors/SensorType.aidl b/sensors/aidl/aidl_api/android.hardware.sensors/current/android/hardware/sensors/SensorType.aidl
index 3d7ab45..8c864e9 100644
--- a/sensors/aidl/aidl_api/android.hardware.sensors/current/android/hardware/sensors/SensorType.aidl
+++ b/sensors/aidl/aidl_api/android.hardware.sensors/current/android/hardware/sensors/SensorType.aidl
@@ -71,5 +71,10 @@
   ACCELEROMETER_UNCALIBRATED = 35,
   HINGE_ANGLE = 36,
   HEAD_TRACKER = 37,
+  ACCELEROMETER_LIMITED_AXES = 38,
+  GYROSCOPE_LIMITED_AXES = 39,
+  ACCELEROMETER_LIMITED_AXES_UNCALIBRATED = 40,
+  GYROSCOPE_LIMITED_AXES_UNCALIBRATED = 41,
+  HEADING = 42,
   DEVICE_PRIVATE_BASE = 65536,
 }
diff --git a/sensors/aidl/android/hardware/sensors/Event.aidl b/sensors/aidl/android/hardware/sensors/Event.aidl
index fd6a8cc..e8550f1 100644
--- a/sensors/aidl/android/hardware/sensors/Event.aidl
+++ b/sensors/aidl/android/hardware/sensors/Event.aidl
@@ -132,6 +132,23 @@
          */
         HeadTracker headTracker;
 
+        /**
+         * SensorType::ACCELEROMETER_LIMITED_AXES
+         * SensorType::GYROSCOPE_LIMITED_AXES
+         */
+        LimitedAxesImu limitedAxesImu;
+
+        /**
+         * SensorType::ACCELEROMETER_LIMITED_AXES_UNCALIBRATED
+         * SensorType::GYROSCOPE_LIMITED_AXES_UNCALIBRATED
+         */
+        LimitedAxesImuUncal limitedAxesImuUncal;
+
+        /**
+         * SensorType::HEADING
+         */
+        Heading heading;
+
         @FixedSize
         @VintfStability
         parcelable Vec4 {
@@ -201,6 +218,70 @@
             int discontinuityCount;
         }
 
+        /**
+         * Payload of the ACCELEROMETER_LIMITED_AXES and GYROSCOPE_LIMITED_AXES
+         * sensor types.
+         */
+        @FixedSize
+        @VintfStability
+        parcelable LimitedAxesImu {
+            /**
+             * Acceleration or angular speed values.  If certain axes are not
+             * supported, the associated value must be set to 0.
+             */
+            float x;
+            float y;
+            float z;
+
+            /**
+             * Limited axes sensors must not be supported for all three axes.
+             * These values indicate which axes are supported with a 1.0 for
+             * supported, and a 0 for not supported. The supported axes should
+             * be determined at build time and these values must not change
+             * during runtime.
+             */
+            float xSupported;
+            float ySupported;
+            float zSupported;
+        }
+
+        /**
+         * Payload of the ACCELEROMETER_LIMITED_AXES_UNCALIBRATED and
+         * GYROSCOPE_LIMITED_AXES_UNCALIBRATED sensor types.
+         */
+        @FixedSize
+        @VintfStability
+        parcelable LimitedAxesImuUncal {
+            /**
+             * Acceleration (without bias compensation) or angular (speed
+             * (without drift compensation) values. If certain axes are not
+             * supported, the associated value must be set to 0.
+             */
+            float x;
+            float y;
+            float z;
+
+            /**
+             * Estimated bias values for uncalibrated accelerometer or
+             * estimated drift values for uncalibrated gyroscope. If certain
+             * axes are not supported, the associated value must be set to 0.
+             */
+            float xBias;
+            float yBias;
+            float zBias;
+
+            /**
+             * Limited axes sensors must not be supported for all three axes.
+             * These values indicate which axes are supported with a 1.0 for
+             * supported, and a 0 for not supported. The supported axes should
+             * be determined at build time and these values must not change
+             * during runtime.
+             */
+            float xSupported;
+            float ySupported;
+            float zSupported;
+        }
+
         @FixedSize
         @VintfStability
         parcelable HeartRate {
@@ -218,6 +299,27 @@
 
         @FixedSize
         @VintfStability
+        parcelable Heading {
+            /**
+             * The direction in which the device is pointing relative to true
+             * north in degrees. The value must be between 0.0 (inclusive) and
+             * 360.0 (exclusive), with 0 indicating north, 90 east, 180 south,
+             * and 270 west.
+             */
+            float heading;
+            /**
+             * Accuracy is defined at 68% confidence. In the case where the
+             * underlying distribution is assumed Gaussian normal, this would be
+             * considered one standard deviation. For example, if the heading
+             * returns 60 degrees, and accuracy returns 10 degrees, then there
+             * is a 68 percent probability of the true heading being between 50
+             * degrees and 70 degrees.
+             */
+            float accuracy;
+        }
+
+        @FixedSize
+        @VintfStability
         parcelable MetaData {
             MetaDataEventType what;
 
diff --git a/sensors/aidl/android/hardware/sensors/SensorType.aidl b/sensors/aidl/android/hardware/sensors/SensorType.aidl
index 01e6bee..9098894 100644
--- a/sensors/aidl/android/hardware/sensors/SensorType.aidl
+++ b/sensors/aidl/android/hardware/sensors/SensorType.aidl
@@ -667,6 +667,57 @@
     HEAD_TRACKER = 37,
 
     /**
+     * ACCELEROMETER_LIMITED_AXES
+     * reporting-mode: continuous
+     *
+     * Equivalent to ACCELEROMETER, but supporting cases where one or two axes
+     * are not supported.
+     */
+    ACCELEROMETER_LIMITED_AXES = 38,
+
+    /**
+     * GYROSCOPE_LIMITED_AXES
+     * reporting-mode: continuous
+     *
+     * Equivalent to GYROSCOPE, but supporting cases where one or two axes are
+     * not supported.
+     */
+    GYROSCOPE_LIMITED_AXES = 39,
+
+    /**
+     * ACCELEROMETER_LIMITED_AXES_UNCALIBRATED
+     * reporting-mode: continuous
+     *
+     * Equivalent to ACCELEROMETER_UNCALIBRATED, but supporting cases where one
+     * or two axes are not supported.
+     */
+    ACCELEROMETER_LIMITED_AXES_UNCALIBRATED = 40,
+
+    /**
+     * GYROSCOPE_LIMITED_AXES_UNCALIBRATED
+     * reporting-mode: continuous
+     *
+     * Equivalent to GYROSCOPE_UNCALIBRATED, but supporting cases where one or
+     * two axes are not supported.
+     */
+    GYROSCOPE_LIMITED_AXES_UNCALIBRATED = 41,
+
+    /**
+     * HEADING
+     * reporting-mode: continuous
+     *
+     * A sensor of this type measures the direction in which the device is
+     * pointing relative to true north in degrees.
+     *
+     * This sensor was added for automotive form factors. Other devices with a
+     * clear forward direction might find it useful as well. However, devices
+     * with a more ambiguous orientation such as phones or wearables might want
+     * to consider using other sensors such as Sensor.TYPE_ROTATION_VECTOR
+     * which might be more suitable.
+     */
+    HEADING = 42,
+
+    /**
      * Base for device manufacturers private sensor types.
      * These sensor types can't be exposed in the SDK.
      */
diff --git a/soundtrigger/aidl/android/hardware/soundtrigger3/ISoundTriggerHw.aidl b/soundtrigger/aidl/android/hardware/soundtrigger3/ISoundTriggerHw.aidl
index 2a3fc64..618331b 100644
--- a/soundtrigger/aidl/android/hardware/soundtrigger3/ISoundTriggerHw.aidl
+++ b/soundtrigger/aidl/android/hardware/soundtrigger3/ISoundTriggerHw.aidl
@@ -18,15 +18,12 @@
 
 import android.hardware.soundtrigger3.ISoundTriggerHwCallback;
 import android.hardware.soundtrigger3.ISoundTriggerHwGlobalCallback;
-
+import android.media.soundtrigger.ModelParameter;
+import android.media.soundtrigger.ModelParameterRange;
 import android.media.soundtrigger.PhraseSoundModel;
 import android.media.soundtrigger.Properties;
 import android.media.soundtrigger.RecognitionConfig;
 import android.media.soundtrigger.SoundModel;
-import android.media.soundtrigger.ModelParameter;
-import android.media.soundtrigger.ModelParameterRange;
-import android.media.soundtrigger.Properties;
-import android.media.soundtrigger.RecognitionConfig;
 
 /**
  * SoundTrigger HAL interface. Used for hardware recognition of hotwords
@@ -196,12 +193,12 @@
      *     an audio stream associated with this recognition session.
      * @param config A RecognitionConfig structure containing attributes of the recognition to
      *     perform.
-      * @throws ServiceSpecificException(RESOURCE_CONTENTION) if the model cannot be started due
+     * @throws ServiceSpecificException(RESOURCE_CONTENTION) if the model cannot be started due
      *     to resource constraints. This is typically a temporary condition and the client may
      *     retry after the onResourcesAvailable() global callback is invoked.
-    */
-    void startRecognition(in int modelHandle, in int deviceHandle,
-                          in int ioHandle, in RecognitionConfig config);
+     */
+    void startRecognition(
+            in int modelHandle, in int deviceHandle, in int ioHandle, in RecognitionConfig config);
 
     /**
      * Stop recognition on a given model.
@@ -235,7 +232,8 @@
      * @return This structure indicates supported attributes of the parameter for the given model
      *      handle. If the parameter is not supported, null is returned.
      */
-    @nullable ModelParameterRange queryParameter(in int modelHandle, in ModelParameter modelParam);
+    @nullable ModelParameterRange queryParameter(
+            in int modelHandle, in ModelParameter modelParam);
 
     /**
      * Get a model specific parameter.
diff --git a/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/FrontendStatus.aidl b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/FrontendStatus.aidl
index fc0efc9..1e0f5f0 100644
--- a/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/FrontendStatus.aidl
+++ b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/FrontendStatus.aidl
@@ -76,4 +76,5 @@
   android.hardware.tv.tuner.FrontendIsdbtPartialReceptionFlag partialReceptionFlag;
   int[] streamIdList;
   int[] dvbtCellIds;
+  android.hardware.tv.tuner.FrontendScanAtsc3PlpInfo[] allPlpInfo;
 }
diff --git a/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/FrontendStatusType.aidl b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/FrontendStatusType.aidl
index 2cc62d5..cd6ccb3 100644
--- a/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/FrontendStatusType.aidl
+++ b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/FrontendStatusType.aidl
@@ -76,4 +76,5 @@
   ISDBT_PARTIAL_RECEPTION_FLAG = 38,
   STREAM_ID_LIST = 39,
   DVBT_CELL_IDS = 40,
+  ATSC3_ALL_PLP_INFO = 41,
 }
diff --git a/tv/tuner/aidl/android/hardware/tv/tuner/FrontendStatus.aidl b/tv/tuner/aidl/android/hardware/tv/tuner/FrontendStatus.aidl
index ae6e46f..b5d0201 100644
--- a/tv/tuner/aidl/android/hardware/tv/tuner/FrontendStatus.aidl
+++ b/tv/tuner/aidl/android/hardware/tv/tuner/FrontendStatus.aidl
@@ -27,6 +27,7 @@
 import android.hardware.tv.tuner.FrontendModulationStatus;
 import android.hardware.tv.tuner.FrontendRollOff;
 import android.hardware.tv.tuner.FrontendSpectralInversion;
+import android.hardware.tv.tuner.FrontendScanAtsc3PlpInfo;
 import android.hardware.tv.tuner.FrontendStatusAtsc3PlpInfo;
 import android.hardware.tv.tuner.FrontendTransmissionMode;
 import android.hardware.tv.tuner.LnbVoltage;
@@ -241,5 +242,9 @@
      */
     int[] dvbtCellIds;
 
-
+    /**
+     * A list of all PLPs in the frequency band for ATSC3 frontend, which includes both tuned
+     * and not tuned PLPs for currently watching service.
+     */
+    FrontendScanAtsc3PlpInfo[] allPlpInfo;
 }
diff --git a/tv/tuner/aidl/android/hardware/tv/tuner/FrontendStatusType.aidl b/tv/tuner/aidl/android/hardware/tv/tuner/FrontendStatusType.aidl
index e7da517..8f3f2c5 100644
--- a/tv/tuner/aidl/android/hardware/tv/tuner/FrontendStatusType.aidl
+++ b/tv/tuner/aidl/android/hardware/tv/tuner/FrontendStatusType.aidl
@@ -130,7 +130,7 @@
     RF_LOCK,
 
     /**
-     * PLP information in a frequency band for ATSC3.0 frontend.
+     * Current tuned PLP information in a frequency band for ATSC3 frontend.
      */
     ATSC3_PLP_INFO,
 
@@ -222,10 +222,16 @@
     /**
      * Stream ID list included in a transponder.
      */
-   STREAM_ID_LIST,
+    STREAM_ID_LIST,
 
-   /**
-    * DVB-T Cell Id.
-    */
-   DVBT_CELL_IDS,
+    /**
+     * DVB-T Cell Id.
+     */
+    DVBT_CELL_IDS,
+
+    /**
+     * All PLP information in a frequency band for ATSC3 frontend, which includes both tuned
+     * and not tuned PLPs for currently watching service.
+     */
+    ATSC3_ALL_PLP_INFO,
 }
diff --git a/tv/tuner/aidl/default/Frontend.cpp b/tv/tuner/aidl/default/Frontend.cpp
index 714612d..445d2b6 100644
--- a/tv/tuner/aidl/default/Frontend.cpp
+++ b/tv/tuner/aidl/default/Frontend.cpp
@@ -708,6 +708,20 @@
                 status.set<FrontendStatus::dvbtCellIds>(dvbtCellIds);
                 break;
             }
+            case FrontendStatusType::ATSC3_ALL_PLP_INFO: {
+                FrontendScanAtsc3PlpInfo info1;
+                info1.plpId = 1;
+                info1.bLlsFlag = false;
+                FrontendScanAtsc3PlpInfo info2;
+                info2.plpId = 2;
+                info2.bLlsFlag = true;
+                FrontendScanAtsc3PlpInfo info3;
+                info3.plpId = 3;
+                info3.bLlsFlag = false;
+                vector<FrontendScanAtsc3PlpInfo> infos = {info1, info2, info3};
+                status.set<FrontendStatus::allPlpInfo>(infos);
+                break;
+            }
             default: {
                 continue;
             }
diff --git a/tv/tuner/aidl/default/Tuner.cpp b/tv/tuner/aidl/default/Tuner.cpp
index 48c1b66..7a5fa6e 100644
--- a/tv/tuner/aidl/default/Tuner.cpp
+++ b/tv/tuner/aidl/default/Tuner.cpp
@@ -77,6 +77,7 @@
             FrontendStatusType::BERS,
             FrontendStatusType::INTERLEAVINGS,
             FrontendStatusType::BANDWIDTH,
+            FrontendStatusType::ATSC3_ALL_PLP_INFO,
     };
     mFrontendStatusCaps[1] = statusCaps;
     mMaxUsableFrontends[FrontendType::ATSC3] = 1;
diff --git a/tv/tuner/aidl/vts/functional/FrontendTests.cpp b/tv/tuner/aidl/vts/functional/FrontendTests.cpp
index 41e98ea..a8799ab 100644
--- a/tv/tuner/aidl/vts/functional/FrontendTests.cpp
+++ b/tv/tuner/aidl/vts/functional/FrontendTests.cpp
@@ -414,6 +414,13 @@
                         expectStatuses[i].get<FrontendStatus::Tag::dvbtCellIds>().begin()));
                 break;
             }
+            case FrontendStatusType::ATSC3_ALL_PLP_INFO: {
+                ASSERT_TRUE(std::equal(
+                        realStatuses[i].get<FrontendStatus::Tag::allPlpInfo>().begin(),
+                        realStatuses[i].get<FrontendStatus::Tag::allPlpInfo>().end(),
+                        expectStatuses[i].get<FrontendStatus::Tag::allPlpInfo>().begin()));
+                break;
+            }
             default: {
                 continue;
             }