[automerger skipped] Camera: add proper HwLevel check am: cd02298b46 am: 616f595527 am: 8235ceff90  -s ours am: 874cc70085  -s ours am: 72c7579c08  -s ours
am: 1c26316b03  -s ours

Change-Id: Iada7b3c5a712df0b44581acbbc05982ac2c5bbf0
diff --git a/audio/common/all-versions/default/include/common/all-versions/default/HidlUtils.impl.h b/audio/common/all-versions/default/include/common/all-versions/default/HidlUtils.impl.h
index 8ab7350..632e816 100644
--- a/audio/common/all-versions/default/include/common/all-versions/default/HidlUtils.impl.h
+++ b/audio/common/all-versions/default/include/common/all-versions/default/HidlUtils.impl.h
@@ -174,9 +174,9 @@
             config->ext.mix.hwModule = halConfig.ext.mix.hw_module;
             config->ext.mix.ioHandle = halConfig.ext.mix.handle;
             if (halConfig.role == AUDIO_PORT_ROLE_SOURCE) {
-                config->ext.mix.useCase.source = AudioSource(halConfig.ext.mix.usecase.source);
-            } else if (halConfig.role == AUDIO_PORT_ROLE_SINK) {
                 config->ext.mix.useCase.stream = AudioStreamType(halConfig.ext.mix.usecase.stream);
+            } else if (halConfig.role == AUDIO_PORT_ROLE_SINK) {
+                config->ext.mix.useCase.source = AudioSource(halConfig.ext.mix.usecase.source);
             }
             break;
         }
@@ -212,11 +212,11 @@
             halConfig->ext.mix.hw_module = config.ext.mix.hwModule;
             halConfig->ext.mix.handle = config.ext.mix.ioHandle;
             if (config.role == AudioPortRole::SOURCE) {
-                halConfig->ext.mix.usecase.source =
-                    static_cast<audio_source_t>(config.ext.mix.useCase.source);
-            } else if (config.role == AudioPortRole::SINK) {
                 halConfig->ext.mix.usecase.stream =
                     static_cast<audio_stream_type_t>(config.ext.mix.useCase.stream);
+            } else if (config.role == AudioPortRole::SINK) {
+                halConfig->ext.mix.usecase.source =
+                    static_cast<audio_source_t>(config.ext.mix.useCase.source);
             }
             break;
         }
diff --git a/audio/core/4.0/vts/functional/AudioPrimaryHidlHalTest.cpp b/audio/core/4.0/vts/functional/AudioPrimaryHidlHalTest.cpp
index 9c9b749..b811c10 100644
--- a/audio/core/4.0/vts/functional/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/4.0/vts/functional/AudioPrimaryHidlHalTest.cpp
@@ -1105,11 +1105,10 @@
 }
 
 TEST_P(InputStreamTest, getActiveMicrophones) {
-    doc::test("Getting active microphones should always succeed");
+    doc::test("Active microphones of a non started stream may not be retrievable");
     hidl_vec<MicrophoneInfo> microphones;
-    ASSERT_OK(device->getMicrophones(returnIn(res, microphones)));
-    ASSERT_OK(res);
-    ASSERT_TRUE(microphones.size() > 0);
+    ASSERT_OK(stream->getActiveMicrophones(returnIn(res, microphones)));
+    ASSERT_RESULT(okOrNotSupported, res);
 }
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/audio/core/all-versions/default/include/core/all-versions/default/Device.h b/audio/core/all-versions/default/include/core/all-versions/default/Device.h
index eb53b48..222d3de 100644
--- a/audio/core/all-versions/default/include/core/all-versions/default/Device.h
+++ b/audio/core/all-versions/default/include/core/all-versions/default/Device.h
@@ -124,7 +124,8 @@
     Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) override;
 
     // Utility methods for extending interfaces.
-    Result analyzeStatus(const char* funcName, int status);
+    Result analyzeStatus(const char* funcName, int status,
+                         const std::vector<int>& ignoreErrors = {});
     void closeInputStream(audio_stream_in_t* stream);
     void closeOutputStream(audio_stream_out_t* stream);
     audio_hw_device_t* device() const { return mDevice; }
diff --git a/audio/core/all-versions/default/include/core/all-versions/default/Device.impl.h b/audio/core/all-versions/default/include/core/all-versions/default/Device.impl.h
index 230b8de..52d2186 100644
--- a/audio/core/all-versions/default/include/core/all-versions/default/Device.impl.h
+++ b/audio/core/all-versions/default/include/core/all-versions/default/Device.impl.h
@@ -40,8 +40,9 @@
     mDevice = nullptr;
 }
 
-Result Device::analyzeStatus(const char* funcName, int status) {
-    return util::analyzeStatus("Device", funcName, status);
+Result Device::analyzeStatus(const char* funcName, int status,
+                             const std::vector<int>& ignoreErrors) {
+    return util::analyzeStatus("Device", funcName, status, ignoreErrors);
 }
 
 void Device::closeInputStream(audio_stream_in_t* stream) {
@@ -155,7 +156,8 @@
     }
     AudioConfig suggestedConfig;
     HidlUtils::audioConfigFromHal(halConfig, &suggestedConfig);
-    _hidl_cb(analyzeStatus("open_output_stream", status), streamOut, suggestedConfig);
+    _hidl_cb(analyzeStatus("open_output_stream", status, {EINVAL} /* ignore */), streamOut,
+             suggestedConfig);
     return Void();
 }
 
@@ -183,7 +185,8 @@
     }
     AudioConfig suggestedConfig;
     HidlUtils::audioConfigFromHal(halConfig, &suggestedConfig);
-    _hidl_cb(analyzeStatus("open_input_stream", status), streamIn, suggestedConfig);
+    _hidl_cb(analyzeStatus("open_input_stream", status, {EINVAL} /* ignore */), streamIn,
+             suggestedConfig);
     return Void();
 }
 
diff --git a/audio/core/all-versions/default/include/core/all-versions/default/DevicesFactory.impl.h b/audio/core/all-versions/default/include/core/all-versions/default/DevicesFactory.impl.h
index 43e5d6e..a9f59fb 100644
--- a/audio/core/all-versions/default/include/core/all-versions/default/DevicesFactory.impl.h
+++ b/audio/core/all-versions/default/include/core/all-versions/default/DevicesFactory.impl.h
@@ -106,8 +106,8 @@
     return rc;
 }
 
-IDevicesFactory* HIDL_FETCH_IDevicesFactory(const char* /* name */) {
-    return new DevicesFactory();
+IDevicesFactory* HIDL_FETCH_IDevicesFactory(const char* name) {
+    return strcmp(name, "default") == 0 ? new DevicesFactory() : nullptr;
 }
 
 }  // namespace implementation
diff --git a/audio/core/all-versions/default/include/core/all-versions/default/Stream.h b/audio/core/all-versions/default/include/core/all-versions/default/Stream.h
index 6f79429..375759d 100644
--- a/audio/core/all-versions/default/include/core/all-versions/default/Stream.h
+++ b/audio/core/all-versions/default/include/core/all-versions/default/Stream.h
@@ -163,6 +163,22 @@
         if (retval == Result::OK) {
             hidlHandle = native_handle_create(1, 0);
             hidlHandle->data[0] = halInfo.shared_memory_fd;
+
+            // Negative buffer size frame is a legacy hack to indicate that the buffer
+            // is shareable to applications before the relevant flag was introduced
+            bool applicationShareable =
+                halInfo.flags & AUDIO_MMAP_APPLICATION_SHAREABLE || halInfo.buffer_size_frames < 0;
+            halInfo.buffer_size_frames = abs(halInfo.buffer_size_frames);
+#ifdef AUDIO_HAL_VERSION_2_0
+            if (applicationShareable) {
+                halInfo.buffer_size_frames *= -1;
+            }
+#else
+            info.flags =
+                halInfo.flags | (applicationShareable ? MmapBufferFlag::APPLICATION_SHAREABLE
+                                                      : MmapBufferFlag::NONE);
+#endif
+
             info.sharedMemory =
                 hidl_memory("audio_buffer", hidlHandle, frameSize * halInfo.buffer_size_frames);
             info.bufferSizeFrames = halInfo.buffer_size_frames;
diff --git a/audio/effect/all-versions/default/include/effect/all-versions/default/EffectsFactory.impl.h b/audio/effect/all-versions/default/include/effect/all-versions/default/EffectsFactory.impl.h
index 1882a2c..f27c739 100644
--- a/audio/effect/all-versions/default/include/effect/all-versions/default/EffectsFactory.impl.h
+++ b/audio/effect/all-versions/default/include/effect/all-versions/default/EffectsFactory.impl.h
@@ -183,8 +183,8 @@
     return Void();
 }
 
-IEffectsFactory* HIDL_FETCH_IEffectsFactory(const char* /* name */) {
-    return new EffectsFactory();
+IEffectsFactory* HIDL_FETCH_IEffectsFactory(const char* name) {
+    return strcmp(name, "default") == 0 ? new EffectsFactory() : nullptr;
 }
 
 }  // namespace implementation
diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp
index 22ab079..7802ef0 100644
--- a/automotive/vehicle/2.0/default/Android.bp
+++ b/automotive/vehicle/2.0/default/Android.bp
@@ -58,12 +58,14 @@
     vendor: true,
     defaults: ["vhal_v2_0_defaults"],
     srcs: [
+        "impl/vhal_v2_0/CommConn.cpp",
         "impl/vhal_v2_0/EmulatedVehicleHal.cpp",
         "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",
+        "impl/vhal_v2_0/GeneratorHub.cpp",
     ],
     local_include_dirs: ["common/include/vhal_v2_0"],
     export_include_dirs: ["impl"],
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/CommBase.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/CommBase.h
deleted file mode 100644
index 6832ad3..0000000
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/CommBase.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef android_hardware_automotive_vehicle_V2_0_impl_CommBase_H_
-#define android_hardware_automotive_vehicle_V2_0_impl_CommBase_H_
-
-#include <string>
-#include <vector>
-
-namespace android {
-namespace hardware {
-namespace automotive {
-namespace vehicle {
-namespace V2_0 {
-
-namespace impl {
-
-/**
- * This is the communications base class.  It defines the interface used in DefaultVehicleHal to
- * send and receive data to and from the emulator.
- */
-class CommBase {
-public:
-    virtual ~CommBase() = default;
-
-    /**
-     * Closes a connection if it is open.
-     */
-    virtual void stop() {}
-
-    /**
-     * Creates a connection to the other side.
-     *
-     * @return int Returns fd or socket number if connection is successful.
-     *              Otherwise, returns -1 if no connection is availble.
-     */
-    virtual int connect() { return 0; }
-
-    /**
-     * Opens the communications channel.
-     *
-     * @return int Returns 0 if channel is opened, else -errno if failed.
-     */
-    virtual int open() = 0;
-
-    /**
-     * Blocking call to read data from the connection.
-     *
-     * @return std::vector<uint8_t> Serialized protobuf data received from emulator.  This will be
-     *              an empty vector if the connection was closed or some other error occurred.
-     */
-    virtual std::vector<uint8_t> read() = 0;
-
-    /**
-     * Transmits a string of data to the emulator.
-     *
-     * @param data Serialized protobuf data to transmit.
-     *
-     * @return int Number of bytes transmitted, or -1 if failed.
-     */
-    virtual int write(const std::vector<uint8_t>& data) = 0;
-};
-
-}  // impl
-
-}  // namespace V2_0
-}  // namespace vehicle
-}  // namespace automotive
-}  // namespace hardware
-}  // namespace android
-
-
-#endif  // android_hardware_automotive_vehicle_V2_0_impl_CommBase_H_
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/CommConn.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/CommConn.cpp
new file mode 100644
index 0000000..bf1de81
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/CommConn.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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 "CommConn"
+
+#include <thread>
+
+#include <android/hardware/automotive/vehicle/2.0/IVehicle.h>
+#include <log/log.h>
+
+#include "CommConn.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+void CommConn::start() {
+    mReadThread = std::make_unique<std::thread>(std::bind(&CommConn::readThread, this));
+}
+
+void CommConn::stop() {
+    if (mReadThread->joinable()) {
+        mReadThread->join();
+    }
+}
+
+void CommConn::sendMessage(emulator::EmulatorMessage const& msg) {
+    int numBytes = msg.ByteSize();
+    std::vector<uint8_t> buffer(static_cast<size_t>(numBytes));
+    if (!msg.SerializeToArray(buffer.data(), numBytes)) {
+        ALOGE("%s: SerializeToString failed!", __func__);
+        return;
+    }
+
+    write(buffer);
+}
+
+void CommConn::readThread() {
+    std::vector<uint8_t> buffer;
+    while (isOpen()) {
+        buffer = read();
+        if (buffer.size() == 0) {
+            ALOGI("%s: Read returned empty message, exiting read loop.", __func__);
+            break;
+        }
+
+        emulator::EmulatorMessage rxMsg;
+        if (rxMsg.ParseFromArray(buffer.data(), static_cast<int32_t>(buffer.size()))) {
+            emulator::EmulatorMessage respMsg;
+            mMessageProcessor->processMessage(rxMsg, respMsg);
+
+            sendMessage(respMsg);
+        }
+    }
+}
+
+}  // 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/CommConn.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/CommConn.h
new file mode 100644
index 0000000..87b0dfc
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/CommConn.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef android_hardware_automotive_vehicle_V2_0_impl_CommBase_H_
+#define android_hardware_automotive_vehicle_V2_0_impl_CommBase_H_
+
+#include <android/hardware/automotive/vehicle/2.0/IVehicle.h>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include "VehicleHalProto.pb.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+/**
+ * MessageProcess is an interface implemented by VehicleEmulator to process messages received
+ * over a CommConn.
+ */
+class MessageProcessor {
+   public:
+    virtual ~MessageProcessor() = default;
+
+    /**
+     * Process a single message received over a CommConn. Populate the given respMsg with the reply
+     * message we should send.
+     */
+    virtual void processMessage(emulator::EmulatorMessage const& rxMsg,
+                                emulator::EmulatorMessage& respMsg) = 0;
+};
+
+/**
+ * This is the interface that both PipeComm and SocketComm use to represent a connection. The
+ * connection will listen for commands on a separate 'read' thread.
+ */
+class CommConn {
+   public:
+    CommConn(MessageProcessor* messageProcessor) : mMessageProcessor(messageProcessor) {}
+
+    virtual ~CommConn() {}
+
+    /**
+     * Start the read thread reading messages from this connection.
+     */
+    virtual void start();
+
+    /**
+     * Closes a connection if it is open.
+     */
+    virtual void stop();
+
+    /**
+     * Returns true if the connection is open and available to send/receive.
+     */
+    virtual bool isOpen() = 0;
+
+    /**
+     * Blocking call to read data from the connection.
+     *
+     * @return std::vector<uint8_t> Serialized protobuf data received from emulator.  This will be
+     *              an empty vector if the connection was closed or some other error occurred.
+     */
+    virtual std::vector<uint8_t> read() = 0;
+
+    /**
+     * Transmits a string of data to the emulator.
+     *
+     * @param data Serialized protobuf data to transmit.
+     *
+     * @return int Number of bytes transmitted, or -1 if failed.
+     */
+    virtual int write(const std::vector<uint8_t>& data) = 0;
+
+    /**
+     * Serialized and send the given message to the other side.
+     */
+    void sendMessage(emulator::EmulatorMessage const& msg);
+
+   protected:
+    std::unique_ptr<std::thread> mReadThread;
+    MessageProcessor* mMessageProcessor;
+
+    /**
+     * A thread that reads messages in a loop, and responds. You can stop this thread by calling
+     * stop().
+     */
+    void readThread();
+};
+
+}  // namespace impl
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_automotive_vehicle_V2_0_impl_CommBase_H_
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 eb9d660..917fbc3 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
@@ -34,6 +34,8 @@
 constexpr int AP_POWER_STATE_REPORT = (int)VehicleProperty::AP_POWER_STATE_REPORT;
 constexpr int DOOR_1_LEFT = (int)VehicleAreaDoor::ROW_1_LEFT;
 constexpr int DOOR_1_RIGHT = (int)VehicleAreaDoor::ROW_1_RIGHT;
+constexpr int DOOR_2_LEFT = (int)VehicleAreaDoor::ROW_2_LEFT;
+constexpr int DOOR_2_RIGHT = (int)VehicleAreaDoor::ROW_2_RIGHT;
 constexpr int OBD2_LIVE_FRAME = (int)VehicleProperty::OBD2_LIVE_FRAME;
 constexpr int OBD2_FREEZE_FRAME = (int)VehicleProperty::OBD2_FREEZE_FRAME;
 constexpr int OBD2_FREEZE_FRAME_INFO = (int)VehicleProperty::OBD2_FREEZE_FRAME_INFO;
@@ -48,6 +50,14 @@
                                 VehicleAreaSeat::ROW_2_CENTER);
 constexpr int HVAC_RIGHT = (int)(VehicleAreaSeat::ROW_1_RIGHT | VehicleAreaSeat::ROW_2_RIGHT);
 constexpr int HVAC_ALL = HVAC_LEFT | HVAC_RIGHT;
+constexpr int VENDOR_EXTENSION_BOOLEAN_PROPERTY =
+    (int)(0x101 | VehiclePropertyGroup::VENDOR | VehiclePropertyType::BOOLEAN | VehicleArea::DOOR);
+constexpr int VENDOR_EXTENSION_FLOAT_PROPERTY =
+    (int)(0x102 | VehiclePropertyGroup::VENDOR | VehiclePropertyType::FLOAT | VehicleArea::SEAT);
+constexpr int VENDOR_EXTENSION_INT_PROPERTY =
+    (int)(0x103 | VehiclePropertyGroup::VENDOR | VehiclePropertyType::INT32 | VehicleArea::WINDOW);
+constexpr int VENDOR_EXTENSION_STRING_PROPERTY =
+    (int)(0x104 | VehiclePropertyGroup::VENDOR | VehiclePropertyType::STRING | VehicleArea::GLOBAL);
 
 /**
  * This property is used for test purpose to generate fake events. Here is the test package that
@@ -70,33 +80,39 @@
 enum class FakeDataCommand : int32_t {
     /**
      * Starts linear fake data generation. Caller must provide additional data:
-     *     int32Values[1] - VehicleProperty to which command applies
+     *     int32Values[1] - vehicle property to which command applies
      *     int64Values[0] - periodic interval in nanoseconds
      *     floatValues[0] - 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. When reaching to max value, the current value will be set to min.
-     *                      It should be non-negative, otherwise the behavior is undefined.
+     *                      amount. When reaching to max value, the current value will be set to
+     *                      min. It should be non-negative, otherwise the behavior is undefined.
      */
     StartLinear = 0,
 
-    /** Stops generating of fake data that was triggered by Start commands.
-     *     int32Values[1] - VehicleProperty to which command applies. VHAL will stop the
+    /** Stops linear fake data generation that was triggered by StartLinear commands.
+     *     int32Values[1] - vehicle property 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:
+     * Starts JSON-based fake data generation. It iterates through JSON-encoded VHAL events from a
+     * file and inject them to VHAL. The iteration can be repeated multiple times or infinitely.
+     * Caller must provide additional data:
+     *     int32Values[1] - number of iterations. If it is not provided or -1. The iteration will be
+     *                      repeated infinite times.
      *     stringValue    - path to the fake values JSON file
      */
     StartJson = 2,
 
     /**
-     * Stops JSON-based fake data generation. No additional arguments needed.
+     * Stops JSON-based fake data generation. As multiple JSON-based generation can happen at the
+     * same time. Caller must provide the path of fake value JSON file to stop the corresponding
+     * generation:
+     *     stringValue    - path to the fake values JSON file
      */
     StopJson = 3,
 
@@ -366,10 +382,14 @@
                 .access = VehiclePropertyAccess::READ_WRITE,
                 .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
                 .areaConfigs = {VehicleAreaConfig{
-                                    .areaId = HVAC_LEFT, .minFloatValue = 16, .maxFloatValue = 32,
+                                    .areaId = HVAC_LEFT,
+                                    .minFloatValue = 16,
+                                    .maxFloatValue = 32,
                                 },
                                 VehicleAreaConfig{
-                                    .areaId = HVAC_RIGHT, .minFloatValue = 16, .maxFloatValue = 32,
+                                    .areaId = HVAC_RIGHT,
+                                    .minFloatValue = 16,
+                                    .maxFloatValue = 32,
                                 }}},
      .initialAreaValues = {{HVAC_LEFT, {.floatValues = {16}}},
                            {HVAC_RIGHT, {.floatValues = {20}}}}},
@@ -523,6 +543,51 @@
     {.config = {.prop = VEHICLE_MAP_SERVICE,
                 .access = VehiclePropertyAccess::READ_WRITE,
                 .changeMode = VehiclePropertyChangeMode::ON_CHANGE}},
