Merge "Add heart beat event to Default VHAL."
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp
index f410f4c..5b06c33 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp
@@ -15,6 +15,7 @@
  */
 #define LOG_TAG "DefaultVehicleHal_v2_0"
 
+#include <android-base/chrono_utils.h>
 #include <assert.h>
 #include <utils/Log.h>
 #include <utils/SystemClock.h>
@@ -32,6 +33,10 @@
 
 namespace impl {
 
+namespace {
+constexpr std::chrono::nanoseconds kHeartBeatIntervalNs = 3s;
+}  // namespace
+
 DefaultVehicleHal::DefaultVehicleHal(VehiclePropertyStore* propStore, VehicleHalClient* client)
     : mPropStore(propStore), mRecurrentTimer(getTimerAction()), mVehicleClient(client) {
     initStaticConfig();
@@ -297,6 +302,41 @@
     }
 
     mVehicleClient->triggerSendAllValues();
+    registerHeartBeatEvent();
+}
+
+DefaultVehicleHal::~DefaultVehicleHal() {
+    mRecurrentTimer.unregisterRecurrentEvent(static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT));
+}
+
+void DefaultVehicleHal::registerHeartBeatEvent() {
+    mRecurrentTimer.registerRecurrentEvent(kHeartBeatIntervalNs,
+                                           static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT));
+}
+
+VehicleHal::VehiclePropValuePtr DefaultVehicleHal::doInternalHealthCheck() {
+    VehicleHal::VehiclePropValuePtr v = nullptr;
+
+    // This is an example of very simple health checking. VHAL is considered healthy if we can read
+    // PERF_VEHICLE_SPEED. The more comprehensive health checking is required.
+    VehiclePropValue propValue = {
+            .prop = static_cast<int32_t>(VehicleProperty::PERF_VEHICLE_SPEED),
+    };
+    auto internalPropValue = mPropStore->readValueOrNull(propValue);
+    if (internalPropValue != nullptr) {
+        v = createVhalHeartBeatProp();
+    } else {
+        ALOGW("VHAL health check failed");
+    }
+    return v;
+}
+
+VehicleHal::VehiclePropValuePtr DefaultVehicleHal::createVhalHeartBeatProp() {
+    VehicleHal::VehiclePropValuePtr v = getValuePool()->obtainInt64(uptimeMillis());
+    v->prop = static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT);
+    v->areaId = 0;
+    v->status = VehiclePropertyStatus::AVAILABLE;
+    return v;
 }
 
 void DefaultVehicleHal::onContinuousPropertyTimer(const std::vector<int32_t>& properties) {
@@ -309,6 +349,10 @@
             if (internalPropValue != nullptr) {
                 v = pool.obtain(*internalPropValue);
             }
+        } else if (property == static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT)) {
+            // VHAL_HEARTBEAT is not a continuous value, but it needs to be updated periodically.
+            // So, the update is done through onContinuousPropertyTimer.
+            v = doInternalHealthCheck();
         } else {
             ALOGE("Unexpected onContinuousPropertyTimer for property: 0x%x", property);
             continue;
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h
index 2a0d430..ecdb9a8 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h
@@ -35,9 +35,9 @@
 class DefaultVehicleHal : public VehicleHal {
   public:
     DefaultVehicleHal(VehiclePropertyStore* propStore, VehicleHalClient* client);
-    ~DefaultVehicleHal() = default;
+    ~DefaultVehicleHal();
 
-    //  Methods from VehicleHal
+    // Initialize VHAL. Should always call registerHeartBeatEvent() during onCreate.
     void onCreate() override;
     std::vector<VehiclePropConfig> listProperties() override;
     VehiclePropValuePtr get(const VehiclePropValue& requestedPropValue,
@@ -65,6 +65,12 @@
     StatusCode checkPropValue(const VehiclePropValue& propValue, const VehiclePropConfig* config);
     // Check whether the property value is within the range according to area config.
     StatusCode checkValueRange(const VehiclePropValue& propValue, const VehiclePropConfig* config);
+    // Register the heart beat event to be sent every 3s. This is required to inform watch dog that
+    // VHAL is alive. Subclasses should always calls this function during onCreate.
+    void registerHeartBeatEvent();
+
+    VehicleHal::VehiclePropValuePtr doInternalHealthCheck();
+    VehicleHal::VehiclePropValuePtr createVhalHeartBeatProp();
 
   private:
     // Check whether a vendor mixed value property is valid according to its config array.
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
index 1b20bf5..bbefce7 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
@@ -56,14 +56,21 @@
 
 class DefaultVhalImplTest : public ::testing::Test {
   public:
-    ~DefaultVhalImplTest() { mEventQueue.deactivate(); }
+    ~DefaultVhalImplTest() {
+        mEventQueue.deactivate();
+        mHeartBeatQueue.deactivate();
+        // Destroy mHal before destroying its dependencies.
+        mHal.reset();
+        mConnector.reset();
+        mPropStore.reset();
+    }
 
   protected:
     void SetUp() override {
         mPropStore.reset(new VehiclePropertyStore);
         mConnector.reset(new DefaultVehicleConnector);
-        mHal.reset(new DefaultVehicleHal(mPropStore.get(), mConnector.get()));
         mConnector->setValuePool(&mValueObjectPool);
+        mHal.reset(new DefaultVehicleHal(mPropStore.get(), mConnector.get()));
         mHal->init(&mValueObjectPool,
                    std::bind(&DefaultVhalImplTest::onHalEvent, this, std::placeholders::_1),
                    std::bind(&DefaultVhalImplTest::onHalPropertySetError, this,
@@ -71,7 +78,14 @@
     }
 
   private:
-    void onHalEvent(VehiclePropValuePtr v) { mEventQueue.push(std::move(v)); }
+    void onHalEvent(VehiclePropValuePtr v) {
+        if (v->prop != toInt(VehicleProperty::VHAL_HEARTBEAT)) {
+            // Ignore heartbeat properties.
+            mEventQueue.push(std::move(v));
+        } else {
+            mHeartBeatQueue.push(std::move(v));
+        }
+    }
 
     void onHalPropertySetError(StatusCode /*errorCode*/, int32_t /*property*/, int32_t /*areaId*/) {
     }
@@ -82,6 +96,7 @@
     std::unique_ptr<VehiclePropertyStore> mPropStore;
     VehiclePropValuePool mValueObjectPool;
     android::ConcurrentQueue<VehiclePropValuePtr> mEventQueue;
+    android::ConcurrentQueue<VehiclePropValuePtr> mHeartBeatQueue;
 };
 
 TEST_F(DefaultVhalImplTest, testListProperties) {
@@ -724,4 +739,14 @@
     EXPECT_EQ(2, events[1]->value.int32Values[2]);
 }
 
+TEST_F(DefaultVhalImplTest, testHeartBeatEvent) {
+    // A heart beat would be sent every 3s, but let's wait for 6s to be sure at least 2 events have
+    // been generated (at 0s and 3s).
+    std::this_thread::sleep_for(std::chrono::milliseconds(6000));
+
+    auto events = mHeartBeatQueue.flush();
+    ASSERT_GE(events.size(), (size_t)2);
+    ASSERT_EQ(toInt(VehicleProperty::VHAL_HEARTBEAT), events[0]->prop);
+}
+
 }  // namespace