Merge changes from topic "pi_matrix" into pi-dev

* changes:
  matrix: broadcastradio@2.0::IBroadcastRadio/.*
  matrix: nn@1.0-1
  matrix: graphics.mapper@2.0-1
  matrix: graphics.composer@2.1-2
diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp
index 774bc4f..22ab079 100644
--- a/automotive/vehicle/2.0/default/Android.bp
+++ b/automotive/vehicle/2.0/default/Android.bp
@@ -62,6 +62,8 @@
         "impl/vhal_v2_0/VehicleEmulator.cpp",
         "impl/vhal_v2_0/PipeComm.cpp",
         "impl/vhal_v2_0/SocketComm.cpp",
+        "impl/vhal_v2_0/LinearFakeValueGenerator.cpp",
+        "impl/vhal_v2_0/JsonFakeValueGenerator.cpp",
     ],
     local_include_dirs: ["common/include/vhal_v2_0"],
     export_include_dirs: ["impl"],
@@ -71,6 +73,7 @@
         "libprotobuf-cpp-lite",
     ],
     static_libs: [
+        "libjsoncpp",
         "libqemu_pipe",
         "android.hardware.automotive.vehicle@2.0-libproto-native",
     ],
@@ -107,6 +110,7 @@
         "android.hardware.automotive.vehicle@2.0-manager-lib",
         "android.hardware.automotive.vehicle@2.0-default-impl-lib",
         "android.hardware.automotive.vehicle@2.0-libproto-native",
+        "libjsoncpp",
         "libqemu_pipe",
     ],
 }
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
index 348f878..012af1e 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
@@ -42,38 +42,66 @@
 constexpr int ALL_WHEELS =
     (int)(Wheel::LEFT_FRONT | Wheel::RIGHT_FRONT | Wheel::LEFT_REAR | Wheel::RIGHT_REAR);
 
-/*
- * This property is used for test purpose to generate fake events.
- *
- * It has the following format:
- *
- * int32Values[0] - command (see FakeDataCommand below for possible values)
- * int32Values[1] - VehicleProperty to which command applies
+/**
+ * This property is used for test purpose to generate fake events. Here is the test package that
+ * is referencing this property definition: packages/services/Car/tests/vehiclehal_test
  */
 const int32_t kGenerateFakeDataControllingProperty =
     0x0666 | VehiclePropertyGroup::VENDOR | VehicleArea::GLOBAL | VehiclePropertyType::MIXED;
 