+
+    // Example Vendor Extension properties for testing
+    {.config = {.prop = VENDOR_EXTENSION_BOOLEAN_PROPERTY,
+                .access = VehiclePropertyAccess::READ_WRITE,
+                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+                .areaConfigs = {VehicleAreaConfig{.areaId = DOOR_1_LEFT},
+                                VehicleAreaConfig{.areaId = DOOR_1_RIGHT},
+                                VehicleAreaConfig{.areaId = DOOR_2_LEFT},
+                                VehicleAreaConfig{.areaId = DOOR_2_RIGHT}}},
+     .initialAreaValues = {{DOOR_1_LEFT, {.int32Values = {1}}},
+                           {DOOR_1_RIGHT, {.int32Values = {1}}},
+                           {DOOR_2_LEFT, {.int32Values = {0}}},
+                           {DOOR_2_RIGHT, {.int32Values = {0}}}}},
+
+    {.config = {.prop = VENDOR_EXTENSION_FLOAT_PROPERTY,
+                .access = VehiclePropertyAccess::READ_WRITE,
+                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+                .areaConfigs = {VehicleAreaConfig{
+                                    .areaId = HVAC_LEFT, .minFloatValue = -10, .maxFloatValue = 10},
+                                VehicleAreaConfig{.areaId = HVAC_RIGHT,
+                                                  .minFloatValue = -10,
+                                                  .maxFloatValue = 10}}},
+     .initialAreaValues = {{HVAC_LEFT, {.floatValues = {1}}}, {HVAC_RIGHT, {.floatValues = {2}}}}},
+
+    {.config = {.prop = VENDOR_EXTENSION_INT_PROPERTY,
+                .access = VehiclePropertyAccess::READ_WRITE,
+                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+                .areaConfigs = {VehicleAreaConfig{
+                                    .areaId = (int)VehicleAreaWindow::FRONT_WINDSHIELD,
+                                    .minInt32Value = -100,
+                                    .maxInt32Value = 100},
+                                VehicleAreaConfig{.areaId = (int)VehicleAreaWindow::REAR_WINDSHIELD,
+                                                  .minInt32Value = -100,
+                                                  .maxInt32Value = 100},
+                                VehicleAreaConfig{.areaId = (int)VehicleAreaWindow::ROOF_TOP_1,
+                                                  .minInt32Value = -100,
+                                                  .maxInt32Value = 100}}},
+     .initialAreaValues = {{(int)VehicleAreaWindow::FRONT_WINDSHIELD, {.int32Values = {1}}},
+                           {(int)VehicleAreaWindow::REAR_WINDSHIELD, {.int32Values = {0}}},
+                           {(int)VehicleAreaWindow::ROOF_TOP_1, {.int32Values = {-1}}}}},
+
+    {.config = {.prop = VENDOR_EXTENSION_STRING_PROPERTY,
+                .access = VehiclePropertyAccess::READ_WRITE,
+                .changeMode = VehiclePropertyChangeMode::ON_CHANGE},
+     .initialValue = {.stringValue = "Vendor String Property"}},
 };
 
 }  // impl
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 07695bf..58d8867 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
@@ -92,10 +92,8 @@
       mHvacPowerProps(std::begin(kHvacPowerProperties), std::end(kHvacPowerProperties)),
       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))) {
+      mGeneratorHub(
+          std::bind(&EmulatedVehicleHal::onFakeValueGenerated, this, std::placeholders::_1)) {
     initStaticConfig();
     for (size_t i = 0; i < arraysize(kVehicleProperties); i++) {
         mPropStore->registerProperty(kVehicleProperties[i].config);
@@ -189,6 +187,7 @@
     }
 
     getEmulatorOrDie()->doSetValueFromClient(propValue);
+    doHalEvent(getValuePool()->obtain(propValue));
 
     return StatusCode::OK;
 }
@@ -343,19 +342,54 @@
     switch (command) {
         case FakeDataCommand::StartLinear: {
             ALOGI("%s, FakeDataCommand::StartLinear", __func__);
-            return mLinearFakeValueGenerator->start(request);
+            if (v.int32Values.size() < 2) {
+                ALOGE("%s: expected property ID in int32Values", __func__);
+                return StatusCode::INVALID_ARG;
+            }
+            if (!v.int64Values.size()) {
+                ALOGE("%s: interval is not provided in int64Values", __func__);
+                return StatusCode::INVALID_ARG;
+            }
+            if (v.floatValues.size() < 3) {
+                ALOGE("%s: expected at least 3 elements in floatValues, got: %zu", __func__,
+                      v.floatValues.size());
+                return StatusCode::INVALID_ARG;
+            }
+            int32_t cookie = v.int32Values[1];
+            mGeneratorHub.registerGenerator(cookie,
+                                            std::make_unique<LinearFakeValueGenerator>(request));
+            break;
         }
         case FakeDataCommand::StartJson: {
             ALOGI("%s, FakeDataCommand::StartJson", __func__);
-            return mJsonFakeValueGenerator->start(request);
+            if (v.stringValue.empty()) {
+                ALOGE("%s: path to JSON file is missing", __func__);
+                return StatusCode::INVALID_ARG;
+            }
+            int32_t cookie = std::hash<std::string>()(v.stringValue);
+            mGeneratorHub.registerGenerator(cookie,
+                                            std::make_unique<JsonFakeValueGenerator>(request));
+            break;
         }
         case FakeDataCommand::StopLinear: {
             ALOGI("%s, FakeDataCommand::StopLinear", __func__);
-            return mLinearFakeValueGenerator->stop(request);
+            if (v.int32Values.size() < 2) {
+                ALOGE("%s: expected property ID in int32Values", __func__);
+                return StatusCode::INVALID_ARG;
+            }
+            int32_t cookie = v.int32Values[1];
+            mGeneratorHub.unregisterGenerator(cookie);
+            break;
         }
         case FakeDataCommand::StopJson: {
             ALOGI("%s, FakeDataCommand::StopJson", __func__);
-            return mJsonFakeValueGenerator->stop(request);
+            if (v.stringValue.empty()) {
+                ALOGE("%s: path to JSON file is missing", __func__);
+                return StatusCode::INVALID_ARG;
+            }
+            int32_t cookie = std::hash<std::string>()(v.stringValue);
+            mGeneratorHub.unregisterGenerator(cookie);
+            break;
         }
         case FakeDataCommand::KeyPress: {
             ALOGI("%s, FakeDataCommand::KeyPress", __func__);
@@ -398,7 +432,7 @@
         mPropStore->writeValue(*updatedPropValue, shouldUpdateStatus);
         auto changeMode = mPropStore->getConfigOrDie(value.prop)->changeMode;
         if (VehiclePropertyChangeMode::ON_CHANGE == changeMode) {
-            doHalEvent(move(updatedPropValue));
+            doHalEvent(std::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 c188aef..ec59690 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,8 +30,7 @@
 #include "vhal_v2_0/VehiclePropertyStore.h"
 
 #include "DefaultConfig.h"
-#include "FakeValueGenerator.h"
-
+#include "GeneratorHub.h"
 #include "VehicleEmulator.h"
 
 namespace android {
@@ -85,8 +84,7 @@
     VehiclePropertyStore* mPropStore;
     std::unordered_set<int32_t> mHvacPowerProps;
     RecurrentTimer mRecurrentTimer;
-    std::unique_ptr<FakeValueGenerator> mLinearFakeValueGenerator;
-    std::unique_ptr<FakeValueGenerator> mJsonFakeValueGenerator;
+    GeneratorHub mGeneratorHub;
 };
 
 }  // 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 1eeb88d..d6ad77d 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
@@ -27,28 +27,22 @@
 
 namespace impl {
 
-using OnHalEvent = std::function<void(const VehiclePropValue& event)>;
-using MuxGuard = std::lock_guard<std::mutex>;
-
 class FakeValueGenerator {
 public:
     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;
+
+    virtual VehiclePropValue nextEvent() = 0;
+
+    virtual bool hasNext() = 0;
 };
 
-}  // impl
+using Clock = std::chrono::steady_clock;
+using Nanos = std::chrono::nanoseconds;
+using TimePoint = std::chrono::time_point<Clock, Nanos>;
+
+using FakeValueGeneratorPtr = std::unique_ptr<FakeValueGenerator>;
+
+}  // namespace impl
 
 }  // namespace V2_0
 }  // namespace vehicle
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/GeneratorHub.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/GeneratorHub.cpp
new file mode 100644
index 0000000..548285a
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/GeneratorHub.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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 "GeneratorHub"
+
+#include <log/log.h>
+
+#include "GeneratorHub.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+GeneratorHub::GeneratorHub(const OnHalEvent& onHalEvent)
+    : mOnHalEvent(onHalEvent), mThread(&GeneratorHub::run, this) {}
+
+void GeneratorHub::registerGenerator(int32_t cookie, FakeValueGeneratorPtr generator) {
+    {
+        std::lock_guard<std::mutex> g(mLock);
+        // Register only if the generator can produce event
+        if (generator->hasNext()) {
+            // Push the next event if it is a new generator
+            if (mGenerators.find(cookie) == mGenerators.end()) {
+                ALOGI("%s: Registering new generator, cookie: %d", __func__, cookie);
+                mEventQueue.push({cookie, generator->nextEvent()});
+            }
+            mGenerators[cookie] = std::move(generator);
+            ALOGI("%s: Registered generator, cookie: %d", __func__, cookie);
+        }
+    }
+    mCond.notify_one();
+}
+
+void GeneratorHub::unregisterGenerator(int32_t cookie) {
+    {
+        std::lock_guard<std::mutex> g(mLock);
+        mGenerators.erase(cookie);
+    }
+    mCond.notify_one();
+    ALOGI("%s: Unregistered generator, cookie: %d", __func__, cookie);
+}
+
+void GeneratorHub::run() {
+    while (true) {
+        std::unique_lock<std::mutex> g(mLock);
+        // Pop events whose generator does not exist (may be already unregistered)
+        while (!mEventQueue.empty()
+               && mGenerators.find(mEventQueue.top().cookie) == mGenerators.end()) {
+             mEventQueue.pop();
+        }
+        // Wait until event queue is not empty
+        mCond.wait(g, [this] { return !mEventQueue.empty(); });
+
+        const VhalEvent& curEvent = mEventQueue.top();
+
+        TimePoint eventTime(Nanos(curEvent.val.timestamp));
+        // Wait until the soonest event happen
+        if (mCond.wait_until(g, eventTime) != std::cv_status::timeout) {
+        // It is possible that a new generator is registered and produced a sooner event, or current
+        // generator is unregistered, in this case the thread will re-evaluate the soonest event
+            ALOGI("Something happened while waiting");
+            continue;
+        }
+        // Now it's time to handle current event.
+        mOnHalEvent(curEvent.val);
+        // Update queue by popping current event and producing next event from the same generator
+        int32_t cookie = curEvent.cookie;
+        mEventQueue.pop();
+        if (hasNext(cookie)) {
+            mEventQueue.push({cookie, mGenerators[cookie]->nextEvent()});
+        } else {
+            ALOGI("%s: Generator ended, unregister it, cookie: %d", __func__, cookie);
+            mGenerators.erase(cookie);
+        }
+    }
+}
+
+bool GeneratorHub::hasNext(int32_t cookie) {
+    return mGenerators.find(cookie) != mGenerators.end() && mGenerators[cookie]->hasNext();
+}
+
+}  // 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/GeneratorHub.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/GeneratorHub.h
new file mode 100644
index 0000000..dcf6a4f
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/GeneratorHub.h
@@ -0,0 +1,97 @@
+/*
+ * 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_GeneratorHub_H_
+#define android_hardware_automotive_vehicle_V2_0_impl_GeneratorHub_H_
+
+#include <atomic>
+#include <chrono>
+#include <condition_variable>
+#include <iostream>
+#include <queue>
+#include <thread>
+#include <unordered_map>
+
+#include "FakeValueGenerator.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+/**
+ * This is the scheduler for all VHAL event generators. It manages all generators and uses priority
+ * queue to maintain generated events ordered by timestamp. The scheduler uses a single thread to
+ * keep querying and updating the event queue to make sure events from all generators are produced
+ * in order.
+ */
+class GeneratorHub {
+private:
+    struct VhalEvent {
+        int32_t cookie;  // Cookie is used to find the associated generator.
+        VehiclePropValue val;
+    };
+    // Comparator used by priority queue to keep track of soonest event.
+    struct GreaterByTime {
+        bool operator()(const VhalEvent& lhs, const VhalEvent& rhs) const {
+            return lhs.val.timestamp > rhs.val.timestamp;
+        }
+    };
+
+    using OnHalEvent = std::function<void(const VehiclePropValue& event)>;
+
+public:
+    GeneratorHub(const OnHalEvent& onHalEvent);
+    ~GeneratorHub() = default;
+
+    /**
+     * Register a new generator. The generator will be discarded if it could not produce next event.
+     * The existing generator will be overridden if it has the same cookie.
+     */
+    void registerGenerator(int32_t cookie, FakeValueGeneratorPtr generator);
+
+    void unregisterGenerator(int32_t cookie);
+
+private:
+    /**
+     * Main loop of the single thread to producing event and updating event queue.
+     */
+    void run();
+
+    bool hasNext(int32_t cookie);
+
+private:
+    std::priority_queue<VhalEvent, std::vector<VhalEvent>, GreaterByTime> mEventQueue;
+    std::unordered_map<int32_t, FakeValueGeneratorPtr> mGenerators;
+    OnHalEvent mOnHalEvent;
+
+    mutable std::mutex mLock;
+    std::condition_variable mCond;
+    std::thread mThread;
+};
+
+}  // namespace impl
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_automotive_vehicle_V2_0_impl_GeneratorHub_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
index 88b8f86..b8fd2ba 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.cpp
@@ -17,6 +17,8 @@
 #define LOG_TAG "JsonFakeValueGenerator"
 
 #include <fstream>
+#include <type_traits>
+#include <typeinfo>
 
 #include <log/log.h>
 #include <vhal_v2_0/VehicleUtils.h>
@@ -31,57 +33,48 @@
 
 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) {
+JsonFakeValueGenerator::JsonFakeValueGenerator(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;
+    mGenCfg = {
+        .index = 0,
+        .events = parseFakeValueJson(ifs),
+    };
+    // Iterate infinitely if repetition number is not provided
+    mNumOfIterations = v.int32Values.size() < 2 ? -1 : v.int32Values[1];
 }
 
-StatusCode JsonFakeValueGenerator::stop(const VehiclePropValue& request) {
-    const auto& v = request.value;
-    if (!v.stringValue.empty()) {
-        ALOGI("%s: %s", __func__, v.stringValue.c_str());
+VehiclePropValue JsonFakeValueGenerator::nextEvent() {
+    VehiclePropValue generatedValue;
+    if (!hasNext()) {
+        return generatedValue;
     }
+    TimePoint eventTime = Clock::now();
+    if (mGenCfg.index != 0) {
+        // All events (start from 2nd one) are supposed to happen in the future with a delay
+        // equals to the duration between previous and current event.
+        eventTime += Nanos(mGenCfg.events[mGenCfg.index].timestamp -
+                           mGenCfg.events[mGenCfg.index - 1].timestamp);
+    }
+    generatedValue = mGenCfg.events[mGenCfg.index];
+    generatedValue.timestamp = eventTime.time_since_epoch().count();
 
-    {
-        MuxGuard g(mLock);
+    mGenCfg.index++;
+    if (mGenCfg.index == mGenCfg.events.size()) {
         mGenCfg.index = 0;
-        mGenCfg.events.clear();
+        if (mNumOfIterations > 0) {
+            mNumOfIterations--;
+        }
     }
-    mCond.notify_one();
-    return StatusCode::OK;
+    return generatedValue;
+}
+
+bool JsonFakeValueGenerator::hasNext() {
+    return mNumOfIterations != 0 && mGenCfg.events.size() > 0;
 }
 
 std::vector<VehiclePropValue> JsonFakeValueGenerator::parseFakeValueJson(std::istream& is) {
@@ -131,9 +124,14 @@
             case VehiclePropertyType::STRING:
                 value.stringValue = rawEventValue.asString();
                 break;
+            case VehiclePropertyType::MIXED:
+                copyMixedValueJson(value, rawEventValue);
+                if (isDiagnosticProperty(event.prop)) {
+                    value.bytes = generateDiagnosticBytes(value);
+                }
+                break;
             default:
-                ALOGE("%s: unsupported type for property: 0x%x with value: %s", __func__,
-                      event.prop, rawEventValue.asString().c_str());
+                ALOGE("%s: unsupported type for property: 0x%x", __func__, event.prop);
                 continue;
         }
         fakeVhalEvents.push_back(event);
@@ -141,30 +139,60 @@
     return fakeVhalEvents;
 }
 
-void JsonFakeValueGenerator::loop() {
-    static constexpr auto kInvalidTime = TimePoint(Nanos::max());
+void JsonFakeValueGenerator::copyMixedValueJson(VehiclePropValue::RawValue& dest,
+                                                const Json::Value& jsonValue) {
+    copyJsonArray(dest.int32Values, jsonValue["int32Values"]);
+    copyJsonArray(dest.int64Values, jsonValue["int64Values"]);
+    copyJsonArray(dest.floatValues, jsonValue["floatValues"]);
+    dest.stringValue = jsonValue["stringValue"].asString();
+}
 
-    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++;
+template <typename T>
+void JsonFakeValueGenerator::copyJsonArray(hidl_vec<T>& dest, const Json::Value& jsonArray) {
+    dest.resize(jsonArray.size());
+    for (Json::Value::ArrayIndex i = 0; i < jsonArray.size(); i++) {
+        if (std::is_same<T, int32_t>::value) {
+            dest[i] = jsonArray[i].asInt();
+        } else if (std::is_same<T, int64_t>::value) {
+            dest[i] = jsonArray[i].asInt64();
+        } else if (std::is_same<T, float>::value) {
+            dest[i] = jsonArray[i].asFloat();
         }
-
-        std::unique_lock<std::mutex> g(mLock);
-        mCond.wait_until(g, nextEventTime);
     }
 }
 
+bool JsonFakeValueGenerator::isDiagnosticProperty(int32_t prop) {
+    return prop == (int32_t)VehicleProperty::OBD2_LIVE_FRAME ||
+           prop == (int32_t)VehicleProperty::OBD2_FREEZE_FRAME;
+}
+
+hidl_vec<uint8_t> JsonFakeValueGenerator::generateDiagnosticBytes(
+    const VehiclePropValue::RawValue& diagnosticValue) {
+    size_t byteSize = ((size_t)DiagnosticIntegerSensorIndex::LAST_SYSTEM_INDEX +
+                       (size_t)DiagnosticFloatSensorIndex::LAST_SYSTEM_INDEX + 2);
+    hidl_vec<uint8_t> bytes(byteSize % 8 == 0 ? byteSize / 8 : byteSize / 8 + 1);
+
+    auto& int32Values = diagnosticValue.int32Values;
+    for (size_t i = 0; i < int32Values.size(); i++) {
+        if (int32Values[i] != 0) {
+            setBit(bytes, i);
+        }
+    }
+
+    auto& floatValues = diagnosticValue.floatValues;
+    for (size_t i = 0; i < floatValues.size(); i++) {
+        if (floatValues[i] != 0.0) {
+            setBit(bytes, i + (size_t)DiagnosticIntegerSensorIndex::LAST_SYSTEM_INDEX + 1);
+        }
+    }
+    return bytes;
+}
+
+void JsonFakeValueGenerator::setBit(hidl_vec<uint8_t>& bytes, size_t idx) {
+    uint8_t mask = 1 << (idx % 8);
+    bytes[idx / 8] |= mask;
+}
+
 }  // namespace impl
 
 }  // namespace V2_0
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
index 51da4c5..70575f7 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.h
@@ -17,11 +17,8 @@
 #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>
 
@@ -37,32 +34,33 @@
 
 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;
+    JsonFakeValueGenerator(const VehiclePropValue& request);
+    ~JsonFakeValueGenerator() = default;
+
+    VehiclePropValue nextEvent();
+
+    bool hasNext();
 
 private:
     std::vector<VehiclePropValue> parseFakeValueJson(std::istream& is);
-    void loop();
+    void copyMixedValueJson(VehiclePropValue::RawValue& dest, const Json::Value& jsonValue);
+
+    template <typename T>
+    void copyJsonArray(hidl_vec<T>& dest, const Json::Value& jsonArray);
+
+    bool isDiagnosticProperty(int32_t prop);
+    hidl_vec<uint8_t> generateDiagnosticBytes(const VehiclePropValue::RawValue& diagnosticValue);
+    void setBit(hidl_vec<uint8_t>& bytes, size_t idx);
 
 private:
-    OnHalEvent mOnHalEvent;
-    std::thread mThread;
-    mutable std::mutex mLock;
-    std::condition_variable mCond;
     GeneratorCfg mGenCfg;
-    std::atomic_bool mStopRequested{false};
+    int32_t mNumOfIterations;
 };
 
 }  // namespace impl
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
index 8cb9322..7bdc97c 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/LinearFakeValueGenerator.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/LinearFakeValueGenerator.cpp
@@ -29,101 +29,48 @@
 
 namespace impl {
 
-LinearFakeValueGenerator::LinearFakeValueGenerator(const OnHalEvent& onHalEvent)
-    : mOnHalEvent(onHalEvent),
-      mRecurrentTimer(std::bind(&LinearFakeValueGenerator::onTimer, this, std::placeholders::_1)) {}
-
-StatusCode LinearFakeValueGenerator::start(const VehiclePropValue& request) {
+LinearFakeValueGenerator::LinearFakeValueGenerator(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;
+    mGenCfg = GeneratorCfg{
+        .propId = v.int32Values[1],
+        .initialValue = v.floatValues[0],
+        .currentValue = v.floatValues[0],
+        .dispersion = v.floatValues[1],
+        .increment = v.floatValues[2],
+        .interval = Nanos(v.int64Values[0]),
+    };
 }
 
-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;
+VehiclePropValue LinearFakeValueGenerator::nextEvent() {
+    mGenCfg.currentValue += mGenCfg.increment;
+    if (mGenCfg.currentValue > mGenCfg.initialValue + mGenCfg.dispersion) {
+        mGenCfg.currentValue = mGenCfg.initialValue - mGenCfg.dispersion;
     }
-    int32_t propId = v.int32Values[1];
-
-    MuxGuard g(mLock);
-    if (propId == 0) {
-        // Remove all.
-        for (auto&& it : mGenCfg) {
-            removeLocked(it.first);
-        }
-    } else {
-        removeLocked(propId);
+    VehiclePropValue event = {.prop = mGenCfg.propId};
+    auto& value = event.value;
+    switch (getPropType(event.prop)) {
+        case VehiclePropertyType::INT32:
+            value.int32Values.resize(1);
+            value.int32Values[0] = static_cast<int32_t>(mGenCfg.currentValue);
+            break;
+        case VehiclePropertyType::INT64:
+            value.int64Values.resize(1);
+            value.int64Values[0] = static_cast<int64_t>(mGenCfg.currentValue);
+            break;
+        case VehiclePropertyType::FLOAT:
+            value.floatValues.resize(1);
+            value.floatValues[0] = mGenCfg.currentValue;
+            break;
+        default:
+            ALOGE("%s: unsupported property type for 0x%x", __func__, event.prop);
     }
-    return StatusCode::OK;
+    TimePoint eventTime = Clock::now() + mGenCfg.interval;
+    event.timestamp = eventTime.time_since_epoch().count();
+    return event;
 }
 
-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);
-    }
+bool LinearFakeValueGenerator::hasNext() {
+    return true;
 }
 
 }  // namespace impl
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
index fe6d097..d3b666d 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/LinearFakeValueGenerator.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/LinearFakeValueGenerator.h
@@ -17,8 +17,6 @@
 #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 {
