Add heart beat event to Default VHAL.

VHAL needs to send out heartbeat event so that watchdog does not
kill it. This is required for all VHAL so move the logic to default
VHAL.

Test: Local run on emulator. Add a test case in
android.hardware.automotive.vehicle@2.0-default-impl-unit-tests

Bug: 193831021
Change-Id: Ieacd991c22fa0d70d254426d728d3827a38d0b42
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