+/**
+ * FakeDataCommand enum defines the supported command type for kGenerateFakeDataControllingProperty.
+ * All those commands can be send independently with each other. And each will override the one sent
+ * previously.
+ *
+ * The controlling property has the following format:
+ *
+ *     int32Values[0] - command enum defined in FakeDataCommand
+ *
+ * The format of the arguments is defined for each command type as below:
+ */
 enum class FakeDataCommand : int32_t {
-    /** Stops generating of fake data that was triggered by Start command */
-    Stop = 0,
-
     /**
-     * Starts fake data generation.  Caller must provide additional data:
+     * Starts linear fake data generation. Caller must provide additional data:
+     *     int32Values[1] - VehicleProperty to which command applies
      *     int64Values[0] - periodic interval in nanoseconds
      *     floatValues[0] - initial value
-     *     floatValues[1] - dispersion defines min and max range relative to initial value
+     *     floatValues[1] - dispersion defines the min/max value relative to initial value, where
+     *                      max = initial_value + dispersion, min = initial_value - dispersion.
+     *                      Dispersion should be non-negative, otherwise the behavior is undefined.
      *     floatValues[2] - increment, with every timer tick the value will be incremented by this
-     * amount
+     *                      amount. When reaching to max value, the current value will be set to min.
+     *                      It should be non-negative, otherwise the behavior is undefined.
      */
-    Start = 1,
+    StartLinear = 0,
+
+    /** Stops generating of fake data that was triggered by Start commands.
+     *     int32Values[1] - VehicleProperty to which command applies. VHAL will stop the
+     *                      corresponding linear generation for that property.
+     */
+    StopLinear = 1,
+
+    /**
+     * Starts JSON-based fake data generation. Caller must provide a string value specifying
+     * the path to fake value JSON file:
+     *     stringValue    - path to the fake values JSON file
+     */
+    StartJson = 2,
+
+    /**
+     * Stops JSON-based fake data generation. No additional arguments needed.
+     */
+    StopJson = 3,
 
     /**
      * Injects key press event (HAL incorporates UP/DOWN acction and triggers 2 HAL events for every
-     * key-press). Caller must provide the following data: int32Values[2] - Android key code
+     * key-press). We set the enum with high number to leave space for future start/stop commands.
+     * Caller must provide the following data:
+     *     int32Values[2] - Android key code
      *     int32Values[3] - target display (0 - for main display, 1 - for instrument cluster, see
-     * VehicleDisplay)
+     *                      VehicleDisplay)
      */
-    KeyPress = 2,
+    KeyPress = 100,
 };
 
 const int32_t kHvacPowerProperties[] = {
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
index 2eb905d..fb54195 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
@@ -19,6 +19,8 @@
 #include <android-base/macros.h>
 
 #include "EmulatedVehicleHal.h"
+#include "JsonFakeValueGenerator.h"
+#include "LinearFakeValueGenerator.h"
 #include "Obd2SensorStore.h"
 
 namespace android {
@@ -88,10 +90,12 @@
 EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore)
     : mPropStore(propStore),
       mHvacPowerProps(std::begin(kHvacPowerProperties), std::end(kHvacPowerProperties)),
-      mRecurrentTimer(std::bind(&EmulatedVehicleHal::onContinuousPropertyTimer,
-                                  this, std::placeholders::_1)),
-      mFakeValueGenerator(std::bind(&EmulatedVehicleHal::onFakeValueGenerated,
-                                    this, std::placeholders::_1, std::placeholders::_2)) {
+      mRecurrentTimer(
+          std::bind(&EmulatedVehicleHal::onContinuousPropertyTimer, this, std::placeholders::_1)),
+      mLinearFakeValueGenerator(std::make_unique<LinearFakeValueGenerator>(
+          std::bind(&EmulatedVehicleHal::onFakeValueGenerated, this, std::placeholders::_1))),
+      mJsonFakeValueGenerator(std::make_unique<JsonFakeValueGenerator>(
+          std::bind(&EmulatedVehicleHal::onFakeValueGenerated, this, std::placeholders::_1))) {
     initStaticConfig();
     for (size_t i = 0; i < arraysize(kVehicleProperties); i++) {
         mPropStore->registerProperty(kVehicleProperties[i].config);
@@ -328,42 +332,29 @@
 StatusCode EmulatedVehicleHal::handleGenerateFakeDataRequest(const VehiclePropValue& request) {
     ALOGI("%s", __func__);
     const auto& v = request.value;
-    if (v.int32Values.size() < 2) {
-        ALOGE("%s: expected at least 2 elements in int32Values, got: %zu", __func__,
-                v.int32Values.size());
+    if (!v.int32Values.size()) {
+        ALOGE("%s: expected at least \"command\" field in int32Values", __func__);
         return StatusCode::INVALID_ARG;
     }
 
     FakeDataCommand command = static_cast<FakeDataCommand>(v.int32Values[0]);
-    int32_t propId = v.int32Values[1];
 
     switch (command) {
-        case FakeDataCommand::Start: {
-            if (!v.int64Values.size()) {
-                ALOGE("%s: interval is not provided in int64Values", __func__);
-                return StatusCode::INVALID_ARG;
-            }
-            auto interval = std::chrono::nanoseconds(v.int64Values[0]);
-
-            if (v.floatValues.size() < 3) {
-                ALOGE("%s: expected at least 3 element sin floatValues, got: %zu", __func__,
-                        v.floatValues.size());
-                return StatusCode::INVALID_ARG;
-            }
-            float initialValue = v.floatValues[0];
-            float dispersion = v.floatValues[1];
-            float increment = v.floatValues[2];
-
-            ALOGI("%s, propId: %d, initalValue: %f", __func__, propId, initialValue);
-            mFakeValueGenerator.startGeneratingHalEvents(
-                interval, propId, initialValue, dispersion, increment);
-
-            break;
+        case FakeDataCommand::StartLinear: {
+            ALOGI("%s, FakeDataCommand::StartLinear", __func__);
+            return mLinearFakeValueGenerator->start(request);
         }
-        case FakeDataCommand::Stop: {
-            ALOGI("%s, FakeDataCommand::Stop", __func__);
-            mFakeValueGenerator.stopGeneratingHalEvents(propId);
-            break;
+        case FakeDataCommand::StartJson: {
+            ALOGI("%s, FakeDataCommand::StartJson", __func__);
+            return mJsonFakeValueGenerator->start(request);
+        }
+        case FakeDataCommand::StopLinear: {
+            ALOGI("%s, FakeDataCommand::StopLinear", __func__);
+            return mLinearFakeValueGenerator->stop(request);
+        }
+        case FakeDataCommand::StopJson: {
+            ALOGI("%s, FakeDataCommand::StopJson", __func__);
+            return mJsonFakeValueGenerator->stop(request);
         }
         case FakeDataCommand::KeyPress: {
             ALOGI("%s, FakeDataCommand::KeyPress", __func__);
@@ -374,7 +365,6 @@
             doHalEvent(createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_UP, keyCode, display));
             break;
         }
-
         default: {
             ALOGE("%s: unexpected command: %d", __func__, command);
             return StatusCode::INVALID_ARG;
@@ -396,30 +386,16 @@
     return keyEvent;
 }
 
-void EmulatedVehicleHal::onFakeValueGenerated(int32_t propId, float value) {
+void EmulatedVehicleHal::onFakeValueGenerated(const VehiclePropValue& value) {
+    ALOGD("%s: %s", __func__, toString(value).c_str());
     static constexpr bool shouldUpdateStatus = false;
 
-    VehiclePropValuePtr updatedPropValue {};
-    switch (getPropType(propId)) {
-        case VehiclePropertyType::FLOAT:
-            updatedPropValue = getValuePool()->obtainFloat(value);
-            break;
-        case VehiclePropertyType::INT32:
-            updatedPropValue = getValuePool()->obtainInt32(static_cast<int32_t>(value));
-            break;
-        default:
-            ALOGE("%s: data type for property: 0x%x not supported", __func__, propId);
-            return;
-
-    }
-
+    VehiclePropValuePtr updatedPropValue = getValuePool()->obtain(value);
     if (updatedPropValue) {
-        updatedPropValue->prop = propId;
-        updatedPropValue->areaId = 0;  // Add area support if necessary.
         updatedPropValue->timestamp = elapsedRealtimeNano();
         updatedPropValue->status = VehiclePropertyStatus::AVAILABLE;
         mPropStore->writeValue(*updatedPropValue, shouldUpdateStatus);
-        auto changeMode = mPropStore->getConfigOrDie(propId)->changeMode;
+        auto changeMode = mPropStore->getConfigOrDie(value.prop)->changeMode;
         if (VehiclePropertyChangeMode::ON_CHANGE == changeMode) {
             doHalEvent(move(updatedPropValue));
         }
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
index d291dba..c188aef 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
@@ -30,9 +30,10 @@
 #include "vhal_v2_0/VehiclePropertyStore.h"
 
 #include "DefaultConfig.h"
-#include "VehicleEmulator.h"
 #include "FakeValueGenerator.h"
 
+#include "VehicleEmulator.h"
+
 namespace android {
 namespace hardware {
 namespace automotive {
@@ -66,7 +67,7 @@
     }
 
     StatusCode handleGenerateFakeDataRequest(const VehiclePropValue& request);
-    void onFakeValueGenerated(int32_t propId, float value);
+    void onFakeValueGenerated(const VehiclePropValue& value);
     VehiclePropValuePtr createHwInputKeyProp(VehicleHwKeyInputAction action, int32_t keyCode,
                                              int32_t targetDisplay);
 
@@ -84,7 +85,8 @@
     VehiclePropertyStore* mPropStore;
     std::unordered_set<int32_t> mHvacPowerProps;
     RecurrentTimer mRecurrentTimer;
-    FakeValueGenerator mFakeValueGenerator;
+    std::unique_ptr<FakeValueGenerator> mLinearFakeValueGenerator;
+    std::unique_ptr<FakeValueGenerator> mJsonFakeValueGenerator;
 };
 
 }  // impl
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/FakeValueGenerator.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/FakeValueGenerator.h
index 7bbbb08..1eeb88d 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/FakeValueGenerator.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/FakeValueGenerator.h
@@ -14,15 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef android_hardware_automotive_vehicle_V2_0_impl_FakeHalEventGenerator_H_
-#define android_hardware_automotive_vehicle_V2_0_impl_FakeHalEventGenerator_H_
-
-#include <chrono>
+#ifndef android_hardware_automotive_vehicle_V2_0_impl_FakeValueGenerator_H_
+#define android_hardware_automotive_vehicle_V2_0_impl_FakeValueGenerator_H_
 
 #include <android/hardware/automotive/vehicle/2.0/types.h>
 
-#include <vhal_v2_0/RecurrentTimer.h>
-
 namespace android {
 namespace hardware {
 namespace automotive {
@@ -31,89 +27,27 @@
 
 namespace impl {
 
+using OnHalEvent = std::function<void(const VehiclePropValue& event)>;
+using MuxGuard = std::lock_guard<std::mutex>;
+
 class FakeValueGenerator {
-private:
-    // In every timer tick we may want to generate new value based on initial value for debug
-    // purpose. It's better to have sequential values to see if events gets delivered in order
-    // to the client.
-
-    struct GeneratorCfg {
-        float initialValue;  //
-        float currentValue;  //  Should be in range (initialValue +/- dispersion).
-        float dispersion;    //  Defines minimum and maximum value based on initial value.
-        float increment;     //  Value that we will be added to currentValue with each timer tick.
-    };
-
 public:
-    using OnHalEvent = std::function<void(int32_t propId, float value)>;
-
-    FakeValueGenerator(const OnHalEvent& onHalEvent) :
-        mOnHalEvent(onHalEvent),
-        mRecurrentTimer(std::bind(&FakeValueGenerator::onTimer, this,
-                                  std::placeholders::_1))
-    {}
-
-    ~FakeValueGenerator() = default;
-
-
-    void startGeneratingHalEvents(std::chrono::nanoseconds interval, int propId, float initialValue,
-                                  float dispersion, float increment) {
-        MuxGuard g(mLock);
-
-        removeLocked(propId);
-
-        mGenCfg.insert({propId, GeneratorCfg {
-            .initialValue = initialValue,
-            .currentValue = initialValue,
-            .dispersion = dispersion,
-            .increment = increment,
-        }});
-
-        mRecurrentTimer.registerRecurrentEvent(interval, propId);
-    }
-
-    void stopGeneratingHalEvents(int propId) {
-        MuxGuard g(mLock);
-        if (propId == 0) {
-            // Remove all.
-            for (auto&& it : mGenCfg) {
-                removeLocked(it.first);
-            }
-        } else {
-            removeLocked(propId);
-        }
-    }
-
-private:
-    void removeLocked(int propId) {
-        if (mGenCfg.erase(propId)) {
-            mRecurrentTimer.unregisterRecurrentEvent(propId);
-        }
-    }
-
-    void onTimer(const std::vector<int32_t>& properties) {
-        MuxGuard g(mLock);
-
-        for (int32_t propId : properties) {
-            auto& cfg = mGenCfg[propId];
-            cfg.currentValue += cfg.increment;
-            if (cfg.currentValue > cfg.initialValue + cfg.dispersion) {
-                cfg.currentValue = cfg.initialValue - cfg.dispersion;
-            }
-            mOnHalEvent(propId, cfg.currentValue);
-        }
-    }
-
-private:
-    using MuxGuard = std::lock_guard<std::mutex>;
-
-    mutable std::mutex mLock;
-    OnHalEvent mOnHalEvent;
-    RecurrentTimer mRecurrentTimer;
-    std::unordered_map<int32_t, GeneratorCfg> mGenCfg;
+    virtual ~FakeValueGenerator() = default;
+    /**
+     * Starts generating VHAL events
+     *
+     * @param request in VehiclePropValue with required information to start fake data generation
+     * @return StatusCode of the start request
+     */
+    virtual StatusCode start(const VehiclePropValue& request) = 0;
+    /**
+     * Stops generating VHAL events
+     * @param request in VehiclePropValue with required information to stop fake data generation
+     * @return StatusCode of the stop request
+     */
+    virtual StatusCode stop(const VehiclePropValue& request) = 0;
 };
 
-
 }  // impl
 
 }  // namespace V2_0
@@ -122,6 +56,4 @@
 }  // namespace hardware
 }  // namespace android
 
-
-
-#endif //android_hardware_automotive_vehicle_V2_0_impl_FakeHalEventGenerator_H_
+#endif  // android_hardware_automotive_vehicle_V2_0_impl_FakeValueGenerator_H_
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.cpp
new file mode 100644
index 0000000..88b8f86
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "JsonFakeValueGenerator"
+
+#include <fstream>
+
+#include <log/log.h>
+#include <vhal_v2_0/VehicleUtils.h>
+
+#include "JsonFakeValueGenerator.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+JsonFakeValueGenerator::JsonFakeValueGenerator(const OnHalEvent& onHalEvent)
+    : mOnHalEvent(onHalEvent), mThread(&JsonFakeValueGenerator::loop, this) {}
+
+JsonFakeValueGenerator::~JsonFakeValueGenerator() {
+    mStopRequested = true;
+    {
+        MuxGuard g(mLock);
+        mGenCfg.index = 0;
+        mGenCfg.events.clear();
+    }
+    mCond.notify_one();
+    if (mThread.joinable()) {
+        mThread.join();
+    }
+}
+
+StatusCode JsonFakeValueGenerator::start(const VehiclePropValue& request) {
+    const auto& v = request.value;
+    if (v.stringValue.empty()) {
+        ALOGE("%s: path to JSON file is missing", __func__);
+        return StatusCode::INVALID_ARG;
+    }
+    const char* file = v.stringValue.c_str();
+    std::ifstream ifs(file);
+    if (!ifs) {
+        ALOGE("%s: couldn't open %s for parsing.", __func__, file);
+        return StatusCode::INTERNAL_ERROR;
+    }
+    std::vector<VehiclePropValue> fakeVhalEvents = parseFakeValueJson(ifs);
+
+    {
+        MuxGuard g(mLock);
+        mGenCfg = {0, fakeVhalEvents};
+    }
+    mCond.notify_one();
+    return StatusCode::OK;
+}
+
+StatusCode JsonFakeValueGenerator::stop(const VehiclePropValue& request) {
+    const auto& v = request.value;
+    if (!v.stringValue.empty()) {
+        ALOGI("%s: %s", __func__, v.stringValue.c_str());
+    }
+
+    {
+        MuxGuard g(mLock);
+        mGenCfg.index = 0;
+        mGenCfg.events.clear();
+    }
+    mCond.notify_one();
+    return StatusCode::OK;
+}
+
+std::vector<VehiclePropValue> JsonFakeValueGenerator::parseFakeValueJson(std::istream& is) {
+    std::vector<VehiclePropValue> fakeVhalEvents;
+
+    Json::Reader reader;
+    Json::Value rawEvents;
+    if (!reader.parse(is, rawEvents)) {
+        ALOGE("%s: Failed to parse fake data JSON file. Error: %s", __func__,
+              reader.getFormattedErrorMessages().c_str());
+        return fakeVhalEvents;
+    }
+
+    for (Json::Value::ArrayIndex i = 0; i < rawEvents.size(); i++) {
+        Json::Value rawEvent = rawEvents[i];
+        if (!rawEvent.isObject()) {
+            ALOGE("%s: VHAL JSON event should be an object, %s", __func__,
+                  rawEvent.toStyledString().c_str());
+            continue;
+        }
+        if (rawEvent["prop"].empty() || rawEvent["areaId"].empty() || rawEvent["value"].empty() ||
+            rawEvent["timestamp"].empty()) {
+            ALOGE("%s: VHAL JSON event has missing fields, skip it, %s", __func__,
+                  rawEvent.toStyledString().c_str());
+            continue;
+        }
+        VehiclePropValue event = {.prop = rawEvent["prop"].asInt(),
+                                  .areaId = rawEvent["areaId"].asInt(),
+                                  .timestamp = rawEvent["timestamp"].asInt64()};
+
+        Json::Value rawEventValue = rawEvent["value"];
+        auto& value = event.value;
+        switch (getPropType(event.prop)) {
+            case VehiclePropertyType::BOOLEAN:
+            case VehiclePropertyType::INT32:
+                value.int32Values.resize(1);
+                value.int32Values[0] = rawEventValue.asInt();
+                break;
+            case VehiclePropertyType::INT64:
+                value.int64Values.resize(1);
+                value.int64Values[0] = rawEventValue.asInt64();
+                break;
+            case VehiclePropertyType::FLOAT:
+                value.floatValues.resize(1);
+                value.floatValues[0] = rawEventValue.asFloat();
+                break;
+            case VehiclePropertyType::STRING:
+                value.stringValue = rawEventValue.asString();
+                break;
+            default:
+                ALOGE("%s: unsupported type for property: 0x%x with value: %s", __func__,
+                      event.prop, rawEventValue.asString().c_str());
+                continue;
+        }
+        fakeVhalEvents.push_back(event);
+    }
+    return fakeVhalEvents;
+}
+
+void JsonFakeValueGenerator::loop() {
+    static constexpr auto kInvalidTime = TimePoint(Nanos::max());
+
+    while (!mStopRequested) {
+        auto nextEventTime = kInvalidTime;
+        {
+            MuxGuard g(mLock);
+            if (mGenCfg.index < mGenCfg.events.size()) {
+                mOnHalEvent(mGenCfg.events[mGenCfg.index]);
+            }
+            if (!mGenCfg.events.empty() && mGenCfg.index < mGenCfg.events.size() - 1) {
+                Nanos intervalNano =
+                    static_cast<Nanos>(mGenCfg.events[mGenCfg.index + 1].timestamp -
+                                       mGenCfg.events[mGenCfg.index].timestamp);
+                nextEventTime = Clock::now() + intervalNano;
+            }
+            mGenCfg.index++;
+        }
+
+        std::unique_lock<std::mutex> g(mLock);
+        mCond.wait_until(g, nextEventTime);
+    }
+}
+
+}  // namespace impl
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.h
new file mode 100644
index 0000000..51da4c5
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef android_hardware_automotive_vehicle_V2_0_impl_JsonFakeValueGenerator_H_
+#define android_hardware_automotive_vehicle_V2_0_impl_JsonFakeValueGenerator_H_
+
+#include <atomic>
+#include <chrono>
+#include <condition_variable>
+#include <iostream>
+#include <thread>
+
+#include <json/json.h>
+
+#include "FakeValueGenerator.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+class JsonFakeValueGenerator : public FakeValueGenerator {
+private:
+    using Nanos = std::chrono::nanoseconds;
+    using Clock = std::chrono::steady_clock;
+    using TimePoint = std::chrono::time_point<Clock, Nanos>;
+
+    struct GeneratorCfg {
+        size_t index;
+        std::vector<VehiclePropValue> events;
+    };
+
+public:
+    JsonFakeValueGenerator(const OnHalEvent& onHalEvent);
+    ~JsonFakeValueGenerator();
+    StatusCode start(const VehiclePropValue& request) override;
+    StatusCode stop(const VehiclePropValue& request) override;
+
+private:
+    std::vector<VehiclePropValue> parseFakeValueJson(std::istream& is);
+    void loop();
+
+private:
+    OnHalEvent mOnHalEvent;
+    std::thread mThread;
+    mutable std::mutex mLock;
+    std::condition_variable mCond;
+    GeneratorCfg mGenCfg;
+    std::atomic_bool mStopRequested{false};
+};
+
+}  // namespace impl
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_automotive_vehicle_V2_0_impl_JsonFakeValueGenerator_H_
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/LinearFakeValueGenerator.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/LinearFakeValueGenerator.cpp
new file mode 100644
index 0000000..8cb9322
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/LinearFakeValueGenerator.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "LinearFakeValueGenerator"
+
+#include <log/log.h>
+#include <vhal_v2_0/VehicleUtils.h>
+
+#include "LinearFakeValueGenerator.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+LinearFakeValueGenerator::LinearFakeValueGenerator(const OnHalEvent& onHalEvent)
+    : mOnHalEvent(onHalEvent),
+      mRecurrentTimer(std::bind(&LinearFakeValueGenerator::onTimer, this, std::placeholders::_1)) {}
+
+StatusCode LinearFakeValueGenerator::start(const VehiclePropValue& request) {
+    const auto& v = request.value;
+    if (v.int32Values.size() < 2) {
+        ALOGE("%s: expected property ID in int32Values", __func__);
+        return StatusCode::INVALID_ARG;
+    }
+    int32_t propId = v.int32Values[1];
+
+    if (!v.int64Values.size()) {
+        ALOGE("%s: interval is not provided in int64Values", __func__);
+        return StatusCode::INVALID_ARG;
+    }
+    auto interval = std::chrono::nanoseconds(v.int64Values[0]);
+
+    if (v.floatValues.size() < 3) {
+        ALOGE("%s: expected at least 3 elements in floatValues, got: %zu", __func__,
+              v.floatValues.size());
+        return StatusCode::INVALID_ARG;
+    }
+    float initialValue = v.floatValues[0];
+    float dispersion = v.floatValues[1];
+    float increment = v.floatValues[2];
+
+    MuxGuard g(mLock);
+    removeLocked(propId);
+    mGenCfg.insert({propId, GeneratorCfg{
+                                .initialValue = initialValue,
+                                .currentValue = initialValue,
+                                .dispersion = dispersion,
+                                .increment = increment,}});
+
+    mRecurrentTimer.registerRecurrentEvent(interval, propId);
+    return StatusCode::OK;
+}
+
+StatusCode LinearFakeValueGenerator::stop(const VehiclePropValue& request) {
+    const auto& v = request.value;
+    if (v.int32Values.size() < 2) {
+        ALOGE("%s: expected property ID in int32Values", __func__);
+        return StatusCode::INVALID_ARG;
+    }
+    int32_t propId = v.int32Values[1];
+
+    MuxGuard g(mLock);
+    if (propId == 0) {
+        // Remove all.
+        for (auto&& it : mGenCfg) {
+            removeLocked(it.first);
+        }
+    } else {
+        removeLocked(propId);
+    }
+    return StatusCode::OK;
+}
+
+void LinearFakeValueGenerator::removeLocked(int propId) {
+    if (mGenCfg.erase(propId)) {
+        mRecurrentTimer.unregisterRecurrentEvent(propId);
+    }
+}
+
+void LinearFakeValueGenerator::onTimer(const std::vector<int32_t>& properties) {
+    MuxGuard g(mLock);
+
+    for (int32_t propId : properties) {
+        auto& cfg = mGenCfg[propId];
+        cfg.currentValue += cfg.increment;
+        if (cfg.currentValue > cfg.initialValue + cfg.dispersion) {
+            cfg.currentValue = cfg.initialValue - cfg.dispersion;
+        }
+        VehiclePropValue event = {.prop = propId};
+        auto& value = event.value;
+        switch (getPropType(event.prop)) {
+            case VehiclePropertyType::INT32:
+                value.int32Values.resize(1);
+                value.int32Values[0] = static_cast<int32_t>(cfg.currentValue);
+                break;
+            case VehiclePropertyType::INT64:
+                value.int64Values.resize(1);
+                value.int64Values[0] = static_cast<int64_t>(cfg.currentValue);
+                break;
+            case VehiclePropertyType::FLOAT:
+                value.floatValues.resize(1);
+                value.floatValues[0] = cfg.currentValue;
+                break;
+            default:
+                ALOGE("%s: unsupported property type for 0x%x", __func__, event.prop);
+                continue;
+        }
+        mOnHalEvent(event);
+    }
+}
+
+}  // namespace impl
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/LinearFakeValueGenerator.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/LinearFakeValueGenerator.h
new file mode 100644
index 0000000..fe6d097
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/LinearFakeValueGenerator.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef android_hardware_automotive_vehicle_V2_0_impl_LinearFakeValueGenerator_H_
+#define android_hardware_automotive_vehicle_V2_0_impl_LinearFakeValueGenerator_H_
+
+#include <vhal_v2_0/RecurrentTimer.h>
+
+#include "FakeValueGenerator.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+class LinearFakeValueGenerator : public FakeValueGenerator {
+private:
+    // In every timer tick we may want to generate new value based on initial value for debug
+    // purpose. It's better to have sequential values to see if events gets delivered in order
+    // to the client.
+
+    struct GeneratorCfg {
+        float initialValue;  //
+        float currentValue;  //  Should be in range (initialValue +/- dispersion).
+        float dispersion;    //  Defines minimum and maximum value based on initial value.
+        float increment;     //  Value that we will be added to currentValue with each timer tick.
+    };
+
+public:
+    LinearFakeValueGenerator(const OnHalEvent& onHalEvent);
+    ~LinearFakeValueGenerator() = default;
+    StatusCode start(const VehiclePropValue& request) override;
+    StatusCode stop(const VehiclePropValue& request) override;
+
+private:
+    void removeLocked(int propId);
+    void onTimer(const std::vector<int32_t>& properties);
+
+private:
+    mutable std::mutex mLock;
+    OnHalEvent mOnHalEvent;
+    RecurrentTimer mRecurrentTimer;
+    std::unordered_map<int32_t, GeneratorCfg> mGenCfg;
+};
+
+}  // namespace impl
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_automotive_vehicle_V2_0_impl_LinearFakeValueGenerator_H_
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index faa1adc..23f9b77 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -42,6 +42,9 @@
 };
 
 /**
+ * Vehicle Areas
+ * Used to construct property IDs in the VehicleProperty enum.
+ *
  * Some properties may be associated with particular vehicle areas. For
  * example, VehicleProperty:DOOR_LOCK property must be associated with
  * particular door, thus this property must be marked with
@@ -50,8 +53,34 @@
  * Other properties may not be associated with particular vehicle area,
  * these kind of properties must have VehicleArea:GLOBAL flag.
  *
- * Used to create property ID in VehicleProperty enum.
- */
+ * [Definition] Area: An area represents a unique element of an AreaType.
+ *   For instance, if AreaType is WINDOW, then an area may be FRONT_WINDSHIELD.
+ *
+ * [Definition] AreaID: An AreaID is a combination of one or more areas,
+ *   and is represented using a bitmask of Area enums. Different AreaTypes may
+ *   not be mixed in a single AreaID. For instance, a window area cannot be
+ *   combined with a seat area in an AreaID.
+ *
+ * Rules for mapping a zoned property to AreaIDs:
+ *  - A property must be mapped to an array of AreaIDs that are impacted when
+ *    the property value changes.
+ *  - Each element in the array must represent an AreaID, in which, the
+ *    property value can only be changed together in all the areas within
+ *    an AreaID and never independently. That is, when the property value
+ *    changes in one of the areas in an AreaID in the array, then it must
+ *    automatically change in all other areas in the AreaID.
+ *  - The property value must be independently controllable in any two
+ *    different AreaIDs in the array.
+ *  - An area must only appear once in the array of AreaIDs. That is, an
+ *    area must only be part of a single AreaID in the array.
+ *
+ * [Definition] Global Property: A property that applies to the entire car
+ *   and is not associated with a specific area. For example, FUEL_LEVEL,
+ *   HVAC_STEERING_WHEEL_HEAT.
+ *
+ * Rules for mapping a global property to AreaIDs:
+ *  - A global property must not be mapped to AreaIDs.
+*/
 enum VehicleArea : int32_t {
     GLOBAL      = 0x01000000,
     /** WINDOW maps to enum VehicleAreaWindow */
@@ -331,16 +360,16 @@
     /**
      * Reports wheel ticks
      *
-     * The first four elements represent ticks for individual wheels in the
+     * The first element in the vector is a reset count.  A reset indicates
+     * previous tick counts are not comparable with this and future ones.  Some
+     * sort of discontinuity in tick counting has occurred.
+     *
+     * The next four elements represent ticks for individual wheels in the
      * following order: front left, front right, rear right, rear left.  All
      * tick counts are cumulative.  Tick counts increment when the vehicle
      * moves forward, and decrement when vehicles moves in reverse.  The ticks
      * should be reset to 0 when the vehicle is started by the user.
      *
-     * The next element in the vector is a reset count.  A reset indicates
-     * previous tick counts are not comparable with this and future ones.  Some
-     * sort of discontinuity in tick counting has occurred.
-     *
      *  int64Values[0] = reset count
      *  int64Values[1] = front left ticks
      *  int64Values[2] = front right ticks
@@ -489,6 +518,12 @@
      *
      * This is the gear selected by the user.
      *
+     * Values in the config data must represent the list of supported gears
+     * for this vehicle.  For example, config data for an automatic transmission
+     * must contain {GEAR_NEUTRAL, GEAR_REVERSE, GEAR_PARK, GEAR_DRIVE,
+     * GEAR_1, GEAR_2,...} and for manual transmission the list must be
+     * {GEAR_NEUTRAL, GEAR_REVERSE, GEAR_1, GEAR_2,...}
+     *
      * @change_mode VehiclePropertyChangeMode:ON_CHANGE
      * @access VehiclePropertyAccess:READ
      * @data_enum VehicleGear
@@ -500,8 +535,17 @@
         | VehicleArea:GLOBAL),
 
     /**
-     * Current gear. In non-manual case, selected gear does not necessarily
-     * match the current gear.
+     * Current gear. In non-manual case, selected gear may not
+     * match the current gear. For example, if the selected gear is GEAR_DRIVE,
+     * the current gear will be one of GEAR_1, GEAR_2 etc, which reflects
+     * the actual gear the transmission is currently running in.
+     *
+     * Values in the config data must represent the list of supported gears
+     * for this vehicle.  For example, config data for an automatic transmission
+     * must contain {GEAR_NEUTRAL, GEAR_REVERSE, GEAR_PARK, GEAR_1, GEAR_2,...}
+     * and for manual transmission the list must be
+     * {GEAR_NEUTRAL, GEAR_REVERSE, GEAR_1, GEAR_2,...}. This list need not be the
+     * same as that of the supported gears reported in GEAR_SELECTION.
      *
      * @change_mode VehiclePropertyChangeMode:ON_CHANGE
      * @access VehiclePropertyAccess:READ
@@ -625,6 +669,41 @@
         | VehiclePropertyType:BOOLEAN
         | VehicleArea:GLOBAL),
 
+    /*
+     * HVAC Properties
+     *
+     * Additional rules for mapping a zoned HVAC property to AreaIDs:
+     *  - Every seat in VehicleAreaSeat that is available in the car, must be
+     *    part of an AreaID in the AreaID array.
+     *
+     * Example 1: A car has two front seats (ROW_1_LEFT, ROW_1_RIGHT) and three
+     *  back seats (ROW_2_LEFT, ROW_2_CENTER, ROW_2_RIGHT). There are two
+     *  temperature control units -- driver side and passenger side.
+     *   - A valid mapping set of AreaIDs for HVAC_TEMPERATURE_SET would be a
+     *     two element array:
+     *      - ROW_1_LEFT  | ROW_2_LEFT
+     *      - ROW_1_RIGHT | ROW_2_CENTER | ROW_2_RIGHT
+     *   - An alternative mapping for the same hardware configuration would be:
+     *      - ROW_1_LEFT  | ROW_2_CENTER | ROW_2_LEFT
+     *      - ROW_1_RIGHT | ROW_2_RIGHT
+     *  The temperature controllers are assigned to the seats which they
+     *  "most influence", but every seat must be included exactly once. The
+     *  assignment of the center rear seat to the left or right AreaID may seem
+     *  arbitrary, but the inclusion of every seat in exactly one AreaID ensures
+     *  that the seats in the car are all expressed and that a "reasonable" way
+     *  to affect each seat is available.
+     *
+     * Example 2: A car has three seat rows with two seats in the front row (ROW_1_LEFT,
+     *  ROW_1_RIGHT) and three seats in the second (ROW_2_LEFT, ROW_2_CENTER,
+     *  ROW_2_RIGHT) and third rows (ROW_3_LEFT, ROW_3_CENTER, ROW_3_RIGHT). There
+     *  are three temperature control units -- driver side, passenger side, and rear.
+     *   - A reasonable way to map HVAC_TEMPERATURE_SET to AreaIDs is a three
+     *     element array:
+     *     - ROW_1_LEFT
+     *     - ROW_1_RIGHT
+     *     - ROW_2_LEFT | ROW_2_CENTER | ROW_2_RIGHT | ROW_3_LEFT | ROW_3_CENTER | ROW_3_RIGHT
+     */
+
     /**
      * Fan speed setting
      *
@@ -754,22 +833,34 @@
         | VehicleArea:SEAT),
 
     /**
-     * Enable temperature coupling between zones.
+     * Enable temperature coupling between areas.
      *
-     * The areaId for this property must include the zones that are coupled
-     * together.  Typically, the front two zones (ROW_1_LEFT and ROW_1_RIGHT)
-     * are coupled together when this property is enabled.  Thus, the areaId
-     * shall be (ROW_1_LEFT | ROW_1_RIGHT).  When the property is enabled, the
-     * ECU may synchronize the temperature for the affected zones.  Any
-     * parameters modified as a side effect of turning on/off the DUAL_ON
-     * parameter shall generate onPropertyEvent() callbacks to the VHAL.  In
-     * addition, if setting a temperature (i.e. driver's temp) changes another
-     * temperature (i.e. front passenger's temp), then the appropriate
+     * The AreaIDs for HVAC_DUAL_ON property shall contain a combination of
+     * HVAC_TEMPERATURE_SET AreaIDs that can be coupled together. If
+     * HVAC_TEMPERATURE_SET is mapped to AreaIDs [a_1, a_2, ..., a_n], and if
+     * HVAC_DUAL_ON can be enabled to couple a_i and a_j, then HVAC_DUAL_ON
+     * property must be mapped to [a_i | a_j]. Further, if a_k and a_l can also
+     * be coupled together separately then HVAC_DUAL_ON must be mapped to
+     * [a_i | a_j, a_k | a_l].
+     *
+     * Example: A car has two front seats (ROW_1_LEFT, ROW_1_RIGHT) and three
+     *  back seats (ROW_2_LEFT, ROW_2_CENTER, ROW_2_RIGHT). There are two
+     *  temperature control units -- driver side and passenger side -- which can
+     *  be optionally synchronized. This may be expressed in the AreaIDs this way:
+     *  - HVAC_TEMPERATURE_SET->[ROW_1_LEFT | ROW_2_LEFT, ROW_1_RIGHT | ROW_2_CENTER | ROW_2_RIGHT]
+     *  - HVAC_DUAL_ON->[ROW_1_LEFT | ROW_2_LEFT | ROW_1_RIGHT | ROW_2_CENTER | ROW_2_RIGHT]
+     *
+     * When the property is enabled, the ECU must synchronize the temperature
+     * for the affected areas. Any parameters modified as a side effect
+     * of turning on/off the DUAL_ON parameter shall generate
+     * onPropertyEvent() callbacks to the VHAL. In addition, if setting
+     * a temperature (i.e. driver's temperature) changes another temperature
+     * (i.e. front passenger's temperature), then the appropriate
      * onPropertyEvent() callbacks must be generated.  If a user changes a
      * temperature that breaks the coupling (e.g. setting the passenger
      * temperature independently) then the VHAL must send the appropriate
      * onPropertyEvent() callbacks (i.e. HVAC_DUAL_ON = false,
-     * HVAC_TEMPERATURE_SET[zone] = xxx, etc).
+     * HVAC_TEMPERATURE_SET[AreaID] = xxx, etc).
      *
      * @change_mode VehiclePropertyChangeMode:ON_CHANGE
      * @access VehiclePropertyAccess:READ_WRITE
@@ -877,13 +968,41 @@
         | VehicleArea:SEAT),
 
     /**
-     * Represents global power state for HVAC.  Setting this property to false
+     * Represents global power state for HVAC. Setting this property to false
      * MAY mark some properties that control individual HVAC features/subsystems
      * to UNAVAILABLE state. Setting this property to true MAY mark some
      * properties that control individual HVAC features/subsystems to AVAILABLE
      * state (unless any/all of them are UNAVAILABLE on their own individual
-     * merits).  The list of properties affected by HVAC_POWER_ON must be set
-     * in the VehiclePropConfig.configArray.
+     * merits).
+     *
+     * [Definition] HvacPower_DependentProperties: Properties that need HVAC to be
+     *   powered on in order to enable their functionality. For example, in some cars,
+     *   in order to turn on the AC, HVAC must be powered on first.
+     *
+     * HvacPower_DependentProperties list must be set in the
+     * VehiclePropConfig.configArray. HvacPower_DependentProperties must only contain
+     * properties that are associated with VehicleArea:SEAT. Properties that are not
+     * associated with VehicleArea:SEAT, for example, HVAC_DEFROSTER, must never
+     * depend on HVAC_POWER_ON property and must never be part of
+     * HvacPower_DependentProperties list.
+     *
+     * AreaID mapping for HVAC_POWER_ON property must contain all AreaIDs that
+     * HvacPower_DependentProperties are mapped to.
+     *
+     * Example 1: A car has two front seats (ROW_1_LEFT, ROW_1_RIGHT) and three back
+     *  seats (ROW_2_LEFT, ROW_2_CENTER, ROW_2_RIGHT). If the HVAC features (AC,
+     *  Temperature etc.) throughout the car are dependent on a single HVAC power
+     *  controller then HVAC_POWER_ON must be mapped to
+     *  [ROW_1_LEFT | ROW_1_RIGHT | ROW_2_LEFT | ROW_2_CENTER | ROW_2_RIGHT].
+     *
+     * Example 2: A car has two seats in the front row (ROW_1_LEFT, ROW_1_RIGHT) and
+     *   three seats in the second (ROW_2_LEFT, ROW_2_CENTER, ROW_2_RIGHT) and third
+     *   rows (ROW_3_LEFT, ROW_3_CENTER, ROW_3_RIGHT). If the car has temperature
+     *   controllers in the front row which can operate entirely independently of
+     *   temperature controllers in the back of the vehicle, then HVAC_POWER_ON
+     *   must be mapped to a two element array:
+     *   - ROW_1_LEFT | ROW_1_RIGHT
+     *   - ROW_2_LEFT | ROW_2_CENTER | ROW_2_RIGHT | ROW_3_LEFT | ROW_3_CENTER | ROW_3_RIGHT
      *
      * @change_mode VehiclePropertyChangeMode:ON_CHANGE
      * @access VehiclePropertyAccess:READ_WRITE
diff --git a/camera/provider/2.4/default/Android.bp b/camera/provider/2.4/default/Android.bp
index 31c5fdd..ae24d78 100644
--- a/camera/provider/2.4/default/Android.bp
+++ b/camera/provider/2.4/default/Android.bp
@@ -61,6 +61,30 @@
     ],
 }
 
+
+cc_binary {
+    name: "android.hardware.camera.provider@2.4-service_64",
+    defaults: ["hidl_defaults"],
+    proprietary: true,
+    relative_install_path: "hw",
+    srcs: ["service.cpp"],
+    compile_multilib: "64",
+    init_rc: ["android.hardware.camera.provider@2.4-service_64.rc"],
+    shared_libs: [
+        "libhidlbase",
+        "libhidltransport",
+        "libbinder",
+        "liblog",
+        "libutils",
+        "android.hardware.camera.device@1.0",
+        "android.hardware.camera.device@3.2",
+        "android.hardware.camera.device@3.3",
+        "android.hardware.camera.device@3.4",
+        "android.hardware.camera.provider@2.4",
+        "android.hardware.camera.common@1.0",
+    ],
+}
+
 cc_binary {
     name: "android.hardware.camera.provider@2.4-external-service",
     defaults: ["hidl_defaults"],
diff --git a/camera/provider/2.4/default/android.hardware.camera.provider@2.4-service_64.rc b/camera/provider/2.4/default/android.hardware.camera.provider@2.4-service_64.rc
new file mode 100644
index 0000000..4c721ec
--- /dev/null
+++ b/camera/provider/2.4/default/android.hardware.camera.provider@2.4-service_64.rc
@@ -0,0 +1,7 @@
+service vendor.camera-provider-2-4 /vendor/bin/hw/android.hardware.camera.provider@2.4-service_64
+    class hal
+    user cameraserver
+    group audio camera input drmrpc
+    ioprio rt 4
+    capabilities SYS_NICE
+    writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks
diff --git a/cas/1.0/default/DescramblerImpl.cpp b/cas/1.0/default/DescramblerImpl.cpp
index 1f89933..6d5e2d5 100644
--- a/cas/1.0/default/DescramblerImpl.cpp
+++ b/cas/1.0/default/DescramblerImpl.cpp
@@ -96,13 +96,13 @@
         descramble_cb _hidl_cb) {
     ALOGV("%s", __FUNCTION__);
 
-    // Get a local copy of the shared_ptr for the plugin. Note that before
-    // calling the HIDL callback, this shared_ptr must be manually reset,
-    // since the client side could proceed as soon as the callback is called
-    // without waiting for this method to go out of scope.
-    std::shared_ptr<DescramblerPlugin> holder = std::atomic_load(&mPluginHolder);
-    if (holder.get() == nullptr) {
-        _hidl_cb(toStatus(INVALID_OPERATION), 0, NULL);
+    // hidl_memory's size is stored in uint64_t, but mapMemory's mmap will map
+    // size in size_t. If size is over SIZE_MAX, mapMemory mapMemory could succeed
+    // but the mapped memory's actual size will be smaller than the reported size.
+    if (srcBuffer.heapBase.size() > SIZE_MAX) {
+        ALOGE("Invalid hidl_memory size: %llu", srcBuffer.heapBase.size());
+        android_errorWriteLog(0x534e4554, "79376389");
+        _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
         return Void();
     }
 
@@ -112,7 +112,6 @@
     // mapped ashmem, since the offset and size is controlled by client.
     if (srcMem == NULL) {
         ALOGE("Failed to map src buffer.");
-        holder.reset();
         _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
         return Void();
     }
@@ -121,7 +120,6 @@
         ALOGE("Invalid src buffer range: offset %llu, size %llu, srcMem size %llu",
                 srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize());
         android_errorWriteLog(0x534e4554, "67962232");
-        holder.reset();
         _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
         return Void();
     }
@@ -139,7 +137,6 @@
                 "srcOffset %llu, totalBytesInSubSamples %llu, srcBuffer size %llu",
                 srcOffset, totalBytesInSubSamples, srcBuffer.size);
         android_errorWriteLog(0x534e4554, "67962232");
-        holder.reset();
         _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
         return Void();
     }
@@ -158,7 +155,6 @@
                     "dstOffset %llu, totalBytesInSubSamples %llu, srcBuffer size %llu",
                     dstOffset, totalBytesInSubSamples, srcBuffer.size);
             android_errorWriteLog(0x534e4554, "67962232");
-            holder.reset();
             _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
             return Void();
         }
@@ -167,6 +163,17 @@
                 dstBuffer.secureMemory.getNativeHandle());
         dstPtr = static_cast<void *>(handle);
     }
+
+    // Get a local copy of the shared_ptr for the plugin. Note that before
+    // calling the HIDL callback, this shared_ptr must be manually reset,
+    // since the client side could proceed as soon as the callback is called
+    // without waiting for this method to go out of scope.
+    std::shared_ptr<DescramblerPlugin> holder = std::atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        _hidl_cb(toStatus(INVALID_OPERATION), 0, NULL);
+        return Void();
+    }
+
     // Casting hidl SubSample to DescramblerPlugin::SubSample, but need
     // to ensure structs are actually idential
 
diff --git a/compatibility_matrices/Android.mk b/compatibility_matrices/Android.mk
index 8904c68..86fe00d 100644
--- a/compatibility_matrices/Android.mk
+++ b/compatibility_matrices/Android.mk
@@ -64,9 +64,9 @@
 LOCAL_MODULE_STEM := compatibility_matrix.3.xml
 LOCAL_SRC_FILES := $(LOCAL_MODULE_STEM)
 LOCAL_KERNEL_CONFIG_DATA_PATHS := \
-    4.4.0:$(my_kernel_config_data)/p/android-4.4 \
-    4.9.0:$(my_kernel_config_data)/p/android-4.9 \
-    4.14.0:$(my_kernel_config_data)/p/android-4.14 \
+    4.4.107:$(my_kernel_config_data)/p/android-4.4 \
+    4.9.84:$(my_kernel_config_data)/p/android-4.9 \
+    4.14.40:$(my_kernel_config_data)/p/android-4.14 \
 
 include $(BUILD_FRAMEWORK_COMPATIBILITY_MATRIX)
 
diff --git a/current.txt b/current.txt
index 5ccd6ab..7d630d2 100644
--- a/current.txt
+++ b/current.txt
@@ -299,7 +299,7 @@
 3b17c1fdfc389e0abe626c37054954b07201127d890c2bc05d47613ec1f4de4f android.hardware.automotive.evs@1.0::types
 b3caf524c46a47d67e6453a34419e1881942d059e146cda740502670e9a752c3 android.hardware.automotive.vehicle@2.0::IVehicle
 7ce8728b27600e840cacf0a832f6942819fe535f9d3797ae052d5eef5065921c android.hardware.automotive.vehicle@2.0::IVehicleCallback
-9cf9690f559f8425fa86e409137a42435ca225505152f03736ac8f3773ef4f89 android.hardware.automotive.vehicle@2.0::types
+44c8b452186b65f5140a224721f238b76ed3adc42460f26b801b561593744c7e android.hardware.automotive.vehicle@2.0::types
 32cc50cc2a7658ec613c0c2dd2accbf6a05113b749852879e818b8b7b438db19 android.hardware.bluetooth.a2dp@1.0::IBluetoothAudioHost
 ff4be64d7992f8bec97dff37f35450e79b3430c61f85f54322ce45bef229dc3b android.hardware.bluetooth.a2dp@1.0::IBluetoothAudioOffload
 27f22d2e873e6201f9620cf4d8e2facb25bd0dd30a2b911e441b4600d560fa62 android.hardware.bluetooth.a2dp@1.0::types
@@ -327,7 +327,7 @@
 83e7a10ff3702147bd7ffa04567b20d407a3b16bbb7705644af44d919afe9103 android.hardware.gnss@1.1::IGnssMeasurementCallback
 0b96e0254e2168cfecb30c1ed5fb42681652cc00faa68c6e07568fafe64d1d50 android.hardware.graphics.common@1.1::types
 d9b40a5b09962a5a0780b10fe33a4e607e69e2e088fc83de88a584115b7cb1c0 android.hardware.graphics.composer@2.2::IComposer
-18eff12102db47b03a5fa906f8d4fd9018f0fb9236c663d457b8eac8d57c2937 android.hardware.graphics.composer@2.2::IComposerClient
+e7717f2ff2f6db43b24370ff08e14cd353da3004b32b17740e4a7ed4894b7e45 android.hardware.graphics.composer@2.2::IComposerClient
 dd83be076b6b3f10ed62ab34d8c8b95f2415961fb785200eb842e7bfb2b0ee92 android.hardware.graphics.mapper@2.1::IMapper
 675682dd3007805c985eaaec91612abc88f4c25b3431fb84070b7584a1a741fb android.hardware.health@2.0::IHealth
 434c4c32c00b0e54bb05e40c79503208b40f786a318029a2a4f66e34f10f2a76 android.hardware.health@2.0::IHealthInfoCallback
diff --git a/graphics/composer/2.2/IComposerClient.hal b/graphics/composer/2.2/IComposerClient.hal
index a6665a1..2f0a3cc 100644
--- a/graphics/composer/2.2/IComposerClient.hal
+++ b/graphics/composer/2.2/IComposerClient.hal
@@ -152,6 +152,11 @@
      * output of the display remains the same (subject to the note about protected
      * content in the description of setReadbackBuffer).
      *
+     * If the active configuration or color mode of this display has changed
+     * since a previous call to this function, it must be called again prior to
+     * setting a readback buffer such that the returned format and dataspace can
+     * be updated accordingly.
+     *
      * Parameters:
      * @param display - the display on which to create the layer.
      *
@@ -203,7 +208,13 @@
      *       must be non-NULL
      * @return error - is HWC2_ERROR_NONE or one of the following errors:
      *         BAD_DISPLAY - an invalid display handle was passed in
-     *         UNSUPPORTED if not supported on underlying HAL
+     *         NO_RESOURCES - the readback operation was successful, but
+     *                        resulted in a different validate result than would
+     *                        have occurred without readback
+     *         UNSUPPORTED - the readback operation was unsuccessful because of
+     *                       resource constraints, the presence of protected
+     *                       content, or other reasons; -1 must be returned for
+     *                       acquireFence
      *
      * See also:
      *   getReadbackBufferAttributes
diff --git a/keymaster/4.0/support/Keymaster.cpp b/keymaster/4.0/support/Keymaster.cpp
index fac0017..066bca4 100644
--- a/keymaster/4.0/support/Keymaster.cpp
+++ b/keymaster/4.0/support/Keymaster.cpp
@@ -16,24 +16,73 @@
 
 #include <keymasterV4_0/Keymaster.h>
 
+#include <iomanip>
+
 #include <android-base/logging.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <keymasterV4_0/Keymaster3.h>
 #include <keymasterV4_0/Keymaster4.h>
+#include <keymasterV4_0/key_param_output.h>
+#include <keymasterV4_0/keymaster_utils.h>
 
 namespace android {
 namespace hardware {
+
+template <class T>
+std::ostream& operator<<(std::ostream& os, const hidl_vec<T>& vec) {
+    os << "{ ";
+    if (vec.size()) {
+        for (size_t i = 0; i < vec.size() - 1; ++i) os << vec[i] << ", ";
+        os << vec[vec.size() - 1];
+    }
+    os << " }";
+    return os;
+}
+
+std::ostream& operator<<(std::ostream& os, const hidl_vec<uint8_t>& vec) {
+    std::ios_base::fmtflags flags(os.flags());
+    os << std::setw(2) << std::setfill('0') << std::hex;
+    for (uint8_t c : vec) os << static_cast<int>(c);
+    os.flags(flags);
+    return os;
+}
+
+template <size_t N>
+std::ostream& operator<<(std::ostream& os, const hidl_array<uint8_t, N>& vec) {
+    std::ios_base::fmtflags flags(os.flags());
+    os << std::setw(2) << std::setfill('0') << std::hex;
+    for (size_t i = 0; i < N; ++i) os << static_cast<int>(vec[i]);
+    os.flags(flags);
+    return os;
+}
+
 namespace keymaster {
 namespace V4_0 {
+
+std::ostream& operator<<(std::ostream& os, const HmacSharingParameters& params) {
+    // Note that by design, although seed and nonce are used to compute a secret, they are
+    // not secrets and it's just fine to log them.
+    os << "(seed: " << params.seed << ", nonce: " << params.nonce << ')';
+    return os;
+}
+
 namespace support {
 
 using ::android::sp;
 using ::android::hidl::manager::V1_0::IServiceManager;
 
+std::ostream& operator<<(std::ostream& os, const Keymaster& keymaster) {
+    auto& version = keymaster.halVersion();
+    os << version.keymasterName << " from " << version.authorName
+       << " SecurityLevel: " << toString(version.securityLevel)
+       << " HAL: " << keymaster.descriptor() << "/" << keymaster.instanceName();
+    return os;
+}
+
 template <typename Wrapper>
 std::vector<std::unique_ptr<Keymaster>> enumerateDevices(
     const sp<IServiceManager>& serviceManager) {
-    std::vector<std::unique_ptr<Keymaster>> result;
+    Keymaster::KeymasterSet result;
 
     bool foundDefault = false;
     auto& descriptor = Wrapper::WrappedIKeymasterDevice::descriptor;
@@ -57,7 +106,7 @@
     return result;
 }
 
-std::vector<std::unique_ptr<Keymaster>> Keymaster::enumerateAvailableDevices() {
+Keymaster::KeymasterSet Keymaster::enumerateAvailableDevices() {
     auto serviceManager = IServiceManager::getService();
     CHECK(serviceManager) << "Could not retrieve ServiceManager";
 
@@ -73,18 +122,62 @@
 
     size_t i = 1;
     LOG(INFO) << "List of Keymaster HALs found:";
-    for (auto& hal : result) {
-        auto& version = hal->halVersion();
-        LOG(INFO) << "Keymaster HAL #" << i << ": " << version.keymasterName << " from "
-                  << version.authorName << " SecurityLevel: " << toString(version.securityLevel)
-                  << " HAL : " << hal->descriptor() << " instance " << hal->instanceName();
-    }
+    for (auto& hal : result) LOG(INFO) << "Keymaster HAL #" << i++ << ": " << *hal;
 
     return result;
 }
 
+static hidl_vec<HmacSharingParameters> getHmacParameters(
+    const Keymaster::KeymasterSet& keymasters) {
+    std::vector<HmacSharingParameters> params_vec;
+    params_vec.reserve(keymasters.size());
+    for (auto& keymaster : keymasters) {
+        if (keymaster->halVersion().majorVersion < 4) continue;
+        auto rc = keymaster->getHmacSharingParameters([&](auto error, auto& params) {
+            CHECK(error == ErrorCode::OK)
+                << "Failed to get HMAC parameters from " << *keymaster << " error " << error;
+            params_vec.push_back(params);
+        });
+        CHECK(rc.isOk()) << "Failed to communicate with " << *keymaster
+                         << " error: " << rc.description();
+    }
+    std::sort(params_vec.begin(), params_vec.end());
+
+    return params_vec;
+}
+
+static void computeHmac(const Keymaster::KeymasterSet& keymasters,
+                        const hidl_vec<HmacSharingParameters>& params) {
+    if (!params.size()) return;
+
+    hidl_vec<uint8_t> sharingCheck;
+    bool firstKeymaster = true;
+    LOG(DEBUG) << "Computing HMAC with params " << params;
+    for (auto& keymaster : keymasters) {
+        if (keymaster->halVersion().majorVersion < 4) continue;
+        LOG(DEBUG) << "Computing HMAC for " << *keymaster;
+        auto rc = keymaster->computeSharedHmac(params, [&](auto error, auto& curSharingCheck) {
+            CHECK(error == ErrorCode::OK)
+                << "Failed to get HMAC parameters from " << *keymaster << " error " << error;
+            if (firstKeymaster) {
+                sharingCheck = curSharingCheck;
+                firstKeymaster = false;
+            }
+            // TODO: Validate that curSharingCheck == sharingCheck.  b/77588764
+            // CHECK(curSharingCheck == sharingCheck) << "HMAC computation failed for " <<
+            // *keymaster;
+        });
+        CHECK(rc.isOk()) << "Failed to communicate with " << *keymaster
+                         << " error: " << rc.description();
+    }
+}
+
+void Keymaster::performHmacKeyAgreement(const KeymasterSet& keymasters) {
+    computeHmac(keymasters, getHmacParameters(keymasters));
+}
+
 }  // namespace support
 }  // namespace V4_0
 }  // namespace keymaster
 }  // namespace hardware
-};  // namespace android
+}  // namespace android
diff --git a/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h b/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h
index f9efd51..83b1d69 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h
@@ -37,6 +37,8 @@
  */
 class Keymaster : public IKeymasterDevice {
    public:
+    using KeymasterSet = std::vector<std::unique_ptr<Keymaster>>;
+
     Keymaster(const hidl_string& descriptor, const hidl_string& instanceName)
         : descriptor_(descriptor), instanceName_(instanceName) {}
     virtual ~Keymaster() {}
@@ -55,21 +57,33 @@
         }
     };
 
-    virtual const VersionResult& halVersion() = 0;
-    const hidl_string& descriptor() { return descriptor_; }
-    const hidl_string& instanceName() { return instanceName_; }
+    virtual const VersionResult& halVersion() const = 0;
+    const hidl_string& descriptor() const { return descriptor_; }
+    const hidl_string& instanceName() const { return instanceName_; }
 
     /**
      * Returns all available Keymaster3 and Keymaster4 instances, in order of most secure to least
      * secure (as defined by VersionResult::operator<).
      */
-    static std::vector<std::unique_ptr<Keymaster>> enumerateAvailableDevices();
+    static KeymasterSet enumerateAvailableDevices();
+
+    /**
+     * Ask provided Keymaster instances to compute a shared HMAC key using
+     * getHmacSharingParameters() and computeSharedHmac().  This computation is idempotent as long
+     * as the same set of Keymaster instances is used each time (and if all of the instances work
+     * correctly).  It must be performed once per boot, but should do no harm to be repeated.
+     *
+     * If key agreement fails, this method will crash the process (with CHECK).
+     */
+    static void performHmacKeyAgreement(const KeymasterSet& keymasters);
 
    private:
     hidl_string descriptor_;
     hidl_string instanceName_;
 };
 
+std::ostream& operator<<(std::ostream& os, const Keymaster& keymaster);
+
 }  // namespace support
 }  // namespace V4_0
 }  // namespace keymaster