@@ -36,27 +34,24 @@
     // to the client.
 
     struct GeneratorCfg {
-        float initialValue;  //
+        int32_t propId;
+        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.
+        Nanos interval;
     };
 
 public:
-    LinearFakeValueGenerator(const OnHalEvent& onHalEvent);
+    LinearFakeValueGenerator(const VehiclePropValue& request);
     ~LinearFakeValueGenerator() = default;
-    StatusCode start(const VehiclePropValue& request) override;
-    StatusCode stop(const VehiclePropValue& request) override;
+
+    VehiclePropValue nextEvent();
+
+    bool hasNext();
 
 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;
+    GeneratorCfg mGenCfg;
 };
 
 }  // namespace impl
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/PipeComm.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/PipeComm.cpp
index 5a9b254..f024287 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/PipeComm.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/PipeComm.cpp
@@ -33,23 +33,28 @@
 
 namespace impl {
 
-PipeComm::PipeComm() {
-    // Initialize member vars
-    mPipeFd = -1;
-}
+PipeComm::PipeComm(MessageProcessor* messageProcessor) : CommConn(messageProcessor), mPipeFd(-1) {}
 
-
-int PipeComm::open() {
+void PipeComm::start() {
     int fd = qemu_pipe_open(CAR_SERVICE_NAME);
 
     if (fd < 0) {
         ALOGE("%s: Could not open connection to service: %s %d", __FUNCTION__, strerror(errno), fd);
-        return -errno;
+        return;
     }
 
-    ALOGI("%s: OPENED PIPE, fd=%d", __FUNCTION__, fd);
+    ALOGI("%s: Starting pipe connection, fd=%d", __FUNCTION__, fd);
     mPipeFd = fd;
-    return 0;
+
+    CommConn::start();
+}
+
+void PipeComm::stop() {
+    if (mPipeFd > 0) {
+        ::close(mPipeFd);
+        mPipeFd = -1;
+    }
+    CommConn::stop();
 }
 
 std::vector<uint8_t> PipeComm::read() {
@@ -60,16 +65,13 @@
     numBytes = qemu_pipe_frame_recv(mPipeFd, msg.data(), msg.size());
 
     if (numBytes == MAX_RX_MSG_SZ) {
-        ALOGE("%s:  Received max size = %d", __FUNCTION__, MAX_RX_MSG_SZ);
+        ALOGE("%s: Received max size = %d", __FUNCTION__, MAX_RX_MSG_SZ);
     } else if (numBytes > 0) {
         msg.resize(numBytes);
         return msg;
     } else {
         ALOGD("%s: Connection terminated on pipe %d, numBytes=%d", __FUNCTION__, mPipeFd, numBytes);
-        {
-            std::lock_guard<std::mutex> lock(mMutex);
-            mPipeFd = -1;
-        }
+        mPipeFd = -1;
     }
 
     return std::vector<uint8_t>();
@@ -78,11 +80,8 @@
 int PipeComm::write(const std::vector<uint8_t>& data) {
     int retVal = 0;
 
-    {
-        std::lock_guard<std::mutex> lock(mMutex);
-        if (mPipeFd != -1) {
-            retVal = qemu_pipe_frame_send(mPipeFd, data.data(), data.size());
-        }
+    if (mPipeFd != -1) {
+        retVal = qemu_pipe_frame_send(mPipeFd, data.data(), data.size());
     }
 
     if (retVal < 0) {
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/PipeComm.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/PipeComm.h
index bcd32d0..c8eabb8 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/PipeComm.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/PipeComm.h
@@ -19,7 +19,7 @@
 
 #include <mutex>
 #include <vector>
-#include "CommBase.h"
+#include "CommConn.h"
 
 namespace android {
 namespace hardware {
@@ -30,38 +30,25 @@
 namespace impl {
 
 /**
- * PipeComm uses a qemu pipe interface to connect to the Goldfish Emulator.
+ * PipeComm opens a qemu pipe to connect to the emulator, allowing the emulator UI to access the
+ * Vehicle HAL and simulate changing properties.
+ *
+ * Since the pipe is a client, it directly implements CommConn, and only one PipeComm can be open
+ * at a time.
  */
-class PipeComm : public CommBase {
-public:
-    PipeComm();
+class PipeComm : public CommConn {
+   public:
+    PipeComm(MessageProcessor* messageProcessor);
 
-    /**
-     * Opens a pipe and begins listening.
-     *
-     * @return int Returns 0 on success.
-     */
-    int open() override;
+    void start() override;
+    void stop() override;
 
-    /**
-     * Blocking call to read data from the connection.
-     *
-     * @return std::vector<uint8_t> Serialized protobuf data received from emulator.  This will be
-     *              an empty vector if the connection was closed or some other error occurred.
-     */
     std::vector<uint8_t> read() override;
-
-    /**
-     * Transmits a string of data to the emulator.
-     *
-     * @param data Serialized protobuf data to transmit.
-     *
-     * @return int Number of bytes transmitted, or -1 if failed.
-     */
     int write(const std::vector<uint8_t>& data) override;
 
-private:
-    std::mutex mMutex;
+    inline bool isOpen() override { return mPipeFd > 0; }
+
+   private:
     int mPipeFd;
 };
 
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/SocketComm.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/SocketComm.cpp
index 42c1c78..9eb8894 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/SocketComm.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/SocketComm.cpp
@@ -18,6 +18,7 @@
 
 #include <android/hardware/automotive/vehicle/2.0/IVehicle.h>
 #include <android/log.h>
+#include <arpa/inet.h>
 #include <log/log.h>
 #include <netinet/in.h>
 #include <sys/socket.h>
@@ -35,45 +36,46 @@
 
 namespace impl {
 
-SocketComm::SocketComm() {
-    // Initialize member vars
-    mCurSockFd = -1;
-    mExit      =  0;
-    mSockFd    = -1;
-}
-
+SocketComm::SocketComm(MessageProcessor* messageProcessor)
+    : mListenFd(-1), mMessageProcessor(messageProcessor) {}
 
 SocketComm::~SocketComm() {
-    stop();
 }
 
-int SocketComm::connect() {
-    sockaddr_in cliAddr;
-    socklen_t cliLen = sizeof(cliAddr);
-    int cSockFd = accept(mSockFd, reinterpret_cast<struct sockaddr*>(&cliAddr), &cliLen);
-
-    if (cSockFd >= 0) {
-        {
-            std::lock_guard<std::mutex> lock(mMutex);
-            mCurSockFd = cSockFd;
-        }
-        ALOGD("%s: Incoming connection received on socket %d", __FUNCTION__, cSockFd);
-    } else {
-        cSockFd = -1;
+void SocketComm::start() {
+    if (!listen()) {
+        return;
     }
 
-    return cSockFd;
+    mListenThread = std::make_unique<std::thread>(std::bind(&SocketComm::listenThread, this));
 }
 
-int SocketComm::open() {
+void SocketComm::stop() {
+    if (mListenFd > 0) {
+        ::close(mListenFd);
+        if (mListenThread->joinable()) {
+            mListenThread->join();
+        }
+        mListenFd = -1;
+    }
+}
+
+void SocketComm::sendMessage(emulator::EmulatorMessage const& msg) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    for (std::unique_ptr<SocketConn> const& conn : mOpenConnections) {
+        conn->sendMessage(msg);
+    }
+}
+
+bool SocketComm::listen() {
     int retVal;
     struct sockaddr_in servAddr;
 
-    mSockFd = socket(AF_INET, SOCK_STREAM, 0);
-    if (mSockFd < 0) {
-        ALOGE("%s: socket() failed, mSockFd=%d, errno=%d", __FUNCTION__, mSockFd, errno);
-        mSockFd = -1;
-        return -errno;
+    mListenFd = socket(AF_INET, SOCK_STREAM, 0);
+    if (mListenFd < 0) {
+        ALOGE("%s: socket() failed, mSockFd=%d, errno=%d", __FUNCTION__, mListenFd, errno);
+        mListenFd = -1;
+        return false;
     }
 
     memset(&servAddr, 0, sizeof(servAddr));
@@ -81,82 +83,114 @@
     servAddr.sin_addr.s_addr = INADDR_ANY;
     servAddr.sin_port = htons(DEBUG_SOCKET);
 
-    retVal = bind(mSockFd, reinterpret_cast<struct sockaddr*>(&servAddr), sizeof(servAddr));
+    retVal = bind(mListenFd, reinterpret_cast<struct sockaddr*>(&servAddr), sizeof(servAddr));
     if(retVal < 0) {
         ALOGE("%s: Error on binding: retVal=%d, errno=%d", __FUNCTION__, retVal, errno);
+        close(mListenFd);
+        mListenFd = -1;
+        return false;
+    }
+
+    ALOGI("%s: Listening for connections on port %d", __FUNCTION__, DEBUG_SOCKET);
+    ::listen(mListenFd, 1);
+    return true;
+}
+
+SocketConn* SocketComm::accept() {
+    sockaddr_in cliAddr;
+    socklen_t cliLen = sizeof(cliAddr);
+    int sfd = ::accept(mListenFd, reinterpret_cast<struct sockaddr*>(&cliAddr), &cliLen);
+
+    if (sfd > 0) {
+        char addr[INET_ADDRSTRLEN];
+        inet_ntop(AF_INET, &cliAddr.sin_addr, addr, INET_ADDRSTRLEN);
+
+        ALOGD("%s: Incoming connection received from %s:%d", __FUNCTION__, addr, cliAddr.sin_port);
+        return new SocketConn(mMessageProcessor, sfd);
+    }
+
+    return nullptr;
+}
+
+void SocketComm::listenThread() {
+    while (true) {
+        SocketConn* conn = accept();
+        if (conn == nullptr) {
+            return;
+        }
+
+        conn->start();
+        {
+            std::lock_guard<std::mutex> lock(mMutex);
+            mOpenConnections.push_back(std::unique_ptr<SocketConn>(conn));
+        }
+    }
+}
+
+/**
+ * Called occasionally to clean up connections that have been closed.
+ */
+void SocketComm::removeClosedConnections() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    std::remove_if(mOpenConnections.begin(), mOpenConnections.end(),
+                   [](std::unique_ptr<SocketConn> const& c) { return !c->isOpen(); });
+}
+
+SocketConn::SocketConn(MessageProcessor* messageProcessor, int sfd)
+    : CommConn(messageProcessor), mSockFd(sfd) {}
+
+/**
+ * Reads, in a loop, exactly numBytes from the given fd. If the connection is closed, returns
+ * an empty buffer, otherwise will return exactly the given number of bytes.
+ */
+std::vector<uint8_t> readExactly(int fd, int numBytes) {
+    std::vector<uint8_t> buffer(numBytes);
+    int totalRead = 0;
+    int offset = 0;
+    while (totalRead < numBytes) {
+        int numRead = ::read(fd, &buffer.data()[offset], numBytes - offset);
+        if (numRead == 0) {
+            buffer.resize(0);
+            return buffer;
+        }
+
+        totalRead += numRead;
+    }
+    return buffer;
+}
+
+/**
+ * Reads an int, guaranteed to be non-zero, from the given fd. If the connection is closed, returns
+ * -1.
+ */
+int32_t readInt(int fd) {
+    std::vector<uint8_t> buffer = readExactly(fd, sizeof(int32_t));
+    if (buffer.size() == 0) {
+        return -1;
+    }
+
+    int32_t value = *reinterpret_cast<int32_t*>(buffer.data());
+    return ntohl(value);
+}
+
+std::vector<uint8_t> SocketConn::read() {
+    int32_t msgSize = readInt(mSockFd);
+    if (msgSize <= 0) {
+        ALOGD("%s: Connection terminated on socket %d", __FUNCTION__, mSockFd);
+        return std::vector<uint8_t>();
+    }
+
+    return readExactly(mSockFd, msgSize);
+}
+
+void SocketConn::stop() {
+    if (mSockFd > 0) {
         close(mSockFd);
         mSockFd = -1;
-        return -errno;
-    }
-
-    listen(mSockFd, 1);
-
-    // Set the socket to be non-blocking so we can poll it continouously
-    fcntl(mSockFd, F_SETFL, O_NONBLOCK);
-
-    return 0;
-}
-
-std::vector<uint8_t> SocketComm::read() {
-    int32_t msgSize;
-    int numBytes = 0;
-
-    // This is a variable length message.
-    // Read the number of bytes to rx over the socket
-    numBytes = ::read(mCurSockFd, &msgSize, sizeof(msgSize));
-    msgSize = ntohl(msgSize);
-
-    if (numBytes != sizeof(msgSize)) {
-        // This happens when connection is closed
-        ALOGD("%s: numBytes=%d, expected=4", __FUNCTION__, numBytes);
-        ALOGD("%s: Connection terminated on socket %d", __FUNCTION__, mCurSockFd);
-        {
-            std::lock_guard<std::mutex> lock(mMutex);
-            mCurSockFd = -1;
-        }
-
-        return std::vector<uint8_t>();
-    }
-
-    std::vector<uint8_t> msg = std::vector<uint8_t>(msgSize);
-
-    numBytes = ::read(mCurSockFd, msg.data(), msgSize);
-
-    if ((numBytes == msgSize) && (msgSize > 0)) {
-        // Received a message.
-        return msg;
-    } else {
-        // This happens when connection is closed
-        ALOGD("%s: numBytes=%d, msgSize=%d", __FUNCTION__, numBytes, msgSize);
-        ALOGD("%s: Connection terminated on socket %d", __FUNCTION__, mCurSockFd);
-        {
-            std::lock_guard<std::mutex> lock(mMutex);
-            mCurSockFd = -1;
-        }
-
-        return std::vector<uint8_t>();
     }
 }
 
-void SocketComm::stop() {
-    if (mExit == 0) {
-        std::lock_guard<std::mutex> lock(mMutex);
-        mExit = 1;
-
-        // Close emulator socket if it is open
-        if (mCurSockFd != -1) {
-            close(mCurSockFd);
-            mCurSockFd = -1;
-        }
-
-        if (mSockFd != -1) {
-            close(mSockFd);
-            mSockFd = -1;
-        }
-    }
-}
-
-int SocketComm::write(const std::vector<uint8_t>& data) {
+int SocketConn::write(const std::vector<uint8_t>& data) {
     static constexpr int MSG_HEADER_LEN = 4;
     int retVal = 0;
     union {
@@ -168,19 +202,17 @@
     msgLen = static_cast<uint32_t>(data.size());
     msgLen = htonl(msgLen);
 
-    std::lock_guard<std::mutex> lock(mMutex);
-    if (mCurSockFd != -1) {
-        retVal = ::write(mCurSockFd, msgLenBytes, MSG_HEADER_LEN);
+    if (mSockFd > 0) {
+        retVal = ::write(mSockFd, msgLenBytes, MSG_HEADER_LEN);
 
         if (retVal == MSG_HEADER_LEN) {
-            retVal = ::write(mCurSockFd, data.data(), data.size());
+            retVal = ::write(mSockFd, data.data(), data.size());
         }
     }
 
     return retVal;
 }
 
-
 }  // impl
 
 }  // namespace V2_0
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/SocketComm.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/SocketComm.h
index 12cfb29..88b852b 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/SocketComm.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/SocketComm.h
@@ -18,8 +18,9 @@
 #define android_hardware_automotive_vehicle_V2_0_impl_SocketComm_H_
 
 #include <mutex>
+#include <thread>
 #include <vector>
-#include "CommBase.h"
+#include "CommConn.h"
 
 namespace android {
 namespace hardware {
@@ -29,29 +30,60 @@
 
 namespace impl {
 
+class SocketConn;
+
 /**
- * SocketComm opens a socket via adb's TCP port forwarding to enable a Host PC to connect to
- * the VehicleHAL.
+ * SocketComm opens a socket, and listens for connections from clients. Typically the client will be
+ * adb's TCP port-forwarding to enable a host PC to connect to the VehicleHAL.
  */
-class SocketComm : public CommBase {
-public:
-    SocketComm();
+class SocketComm {
+   public:
+    SocketComm(MessageProcessor* messageProcessor);
     virtual ~SocketComm();
 
+    void start();
+    void stop();
+
     /**
-     * Creates a connection to the other side.
+     * Serialized and send the given message to all connected clients.
+     */
+    void sendMessage(emulator::EmulatorMessage const& msg);
+
+   private:
+    int mListenFd;
+    std::unique_ptr<std::thread> mListenThread;
+    std::vector<std::unique_ptr<SocketConn>> mOpenConnections;
+    MessageProcessor* mMessageProcessor;
+    std::mutex mMutex;
+
+    /**
+     * Opens the socket and begins listening.
+     *
+     * @return bool Returns true on success.
+     */
+    bool listen();
+
+    /**
+     * Blocks and waits for a connection from a client, returns a new SocketConn with the connection
+     * or null, if the connection has been closed.
      *
      * @return int Returns fd or socket number if connection is successful.
      *              Otherwise, returns -1 if no connection is availble.
      */
-    int connect() override;
+    SocketConn* accept();
 
-    /**
-     * Opens a socket and begins listening.
-     *
-     * @return int Returns 0 on success.
-     */
-    int open() override;
+    void listenThread();
+
+    void removeClosedConnections();
+};
+
+/**
+ * SocketConn represents a single connection to a client.
+ */
+class SocketConn : public CommConn {
+   public:
+    SocketConn(MessageProcessor* messageProcessor, int sfd);
+    virtual ~SocketConn() = default;
 
     /**
      * Blocking call to read data from the connection.
@@ -75,10 +107,9 @@
      */
     int write(const std::vector<uint8_t>& data) override;
 
-private:
-    int mCurSockFd;
-    std::atomic<int> mExit;
-    std::mutex mMutex;
+    inline bool isOpen() override { return mSockFd > 0; }
+
+   private:
     int mSockFd;
 };
 
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp
index bf7be09..356a6b9 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp
@@ -16,9 +16,10 @@
 #define LOG_TAG "VehicleEmulator_v2_0"
 #include <android/log.h>
 
-#include <algorithm>
 #include <android-base/properties.h>
+#include <log/log.h>
 #include <utils/SystemClock.h>
+#include <algorithm>
 
 #include <vhal_v2_0/VehicleUtils.h>
 
@@ -35,32 +36,45 @@
 
 namespace impl {
 
-std::unique_ptr<CommBase> CommFactory::create() {
-    bool isEmulator = android::base::GetBoolProperty("ro.kernel.qemu", false);
+VehicleEmulator::VehicleEmulator(EmulatedVehicleHalIface* hal) : mHal{hal} {
+    mHal->registerEmulator(this);
 
-    if (isEmulator) {
-        return std::make_unique<PipeComm>();
-    } else {
-        return std::make_unique<SocketComm>();
+    ALOGI("Starting SocketComm");
+    mSocketComm = std::make_unique<SocketComm>(this);
+    mSocketComm->start();
+
+    if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
+        ALOGI("Starting PipeComm");
+        mPipeComm = std::make_unique<PipeComm>(this);
+        mPipeComm->start();
     }
 }
 
 VehicleEmulator::~VehicleEmulator() {
-    mExit = true;   // Notify thread to finish and wait for it to terminate.
-    mComm->stop();  // Close emulator socket if it is open.
-    if (mThread.joinable()) mThread.join();
+    mSocketComm->stop();
+    if (mPipeComm) {
+        mPipeComm->stop();
+    }
 }
 
+/**
+ * This is called by the HAL when a property changes. We need to notify our clients that it has
+ * changed.
+ */
 void VehicleEmulator::doSetValueFromClient(const VehiclePropValue& propValue) {
     emulator::EmulatorMessage msg;
     emulator::VehiclePropValue *val = msg.add_value();
     populateProtoVehiclePropValue(val, &propValue);
     msg.set_status(emulator::RESULT_OK);
     msg.set_msg_type(emulator::SET_PROPERTY_ASYNC);
-    txMsg(msg);
+
+    mSocketComm->sendMessage(msg);
+    if (mPipeComm) {
+        mPipeComm->sendMessage(msg);
+    }
 }
 
-void VehicleEmulator::doGetConfig(VehicleEmulator::EmulatorMessage& rxMsg,
+void VehicleEmulator::doGetConfig(VehicleEmulator::EmulatorMessage const& rxMsg,
                                   VehicleEmulator::EmulatorMessage& respMsg) {
     std::vector<VehiclePropConfig> configs = mHal->listProperties();
     emulator::VehiclePropGet getProp = rxMsg.prop(0);
@@ -79,7 +93,7 @@
     }
 }
 
-void VehicleEmulator::doGetConfigAll(VehicleEmulator::EmulatorMessage& /* rxMsg */,
+void VehicleEmulator::doGetConfigAll(VehicleEmulator::EmulatorMessage const& /* rxMsg */,
                                      VehicleEmulator::EmulatorMessage& respMsg) {
     std::vector<VehiclePropConfig> configs = mHal->listProperties();
 
@@ -92,8 +106,8 @@
     }
 }
 
-void VehicleEmulator::doGetProperty(VehicleEmulator::EmulatorMessage& rxMsg,
-                                    VehicleEmulator::EmulatorMessage& respMsg)  {
+void VehicleEmulator::doGetProperty(VehicleEmulator::EmulatorMessage const& rxMsg,
+                                    VehicleEmulator::EmulatorMessage& respMsg) {
     int32_t areaId = 0;
     emulator::VehiclePropGet getProp = rxMsg.prop(0);
     int32_t propId = getProp.prop();
@@ -119,8 +133,8 @@
     respMsg.set_status(status);
 }
 
-void VehicleEmulator::doGetPropertyAll(VehicleEmulator::EmulatorMessage& /* rxMsg */,
-                                       VehicleEmulator::EmulatorMessage& respMsg)  {
+void VehicleEmulator::doGetPropertyAll(VehicleEmulator::EmulatorMessage const& /* rxMsg */,
+                                       VehicleEmulator::EmulatorMessage& respMsg) {
     respMsg.set_msg_type(emulator::GET_PROPERTY_ALL_RESP);
     respMsg.set_status(emulator::RESULT_OK);
 
@@ -132,7 +146,7 @@
     }
 }
 
-void VehicleEmulator::doSetProperty(VehicleEmulator::EmulatorMessage& rxMsg,
+void VehicleEmulator::doSetProperty(VehicleEmulator::EmulatorMessage const& rxMsg,
                                     VehicleEmulator::EmulatorMessage& respMsg) {
     emulator::VehiclePropValue protoVal = rxMsg.value(0);
     VehiclePropValue val = {
@@ -173,58 +187,28 @@
     respMsg.set_status(halRes ? emulator::RESULT_OK : emulator::ERROR_INVALID_PROPERTY);
 }
 
-void VehicleEmulator::txMsg(emulator::EmulatorMessage& txMsg) {
-    int numBytes = txMsg.ByteSize();
-    std::vector<uint8_t> msg(static_cast<size_t>(numBytes));
-
-    if (!txMsg.SerializeToArray(msg.data(), static_cast<int32_t>(msg.size()))) {
-        ALOGE("%s: SerializeToString failed!", __func__);
-        return;
-    }
-
-    if (mExit) {
-        ALOGW("%s: unable to transmit a message, connection closed", __func__);
-        return;
-    }
-
-    // Send the message
-    int retVal = mComm->write(msg);
-    if (retVal < 0) {
-        ALOGE("%s: Failed to tx message: retval=%d, errno=%d", __func__, retVal, errno);
-    }
-}
-
-void VehicleEmulator::parseRxProtoBuf(std::vector<uint8_t>& msg) {
-    emulator::EmulatorMessage rxMsg;
-    emulator::EmulatorMessage respMsg;
-
-    if (rxMsg.ParseFromArray(msg.data(), static_cast<int32_t>(msg.size()))) {
-        switch (rxMsg.msg_type()) {
-            case emulator::GET_CONFIG_CMD:
-                doGetConfig(rxMsg, respMsg);
-                break;
-            case emulator::GET_CONFIG_ALL_CMD:
-                doGetConfigAll(rxMsg, respMsg);
-                break;
-            case emulator::GET_PROPERTY_CMD:
-                doGetProperty(rxMsg, respMsg);
-                break;
-            case emulator::GET_PROPERTY_ALL_CMD:
-                doGetPropertyAll(rxMsg, respMsg);
-                break;
-            case emulator::SET_PROPERTY_CMD:
-                doSetProperty(rxMsg, respMsg);
-                break;
-            default:
-                ALOGW("%s: Unknown message received, type = %d", __func__, rxMsg.msg_type());
-                respMsg.set_status(emulator::ERROR_UNIMPLEMENTED_CMD);
-                break;
-        }
-
-        // Send the reply
-        txMsg(respMsg);
-    } else {
-        ALOGE("%s: ParseFromString() failed. msgSize=%d", __func__, static_cast<int>(msg.size()));
+void VehicleEmulator::processMessage(emulator::EmulatorMessage const& rxMsg,
+                                     emulator::EmulatorMessage& respMsg) {
+    switch (rxMsg.msg_type()) {
+        case emulator::GET_CONFIG_CMD:
+            doGetConfig(rxMsg, respMsg);
+            break;
+        case emulator::GET_CONFIG_ALL_CMD:
+            doGetConfigAll(rxMsg, respMsg);
+            break;
+        case emulator::GET_PROPERTY_CMD:
+            doGetProperty(rxMsg, respMsg);
+            break;
+        case emulator::GET_PROPERTY_ALL_CMD:
+            doGetPropertyAll(rxMsg, respMsg);
+            break;
+        case emulator::SET_PROPERTY_CMD:
+            doSetProperty(rxMsg, respMsg);
+            break;
+        default:
+            ALOGW("%s: Unknown message received, type = %d", __func__, rxMsg.msg_type());
+            respMsg.set_status(emulator::ERROR_UNIMPLEMENTED_CMD);
+            break;
     }
 }
 
@@ -316,40 +300,6 @@
     }
 }
 
-void VehicleEmulator::rxMsg() {
-    while (!mExit) {
-        std::vector<uint8_t> msg = mComm->read();
-
-        if (msg.size() > 0) {
-            // Received a message.
-            parseRxProtoBuf(msg);
-        } else {
-            // This happens when connection is closed
-            ALOGD("%s: msgSize=%zu", __func__, msg.size());
-            break;
-        }
-    }
-}
-
-void VehicleEmulator::rxThread() {
-    if (mExit) return;
-
-    int retVal = mComm->open();
-    if (retVal != 0) mExit = true;
-
-    // Comms are properly opened
-    while (!mExit) {
-        retVal = mComm->connect();
-
-        if (retVal >= 0) {
-            rxMsg();
-        }
-
-        // Check every 100ms for a new connection
-        std::this_thread::sleep_for(std::chrono::milliseconds(100));
-    }
-}
-
 }  // impl
 
 }  // namespace V2_0
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.h
index 1a8cfe2..58e387a 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.h
@@ -24,7 +24,9 @@
 
 #include "vhal_v2_0/VehicleHal.h"
 
-#include "CommBase.h"
+#include "CommConn.h"
+#include "PipeComm.h"
+#include "SocketComm.h"
 #include "VehicleHalProto.pb.h"
 
 namespace android {
@@ -61,48 +63,36 @@
     VehicleEmulator* mEmulator;
 };
 
-struct CommFactory {
-    static std::unique_ptr<CommBase> create();
-};
-
 /**
  * Emulates vehicle by providing controlling interface from host side either through ADB or Pipe.
  */
-class VehicleEmulator {
-public:
-    VehicleEmulator(EmulatedVehicleHalIface* hal,
-                    std::unique_ptr<CommBase> comm = CommFactory::create())
-            : mHal { hal },
-              mComm(comm.release()),
-              mThread { &VehicleEmulator::rxThread, this} {
-        mHal->registerEmulator(this);
-    }
+class VehicleEmulator : public MessageProcessor {
+   public:
+    VehicleEmulator(EmulatedVehicleHalIface* hal);
     virtual ~VehicleEmulator();
 
     void doSetValueFromClient(const VehiclePropValue& propValue);
+    void processMessage(emulator::EmulatorMessage const& rxMsg,
+                        emulator::EmulatorMessage& respMsg) override;
 
-private:
+   private:
+    friend class ConnectionThread;
     using EmulatorMessage = emulator::EmulatorMessage;
 
-    void doGetConfig(EmulatorMessage& rxMsg, EmulatorMessage& respMsg);
-    void doGetConfigAll(EmulatorMessage& rxMsg, EmulatorMessage& respMsg);
-    void doGetProperty(EmulatorMessage& rxMsg, EmulatorMessage& respMsg);
-    void doGetPropertyAll(EmulatorMessage& rxMsg, EmulatorMessage& respMsg);
-    void doSetProperty(EmulatorMessage& rxMsg, EmulatorMessage& respMsg);
-    void txMsg(emulator::EmulatorMessage& txMsg);
-    void parseRxProtoBuf(std::vector<uint8_t>& msg);
+    void doGetConfig(EmulatorMessage const& rxMsg, EmulatorMessage& respMsg);
+    void doGetConfigAll(EmulatorMessage const& rxMsg, EmulatorMessage& respMsg);
+    void doGetProperty(EmulatorMessage const& rxMsg, EmulatorMessage& respMsg);
+    void doGetPropertyAll(EmulatorMessage const& rxMsg, EmulatorMessage& respMsg);
+    void doSetProperty(EmulatorMessage const& rxMsg, EmulatorMessage& respMsg);
     void populateProtoVehicleConfig(emulator::VehiclePropConfig* protoCfg,
                                     const VehiclePropConfig& cfg);
     void populateProtoVehiclePropValue(emulator::VehiclePropValue* protoVal,
                                        const VehiclePropValue* val);
-    void rxMsg();
-    void rxThread();
 
 private:
-    std::atomic<bool> mExit { false };
     EmulatedVehicleHalIface* mHal;
-    std::unique_ptr<CommBase> mComm;
-    std::thread mThread;
+    std::unique_ptr<SocketComm> mSocketComm;
+    std::unique_ptr<PipeComm> mPipeComm;
 };
 
 }  // impl
diff --git a/biometrics/face/1.0/Android.bp b/biometrics/face/1.0/Android.bp
new file mode 100644
index 0000000..61bf247
--- /dev/null
+++ b/biometrics/face/1.0/Android.bp
@@ -0,0 +1,26 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.biometrics.face@1.0",
+    root: "android.hardware",
+    vndk: {
+        enabled: true,
+    },
+    srcs: [
+        "types.hal",
+        "IBiometricsFace.hal",
+        "IBiometricsFaceClientCallback.hal",
+    ],
+    interfaces: [
+        "android.hidl.base@1.0",
+    ],
+    types: [
+        "FaceAcquiredInfo",
+        "FaceError",
+        "OptionalUint64",
+        "Status",
+        "UserHandle",
+    ],
+    gen_java: true,
+}
+
diff --git a/biometrics/face/1.0/IBiometricsFace.hal b/biometrics/face/1.0/IBiometricsFace.hal
new file mode 100644
index 0000000..fbdb210
--- /dev/null
+++ b/biometrics/face/1.0/IBiometricsFace.hal
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics.face@1.0;
+
+import IBiometricsFaceClientCallback;
+
+// TODO(b/78538290): Update comments with state machine transitions when ready.
+// TODO(b/78537981): Update comments with callback interaction contract.
+// TODO(b/79496983): Update comments with status returns fully enumerated.
+/**
+ * The HAL interface for biometric face authentication.
+ */
+interface IBiometricsFace {
+
+    /**
+     * Sets the current client callback.
+     *
+     * Registers a user function that must receive notifications from the HAL.
+     * There is usually only one client (FaceService). This call must block
+     * if the HAL state machine is in busy state until the HAL leaves the
+     * busy state.
+     *
+     * All callback methods pass a deviceId to differentiate callback
+     * invocations in the case where multiple sensors exist.
+     *
+     * @param clientCallback The client defined callback to register.
+     * @return result, with its "value" parameter representing a "deviceId",
+     *     which must be unique for a given sensor.
+     */
+    @callflow(next={"setActiveUser"})
+    @entry
+    setCallback(IBiometricsFaceClientCallback clientCallback)
+        generates (OptionalUint64 result);
+
+    /**
+     * Sets the active user, which all subsequent HAL operations are applied to.
+     *
+     * HAL service implementors must ensure that operations are restricted to
+     * the given user. Clients must not call any part of this interface, except
+     * for setCallback(), without first having set an active user. The
+     * implementation is responsible for cancelling the current operation and
+     * returning to the idle state. Calling this method with the same userId
+     * should have no effect on the state machine.
+     *
+     * @param userId A non-negative user identifier that must be unique and
+     *     persistent for a given user.
+     * @param storePath filesystem path to the template storage directory.
+     */
+    @callflow(next={"authenticate", "preEnroll", "enumerate", "remove"})
+    setActiveUser(int32_t userId, string storePath) generates (Status status);
+
+    /**
+     * Begins a pre-enrollment request.
+     *
+     * Generates a unique and cryptographically secure random token used to
+     * indicate the start of an enrollment transaction. preEnroll() and
+     * postEnroll() specify a pin/pattern/password cleared time window where
+     * enrollment is allowed.
+     *
+     * preEnroll() generates a challenge which must then be wrapped by the
+     * gatekeeper after verifying a successful strong authentication attempt,
+     * which generates a Hardware Authentication Token. The challenge prevents
+     * spoofing and replay attacks and ensures that we only update a user’s face
+     * template if the operation was preceded by some kind of strong credential
+     * confirmation (e.g. device password).
+     *
+     * @return result, with its "value" parameter representing a "challenge": a
+     *     unique and cryptographically secure random token.
+     */
+    @callflow(next={"enroll", "postEnroll"})
+    preEnroll() generates (OptionalUint64 result);
+
+    /**
+     * Enrolls a user's face.
+     *
+     * Note that this interface permits implementations where multiple faces can
+     * be enrolled for a single user. However, allowing multiple faces to be
+     * enrolled can be a severe security vulnerability and hence, most
+     * implementations must ensure that only a single face be enrolled at a
+     * given time. Multi-enrollment must only be used where there is a clear
+     * necessity for a shared use case, e.g. TVs or cars.
+     *
+     * Note that the Hardware Authentication Token must still be valid after
+     * this call, and must be explicitly invalidated by a call to postEnroll().
+     * This allows clients to immediately reattempt enrollment (for example, if
+     * a user wasn’t satisfied with their enrollment) without having to go
+     * through another strong authentication flow.
+     *
+     * This method triggers the IBiometricsFaceClientCallback#onEnrollResult()
+     * method.
+     *
+     * @param hat A valid Hardware Authentication Token, generated as a result
+     *     of a preEnroll() challenge being wrapped by the gatekeeper after a
+     *     sucessful strong authentication request.
+     * @param timeoutSec A timeout in seconds, after which this enrollment
+     *     attempt is cancelled. Note that the client still needs to
+     *     call postEnroll() to terminate the enrollment session.
+     * @return status The status of this method call.
+     */
+    @callflow(next={"cancel", "enroll", "postEnroll", "remove"})
+    enroll(vec<uint8_t> hat, uint32_t timeoutSec) generates (Status status);
+
+    /**
+     * Finishes the enrollment session and invalidates the challenge generated
+     * by preEnroll().
+     *
+     * Clients must call this method once enrollment is complete, and the user's
+     * face template no longer needs to be updated.
+     *
+     * @return status The status of this method call.
+     */
+    @callflow(next={"authenticate", "setActiveUser", "enumerate", "remove"})
+    postEnroll() generates (Status status);
+
+    /**
+     * Returns an identifier associated with the current face set.
+     *
+     * The authenticator ID must change whenever a new face is enrolled. The
+     * authenticator ID must not be changed when a face is deleted. The
+     * authenticator ID must be an entropy-encoded random number which all
+     * current templates are tied to. The authenticator ID must be immutable
+     * outside of an active enrollment window to prevent replay attacks.
+     *
+     * @return result, with its value parameter representing an
+     *     "authenticatorId": an identifier associated to the user's current
+     *     face enrollment.
+     */
+    @callflow(next={"authenticate"})
+    getAuthenticatorId() generates (OptionalUint64 result);
+
+    /**
+     * Cancels a pending enrollment or authentication request.
+     *
+     * @return status The status of this method call.
+     */
+    @callflow(next={"authenticate", "enroll", "enumerate", "remove",
+        "setActiveUser"})
+    cancel() generates (Status status);
+
+    /**
+     * Enumerates all face templates associated with the active user.
+     *
+     * The onEnumerate() callback method is invoked once for each face template
+     * found.
+     *
+     * @return status The status of this method call.
+     */
+    @callflow(next={"remove", "enroll", "authenticate", "setActiveUser"})
+    enumerate() generates (Status status);
+
+    /**
+     * Removes a face template or all face templates associated with the active
+     * user.
+     *
+     * This method triggers the IBiometricsFaceClientCallback#onRemoved() method.
+     *
+     * @param faceId The id correpsonding to the face to be removed; or 0 if all
+     *    faces are to be removed.
+     * @return status The status of this method call.
+     */
+    @callflow(next={"enumerate", "authenticate", "cancel", "getAuthenticatorId",
+        "setActiveUser"})
+    remove(uint32_t faceId) generates (Status status);
+
+    /**
+     * Authenticates the active user.
+     *
+     * An optional operationId can be specified as a token from the transaction
+     * being authorized.
+     *
+     * @param operationId A non-zero operation id associated with a crypto
+     * object instance; or 0 if not being used.
+     * @return status The status of this method call.
+     */
+    @callflow(next={"cancel", "preEnroll", "remove"})
+    authenticate(uint64_t operationId) generates (Status status);
+};
diff --git a/biometrics/face/1.0/IBiometricsFaceClientCallback.hal b/biometrics/face/1.0/IBiometricsFaceClientCallback.hal
new file mode 100644
index 0000000..93848c5
--- /dev/null
+++ b/biometrics/face/1.0/IBiometricsFaceClientCallback.hal
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics.face@1.0;
+
+/**
+ * This callback interface is used by clients to recieve updates from the face
+ * HAL.
+ */
+interface IBiometricsFaceClientCallback {
+
+    /**
+     * A callback invoked when one enrollment step has been completed.
+     *
+     * @param deviceId A unique id associated with the HAL implementation
+     *     service that processed this enrollment step.
+     * @param faceId The id of the face template being enrolled.
+     * @param userId The active user id for the template being enrolled.
+     * @param remaining The number of remaining steps before enrolllment is
+     *     complete or 0 if enrollment has completed successfully.
+     */
+    oneway onEnrollResult(uint64_t deviceId, uint32_t faceId, int32_t userId,
+        uint32_t remaining);
+
+    /**
+     * A callback invoked when a face has been successfully authenticated.
+     *
+     * @param deviceId A unique id associated with the HAL implementation
+     *     service that processed this autentication attempt.
+     * @param faceId The id of the face template that passed the authentication
+     *     challenge.
+     * @param userId The active user id for the authenticated face.
+     * @param token The hardware authentication token associated with this
+     *     authenticate operation.
+     */
+    oneway onAuthenticated(uint64_t deviceId, uint32_t faceId, int32_t userId,
+        vec<uint8_t> token);
+
+    /**
+     * A callback invoked when a face is acquired.
+     *
+     * If a non-critical, recoverable error occurs during an enrollment or
+     * authentication attempt, the HAL implementation must invoke this callback
+     * to allow clients to inform the user that some actionable change must be
+     * made.
+     *
+     * @param deviceId A unique id associated with the HAL implementation
+     *     service that acquired a face.
+     * @param userId The id of the active user associated with the attempted
+     *     face acquisition.
+     * @param acquiredInfo A message about the quality of the acquired image.
+     * @param vendorCode An optional vendor-specific message. This is only valid
+     *     when acquiredInfo == FaceAcquiredInfo.VENDOR. This message is opaque
+     *     to the framework, and vendors must provide code to handle it. For
+     *     example this can be used to guide enrollment in Settings or provide
+     *     a message during authentication that is vendor-specific. The vendor
+     *     is expected to provide help strings to cover all known values.
+     */
+     oneway onAcquired(uint64_t deviceId, int32_t userId,
+         FaceAcquiredInfo acquiredInfo, int32_t vendorCode);
+
+    /**
+     * A callback invoked when an error has occured.
+     *
+     * @param deviceId A unique id associated with the HAL implementation
+     *     service where this error occured.
+     * @param userId The id of the active user when the error occured, or
+     *     UserHandle::NONE if an active user had not been set yet.
+     * @param error A message about the error that occurred.
+     * @param vendorCode An optional, vendor-speicifc error message. Only valid
+     *     when error == FaceError.VENDOR. This message is opaque to the
+     *     framework, and vendors must provide code to handle it. For example,
+     *     this scan be used to show the user an error message specific to the
+     *     device. The vendor is expected to provide error strings to cover
+     *     all known values.
+     */
+    oneway onError(uint64_t deviceId, int32_t userId, FaceError error,
+        int32_t vendorCode);
+
+    /**
+     * A callback invoked when a face template has been removed.
+     *
+     * @param deviceId A unique id associated with the HAL implementation
+     *     service that processed this removal.
+     * @param faceId The id of the face template that was removed.
+     * @param userId The active user id for the removed face template.
+     * @param remaining The number of face templates remaining after this
+     *     removal, or 0 if there are no more.
+     */
+    oneway onRemoved(uint64_t deviceId, uint32_t faceId, int32_t userId,
+        uint32_t remaining);
+
+    /**
+     * A callback invoked to enumerate all current face templates.
+     *
+     * @param deviceId A unique id associated with the HAL implementation
+     *     service that processed this enumeration.
+     * @param faceIds A list of ids of all currently enrolled face templates.
+     * @param userId The active user id for the enumerated face template.
+     */
+    oneway onEnumerate(uint64_t deviceId, vec<uint32_t> faceIds,
+        int32_t userId);
+};
diff --git a/biometrics/face/1.0/types.hal b/biometrics/face/1.0/types.hal
new file mode 100644
index 0000000..08af919
--- /dev/null
+++ b/biometrics/face/1.0/types.hal
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics.face@1.0;
+
+/*
+ * In the event setActiveUser is not called, all error messages will return
+ * this userId.
+ */
+enum UserHandle : int32_t {
+    NONE = -1
+};
+
+/**
+ * Status codes returned directly by the HIDL method calls upon critical errors
+ * where the callback cannot be invoked. Most errors should sent through the
+ * onError callback using one of the FaceErrors below.
+ */
+enum Status : uint32_t {
+    /**
+     * The method was invoked successfully.
+     */
+    OK = 0,
+
+    /**
+     * One of the arguments to the method call is invalid.
+     */
+    ILLEGAL_ARGUMENT = 1,
+
+    /**
+     * This face HAL does not support this operation.
+     */
+    OPERATION_NOT_SUPPORTED = 2,
+
+    /**
+     * The HAL has encountered an internal error and cannot complete the request.
+     */
+    INTERNAL_ERROR = 3
+};
+
+/**
+ * Face errors represent events that can't be immediately recovered by user
+ * intervention. These are returned in the onError callback.
+ *
+ * Upon receiving a face error, clients must terminate the current operation and
+ * notify the user where possible.
+ */
+enum FaceError : int32_t {
+
+    /**
+     * A hardware error has occured that cannot be resolved. Try again later.
+     */
+    HW_UNAVAILABLE = 1,
+
+    /**
+     * The current enroll or authenticate operation could not be completed;
+     * the sensor was unable to process the current image.
+     */
+    UNABLE_TO_PROCESS = 2,
+
+    /**
+     * The current operation took too long to complete. This is intended to
+     * prevent programs from blocking the face HAL indefinitely. The timeout is
+     * framework and sensor-specific, but is generally on the order of 30
+     * seconds.
+     */
+    TIMEOUT = 3,
+
+    /**
+     * The current operation could not be completed because there is not enough
+     * storage space remaining to do so.
+     */
+    NO_SPACE = 4,
+
+    /**
+     * The current operation has been cancelled. This may happen if a new
+     * request (authenticate, remove) is initiated while an on-going operation
+     * is in progress, or if cancel() was called.
+     */
+    CANCELED = 5,
+
+    /**
+     * The current remove operation could not be completed; the face template
+     * provided could not be removed.
+     */
+    UNABLE_TO_REMOVE = 6,
+
+    /**
+     * Face authentication is locked out due to too many unsuccessful attempts.
+     */
+    LOCKOUT = 7,
+
+    /**
+     * Used to enable a vendor-specific error message.
+     */
+    VENDOR = 8,
+};
+
+/**
+ * Face acquisition information provides feedback for the current enrollment
+ * or authentication operation.
+ *
+ * This information indicates that the user can take immediate action to resolve
+ * an issue, and clients must ensure that this information is surfaced to the
+ * user.
+ */
+enum FaceAcquiredInfo : int32_t {
+
+    /**
+     * The face acquired was good; no further user interaction is necessary.
+     */
+    GOOD = 0,
+
+    /**
+     * The face data acquired was too noisy or did not have sufficient detail.
+     * This is a catch-all for all acquisition errors not captured by the other
+     * constants.
+     */
+    INSUFFICIENT = 1,
+
+    /**
+     * Because there was too much ambient light, the captured face data was too
+     * bright. It's reasonable to return this after multiple
+     * FaceAcquiredInfo.INSUFFICIENT.
+     *
+     * The user is expected to take action to retry the operation in better
+     * lighting conditions when this is returned.
+     */
+    TOO_BRIGHT = 2,
+
+    /**
+     * Because there was not enough illumination, the captured face data was too
+     * dark. It's reasonable to return this after multiple
+     * FaceAcquiredInfo.INSUFFICIENT.
+     *
+     * The user is expected to take action to retry the operation in better
+     * lighting conditions when this is returned.
+     */
+    TOO_DARK = 3,
+
+    /**
+     * The detected face is too close to the sensor, and the image cannot be
+     * processed.
+     *
+     * The user is expected to be informed to move further from the sensor when
+     * this is returned.
+     */
+    TOO_CLOSE = 4,
+
+    /**
+     * The detected face is too small, as the user might be too far away from
+     * the sensor.
+     *
+     * The user is expected to be informed to move closer to the sensor when
+     * this is returned.
+     */
+    TOO_FAR = 5,
+
+    /**
+     * Only the upper part of the face was detected. The sensor's field of view
+     * is too high.
+     *
+     * The user should be informed to move up with respect to the sensor when
+     * this is returned.
+     */
+    FACE_TOO_HIGH = 6,
+
+    /**
+     * Only the lower part of the face was detected. The sensor's field of view
+     * is too low.
+     *
+     * The user should be informed to move down with respect to the sensor when
+     * this is returned.
+     */
+    FACE_TOO_LOW = 7,
+
+    /**
+     * Only the right part of the face was detected. The sensor's field of view
+     * is too far right.
+     *
+     * The user should be informed to move to the right with respect to the
+     * sensor when this is returned.
+     */
+    FACE_TOO_RIGHT = 8,
+
+    /**
+     * Only the left part of the face was detected. The sensor's field of view
+     * is too far left.
+     *
+     * The user should be informed to move to the left with respect to the
+     * sensor when this is returned.
+     */
+    FACE_TOO_LEFT = 9,
+
+    /**
+     * The user's face was directed away from the sensor. The user should be
+     * informed to face the sensor when this is returned.
+     */
+    POOR_GAZE = 10,
+
+    /**
+     * No face was detected within the sensor's field of view.
+     *
+     * The user should be informed to point the sensor to a face when this is
+     * returned.
+     */
+    NOT_DETECTED = 11,
+
+    /**
+     * Too much motion was detected.
+     *
+     * The user should be informed to keep their face steady relative to the
+     * sensor.
+     */
+    TOO_MUCH_MOTION = 12,
+
+    /**
+     * The sensor needs to be recalibrated.
+     */
+    RECALIBRATE = 13,
+
+    /**
+     * Used to enable a vendor-specific acquisition message.
+     */
+    VENDOR = 14
+};
+
+/**
+ * Result structure with an additional uint64_t field. See documentation in
+ * setCallback(), preEnroll(), and getAuthenticatorId() for usage of the value.
+ */
+struct OptionalUint64 {
+    /**
+     * The return status.
+     */
+    Status status;
+
+    /**
+     * This value is only meaningful if status is OK.
+     */
+    uint64_t value;
+};
diff --git a/camera/device/3.4/default/ExternalCameraDevice.cpp b/camera/device/3.4/default/ExternalCameraDevice.cpp
index ee7ffaa..e093822 100644
--- a/camera/device/3.4/default/ExternalCameraDevice.cpp
+++ b/camera/device/3.4/default/ExternalCameraDevice.cpp
@@ -290,8 +290,14 @@
     UPDATE(ANDROID_HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES, &hotPixelMode, 1);
 
     // android.jpeg
-    // TODO: b/72261675 See if we can provide thumbnail size for all jpeg aspect ratios
-    const int32_t jpegAvailableThumbnailSizes[] = {0, 0, 240, 180};
+    const int32_t jpegAvailableThumbnailSizes[] = {0, 0,
+                                                  176, 144,
+                                                  240, 144,
+                                                  256, 144,
+                                                  240, 160,
+                                                  256, 154,
+                                                  240, 240,
+                                                  320, 240};
     UPDATE(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES, jpegAvailableThumbnailSizes,
            ARRAY_SIZE(jpegAvailableThumbnailSizes));
 
@@ -787,7 +793,8 @@
 std::vector<SupportedV4L2Format>
 ExternalCameraDevice::getCandidateSupportedFormatsLocked(
         int fd, CroppingType cropType,
-        const std::vector<ExternalCameraConfig::FpsLimitation>& fpsLimits) {
+        const std::vector<ExternalCameraConfig::FpsLimitation>& fpsLimits,
+        const Size& minStreamSize) {
     std::vector<SupportedV4L2Format> outFmts;
     struct v4l2_fmtdesc fmtdesc {
         .index = 0,
@@ -822,6 +829,11 @@
                         if (frameSize.discrete.height > frameSize.discrete.width) {
                             continue;
                         }
+                        // Discard all formats which is smaller than minStreamSize
+                        if (frameSize.discrete.width < minStreamSize.width
+                            || frameSize.discrete.height < minStreamSize.height) {
+                            continue;
+                        }
                         SupportedV4L2Format format {
                             .width = frameSize.discrete.width,
                             .height = frameSize.discrete.height,
@@ -864,9 +876,9 @@
 void ExternalCameraDevice::initSupportedFormatsLocked(int fd) {
 
     std::vector<SupportedV4L2Format> horizontalFmts =
-            getCandidateSupportedFormatsLocked(fd, HORIZONTAL, mCfg.fpsLimits);
+            getCandidateSupportedFormatsLocked(fd, HORIZONTAL, mCfg.fpsLimits, mCfg.minStreamSize);
     std::vector<SupportedV4L2Format> verticalFmts =
-            getCandidateSupportedFormatsLocked(fd, VERTICAL, mCfg.fpsLimits);
+            getCandidateSupportedFormatsLocked(fd, VERTICAL, mCfg.fpsLimits, mCfg.minStreamSize);
 
     size_t horiSize = horizontalFmts.size();
     size_t vertSize = verticalFmts.size();
diff --git a/camera/device/3.4/default/ExternalCameraUtils.cpp b/camera/device/3.4/default/ExternalCameraUtils.cpp
index 2e2f73b..680c95a 100644
--- a/camera/device/3.4/default/ExternalCameraUtils.cpp
+++ b/camera/device/3.4/default/ExternalCameraUtils.cpp
@@ -267,6 +267,15 @@
         ret.fpsLimits = limits;
     }
 
+    XMLElement *minStreamSize = deviceCfg->FirstChildElement("MinimumStreamSize");
+    if (minStreamSize == nullptr) {
+       ALOGI("%s: no minimum stream size specified", __FUNCTION__);
+    } else {
+        ret.minStreamSize = {
+                minStreamSize->UnsignedAttribute("width", /*Default*/0),
+                minStreamSize->UnsignedAttribute("height", /*Default*/0)};
+    }
+
     ALOGI("%s: external camera cfg loaded: maxJpgBufSize %d,"
             " num video buffers %d, num still buffers %d",
             __FUNCTION__, ret.maxJpegBufSize,
@@ -275,6 +284,8 @@
         ALOGI("%s: fpsLimitList: %dx%d@%f", __FUNCTION__,
                 limit.size.width, limit.size.height, limit.fpsUpperBound);
     }
+    ALOGI("%s: minStreamSize: %dx%d" , __FUNCTION__,
+         ret.minStreamSize.width, ret.minStreamSize.height);
     return ret;
 }
 
@@ -285,6 +296,7 @@
     fpsLimits.push_back({/*Size*/{ 640,  480}, /*FPS upper bound*/30.0});
     fpsLimits.push_back({/*Size*/{1280,  720}, /*FPS upper bound*/7.5});
     fpsLimits.push_back({/*Size*/{1920, 1080}, /*FPS upper bound*/5.0});
+    minStreamSize = {0, 0};
 }
 
 
diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h
index a52f0e4..ff0cfb3 100644
--- a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h
+++ b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h
@@ -96,7 +96,8 @@
     // Get candidate supported formats list of input cropping type.
     static std::vector<SupportedV4L2Format> getCandidateSupportedFormatsLocked(
             int fd, CroppingType cropType,
-            const std::vector<ExternalCameraConfig::FpsLimitation>& fpsLimits);
+            const std::vector<ExternalCameraConfig::FpsLimitation>& fpsLimits,
+            const Size& minStreamSize);
     // Trim supported format list by the cropping type. Also sort output formats by width/height
     static void trimSupportedFormats(CroppingType cropType,
             /*inout*/std::vector<SupportedV4L2Format>* pFmts);
diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraUtils.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraUtils.h
index 37e7cfb..5754ccb 100644
--- a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraUtils.h
+++ b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraUtils.h
@@ -77,6 +77,9 @@
     };
     std::vector<FpsLimitation> fpsLimits;
 
+    // Minimum output stream size
+    Size minStreamSize;
+
 private:
     ExternalCameraConfig();
 };
diff --git a/compatibility_matrices/Android.mk b/compatibility_matrices/Android.mk
index bf2e447..3051c53 100644
--- a/compatibility_matrices/Android.mk
+++ b/compatibility_matrices/Android.mk
@@ -70,6 +70,18 @@
 
 include $(BUILD_FRAMEWORK_COMPATIBILITY_MATRIX)
 
+include $(CLEAR_VARS)
+include $(LOCAL_PATH)/clear_vars.mk
+LOCAL_MODULE := framework_compatibility_matrix.current.xml
+LOCAL_MODULE_STEM := compatibility_matrix.current.xml
+LOCAL_SRC_FILES := $(LOCAL_MODULE_STEM)
+LOCAL_KERNEL_CONFIG_DATA_PATHS := \
+    4.4.0:$(my_kernel_config_data)/android-4.4 \
+    4.9.0:$(my_kernel_config_data)/android-4.9 \
+    4.14.0:$(my_kernel_config_data)/android-4.14 \
+
+include $(BUILD_FRAMEWORK_COMPATIBILITY_MATRIX)
+
 my_kernel_config_data :=
 
 # Framework Compatibility Matrix (common to all FCM versions)
@@ -120,6 +132,7 @@
     framework_compatibility_matrix.1.xml \
     framework_compatibility_matrix.2.xml \
     framework_compatibility_matrix.3.xml \
+    framework_compatibility_matrix.current.xml \
     framework_compatibility_matrix.device.xml
 
 # Phony target that installs all framework compatibility matrix files
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
new file mode 100644
index 0000000..f14fcea
--- /dev/null
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -0,0 +1,458 @@
+<compatibility-matrix version="1.0" type="framework" level="4">
+    <hal format="hidl" optional="false">
+        <name>android.hardware.audio</name>
+        <version>4.0</version>
+        <interface>
+            <name>IDevicesFactory</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.audio.effect</name>
+        <version>4.0</version>
+        <interface>
+            <name>IEffectsFactory</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.authsecret</name>
+        <version>1.0</version>
+        <interface>
+            <name>IAuthSecret</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.automotive.audiocontrol</name>
+        <version>1.0</version>
+        <interface>
+            <name>IAudioControl</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.automotive.evs</name>
+        <version>1.0</version>
+        <interface>
+            <name>IEvsEnumerator</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.automotive.vehicle</name>
+        <version>2.0</version>
+        <interface>
+            <name>IVehicle</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.biometrics.fingerprint</name>
+        <version>2.1</version>
+        <interface>
+            <name>IBiometricsFingerprint</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.bluetooth</name>
+        <version>1.0</version>
+        <interface>
+            <name>IBluetoothHci</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.bluetooth.a2dp</name>
+        <version>1.0</version>
+        <interface>
+            <name>IBluetoothAudioOffload</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.boot</name>
+        <version>1.0</version>
+        <interface>
+            <name>IBootControl</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.broadcastradio</name>
+        <version>1.0-1</version>
+        <interface>
+            <name>IBroadcastRadioFactory</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.camera.provider</name>
+        <version>2.4</version>
+        <interface>
+            <name>ICameraProvider</name>
+            <regex-instance>[^/]+/[0-9]+</regex-instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.cas</name>
+        <version>1.0</version>
+        <interface>
+            <name>IMediaCasService</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.configstore</name>
+        <version>1.1</version>
+        <interface>
+            <name>ISurfaceFlingerConfigs</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.confirmationui</name>
+        <version>1.0</version>
+        <interface>
+            <name>IConfirmationUI</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.contexthub</name>
+        <version>1.0</version>
+        <interface>
+            <name>IContexthub</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.drm</name>
+        <version>1.0</version>
+        <interface>
+            <name>ICryptoFactory</name>
+            <regex-instance>.*</regex-instance>
+        </interface>
+        <interface>
+            <name>IDrmFactory</name>
+            <regex-instance>.*</regex-instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.drm</name>
+        <version>1.1</version>
+        <interface>
+            <name>ICryptoFactory</name>
+            <regex-instance>.*</regex-instance>
+        </interface>
+        <interface>
+            <name>IDrmFactory</name>
+            <regex-instance>.*</regex-instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.dumpstate</name>
+        <version>1.0</version>
+        <interface>
+            <name>IDumpstateDevice</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.gatekeeper</name>
+        <version>1.0</version>
+        <interface>
+            <name>IGatekeeper</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.gnss</name>
+        <version>1.0-1</version>
+        <interface>
+            <name>IGnss</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.graphics.allocator</name>
+        <version>2.0</version>
+        <interface>
+            <name>IAllocator</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.graphics.composer</name>
+        <version>2.1</version>
+        <interface>
+            <name>IComposer</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.graphics.mapper</name>
+        <version>2.0</version>
+        <interface>
+            <name>IMapper</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.health</name>
+        <version>2.0</version>
+        <interface>
+            <name>IHealth</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.ir</name>
+        <version>1.0</version>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.keymaster</name>
+        <version>3.0</version>
+        <version>4.0</version>
+        <interface>
+            <name>IKeymasterDevice</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.keymaster</name>
+        <version>4.0</version>
+        <interface>
+            <name>IKeymasterDevice</name>
+            <instance>strongbox</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.light</name>
+        <version>2.0</version>
+        <interface>
+            <name>ILight</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.media.omx</name>
+        <version>1.0</version>
+        <interface>
+            <name>IOmx</name>
+            <instance>default</instance>
+        </interface>
+        <interface>
+            <name>IOmxStore</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.memtrack</name>
+        <version>1.0</version>
+        <interface>
+            <name>IMemtrack</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.neuralnetworks</name>
+        <version>1.0</version>
+        <interface>
+            <name>IDevice</name>
+            <regex-instance>.*</regex-instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.nfc</name>
+        <version>1.1</version>
+        <interface>
+            <name>INfc</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.oemlock</name>
+        <version>1.0</version>
+        <interface>
+            <name>IOemLock</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.power</name>
+        <version>1.0-3</version>
+        <interface>
+            <name>IPower</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.radio</name>
+        <version>1.0-2</version>
+        <interface>
+            <name>IRadio</name>
+            <instance>slot1</instance>
+            <instance>slot2</instance>
+            <instance>slot3</instance>
+        </interface>
+        <interface>
+            <name>ISap</name>
+            <instance>slot1</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.radio.config</name>
+        <version>1.0</version>
+        <interface>
+            <name>IRadioConfig</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.renderscript</name>
+        <version>1.0</version>
+        <interface>
+            <name>IDevice</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.secure_element</name>
+        <version>1.0</version>
+        <interface>
+            <name>ISecureElement</name>
+            <regex-instance>eSE[1-9][0-9]*</regex-instance>
+            <regex-instance>SIM[1-9][0-9]*</regex-instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.sensors</name>
+        <version>1.0</version>
+        <interface>
+            <name>ISensors</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.soundtrigger</name>
+        <version>2.0-1</version>
+        <interface>
+            <name>ISoundTriggerHw</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.tetheroffload.config</name>
+        <version>1.0</version>
+        <interface>
+            <name>IOffloadConfig</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.tetheroffload.control</name>
+        <version>1.0</version>
+        <interface>
+            <name>IOffloadControl</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.thermal</name>
+        <version>1.0-1</version>
+        <interface>
+            <name>IThermal</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.tv.cec</name>
+        <version>1.0</version>
+        <interface>
+            <name>IHdmiCec</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.tv.input</name>
+        <version>1.0</version>
+        <interface>
+            <name>ITvInput</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.usb</name>
+        <version>1.0-1</version>
+        <interface>
+            <name>IUsb</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.usb.gadget</name>
+        <version>1.0</version>
+        <interface>
+            <name>IUsbGadget</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.vibrator</name>
+        <version>1.0-2</version>
+        <interface>
+            <name>IVibrator</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.vr</name>
+        <version>1.0</version>
+        <interface>
+            <name>IVr</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.weaver</name>
+        <version>1.0</version>
+        <interface>
+            <name>IWeaver</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.wifi</name>
+        <version>1.0-2</version>
+        <interface>
+            <name>IWifi</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.wifi.hostapd</name>
+        <version>1.0</version>
+        <interface>
+            <name>IHostapd</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.wifi.offload</name>
+        <version>1.0</version>
+        <interface>
+            <name>IOffload</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.wifi.supplicant</name>
+        <version>1.0-1</version>
+        <interface>
+            <name>ISupplicant</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</compatibility-matrix>
diff --git a/drm/1.0/default/CryptoPlugin.cpp b/drm/1.0/default/CryptoPlugin.cpp
index f9c868d..666653b 100644
--- a/drm/1.0/default/CryptoPlugin.cpp
+++ b/drm/1.0/default/CryptoPlugin.cpp
@@ -52,7 +52,6 @@
     Return<void> CryptoPlugin::setSharedBufferBase(const hidl_memory& base,
             uint32_t bufferId) {
         sp<IMemory> hidlMemory = mapMemory(base);
-        ALOGE_IF(hidlMemory == nullptr, "mapMemory returns nullptr");
 
         // allow mapMemory to return nullptr
         mSharedBufferMap[bufferId] = hidlMemory;
diff --git a/graphics/composer/2.1/utils/vts/ComposerVts.cpp b/graphics/composer/2.1/utils/vts/ComposerVts.cpp
index 2f531b4..43a5378 100644
--- a/graphics/composer/2.1/utils/vts/ComposerVts.cpp
+++ b/graphics/composer/2.1/utils/vts/ComposerVts.cpp
@@ -25,21 +25,19 @@
 namespace V2_1 {
 namespace vts {
 
-Composer::Composer() {
-    mComposer = ::testing::VtsHalHidlTargetTestBase::getService<IComposer>();
-    init();
-}
+Composer::Composer() : Composer(::testing::VtsHalHidlTargetTestBase::getService<IComposer>()) {}
 
-Composer::Composer(const std::string& name) {
-    mComposer = ::testing::VtsHalHidlTargetTestBase::getService<IComposer>(name);
-    init();
-}
+Composer::Composer(const std::string& name)
+    : Composer(::testing::VtsHalHidlTargetTestBase::getService<IComposer>(name)) {}
 
-void Composer::init() {
-    ASSERT_NE(nullptr, mComposer.get()) << "failed to get composer service";
+Composer::Composer(const sp<IComposer>& composer) : mComposer(composer) {
+    // ASSERT_* can only be used in functions returning void.
+    [this] {
+        ASSERT_NE(nullptr, mComposer.get()) << "failed to get composer service";
 
-    std::vector<IComposer::Capability> capabilities = getCapabilities();
-    mCapabilities.insert(capabilities.begin(), capabilities.end());
+        std::vector<IComposer::Capability> capabilities = getCapabilities();
+        mCapabilities.insert(capabilities.begin(), capabilities.end());
+    }();
 }
 
 sp<IComposer> Composer::getRaw() const {
diff --git a/graphics/composer/2.1/utils/vts/include/composer-vts/2.1/ComposerVts.h b/graphics/composer/2.1/utils/vts/include/composer-vts/2.1/ComposerVts.h
index 8d5493e..c97be76 100644
--- a/graphics/composer/2.1/utils/vts/include/composer-vts/2.1/ComposerVts.h
+++ b/graphics/composer/2.1/utils/vts/include/composer-vts/2.1/ComposerVts.h
@@ -57,10 +57,10 @@
     std::unique_ptr<ComposerClient> createClient();
 
    protected:
-    sp<IComposer> mComposer;
+    explicit Composer(const sp<IComposer>& composer);
 
    private:
-    void init();
+    const sp<IComposer> mComposer;
 
     std::unordered_set<IComposer::Capability> mCapabilities;
 };
@@ -68,7 +68,7 @@
 // A wrapper to IComposerClient.
 class ComposerClient {
    public:
-    ComposerClient(const sp<IComposerClient>& client);
+    explicit ComposerClient(const sp<IComposerClient>& client);
     ~ComposerClient();
 
     sp<IComposerClient> getRaw() const;
@@ -116,7 +116,7 @@
     std::unordered_map<Display, DisplayResource> mDisplayResources;
 
    private:
-    sp<IComposerClient> mClient;
+    const sp<IComposerClient> mClient;
 };
 
 }  // namespace vts
diff --git a/graphics/composer/2.2/utils/vts/ComposerVts.cpp b/graphics/composer/2.2/utils/vts/ComposerVts.cpp
index 6a32071..f2596a4 100644
--- a/graphics/composer/2.2/utils/vts/ComposerVts.cpp
+++ b/graphics/composer/2.2/utils/vts/ComposerVts.cpp
@@ -27,32 +27,31 @@
 namespace V2_2 {
 namespace vts {
 
-using android::hardware::details::canCastInterface;
-using android::hardware::details::getDescriptor;
-using android::hardware::graphics::composer::V2_2::IComposerClient;
+using details::canCastInterface;
+using details::getDescriptor;
 
-std::unique_ptr<ComposerClient_v2_2> Composer_v2_2::createClient_v2_2() {
-    std::unique_ptr<ComposerClient_v2_2> client;
-    mComposer->createClient([&](const auto& tmpError, const auto& tmpClient) {
+std::unique_ptr<ComposerClient> Composer::createClient() {
+    std::unique_ptr<ComposerClient> client;
+    getRaw()->createClient([&](const auto& tmpError, const auto& tmpClient) {
         ASSERT_EQ(Error::NONE, tmpError) << "failed to create client";
         ALOGV("tmpClient is a %s", getDescriptor(&(*tmpClient)).c_str());
         ASSERT_TRUE(canCastInterface(
             &(*tmpClient), "android.hardware.graphics.composer@2.2::IComposerClient", false))
             << "Cannot create 2.2 IComposerClient";
-        client = std::make_unique<ComposerClient_v2_2>(IComposerClient::castFrom(tmpClient, true));
+        client = std::make_unique<ComposerClient>(IComposerClient::castFrom(tmpClient, true));
     });
 
     return client;
 }
 
-sp<V2_2::IComposerClient> ComposerClient_v2_2::getRaw() const {
-    return mClient_v2_2;
+sp<IComposerClient> ComposerClient::getRaw() const {
+    return mClient;
 }
 
-std::vector<IComposerClient::PerFrameMetadataKey> ComposerClient_v2_2::getPerFrameMetadataKeys(
+std::vector<IComposerClient::PerFrameMetadataKey> ComposerClient::getPerFrameMetadataKeys(
     Display display) {
     std::vector<IComposerClient::PerFrameMetadataKey> keys;
-    mClient_v2_2->getPerFrameMetadataKeys(display, [&](const auto& tmpError, const auto& tmpKeys) {
+    mClient->getPerFrameMetadataKeys(display, [&](const auto& tmpError, const auto& tmpKeys) {
         ASSERT_EQ(Error::NONE, tmpError) << "failed to get HDR metadata keys";
         keys = tmpKeys;
     });
@@ -60,43 +59,42 @@
     return keys;
 }
 
-void ComposerClient_v2_2::execute_v2_2(V2_1::vts::TestCommandReader* reader,
-                                       V2_2::CommandWriterBase* writer) {
+void ComposerClient::execute(V2_1::vts::TestCommandReader* reader, CommandWriterBase* writer) {
     bool queueChanged = false;
     uint32_t commandLength = 0;
     hidl_vec<hidl_handle> commandHandles;
     ASSERT_TRUE(writer->writeQueue(&queueChanged, &commandLength, &commandHandles));
 
     if (queueChanged) {
-        auto ret = mClient_v2_2->setInputCommandQueue(*writer->getMQDescriptor());
+        auto ret = mClient->setInputCommandQueue(*writer->getMQDescriptor());
         ASSERT_EQ(Error::NONE, static_cast<Error>(ret));
         return;
     }
 
-    mClient_v2_2->executeCommands(commandLength, commandHandles,
-                                  [&](const auto& tmpError, const auto& tmpOutQueueChanged,
-                                      const auto& tmpOutLength, const auto& tmpOutHandles) {
-                                      ASSERT_EQ(Error::NONE, tmpError);
+    mClient->executeCommands(commandLength, commandHandles,
+                             [&](const auto& tmpError, const auto& tmpOutQueueChanged,
+                                 const auto& tmpOutLength, const auto& tmpOutHandles) {
+                                 ASSERT_EQ(Error::NONE, tmpError);
 
-                                      if (tmpOutQueueChanged) {
-                                          mClient_v2_2->getOutputCommandQueue(
-                                              [&](const auto& tmpError, const auto& tmpDescriptor) {
-                                                  ASSERT_EQ(Error::NONE, tmpError);
-                                                  reader->setMQDescriptor(tmpDescriptor);
-                                              });
-                                      }
+                                 if (tmpOutQueueChanged) {
+                                     mClient->getOutputCommandQueue(
+                                         [&](const auto& tmpError, const auto& tmpDescriptor) {
+                                             ASSERT_EQ(Error::NONE, tmpError);
+                                             reader->setMQDescriptor(tmpDescriptor);
+                                         });
+                                 }
 
-                                      ASSERT_TRUE(reader->readQueue(tmpOutLength, tmpOutHandles));
-                                      reader->parse();
-                                  });
+                                 ASSERT_TRUE(reader->readQueue(tmpOutLength, tmpOutHandles));
+                                 reader->parse();
+                             });
 }
 
-Display ComposerClient_v2_2::createVirtualDisplay_2_2(uint32_t width, uint32_t height,
-                                                      PixelFormat formatHint,
-                                                      uint32_t outputBufferSlotCount,
-                                                      PixelFormat* outFormat) {
+Display ComposerClient::createVirtualDisplay_2_2(uint32_t width, uint32_t height,
+                                                 PixelFormat formatHint,
+                                                 uint32_t outputBufferSlotCount,
+                                                 PixelFormat* outFormat) {
     Display display = 0;
-    mClient_v2_2->createVirtualDisplay_2_2(
+    mClient->createVirtualDisplay_2_2(
         width, height, formatHint, outputBufferSlotCount,
         [&](const auto& tmpError, const auto& tmpDisplay, const auto& tmpFormat) {
             ASSERT_EQ(Error::NONE, tmpError) << "failed to create virtual display";
@@ -110,29 +108,27 @@
     return display;
 }
 
-bool ComposerClient_v2_2::getClientTargetSupport_2_2(Display display, uint32_t width,
-                                                     uint32_t height, PixelFormat format,
-                                                     Dataspace dataspace) {
-    Error error =
-        mClient_v2_2->getClientTargetSupport_2_2(display, width, height, format, dataspace);
+bool ComposerClient::getClientTargetSupport_2_2(Display display, uint32_t width, uint32_t height,
+                                                PixelFormat format, Dataspace dataspace) {
+    Error error = mClient->getClientTargetSupport_2_2(display, width, height, format, dataspace);
     return error == Error::NONE;
 }
 
-void ComposerClient_v2_2::setPowerMode_2_2(Display display, V2_2::IComposerClient::PowerMode mode) {
-    Error error = mClient_v2_2->setPowerMode_2_2(display, mode);
+void ComposerClient::setPowerMode_2_2(Display display, IComposerClient::PowerMode mode) {
+    Error error = mClient->setPowerMode_2_2(display, mode);
     ASSERT_TRUE(error == Error::NONE || error == Error::UNSUPPORTED) << "failed to set power mode";
 }
 
-void ComposerClient_v2_2::setReadbackBuffer(Display display, const native_handle_t* buffer,
-                                            int32_t /* releaseFence */) {
+void ComposerClient::setReadbackBuffer(Display display, const native_handle_t* buffer,
+                                       int32_t /* releaseFence */) {
     // Ignoring fence, HIDL doesn't care
-    Error error = mClient_v2_2->setReadbackBuffer(display, buffer, nullptr);
+    Error error = mClient->setReadbackBuffer(display, buffer, nullptr);
     ASSERT_EQ(Error::NONE, error) << "failed to setReadbackBuffer";
 }
 
-void ComposerClient_v2_2::getReadbackBufferAttributes(Display display, PixelFormat* outPixelFormat,
-                                                      Dataspace* outDataspace) {
-    mClient_v2_2->getReadbackBufferAttributes(
+void ComposerClient::getReadbackBufferAttributes(Display display, PixelFormat* outPixelFormat,
+                                                 Dataspace* outDataspace) {
+    mClient->getReadbackBufferAttributes(
         display,
         [&](const auto& tmpError, const auto& tmpOutPixelFormat, const auto& tmpOutDataspace) {
             ASSERT_EQ(Error::NONE, tmpError) << "failed to get readback buffer attributes";
@@ -141,42 +137,41 @@
         });
 }
 
-void ComposerClient_v2_2::getReadbackBufferFence(Display display, int32_t* outFence) {
+void ComposerClient::getReadbackBufferFence(Display display, int32_t* outFence) {
     hidl_handle handle;
-    mClient_v2_2->getReadbackBufferFence(display, [&](const auto& tmpError, const auto& tmpHandle) {
+    mClient->getReadbackBufferFence(display, [&](const auto& tmpError, const auto& tmpHandle) {
         ASSERT_EQ(Error::NONE, tmpError) << "failed to get readback fence";
         handle = tmpHandle;
     });
     *outFence = 0;
 }
 
-std::vector<ColorMode> ComposerClient_v2_2::getColorModes(Display display) {
+std::vector<ColorMode> ComposerClient::getColorModes(Display display) {
     std::vector<ColorMode> modes;
-    mClient_v2_2->getColorModes_2_2(display, [&](const auto& tmpError, const auto& tmpModes) {
+    mClient->getColorModes_2_2(display, [&](const auto& tmpError, const auto& tmpModes) {
         ASSERT_EQ(Error::NONE, tmpError) << "failed to get color modes";
         modes = tmpModes;
     });
     return modes;
 }
 
-std::vector<RenderIntent> ComposerClient_v2_2::getRenderIntents(Display display, ColorMode mode) {
+std::vector<RenderIntent> ComposerClient::getRenderIntents(Display display, ColorMode mode) {
     std::vector<RenderIntent> intents;
-    mClient_v2_2->getRenderIntents(
-        display, mode, [&](const auto& tmpError, const auto& tmpIntents) {
-            ASSERT_EQ(Error::NONE, tmpError) << "failed to get render intents";
-            intents = tmpIntents;
-        });
+    mClient->getRenderIntents(display, mode, [&](const auto& tmpError, const auto& tmpIntents) {
+        ASSERT_EQ(Error::NONE, tmpError) << "failed to get render intents";
+        intents = tmpIntents;
+    });
     return intents;
 }
 
-void ComposerClient_v2_2::setColorMode(Display display, ColorMode mode, RenderIntent intent) {
-    Error error = mClient_v2_2->setColorMode_2_2(display, mode, intent);
+void ComposerClient::setColorMode(Display display, ColorMode mode, RenderIntent intent) {
+    Error error = mClient->setColorMode_2_2(display, mode, intent);
     ASSERT_TRUE(error == Error::NONE || error == Error::UNSUPPORTED) << "failed to set color mode";
 }
 
-std::array<float, 16> ComposerClient_v2_2::getDataspaceSaturationMatrix(Dataspace dataspace) {
+std::array<float, 16> ComposerClient::getDataspaceSaturationMatrix(Dataspace dataspace) {
     std::array<float, 16> matrix;
-    mClient_v2_2->getDataspaceSaturationMatrix(
+    mClient->getDataspaceSaturationMatrix(
         dataspace, [&](const auto& tmpError, const auto& tmpMatrix) {
             ASSERT_EQ(Error::NONE, tmpError) << "failed to get datasapce saturation matrix";
             std::copy_n(tmpMatrix.data(), matrix.size(), matrix.begin());
diff --git a/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ComposerVts.h b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ComposerVts.h
index 1c6d7ae..2633021 100644
--- a/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ComposerVts.h
+++ b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ComposerVts.h
@@ -36,36 +36,31 @@
 namespace V2_2 {
 namespace vts {
 
-using android::hardware::graphics::common::V1_0::Hdr;
-using android::hardware::graphics::common::V1_1::ColorMode;
-using android::hardware::graphics::common::V1_1::Dataspace;
-using android::hardware::graphics::common::V1_1::PixelFormat;
-using android::hardware::graphics::common::V1_1::RenderIntent;
-using android::hardware::graphics::composer::V2_2::IComposer;
-using android::hardware::graphics::composer::V2_2::IComposerClient;
+using common::V1_0::Hdr;
+using common::V1_1::ColorMode;
+using common::V1_1::Dataspace;
+using common::V1_1::PixelFormat;
+using common::V1_1::RenderIntent;
 
-class ComposerClient_v2_2;
+class ComposerClient;
 
-// Only thing I need for Composer_v2_2 is to create a v2_2 ComposerClient
-// Everything else is the same
-class Composer_v2_2 : public V2_1::vts::Composer {
+// A wrapper to IComposer.
+class Composer : public V2_1::vts::Composer {
    public:
-    Composer_v2_2() : V2_1::vts::Composer(){};
-    explicit Composer_v2_2(const std::string& name) : V2_1::vts::Composer(name){};
+    using V2_1::vts::Composer::Composer;
 
-    std::unique_ptr<ComposerClient_v2_2> createClient_v2_2();
+    std::unique_ptr<ComposerClient> createClient();
 };
 
 // A wrapper to IComposerClient.
-class ComposerClient_v2_2
-    : public android::hardware::graphics::composer::V2_1::vts::ComposerClient {
+class ComposerClient : public V2_1::vts::ComposerClient {
    public:
-    ComposerClient_v2_2(const sp<IComposerClient>& client)
-        : V2_1::vts::ComposerClient(client), mClient_v2_2(client){};
+    explicit ComposerClient(const sp<IComposerClient>& client)
+        : V2_1::vts::ComposerClient(client), mClient(client) {}
 
-    sp<V2_2::IComposerClient> getRaw() const;
+    sp<IComposerClient> getRaw() const;
 
-    void execute_v2_2(V2_1::vts::TestCommandReader* reader, V2_2::CommandWriterBase* writer);
+    void execute(V2_1::vts::TestCommandReader* reader, CommandWriterBase* writer);
 
     std::vector<IComposerClient::PerFrameMetadataKey> getPerFrameMetadataKeys(Display display);
 
@@ -73,7 +68,7 @@
                                      uint32_t outputBufferSlotCount, PixelFormat* outFormat);
     bool getClientTargetSupport_2_2(Display display, uint32_t width, uint32_t height,
                                     PixelFormat format, Dataspace dataspace);
-    void setPowerMode_2_2(Display display, V2_2::IComposerClient::PowerMode mode);
+    void setPowerMode_2_2(Display display, IComposerClient::PowerMode mode);
     void setReadbackBuffer(Display display, const native_handle_t* buffer, int32_t releaseFence);
     void getReadbackBufferAttributes(Display display, PixelFormat* outPixelFormat,
                                      Dataspace* outDataspace);
@@ -86,7 +81,7 @@
     std::array<float, 16> getDataspaceSaturationMatrix(Dataspace dataspace);
 
    private:
-    sp<V2_2::IComposerClient> mClient_v2_2;
+    const sp<IComposerClient> mClient;
 };
 
 }  // namespace vts
diff --git a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2TargetTest.cpp b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2TargetTest.cpp
index 23bf558..e3b7f55 100644
--- a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2TargetTest.cpp
+++ b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2TargetTest.cpp
@@ -32,17 +32,15 @@
 namespace vts {
 namespace {
 
-using android::hardware::graphics::common::V1_0::BufferUsage;
-using android::hardware::graphics::common::V1_0::ColorTransform;
-using android::hardware::graphics::common::V1_0::Transform;
-using android::hardware::graphics::common::V1_1::ColorMode;
-using android::hardware::graphics::common::V1_1::Dataspace;
-using android::hardware::graphics::common::V1_1::PixelFormat;
-using android::hardware::graphics::common::V1_1::RenderIntent;
-using android::hardware::graphics::composer::V2_2::IComposerClient;
-using android::hardware::graphics::mapper::V2_1::IMapper;
-using android::hardware::graphics::mapper::V2_1::vts::Gralloc;
-using GrallocError = android::hardware::graphics::mapper::V2_0::Error;
+using common::V1_0::BufferUsage;
+using common::V1_0::ColorTransform;
+using common::V1_0::Transform;
+using common::V1_1::ColorMode;
+using common::V1_1::Dataspace;
+using common::V1_1::PixelFormat;
+using common::V1_1::RenderIntent;
+using mapper::V2_1::IMapper;
+using mapper::V2_1::vts::Gralloc;
 
 // Test environment for graphics.composer
 class GraphicsComposerHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
@@ -65,9 +63,9 @@
    protected:
     void SetUp() override {
         ASSERT_NO_FATAL_FAILURE(
-            mComposer = std::make_unique<Composer_v2_2>(
+            mComposer = std::make_unique<Composer>(
                 GraphicsComposerHidlEnvironment::Instance()->getServiceName<IComposer>()));
-        ASSERT_NO_FATAL_FAILURE(mComposerClient = mComposer->createClient_v2_2());
+        ASSERT_NO_FATAL_FAILURE(mComposerClient = mComposer->createClient());
 
         mComposerCallback = new V2_1::vts::GraphicsComposerCallback;
         mComposerClient->registerCallback(mComposerCallback);
@@ -96,8 +94,8 @@
     // use the slot count usually set by SF
     static constexpr uint32_t kBufferSlotCount = 64;
 
-    std::unique_ptr<Composer_v2_2> mComposer;
-    std::unique_ptr<ComposerClient_v2_2> mComposerClient;
+    std::unique_ptr<Composer> mComposer;
+    std::unique_ptr<ComposerClient> mComposerClient;
     sp<V2_1::vts::GraphicsComposerCallback> mComposerCallback;
     // the first display and is assumed never to be removed
     Display mPrimaryDisplay;
@@ -125,7 +123,7 @@
 
         ASSERT_NO_FATAL_FAILURE(mGralloc = std::make_unique<Gralloc>());
 
-        mWriter = std::make_unique<V2_2::CommandWriterBase>(1024);
+        mWriter = std::make_unique<CommandWriterBase>(1024);
         mReader = std::make_unique<V2_1::vts::TestCommandReader>();
     }
 
@@ -143,9 +141,9 @@
         return mGralloc->allocate(info);
     }
 
-    void execute() { mComposerClient->execute_v2_2(mReader.get(), mWriter.get()); }
+    void execute() { mComposerClient->execute(mReader.get(), mWriter.get()); }
 
-    std::unique_ptr<V2_2::CommandWriterBase> mWriter;
+    std::unique_ptr<CommandWriterBase> mWriter;
     std::unique_ptr<V2_1::vts::TestCommandReader> mReader;
 
    private:
diff --git a/graphics/composer/2.3/Android.bp b/graphics/composer/2.3/Android.bp
new file mode 100644
index 0000000..23061dd
--- /dev/null
+++ b/graphics/composer/2.3/Android.bp
@@ -0,0 +1,21 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.graphics.composer@2.3",
+    root: "android.hardware",
+    vndk: {
+        enabled: true,
+    },
+    srcs: [
+        "IComposer.hal",
+        "IComposerClient.hal",
+    ],
+    interfaces: [
+        "android.hardware.graphics.common@1.0",
+        "android.hardware.graphics.composer@2.1",
+        "android.hardware.graphics.composer@2.2",
+        "android.hidl.base@1.0",
+    ],
+    gen_java: false,
+}
+
diff --git a/graphics/composer/2.3/IComposer.hal b/graphics/composer/2.3/IComposer.hal
new file mode 100644
index 0000000..90b2427
--- /dev/null
+++ b/graphics/composer/2.3/IComposer.hal
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.graphics.composer@2.3;
+
+import IComposerClient;
+
+import @2.1::Error;
+import @2.2::IComposer;
+
+interface IComposer extends @2.2::IComposer {
+
+    /**
+     * Creates a v2.3 client of the composer. Supersedes @2.1::createClient.
+     *
+     * @return error is NONE upon success. Otherwise,
+     *         NO_RESOURCES when the client could not be created.
+     * @return client is the newly created client.
+     */
+    @entry
+    @callflow(next="*")
+    createClient_2_3() generates (Error error, IComposerClient client);
+
+};
diff --git a/graphics/composer/2.3/IComposerClient.hal b/graphics/composer/2.3/IComposerClient.hal
new file mode 100644
index 0000000..77dd3ee
--- /dev/null
+++ b/graphics/composer/2.3/IComposerClient.hal
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.graphics.composer@2.3;
+
+import @2.2::IComposerClient;
+import @2.1::Display;
+import @2.1::Error;
+
+interface IComposerClient extends @2.2::IComposerClient {
+
+    /**
+     * Returns the port and data that describe a physical display. The port is
+     * a unique number that identifies a physical connector (e.g. eDP, HDMI)
+     * for display output. The data blob is parsed to determine its format,
+     * typically EDID 1.3 as specified in VESA E-EDID Standard Release A
+     * Revision 1.
+     *
+     * @param display is the display to query.
+     * @return error is NONE upon success. Otherwise,
+     *         BAD_DISPLAY when an invalid display handle was passed in.
+     *         UNSUPPORTED when identification data is unavailable.
+     * @return port is the connector to which the display is connected.
+     * @return data is the EDID 1.3 blob identifying the display.
+     */
+    @callflow(next="*")
+    getDisplayIdentificationData(Display display)
+               generates (Error error,
+                          uint8_t port,
+                          vec<uint8_t> data);
+
+};
diff --git a/graphics/composer/2.3/default/Android.bp b/graphics/composer/2.3/default/Android.bp
new file mode 100644
index 0000000..ad49ec3
--- /dev/null
+++ b/graphics/composer/2.3/default/Android.bp
@@ -0,0 +1,45 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_binary {
+    name: "android.hardware.graphics.composer@2.3-service",
+    defaults: ["hidl_defaults"],
+    vendor: true,
+    relative_install_path: "hw",
+    srcs: ["service.cpp"],
+    init_rc: ["android.hardware.graphics.composer@2.3-service.rc"],
+    header_libs: [
+        "android.hardware.graphics.composer@2.3-passthrough",
+    ],
+    shared_libs: [
+        "android.hardware.graphics.composer@2.1",
+        "android.hardware.graphics.composer@2.2",
+        "android.hardware.graphics.composer@2.3",
+        "android.hardware.graphics.mapper@2.0",
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "libfmq",
+        "libhardware",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwc2on1adapter",
+        "libhwc2onfbadapter",
+        "liblog",
+        "libsync",
+        "libutils",
+    ],
+}
diff --git a/graphics/composer/2.3/default/OWNERS b/graphics/composer/2.3/default/OWNERS
new file mode 100644
index 0000000..3aa5fa1
--- /dev/null
+++ b/graphics/composer/2.3/default/OWNERS
@@ -0,0 +1,4 @@
+# Graphics team
+jessehall@google.com
+olv@google.com
+stoza@google.com
diff --git a/graphics/composer/2.3/default/android.hardware.graphics.composer@2.3-service.rc b/graphics/composer/2.3/default/android.hardware.graphics.composer@2.3-service.rc
new file mode 100644
index 0000000..08e32d8
--- /dev/null
+++ b/graphics/composer/2.3/default/android.hardware.graphics.composer@2.3-service.rc
@@ -0,0 +1,6 @@
+service vendor.hwcomposer-2-3 /vendor/bin/hw/android.hardware.graphics.composer@2.3-service
+    class hal animation
+    user system
+    group graphics drmrpc
+    capabilities SYS_NICE
+    onrestart restart surfaceflinger
diff --git a/graphics/composer/2.3/default/service.cpp b/graphics/composer/2.3/default/service.cpp
new file mode 100644
index 0000000..347d8be
--- /dev/null
+++ b/graphics/composer/2.3/default/service.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sched.h>
+
+#include <android/hardware/graphics/composer/2.3/IComposer.h>
+#include <binder/ProcessState.h>
+#include <composer-passthrough/2.3/HwcLoader.h>
+#include <hidl/HidlTransportSupport.h>
+
+using android::hardware::graphics::composer::V2_3::IComposer;
+using android::hardware::graphics::composer::V2_3::passthrough::HwcLoader;
+
+int main() {
+    // the conventional HAL might start binder services
+    android::ProcessState::initWithDriver("/dev/vndbinder");
+    android::ProcessState::self()->setThreadPoolMaxThreadCount(4);
+    android::ProcessState::self()->startThreadPool();
+
+    // same as SF main thread
+    struct sched_param param = {0};
+    param.sched_priority = 2;
+    if (sched_setscheduler(0, SCHED_FIFO | SCHED_RESET_ON_FORK, &param) != 0) {
+        ALOGE("Couldn't set SCHED_FIFO: %d", errno);
+    }
+
+    android::hardware::configureRpcThreadpool(4, true /* will join */);
+
+    android::sp<IComposer> composer = HwcLoader::load();
+    if (composer == nullptr) {
+        return 1;
+    }
+    if (composer->registerAsService() != android::NO_ERROR) {
+        ALOGE("failed to register service");
+        return 1;
+    }
+
+    android::hardware::joinRpcThreadpool();
+
+    ALOGE("service is terminating");
+    return 1;
+}
diff --git a/graphics/composer/2.3/utils/OWNERS b/graphics/composer/2.3/utils/OWNERS
new file mode 100644
index 0000000..234bc75
--- /dev/null
+++ b/graphics/composer/2.3/utils/OWNERS
@@ -0,0 +1,7 @@
+# Graphics team
+olv@google.com
+stoza@google.com
+
+# VTS team
+yim@google.com
+zhuoyao@google.com
diff --git a/graphics/composer/2.3/utils/hal/Android.bp b/graphics/composer/2.3/utils/hal/Android.bp
new file mode 100644
index 0000000..aa46df1
--- /dev/null
+++ b/graphics/composer/2.3/utils/hal/Android.bp
@@ -0,0 +1,34 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_headers {
+    name: "android.hardware.graphics.composer@2.3-hal",
+    defaults: ["hidl_defaults"],
+    vendor_available: true,
+    shared_libs: [
+        "android.hardware.graphics.composer@2.3",
+    ],
+    export_shared_lib_headers: [
+        "android.hardware.graphics.composer@2.3",
+    ],
+    header_libs: [
+        "android.hardware.graphics.composer@2.2-hal",
+    ],
+    export_header_lib_headers: [
+        "android.hardware.graphics.composer@2.2-hal",
+    ],
+    export_include_dirs: ["include"],
+}
diff --git a/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/Composer.h b/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/Composer.h
new file mode 100644
index 0000000..8e11a5a
--- /dev/null
+++ b/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/Composer.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#ifndef LOG_TAG
+#warning "Composer.h included without LOG_TAG"
+#endif
+
+#include <android/hardware/graphics/composer/2.3/IComposer.h>
+#include <composer-hal/2.2/Composer.h>
+#include <composer-hal/2.3/ComposerClient.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_3 {
+namespace hal {
+
+namespace detail {
+
+// ComposerImpl implements V2_*::IComposer on top of V2_*::ComposerHal
+template <typename Interface, typename Hal>
+class ComposerImpl : public V2_2::hal::detail::ComposerImpl<Interface, Hal> {
+   public:
+    static std::unique_ptr<ComposerImpl> create(std::unique_ptr<Hal> hal) {
+        return std::make_unique<ComposerImpl>(std::move(hal));
+    }
+
+    explicit ComposerImpl(std::unique_ptr<Hal> hal) : BaseType2_2(std::move(hal)) {}
+
+    // IComposer 2.3 interface
+
+    Return<void> createClient_2_3(IComposer::createClient_2_3_cb hidl_cb) override {
+        std::unique_lock<std::mutex> lock(mClientMutex);
+        if (!waitForClientDestroyedLocked(lock)) {
+            hidl_cb(Error::NO_RESOURCES, nullptr);
+            return Void();
+        }
+
+        sp<ComposerClient> client = ComposerClient::create(mHal.get()).release();
+        if (!client) {
+            hidl_cb(Error::NO_RESOURCES, nullptr);
+            return Void();
+        }
+
+        auto clientDestroyed = [this]() { onClientDestroyed(); };
+        client->setOnClientDestroyed(clientDestroyed);
+
+        mClient = client;
+        hidl_cb(Error::NONE, client);
+        return Void();
+    }
+
+   private:
+    using BaseType2_2 = V2_2::hal::detail::ComposerImpl<Interface, Hal>;
+    using BaseType2_1 = V2_1::hal::detail::ComposerImpl<Interface, Hal>;
+
+    using BaseType2_1::mClient;
+    using BaseType2_1::mClientMutex;
+    using BaseType2_1::mHal;
+    using BaseType2_1::onClientDestroyed;
+    using BaseType2_1::waitForClientDestroyedLocked;
+};
+
+}  // namespace detail
+
+using Composer = detail::ComposerImpl<IComposer, ComposerHal>;
+
+}  // namespace hal
+}  // namespace V2_3
+}  // namespace composer
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
diff --git a/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerClient.h b/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerClient.h
new file mode 100644
index 0000000..e717d84
--- /dev/null
+++ b/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerClient.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#ifndef LOG_TAG
+#warning "ComposerClient.h included without LOG_TAG"
+#endif
+
+#include <android/hardware/graphics/composer/2.3/IComposerClient.h>
+#include <composer-hal/2.3/ComposerClient.h>
+#include <composer-hal/2.3/ComposerHal.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_3 {
+namespace hal {
+
+namespace detail {
+
+// ComposerClientImpl implements V2_*::IComposerClient on top of V2_*::ComposerHal
+template <typename Interface, typename Hal>
+class ComposerClientImpl : public V2_2::hal::detail::ComposerClientImpl<Interface, Hal> {
+   public:
+    static std::unique_ptr<ComposerClientImpl> create(Hal* hal) {
+        auto client = std::make_unique<ComposerClientImpl>(hal);
+        return client->init() ? std::move(client) : nullptr;
+    }
+
+    ComposerClientImpl(Hal* hal) : BaseType2_2(hal) {}
+
+    // IComposerClient 2.3 interface
+
+    Return<void> getDisplayIdentificationData(
+        Display display, IComposerClient::getDisplayIdentificationData_cb hidl_cb) override {
+        uint8_t port = 0;
+        std::vector<uint8_t> data;
+        Error error = mHal->getDisplayIdentificationData(display, &port, &data);
+        hidl_cb(error, port, data);
+        return Void();
+    }
+
+   private:
+    using BaseType2_2 = V2_2::hal::detail::ComposerClientImpl<Interface, Hal>;
+    using BaseType2_1 = V2_1::hal::detail::ComposerClientImpl<Interface, Hal>;
+
+    using BaseType2_1::mHal;
+};
+
+}  // namespace detail
+
+using ComposerClient = detail::ComposerClientImpl<IComposerClient, ComposerHal>;
+
+}  // namespace hal
+}  // namespace V2_3
+}  // namespace composer
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
diff --git a/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerHal.h b/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerHal.h
new file mode 100644
index 0000000..37ea0a3
--- /dev/null
+++ b/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerHal.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <composer-hal/2.2/ComposerHal.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_3 {
+namespace hal {
+
+using V2_1::Display;
+using V2_1::Error;
+
+class ComposerHal : public V2_2::hal::ComposerHal {
+   public:
+    virtual Error getDisplayIdentificationData(Display display, uint8_t* outPort,
+                                               std::vector<uint8_t>* outData) = 0;
+};
+
+}  // namespace hal
+}  // namespace V2_3
+}  // namespace composer
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
diff --git a/graphics/composer/2.3/utils/passthrough/Android.bp b/graphics/composer/2.3/utils/passthrough/Android.bp
new file mode 100644
index 0000000..3ae6b16
--- /dev/null
+++ b/graphics/composer/2.3/utils/passthrough/Android.bp
@@ -0,0 +1,30 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_headers {
+    name: "android.hardware.graphics.composer@2.3-passthrough",
+    defaults: ["hidl_defaults"],
+    vendor: true,
+    header_libs: [
+        "android.hardware.graphics.composer@2.2-passthrough",
+        "android.hardware.graphics.composer@2.3-hal",
+    ],
+    export_header_lib_headers: [
+        "android.hardware.graphics.composer@2.2-passthrough",
+        "android.hardware.graphics.composer@2.3-hal",
+    ],
+    export_include_dirs: ["include"],
+}
diff --git a/graphics/composer/2.3/utils/passthrough/include/composer-passthrough/2.3/HwcHal.h b/graphics/composer/2.3/utils/passthrough/include/composer-passthrough/2.3/HwcHal.h
new file mode 100644
index 0000000..53f2d1b
--- /dev/null
+++ b/graphics/composer/2.3/utils/passthrough/include/composer-passthrough/2.3/HwcHal.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#ifndef LOG_TAG
+#warning "HwcHal.h included without LOG_TAG"
+#endif
+
+#include <type_traits>
+
+#include <composer-hal/2.3/ComposerHal.h>
+#include <composer-passthrough/2.2/HwcHal.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_3 {
+namespace passthrough {
+
+namespace detail {
+
+using V2_1::Display;
+using V2_1::Error;
+
+// HwcHalImpl implements V2_*::hal::ComposerHal on top of hwcomposer2
+template <typename Hal>
+class HwcHalImpl : public V2_2::passthrough::detail::HwcHalImpl<Hal> {
+   public:
+    Error getDisplayIdentificationData(Display display, uint8_t* outPort,
+                                       std::vector<uint8_t>* outData) override {
+        if (!mDispatch.getDisplayIdentificationData) {
+            return Error::UNSUPPORTED;
+        }
+
+        uint32_t size = 0;
+        int32_t error =
+            mDispatch.getDisplayIdentificationData(mDevice, display, outPort, &size, nullptr);
+        if (error != HWC2_ERROR_NONE) {
+            return static_cast<Error>(error);
+        }
+
+        std::vector<uint8_t> data(size);
+        error =
+            mDispatch.getDisplayIdentificationData(mDevice, display, outPort, &size, data.data());
+        if (error != HWC2_ERROR_NONE) {
+            return static_cast<Error>(error);
+        }
+
+        data.resize(size);
+        *outData = std::move(data);
+        return Error::NONE;
+    }
+
+   protected:
+    bool initDispatch() override {
+        if (!BaseType2_2::initDispatch()) {
+            return false;
+        }
+
+        this->initOptionalDispatch(HWC2_FUNCTION_GET_DISPLAY_IDENTIFICATION_DATA,
+                                   &mDispatch.getDisplayIdentificationData);
+        return true;
+    }
+
+   private:
+    struct {
+        HWC2_PFN_GET_DISPLAY_IDENTIFICATION_DATA getDisplayIdentificationData;
+    } mDispatch = {};
+
+    using BaseType2_2 = V2_2::passthrough::detail::HwcHalImpl<Hal>;
+    using BaseType2_1 = V2_1::passthrough::detail::HwcHalImpl<Hal>;
+    using BaseType2_1::mDevice;
+};
+
+}  // namespace detail
+
+using HwcHal = detail::HwcHalImpl<hal::ComposerHal>;
+
+}  // namespace passthrough
+}  // namespace V2_3
+}  // namespace composer
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
diff --git a/graphics/composer/2.3/utils/passthrough/include/composer-passthrough/2.3/HwcLoader.h b/graphics/composer/2.3/utils/passthrough/include/composer-passthrough/2.3/HwcLoader.h
new file mode 100644
index 0000000..afef475
--- /dev/null
+++ b/graphics/composer/2.3/utils/passthrough/include/composer-passthrough/2.3/HwcLoader.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#ifndef LOG_TAG
+#warning "HwcLoader.h included without LOG_TAG"
+#endif
+
+#include <composer-hal/2.3/Composer.h>
+#include <composer-hal/2.3/ComposerHal.h>
+#include <composer-passthrough/2.2/HwcLoader.h>
+#include <composer-passthrough/2.3/HwcHal.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_3 {
+namespace passthrough {
+
+class HwcLoader : public V2_2::passthrough::HwcLoader {
+   public:
+    static IComposer* load() {
+        const hw_module_t* module = loadModule();
+        if (!module) {
+            return nullptr;
+        }
+
+        auto hal = createHalWithAdapter(module);
+        if (!hal) {
+            return nullptr;
+        }
+
+        return createComposer(std::move(hal)).release();
+    }
+
+    // create a ComposerHal instance
+    static std::unique_ptr<hal::ComposerHal> createHal(const hw_module_t* module) {
+        auto hal = std::make_unique<HwcHal>();
+        return hal->initWithModule(module) ? std::move(hal) : nullptr;
+    }
+
+    // create a ComposerHal instance, insert an adapter if necessary
+    static std::unique_ptr<hal::ComposerHal> createHalWithAdapter(const hw_module_t* module) {
+        bool adapted;
+        hwc2_device_t* device = openDeviceWithAdapter(module, &adapted);
+        if (!device) {
+            return nullptr;
+        }
+        auto hal = std::make_unique<HwcHal>();
+        return hal->initWithDevice(std::move(device), !adapted) ? std::move(hal) : nullptr;
+    }
+
+    // create an IComposer instance
+    static std::unique_ptr<IComposer> createComposer(std::unique_ptr<hal::ComposerHal> hal) {
+        return hal::Composer::create(std::move(hal));
+    }
+};
+
+}  // namespace passthrough
+}  // namespace V2_3
+}  // namespace composer
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
diff --git a/graphics/composer/2.3/utils/vts/Android.bp b/graphics/composer/2.3/utils/vts/Android.bp
new file mode 100644
index 0000000..0553258
--- /dev/null
+++ b/graphics/composer/2.3/utils/vts/Android.bp
@@ -0,0 +1,41 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_static {
+    name: "android.hardware.graphics.composer@2.3-vts",
+    defaults: ["hidl_defaults"],
+    srcs: [
+        "ComposerVts.cpp",
+    ],
+    static_libs: [
+        "VtsHalHidlTargetTestBase",
+        "android.hardware.graphics.composer@2.1",
+        "android.hardware.graphics.composer@2.1-vts",
+        "android.hardware.graphics.composer@2.2",
+        "android.hardware.graphics.composer@2.2-vts",
+        "android.hardware.graphics.composer@2.3",
+    ],
+    header_libs: [
+        "android.hardware.graphics.composer@2.1-command-buffer",
+        "android.hardware.graphics.composer@2.2-command-buffer",
+    ],
+    cflags: [
+        "-O0",
+        "-g",
+        "-DLOG_TAG=\"ComposerVts\"",
+    ],
+    export_include_dirs: ["include"],
+}
diff --git a/graphics/composer/2.3/utils/vts/ComposerVts.cpp b/graphics/composer/2.3/utils/vts/ComposerVts.cpp
new file mode 100644
index 0000000..f1d3a50
--- /dev/null
+++ b/graphics/composer/2.3/utils/vts/ComposerVts.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <composer-vts/2.3/ComposerVts.h>
+
+#include <VtsHalHidlTargetTestBase.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_3 {
+namespace vts {
+
+using V2_1::Error;
+
+Composer::Composer() : Composer(::testing::VtsHalHidlTargetTestBase::getService<IComposer>()) {}
+
+Composer::Composer(const std::string& name)
+    : Composer(::testing::VtsHalHidlTargetTestBase::getService<IComposer>(name)) {}
+
+Composer::Composer(const sp<IComposer>& composer)
+    : V2_2::vts::Composer(composer), mComposer(composer) {}
+
+std::unique_ptr<ComposerClient> Composer::createClient() {
+    std::unique_ptr<ComposerClient> client;
+    mComposer->createClient_2_3([&client](const auto& tmpError, const auto& tmpClient) {
+        ASSERT_EQ(Error::NONE, tmpError) << "failed to create client";
+        client = std::make_unique<ComposerClient>(tmpClient);
+    });
+
+    return client;
+}
+
+bool ComposerClient::getDisplayIdentificationData(Display display, uint8_t* outPort,
+                                                  std::vector<uint8_t>* outData) {
+    bool supported = true;
+    mClient->getDisplayIdentificationData(
+        display, [&](const auto& tmpError, const auto& tmpPort, const auto& tmpData) {
+            if (tmpError == Error::UNSUPPORTED) {
+                supported = false;
+                return;
+            }
+            ASSERT_EQ(Error::NONE, tmpError) << "failed to get display identification data";
+
+            *outPort = tmpPort;
+            *outData = tmpData;
+            ASSERT_FALSE(outData->empty()) << "data is empty";
+        });
+
+    return supported;
+}
+
+}  // namespace vts
+}  // namespace V2_3
+}  // namespace composer
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
diff --git a/graphics/composer/2.3/utils/vts/include/composer-vts/2.3/ComposerVts.h b/graphics/composer/2.3/utils/vts/include/composer-vts/2.3/ComposerVts.h
new file mode 100644
index 0000000..103b85a
--- /dev/null
+++ b/graphics/composer/2.3/utils/vts/include/composer-vts/2.3/ComposerVts.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include <VtsHalHidlTargetTestBase.h>
+#include <android/hardware/graphics/composer/2.3/IComposer.h>
+#include <android/hardware/graphics/composer/2.3/IComposerClient.h>
+#include <composer-vts/2.2/ComposerVts.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_3 {
+namespace vts {
+
+using V2_1::Display;
+using V2_3::IComposer;
+using V2_3::IComposerClient;
+
+class ComposerClient;
+
+// A wrapper to IComposer.
+class Composer : public V2_2::vts::Composer {
+   public:
+    Composer();
+    explicit Composer(const std::string& name);
+
+    std::unique_ptr<ComposerClient> createClient();
+
+   protected:
+    explicit Composer(const sp<IComposer>& composer);
+
+   private:
+    const sp<IComposer> mComposer;
+};
+
+// A wrapper to IComposerClient.
+class ComposerClient : public V2_2::vts::ComposerClient {
+   public:
+    explicit ComposerClient(const sp<IComposerClient>& client)
+        : V2_2::vts::ComposerClient(client), mClient(client) {}
+
+    bool getDisplayIdentificationData(Display display, uint8_t* outPort,
+                                      std::vector<uint8_t>* outData);
+
+   private:
+    const sp<IComposerClient> mClient;
+};
+
+}  // namespace vts
+}  // namespace V2_3
+}  // namespace composer
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
diff --git a/graphics/composer/2.3/vts/functional/Android.bp b/graphics/composer/2.3/vts/functional/Android.bp
new file mode 100644
index 0000000..df696c9
--- /dev/null
+++ b/graphics/composer/2.3/vts/functional/Android.bp
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+    name: "VtsHalGraphicsComposerV2_3TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: ["VtsHalGraphicsComposerV2_3TargetTest.cpp"],
+
+    // TODO(b/64437680): Assume these libs are always available on the device.
+    shared_libs: [
+        "libfmq",
+        "libhidltransport",
+        "libsync",
+    ],
+    static_libs: [
+        "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.composer@2.1",
+        "android.hardware.graphics.composer@2.1-vts",
+        "android.hardware.graphics.composer@2.2",
+        "android.hardware.graphics.composer@2.2-vts",
+        "android.hardware.graphics.composer@2.3",
+        "android.hardware.graphics.composer@2.3-vts",
+        "android.hardware.graphics.mapper@2.0",
+        "android.hardware.graphics.mapper@2.0-vts",
+        "android.hardware.graphics.mapper@2.1",
+    ],
+    header_libs: [
+        "android.hardware.graphics.composer@2.1-command-buffer",
+        "android.hardware.graphics.composer@2.2-command-buffer",
+    ],
+}
diff --git a/graphics/composer/2.3/vts/functional/OWNERS b/graphics/composer/2.3/vts/functional/OWNERS
new file mode 100644
index 0000000..234bc75
--- /dev/null
+++ b/graphics/composer/2.3/vts/functional/OWNERS
@@ -0,0 +1,7 @@
+# Graphics team
+olv@google.com
+stoza@google.com
+
+# VTS team
+yim@google.com
+zhuoyao@google.com
diff --git a/graphics/composer/2.3/vts/functional/VtsHalGraphicsComposerV2_3TargetTest.cpp b/graphics/composer/2.3/vts/functional/VtsHalGraphicsComposerV2_3TargetTest.cpp
new file mode 100644
index 0000000..f4e34f0
--- /dev/null
+++ b/graphics/composer/2.3/vts/functional/VtsHalGraphicsComposerV2_3TargetTest.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "graphics_composer_hidl_hal_test@2.3"
+
+#include <algorithm>
+
+#include <VtsHalHidlTargetTestBase.h>
+#include <android-base/logging.h>
+#include <composer-vts/2.1/GraphicsComposerCallback.h>
+#include <composer-vts/2.3/ComposerVts.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_3 {
+namespace vts {
+namespace {
+
+// Test environment for graphics.composer
+class GraphicsComposerHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
+   public:
+    // get the test environment singleton
+    static GraphicsComposerHidlEnvironment* Instance() {
+        static GraphicsComposerHidlEnvironment* instance = new GraphicsComposerHidlEnvironment;
+        return instance;
+    }
+
+    virtual void registerTestServices() override { registerTestService<IComposer>(); }
+
+   private:
+    GraphicsComposerHidlEnvironment() {}
+
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(GraphicsComposerHidlEnvironment);
+};
+
+class GraphicsComposerHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+   protected:
+    void SetUp() override {
+        ASSERT_NO_FATAL_FAILURE(
+            mComposer = std::make_unique<Composer>(
+                GraphicsComposerHidlEnvironment::Instance()->getServiceName<IComposer>()));
+        ASSERT_NO_FATAL_FAILURE(mComposerClient = mComposer->createClient());
+
+        mComposerCallback = new V2_1::vts::GraphicsComposerCallback;
+        mComposerClient->registerCallback(mComposerCallback);
+
+        // assume the first display is primary and is never removed
+        mPrimaryDisplay = waitForFirstDisplay();
+
+        // explicitly disable vsync
+        mComposerClient->setVsyncEnabled(mPrimaryDisplay, false);
+        mComposerCallback->setVsyncAllowed(false);
+    }
+
+    void TearDown() override {
+        if (mComposerCallback != nullptr) {
+            EXPECT_EQ(0, mComposerCallback->getInvalidHotplugCount());
+            EXPECT_EQ(0, mComposerCallback->getInvalidRefreshCount());
+            EXPECT_EQ(0, mComposerCallback->getInvalidVsyncCount());
+        }
+    }
+
+    std::unique_ptr<Composer> mComposer;
+    std::unique_ptr<ComposerClient> mComposerClient;
+    sp<V2_1::vts::GraphicsComposerCallback> mComposerCallback;
+    // the first display and is assumed never to be removed
+    Display mPrimaryDisplay;
+
+   private:
+    Display waitForFirstDisplay() {
+        while (true) {
+            std::vector<Display> displays = mComposerCallback->getDisplays();
+            if (displays.empty()) {
+                usleep(5 * 1000);
+                continue;
+            }
+
+            return displays[0];
+        }
+    }
+};
+
+/**
+ * Test IComposerClient::getDisplayIdentificationData.
+ *
+ * TODO: Check that ports are unique for multiple displays.
+ */
+TEST_F(GraphicsComposerHidlTest, GetDisplayIdentificationData) {
+    uint8_t port0;
+    std::vector<uint8_t> data0;
+    if (mComposerClient->getDisplayIdentificationData(mPrimaryDisplay, &port0, &data0)) {
+        uint8_t port1;
+        std::vector<uint8_t> data1;
+        ASSERT_TRUE(mComposerClient->getDisplayIdentificationData(mPrimaryDisplay, &port1, &data1));
+
+        ASSERT_EQ(port0, port1) << "ports are not stable";
+        ASSERT_TRUE(data0.size() == data1.size() &&
+                    std::equal(data0.begin(), data0.end(), data1.begin()))
+            << "data is not stable";
+    }
+}
+
+}  // namespace
+}  // namespace vts
+}  // namespace V2_3
+}  // namespace composer
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
+
+int main(int argc, char** argv) {
+    using android::hardware::graphics::composer::V2_3::vts::GraphicsComposerHidlEnvironment;
+    ::testing::AddGlobalTestEnvironment(GraphicsComposerHidlEnvironment::Instance());
+    ::testing::InitGoogleTest(&argc, argv);
+    GraphicsComposerHidlEnvironment::Instance()->init(&argc, argv);
+    int status = RUN_ALL_TESTS();
+    return status;
+}
diff --git a/media/omx/1.0/vts/functional/common/media_hidl_test_common.cpp b/media/omx/1.0/vts/functional/common/media_hidl_test_common.cpp
index 34a96a0..88a9e26 100644
--- a/media/omx/1.0/vts/functional/common/media_hidl_test_common.cpp
+++ b/media/omx/1.0/vts/functional/common/media_hidl_test_common.cpp
@@ -470,7 +470,7 @@
     status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet),
                                   OMX_StateExecuting);
     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
-    status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT);
+    status = observer->dequeueMessage(&msg, RELAXED_TIMEOUT);
     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
     ASSERT_EQ(msg.type, Message::Type::EVENT);
     ASSERT_EQ(msg.data.eventData.event, OMX_EventCmdComplete);
diff --git a/media/omx/1.0/vts/functional/common/media_hidl_test_common.h b/media/omx/1.0/vts/functional/common/media_hidl_test_common.h
index c1863d5..1575ba2 100644
--- a/media/omx/1.0/vts/functional/common/media_hidl_test_common.h
+++ b/media/omx/1.0/vts/functional/common/media_hidl_test_common.h
@@ -41,6 +41,8 @@
 /* As component is switching states (loaded<->idle<->execute), dequeueMessage()
  * expects the events to be received within this duration */
 #define DEFAULT_TIMEOUT 100000
+// b/70933963
+#define RELAXED_TIMEOUT 400000
 /* Time interval between successive Input/Output enqueues */
 #define DEFAULT_TIMEOUT_Q 2000
 /* While the component is amidst a process call, asynchronous commands like
diff --git a/media/omx/1.0/vts/functional/component/VtsHalMediaOmxV1_0TargetComponentTest.cpp b/media/omx/1.0/vts/functional/component/VtsHalMediaOmxV1_0TargetComponentTest.cpp
index 7750a12..4ddc833 100644
--- a/media/omx/1.0/vts/functional/component/VtsHalMediaOmxV1_0TargetComponentTest.cpp
+++ b/media/omx/1.0/vts/functional/component/VtsHalMediaOmxV1_0TargetComponentTest.cpp
@@ -1149,15 +1149,13 @@
             ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
 
             // do not enable the port until all the buffers are supplied
-            status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT,
-                                              &pBuffer[0], &pBuffer[1]);
+            status = observer->dequeueMessage(&msg, RELAXED_TIMEOUT, &pBuffer[0], &pBuffer[1]);
             ASSERT_EQ(status,
                       android::hardware::media::omx::V1_0::Status::TIMED_OUT);
 
             ASSERT_NO_FATAL_FAILURE(allocatePortBuffers(
                 omxNode, &pBuffer[i - portBase], i, portMode[i - portBase]));
-            status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT,
-                                              &pBuffer[0], &pBuffer[1]);
+            status = observer->dequeueMessage(&msg, RELAXED_TIMEOUT, &pBuffer[0], &pBuffer[1]);
             ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
             ASSERT_EQ(msg.type, Message::Type::EVENT);
             ASSERT_EQ(msg.data.eventData.data1, OMX_CommandPortEnable);
diff --git a/wifi/1.0/vts/OWNERS b/wifi/1.0/vts/OWNERS
new file mode 100644
index 0000000..8bfb148
--- /dev/null
+++ b/wifi/1.0/vts/OWNERS
@@ -0,0 +1,2 @@
+rpius@google.com
+etancohen@google.com
diff --git a/wifi/1.1/vts/OWNERS b/wifi/1.1/vts/OWNERS
new file mode 100644
index 0000000..8bfb148
--- /dev/null
+++ b/wifi/1.1/vts/OWNERS
@@ -0,0 +1,2 @@
+rpius@google.com
+etancohen@google.com
diff --git a/wifi/1.1/vts/functional/OWNERS b/wifi/1.1/vts/functional/OWNERS
deleted file mode 100644
index 2878acc..0000000
--- a/wifi/1.1/vts/functional/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-rpius@google.com
-quiche@google.com
diff --git a/wifi/1.2/default/Android.mk b/wifi/1.2/default/Android.mk
index 3919690..8c5371b 100644
--- a/wifi/1.2/default/Android.mk
+++ b/wifi/1.2/default/Android.mk
@@ -107,11 +107,11 @@
 LOCAL_STATIC_LIBRARIES := \
     libgmock \
     libgtest \
+    libhidlbase \
     android.hardware.wifi@1.0-service-lib
 LOCAL_SHARED_LIBRARIES := \
     libbase \
     libcutils \
-    libhidlbase \
     libhidltransport \
     liblog \
     libnl \
diff --git a/wifi/1.2/default/tests/runtests.sh b/wifi/1.2/default/tests/runtests.sh
index 966a6a7..5906193 100755
--- a/wifi/1.2/default/tests/runtests.sh
+++ b/wifi/1.2/default/tests/runtests.sh
@@ -18,33 +18,9 @@
   echo "You need to source and lunch before you can use this script"
   exit 1
 fi
+set -e
 
-echo "Running tests"
-set -e # fail early
-
-#NOTE We can't actually run these commands, since they rely on functions added by
-#build / envsetup.sh to the bash shell environment.
-echo "+ mmma -j32 $ANDROID_BUILD_TOP/"
-make -j32 -C $ANDROID_BUILD_TOP -f build/core/main.mk \
-    MODULES-IN-hardware-interfaces-wifi-1.2-default
-
-set -x # print commands
-
-adb wait-for-device
+$ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode android.hardware.wifi@1.0-service-tests
 adb root
-adb wait-for-device
-
-#'disable-verity' will appear in 'adb remount' output if
-#dm - verity is enabled and needs to be disabled.
-if adb remount | grep 'disable-verity'; then
-  adb disable-verity
-  adb reboot
-  adb wait-for-device
-  adb root
-  adb wait-for-device
-  adb remount
-fi
-
-adb sync
-
+adb sync data
 adb shell /data/nativetest/vendor/android.hardware.wifi@1.0-service-tests/android.hardware.wifi@1.0-service-tests
diff --git a/wifi/1.2/default/wifi_chip.cpp b/wifi/1.2/default/wifi_chip.cpp
index 3bd0557..c4da184 100644
--- a/wifi/1.2/default/wifi_chip.cpp
+++ b/wifi/1.2/default/wifi_chip.cpp
@@ -111,16 +111,16 @@
 bool removeOldFilesInternal() {
     time_t now = time(0);
     const time_t delete_files_before = now - kMaxRingBufferFileAgeSeconds;
-    DIR* dir_dump = opendir(kTombstoneFolderPath);
+    std::unique_ptr<DIR, decltype(&closedir)> dir_dump(
+        opendir(kTombstoneFolderPath), closedir);
     if (!dir_dump) {
         LOG(ERROR) << "Failed to open directory: " << strerror(errno);
         return false;
     }
-    unique_fd dir_auto_closer(dirfd(dir_dump));
     struct dirent* dp;
     bool success = true;
     std::list<std::pair<const time_t, std::string>> valid_files;
-    while ((dp = readdir(dir_dump))) {
+    while ((dp = readdir(dir_dump.get()))) {
         if (dp->d_type != DT_REG) {
             continue;
         }
@@ -246,13 +246,13 @@
 size_t cpioArchiveFilesInDir(int out_fd, const char* input_dir) {
     struct dirent* dp;
     size_t n_error = 0;
-    DIR* dir_dump = opendir(input_dir);
+    std::unique_ptr<DIR, decltype(&closedir)> dir_dump(opendir(input_dir),
+                                                       closedir);
     if (!dir_dump) {
         LOG(ERROR) << "Failed to open directory: " << strerror(errno);
         return ++n_error;
     }
-    unique_fd dir_auto_closer(dirfd(dir_dump));
-    while ((dp = readdir(dir_dump))) {
+    while ((dp = readdir(dir_dump.get()))) {
         if (dp->d_type != DT_REG) {
             continue;
         }
diff --git a/wifi/1.2/vts/OWNERS b/wifi/1.2/vts/OWNERS
index 811c857..8bfb148 100644
--- a/wifi/1.2/vts/OWNERS
+++ b/wifi/1.2/vts/OWNERS
@@ -1,4 +1,2 @@
 rpius@google.com
-quiche@google.com
-arabawy@google.com
-yim@google.com
\ No newline at end of file
+etancohen@google.com