diff --git a/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h b/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h
index 2bb77ca..c40be7c 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h
@@ -45,8 +45,8 @@
           km3_dev_(km3_dev),
           haveVersion_(false) {}
 
-    const VersionResult& halVersion() override {
-        getVersionIfNeeded();
+    const VersionResult& halVersion() const override {
+        const_cast<Keymaster3*>(this)->getVersionIfNeeded();
         return version_;
     }
 
diff --git a/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h b/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h
index 96afb13..dfd03ef 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h
@@ -37,8 +37,8 @@
           haveVersion_(false),
           dev_(km4_dev) {}
 
-    const VersionResult& halVersion() override {
-        getVersionIfNeeded();
+    const VersionResult& halVersion() const override {
+        const_cast<Keymaster4*>(this)->getVersionIfNeeded();
         return version_;
     }
 
diff --git a/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h b/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h
index 1c1b000..90a0f1b 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h
@@ -23,6 +23,14 @@
 namespace hardware {
 namespace keymaster {
 namespace V4_0 {
+
+/**
+ * Define a lexicographical ordering on HmacSharingParameters.  The parameters to
+ * IKeymasterDevice::computeSharedHmac are required to be delivered in the order specified by this
+ * comparison operator.
+ */
+bool operator<(const HmacSharingParameters& a, const HmacSharingParameters& b);
+
 namespace support {
 
 inline static hidl_vec<uint8_t> blob2hidlVec(const uint8_t* data, const size_t length,
diff --git a/keymaster/4.0/support/keymaster_utils.cpp b/keymaster/4.0/support/keymaster_utils.cpp
index bc610aa..729e1c1 100644
--- a/keymaster/4.0/support/keymaster_utils.cpp
+++ b/keymaster/4.0/support/keymaster_utils.cpp
@@ -19,8 +19,24 @@
 
 namespace android {
 namespace hardware {
+
+inline static bool operator<(const hidl_vec<uint8_t>& a, const hidl_vec<uint8_t>& b) {
+    return memcmp(a.data(), b.data(), std::min(a.size(), b.size())) == -1;
+}
+
+template <size_t SIZE>
+inline static bool operator<(const hidl_array<uint8_t, SIZE>& a,
+                             const hidl_array<uint8_t, SIZE>& b) {
+    return memcmp(a.data(), b.data(), SIZE) == -1;
+}
+
 namespace keymaster {
 namespace V4_0 {
+
+bool operator<(const HmacSharingParameters& a, const HmacSharingParameters& b) {
+    return std::tie(a.seed, a.nonce) < std::tie(b.seed, b.nonce);
+}
+
 namespace support {
 
 template <typename T, typename InIter>
diff --git a/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp b/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp
index 4eef4ec..03b6406 100644
--- a/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp
+++ b/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp
@@ -26,7 +26,6 @@
 #include <android/hardware/tetheroffload/control/1.0/types.h>
 #include <linux/netfilter/nfnetlink.h>
 #include <linux/netlink.h>
-#include <log/log.h>
 #include <net/if.h>
 #include <sys/socket.h>
 #include <unistd.h>
@@ -57,20 +56,16 @@
 constexpr const char* TEST_IFACE = "rmnet_data0";
 
 // We use #defines here so as to get local lamba captures and error message line numbers
-#define ASSERT_TRUE_CALLBACK                            \
-    [&](bool success, std::string errMsg) {             \
-        if (!success) {                                 \
-            ALOGI("Error message: %s", errMsg.c_str()); \
-        }                                               \
-        ASSERT_TRUE(success);                           \
+#define ASSERT_TRUE_CALLBACK                                                    \
+    [&](bool success, std::string errMsg) {                                     \
+        std::string msg = StringPrintf("unexpected error: %s", errMsg.c_str()); \
+        ASSERT_TRUE(success) << msg;                                            \
     }
 
-#define ASSERT_FALSE_CALLBACK                           \
-    [&](bool success, std::string errMsg) {             \
-        if (!success) {                                 \
-            ALOGI("Error message: %s", errMsg.c_str()); \
-        }                                               \
-        ASSERT_FALSE(success);                          \
+#define ASSERT_FALSE_CALLBACK                                                 \
+    [&](bool success, std::string errMsg) {                                   \
+        std::string msg = StringPrintf("expected error: %s", errMsg.c_str()); \
+        ASSERT_FALSE(success) << msg;                                         \
     }
 
 #define ASSERT_ZERO_BYTES_CALLBACK            \
@@ -188,10 +183,9 @@
 
     void initOffload(const bool expected_result) {
         auto init_cb = [&](bool success, std::string errMsg) {
-            if (!success) {
-                ALOGI("Error message: %s", errMsg.c_str());
-            }
-            ASSERT_EQ(expected_result, success);
+            std::string msg = StringPrintf("Unexpectedly %s to init offload: %s",
+                                           success ? "succeeded" : "failed", errMsg.c_str());
+            ASSERT_EQ(expected_result, success) << msg;
         };
         const Return<void> ret = control->initOffload(control_cb, init_cb);
         ASSERT_TRUE(ret.isOk());
@@ -204,15 +198,12 @@
 
     void stopOffload(const ExpectBoolean value) {
         auto cb = [&](bool success, const hidl_string& errMsg) {
-            if (!success) {
-                ALOGI("Error message: %s", errMsg.c_str());
-            }
             switch (value) {
                 case ExpectBoolean::False:
-                    ASSERT_EQ(false, success);
+                    ASSERT_EQ(false, success) << "Unexpectedly able to stop offload: " << errMsg;
                     break;
                 case ExpectBoolean::True:
-                    ASSERT_EQ(true, success);
+                    ASSERT_EQ(true, success) << "Unexpectedly failed to stop offload: " << errMsg;
                     break;
                 case ExpectBoolean::Ignored:
                     break;
@@ -289,8 +280,11 @@
     if (!interfaceIsUp(TEST_IFACE)) {
         return;
     }
-    stopOffload(ExpectBoolean::True);  // balance out initOffload(true)
+    SCOPED_TRACE("Expecting stopOffload to succeed");
+    stopOffload(ExpectBoolean::Ignored);  // balance out initOffload(true)
+    SCOPED_TRACE("Expecting stopOffload to fail the first time");
     stopOffload(ExpectBoolean::False);
+    SCOPED_TRACE("Expecting stopOffload to fail the second time");
     stopOffload(ExpectBoolean::False);
 }
 
diff --git a/wifi/1.2/default/Android.mk b/wifi/1.2/default/Android.mk
index 978cf63..3919690 100644
--- a/wifi/1.2/default/Android.mk
+++ b/wifi/1.2/default/Android.mk
@@ -27,6 +27,9 @@
 ifdef WIFI_HIDL_FEATURE_DUAL_INTERFACE
 LOCAL_CPPFLAGS += -DWIFI_HIDL_FEATURE_DUAL_INTERFACE
 endif
+ifdef WIFI_HIDL_FEATURE_DISABLE_AP
+LOCAL_CPPFLAGS += -DWIFI_HIDL_FEATURE_DISABLE_AP
+endif
 LOCAL_SRC_FILES := \
     hidl_struct_util.cpp \
     hidl_sync_util.cpp \
diff --git a/wifi/1.2/default/tests/mock_wifi_feature_flags.h b/wifi/1.2/default/tests/mock_wifi_feature_flags.h
index 8cf1d4b..2a36dd5 100644
--- a/wifi/1.2/default/tests/mock_wifi_feature_flags.h
+++ b/wifi/1.2/default/tests/mock_wifi_feature_flags.h
@@ -34,6 +34,7 @@
 
     MOCK_METHOD0(isAwareSupported, bool());
     MOCK_METHOD0(isDualInterfaceSupported, bool());
+    MOCK_METHOD0(isApDisabled, bool());
 };
 
 }  // namespace feature_flags
diff --git a/wifi/1.2/default/tests/wifi_chip_unit_tests.cpp b/wifi/1.2/default/tests/wifi_chip_unit_tests.cpp
index 3928c9a..8722d0a 100644
--- a/wifi/1.2/default/tests/wifi_chip_unit_tests.cpp
+++ b/wifi/1.2/default/tests/wifi_chip_unit_tests.cpp
@@ -48,6 +48,8 @@
             .WillRepeatedly(testing::Return(false));
         EXPECT_CALL(*feature_flags_, isDualInterfaceSupported())
             .WillRepeatedly(testing::Return(false));
+        EXPECT_CALL(*feature_flags_, isApDisabled())
+            .WillRepeatedly(testing::Return(false));
     }
 
     void setupV1_AwareIfaceCombination() {
@@ -55,6 +57,17 @@
             .WillRepeatedly(testing::Return(true));
         EXPECT_CALL(*feature_flags_, isDualInterfaceSupported())
             .WillRepeatedly(testing::Return(false));
+        EXPECT_CALL(*feature_flags_, isApDisabled())
+            .WillRepeatedly(testing::Return(false));
+    }
+
+    void setupV1_AwareDisabledApIfaceCombination() {
+        EXPECT_CALL(*feature_flags_, isAwareSupported())
+            .WillRepeatedly(testing::Return(true));
+        EXPECT_CALL(*feature_flags_, isDualInterfaceSupported())
+            .WillRepeatedly(testing::Return(false));
+        EXPECT_CALL(*feature_flags_, isApDisabled())
+            .WillRepeatedly(testing::Return(true));
     }
 
     void setupV2_AwareIfaceCombination() {
@@ -62,6 +75,17 @@
             .WillRepeatedly(testing::Return(true));
         EXPECT_CALL(*feature_flags_, isDualInterfaceSupported())
             .WillRepeatedly(testing::Return(true));
+        EXPECT_CALL(*feature_flags_, isApDisabled())
+            .WillRepeatedly(testing::Return(false));
+    }
+
+    void setupV2_AwareDisabledApIfaceCombination() {
+        EXPECT_CALL(*feature_flags_, isAwareSupported())
+            .WillRepeatedly(testing::Return(true));
+        EXPECT_CALL(*feature_flags_, isDualInterfaceSupported())
+            .WillRepeatedly(testing::Return(true));
+        EXPECT_CALL(*feature_flags_, isApDisabled())
+            .WillRepeatedly(testing::Return(true));
     }
 
     void assertNumberOfModes(uint32_t num_modes) {
@@ -515,6 +539,39 @@
     ASSERT_FALSE(ap_iface_name.empty());
     ASSERT_NE(sta_iface_name, ap_iface_name);
 }
+
+////////// V1 Iface Combinations when AP creation is disabled //////////
+class WifiChipV1_AwareDisabledApIfaceCombinationTest : public WifiChipTest {
+ public:
+  void SetUp() override {
+    setupV1_AwareDisabledApIfaceCombination();
+    WifiChipTest::SetUp();
+  }
+};
+
+TEST_F(WifiChipV1_AwareDisabledApIfaceCombinationTest,
+       StaMode_CreateSta_ShouldSucceed) {
+  findModeAndConfigureForIfaceType(IfaceType::STA);
+  ASSERT_FALSE(createIface(IfaceType::STA).empty());
+  ASSERT_TRUE(createIface(IfaceType::AP).empty());
+}
+
+////////// V2 Iface Combinations when AP creation is disabled //////////
+class WifiChipV2_AwareDisabledApIfaceCombinationTest: public WifiChipTest {
+ public:
+  void SetUp() override {
+    setupV2_AwareDisabledApIfaceCombination();
+    WifiChipTest::SetUp();
+  }
+};
+
+TEST_F(WifiChipV2_AwareDisabledApIfaceCombinationTest,
+       CreateSta_ShouldSucceed) {
+  findModeAndConfigureForIfaceType(IfaceType::STA);
+  ASSERT_FALSE(createIface(IfaceType::STA).empty());
+  ASSERT_TRUE(createIface(IfaceType::AP).empty());
+}
+
 }  // namespace implementation
 }  // namespace V1_2
 }  // namespace wifi
diff --git a/wifi/1.2/default/wifi_chip.cpp b/wifi/1.2/default/wifi_chip.cpp
index ab96ef1..3bd0557 100644
--- a/wifi/1.2/default/wifi_chip.cpp
+++ b/wifi/1.2/default/wifi_chip.cpp
@@ -1211,10 +1211,17 @@
             {chip_iface_combination_limit_1, chip_iface_combination_limit_2}};
         const IWifiChip::ChipIfaceCombination chip_iface_combination_2 = {
             {chip_iface_combination_limit_1, chip_iface_combination_limit_3}};
-        const IWifiChip::ChipMode chip_mode = {
+        if (feature_flags_.lock()->isApDisabled()) {
+          const IWifiChip::ChipMode chip_mode = {
+              kV2ChipModeId,
+              {chip_iface_combination_2}};
+          modes_ = {chip_mode};
+        } else {
+          const IWifiChip::ChipMode chip_mode = {
             kV2ChipModeId,
             {chip_iface_combination_1, chip_iface_combination_2}};
-        modes_ = {chip_mode};
+          modes_ = {chip_mode};
+        }
     } else {
         // V1 Iface combinations for Mode Id = 0. (STA Mode)
         const IWifiChip::ChipIfaceCombinationLimit
@@ -1238,7 +1245,11 @@
             {ap_chip_iface_combination_limit}};
         const IWifiChip::ChipMode ap_chip_mode = {kV1ApChipModeId,
                                                   {ap_chip_iface_combination}};
-        modes_ = {sta_chip_mode, ap_chip_mode};
+        if (feature_flags_.lock()->isApDisabled()) {
+          modes_ = {sta_chip_mode};
+        } else {
+          modes_ = {sta_chip_mode, ap_chip_mode};
+        }
     }
 }
 
diff --git a/wifi/1.2/default/wifi_feature_flags.cpp b/wifi/1.2/default/wifi_feature_flags.cpp
index 554d4d5..778944d 100644
--- a/wifi/1.2/default/wifi_feature_flags.cpp
+++ b/wifi/1.2/default/wifi_feature_flags.cpp
@@ -27,6 +27,12 @@
 #else
 static const bool wifiHidlFeatureDualInterface = false;
 #endif  // WIFI_HIDL_FEATURE_DUAL_INTERFACE
+#ifdef WIFI_HIDL_FEATURE_DISABLE_AP
+static const bool wifiHidlFeatureDisableAp = true;
+#else
+static const bool wifiHidlFeatureDisableAp = false;
+#endif  // WIFI_HIDL_FEATURE_DISABLE_AP
+
 }  // namespace
 
 namespace android {
@@ -41,6 +47,9 @@
 bool WifiFeatureFlags::isDualInterfaceSupported() {
     return wifiHidlFeatureDualInterface;
 }
+bool WifiFeatureFlags::isApDisabled() {
+  return wifiHidlFeatureDisableAp;
+}
 
 }  // namespace feature_flags
 }  // namespace implementation
diff --git a/wifi/1.2/default/wifi_feature_flags.h b/wifi/1.2/default/wifi_feature_flags.h
index dc0c1ff..4a7b2d2 100644
--- a/wifi/1.2/default/wifi_feature_flags.h
+++ b/wifi/1.2/default/wifi_feature_flags.h
@@ -31,6 +31,7 @@
 
     virtual bool isAwareSupported();
     virtual bool isDualInterfaceSupported();
+    virtual bool isApDisabled();
 };
 
 }  // namespace feature_flags