Merge "NNAPI HAL: Remove priority from prepareModelFromCache_1_3"
diff --git a/automotive/vehicle/2.0/default/VirtualizationGrpcServer.cpp b/automotive/vehicle/2.0/default/VirtualizationGrpcServer.cpp
index cca65d9..fb02c58 100644
--- a/automotive/vehicle/2.0/default/VirtualizationGrpcServer.cpp
+++ b/automotive/vehicle/2.0/default/VirtualizationGrpcServer.cpp
@@ -1,6 +1,4 @@
 #include <android-base/logging.h>
-#include <getopt.h>
-#include <unistd.h>
 
 #include "vhal_v2_0/virtualization/GrpcVehicleServer.h"
 #include "vhal_v2_0/virtualization/Utils.h"
@@ -8,42 +6,10 @@
 int main(int argc, char* argv[]) {
     namespace vhal_impl = android::hardware::automotive::vehicle::V2_0::impl;
 
-    vhal_impl::VsockServerInfo serverInfo;
+    auto serverInfo = vhal_impl::VsockServerInfo::fromCommandLine(argc, argv);
+    CHECK(serverInfo.has_value()) << "Invalid server CID/port combination";
 
-    // unique values to identify the options
-    constexpr int OPT_VHAL_SERVER_CID = 1001;
-    constexpr int OPT_VHAL_SERVER_PORT_NUMBER = 1002;
-
-    struct option longOptions[] = {
-            {"server_cid", 1, 0, OPT_VHAL_SERVER_CID},
-            {"server_port", 1, 0, OPT_VHAL_SERVER_PORT_NUMBER},
-            {nullptr, 0, nullptr, 0},
-    };
-
-    int optValue;
-    while ((optValue = getopt_long_only(argc, argv, ":", longOptions, 0)) != -1) {
-        switch (optValue) {
-            case OPT_VHAL_SERVER_CID:
-                serverInfo.serverCid = std::atoi(optarg);
-                LOG(DEBUG) << "Vehicle HAL server CID: " << serverInfo.serverCid;
-                break;
-            case OPT_VHAL_SERVER_PORT_NUMBER:
-                serverInfo.serverPort = std::atoi(optarg);
-                LOG(DEBUG) << "Vehicle HAL server port: " << serverInfo.serverPort;
-                break;
-            default:
-                // ignore other options
-                break;
-        }
-    }
-
-    if (serverInfo.serverCid == 0 || serverInfo.serverPort == 0) {
-        LOG(FATAL) << "Invalid server information, CID: " << serverInfo.serverCid
-                   << "; port: " << serverInfo.serverPort;
-        // Will abort after logging
-    }
-
-    auto server = vhal_impl::makeGrpcVehicleServer(vhal_impl::getVsockUri(serverInfo));
+    auto server = vhal_impl::makeGrpcVehicleServer(serverInfo->toUri());
     server->Start();
     return 0;
 }
diff --git a/automotive/vehicle/2.0/default/VirtualizedVehicleService.cpp b/automotive/vehicle/2.0/default/VirtualizedVehicleService.cpp
index 1de81ae..68813c9 100644
--- a/automotive/vehicle/2.0/default/VirtualizedVehicleService.cpp
+++ b/automotive/vehicle/2.0/default/VirtualizedVehicleService.cpp
@@ -15,7 +15,6 @@
  */
 
 #include <android-base/logging.h>
-#include <cutils/properties.h>
 #include <hidl/HidlTransportSupport.h>
 
 #include <vhal_v2_0/EmulatedVehicleConnector.h>
@@ -29,30 +28,13 @@
 using namespace android::hardware::automotive::vehicle::V2_0;
 
 int main(int argc, char* argv[]) {
-    constexpr const char* VHAL_SERVER_CID_PROPERTY_KEY = "ro.vendor.vehiclehal.server.cid";
-    constexpr const char* VHAL_SERVER_PORT_PROPERTY_KEY = "ro.vendor.vehiclehal.server.port";
+    namespace vhal_impl = android::hardware::automotive::vehicle::V2_0::impl;
 
-    auto property_get_uint = [](const char* key, unsigned int default_value) {
-        auto value = property_get_int64(key, default_value);
-        if (value < 0 || value > UINT_MAX) {
-            LOG(DEBUG) << key << ": " << value << " is out of bound, using default value '"
-                       << default_value << "' instead";
-            return default_value;
-        }
-        return static_cast<unsigned int>(value);
-    };
-
-    impl::VsockServerInfo serverInfo{property_get_uint(VHAL_SERVER_CID_PROPERTY_KEY, 0),
-                                     property_get_uint(VHAL_SERVER_PORT_PROPERTY_KEY, 0)};
-
-    if (serverInfo.serverCid == 0 || serverInfo.serverPort == 0) {
-        LOG(FATAL) << "Invalid server information, CID: " << serverInfo.serverCid
-                   << "; port: " << serverInfo.serverPort;
-        // Will abort after logging
-    }
+    auto serverInfo = vhal_impl::VsockServerInfo::fromRoPropertyStore();
+    CHECK(serverInfo.has_value()) << "Invalid server CID/port combination";
 
     auto store = std::make_unique<VehiclePropertyStore>();
-    auto connector = impl::makeGrpcVehicleClient(impl::getVsockUri(serverInfo));
+    auto connector = impl::makeGrpcVehicleClient(serverInfo->toUri());
     auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get(), connector.get());
     auto emulator = std::make_unique<impl::VehicleEmulator>(hal.get());
     auto service = std::make_unique<VehicleHalManager>(hal.get());
diff --git a/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-virtualization-grpc-server.rc b/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-virtualization-grpc-server.rc
index 29147ad..1101b08 100644
--- a/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-virtualization-grpc-server.rc
+++ b/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-virtualization-grpc-server.rc
@@ -3,8 +3,8 @@
 # so the command line arguments are expected, though not conventionally used in Android
 service vendor.vehicle-hal-2.0-server \
         /vendor/bin/hw/android.hardware.automotive.vehicle@2.0-virtualization-grpc-server \
-        -server_cid ${ro.vendor.vehiclehal.server.cid:-0} \
-        -server_port ${ro.vendor.vehiclehal.server.port:-0}
+        -server_cid ${ro.vendor.vehiclehal.server.cid:-pleaseconfigurethis} \
+        -server_port ${ro.vendor.vehiclehal.server.port:-pleaseconfigurethis}
     class hal
     user vehicle_network
     group system inet
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.cpp
index 41d4827..184d8a4 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.cpp
@@ -16,6 +16,11 @@
 
 #include "Utils.h"
 
+#include <cutils/properties.h>
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <unistd.h>
 #include <sstream>
 
 namespace android {
@@ -25,12 +30,83 @@
 namespace V2_0 {
 namespace impl {
 
-std::string getVsockUri(const VsockServerInfo& serverInfo) {
+std::string VsockServerInfo::toUri() {
     std::stringstream uri_stream;
-    uri_stream << "vsock:" << serverInfo.serverCid << ":" << serverInfo.serverPort;
+    uri_stream << "vsock:" << serverCid << ":" << serverPort;
     return uri_stream.str();
 }
 
+static std::optional<unsigned> parseUnsignedIntFromString(const char* optarg, const char* name) {
+    auto v = strtoul(optarg, nullptr, 0);
+    if (((v == ULONG_MAX) && (errno == ERANGE)) || (v > UINT_MAX)) {
+        LOG(WARNING) << name << " value is out of range: " << optarg;
+    } else if (v != 0) {
+        return v;
+    } else {
+        LOG(WARNING) << name << " value is invalid or missing: " << optarg;
+    }
+
+    return std::nullopt;
+}
+
+static std::optional<unsigned> getNumberFromProperty(const char* key) {
+    auto value = property_get_int64(key, -1);
+    if ((value <= 0) || (value > UINT_MAX)) {
+        LOG(WARNING) << key << " is missing or out of bounds";
+        return std::nullopt;
+    }
+
+    return static_cast<unsigned int>(value);
+};
+
+std::optional<VsockServerInfo> VsockServerInfo::fromCommandLine(int argc, char* argv[]) {
+    std::optional<unsigned int> cid;
+    std::optional<unsigned int> port;
+
+    // unique values to identify the options
+    constexpr int OPT_VHAL_SERVER_CID = 1001;
+    constexpr int OPT_VHAL_SERVER_PORT_NUMBER = 1002;
+
+    struct option longOptions[] = {
+            {"server_cid", 1, 0, OPT_VHAL_SERVER_CID},
+            {"server_port", 1, 0, OPT_VHAL_SERVER_PORT_NUMBER},
+            {},
+    };
+
+    int optValue;
+    while ((optValue = getopt_long_only(argc, argv, ":", longOptions, 0)) != -1) {
+        switch (optValue) {
+            case OPT_VHAL_SERVER_CID:
+                cid = parseUnsignedIntFromString(optarg, "cid");
+                break;
+            case OPT_VHAL_SERVER_PORT_NUMBER:
+                port = parseUnsignedIntFromString(optarg, "port");
+                break;
+            default:
+                // ignore other options
+                break;
+        }
+    }
+
+    if (cid && port) {
+        return VsockServerInfo{*cid, *port};
+    }
+    return std::nullopt;
+}
+
+std::optional<VsockServerInfo> VsockServerInfo::fromRoPropertyStore() {
+    constexpr const char* VHAL_SERVER_CID_PROPERTY_KEY = "ro.vendor.vehiclehal.server.cid";
+    constexpr const char* VHAL_SERVER_PORT_PROPERTY_KEY = "ro.vendor.vehiclehal.server.port";
+
+    const auto cid = getNumberFromProperty(VHAL_SERVER_CID_PROPERTY_KEY);
+    const auto port = getNumberFromProperty(VHAL_SERVER_PORT_PROPERTY_KEY);
+
+    if (cid && port) {
+        return VsockServerInfo{*cid, *port};
+    }
+    return std::nullopt;
+}
+
 }  // namespace impl
 }  // namespace V2_0
 }  // namespace vehicle
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.h
index 6b1049c..8a8bce7 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.h
@@ -17,8 +17,11 @@
 #ifndef android_hardware_automotive_vehicle_V2_0_impl_virtualization_Utils_H_
 #define android_hardware_automotive_vehicle_V2_0_impl_virtualization_Utils_H_
 
+#include <optional>
 #include <string>
 
+#include <android-base/logging.h>
+
 namespace android {
 namespace hardware {
 namespace automotive {
@@ -29,9 +32,12 @@
 struct VsockServerInfo {
     unsigned int serverCid{0};
     unsigned int serverPort{0};
-};
 
-std::string getVsockUri(const VsockServerInfo& serverInfo);
+    static std::optional<VsockServerInfo> fromCommandLine(int argc, char* argv[]);
+    static std::optional<VsockServerInfo> fromRoPropertyStore();
+
+    std::string toUri();
+};
 
 }  // namespace impl
 }  // namespace V2_0
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index 6145ea2..a91bd88 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -2438,6 +2438,217 @@
         | VehiclePropertyType:STRING
         | VehicleArea:GLOBAL),
 
+    /**
+     * Defines the initial Android user to be used during initialization.
+     *
+     * This property is called by the Android system when it initializes and it lets the HAL
+     * define which Android user should be started.
+     *
+     * This request is made by setting a VehiclePropValue (defined by InitialUserInfoRequest),
+     * and the HAL must respond with a property change event (defined by InitialUserInfoResponse).
+     * If the HAL doesn't respond after some time (defined by the Android system), the Android
+     * system will proceed as if HAL returned a response of action
+     * InitialUserInfoResponseAction:DEFAULT.
+     *
+     * For example, on first boot, the request could be:
+     *
+     * int32[0]: 42  // request id (arbitrary number set by Android system)
+     * int32[1]: 1   // InitialUserInfoRequestType::FIRST_BOOT
+     * int32[2]: 0   // id of current user (usersInfo.currentUser.userId)
+     * int32[3]: 1   // flag of current user (usersInfo.currentUser.flags = SYSTEM)
+     * int32[4]: 1   // number of existing users (usersInfo.numberUsers);
+     * int32[5]: 0   // user #0  (usersInfo.existingUsers[0].userId)
+     * int32[6]: 1   // flags of user #0  (usersInfo.existingUsers[0].flags)
+     *
+     * And if the HAL want to respond with the creation of an admin user called "Admin", the
+     * response would be:
+     *
+     * int32[0]: 42    // must match the request id from the request
+     * int32[1]:  2    // action = InitialUserInfoResponseAction::CREATE
+     * int32[2]: -1    // userToSwitchOrCreate.userId (not used as user will be created)
+     * int32[3]:  8    // userToSwitchOrCreate.flags = ADMIN
+     * string: "Admin" // userNameToCreate
+     *
+     * NOTE: if the HAL doesn't support user management, then it should not define this property,
+     * which in turn would disable the other user-related properties (for example, the Android
+     * system would never issue them and user-related requests from the HAL layer would be ignored
+     * by the Android System). But if it supports user management, then it must support all
+     * user-related properties (INITIAL_USER_INFO, SWITCH_USER, TODO(b/146207078):others).
+     *
+     * @change_mode VehiclePropertyChangeMode:ON_CHANGE
+     * @access VehiclePropertyAccess:READ_WRITE
+     */
+    INITIAL_USER_INFO = (
+        0x0F07
+        | VehiclePropertyGroup:SYSTEM
+        | VehiclePropertyType:MIXED
+        | VehicleArea:GLOBAL),
+
+    /**
+     * Defines a request to switch the foreground Android user.
+     *
+     * This property is used primarily by the Android System to inform the HAL that the
+     * current foreground Android user is switching, but it could also be used by the HAL to request
+     * the Android system to switch users - the
+     *
+     * When the request is made by Android, it sets a VehiclePropValue and the HAL must responde
+     * with a property change event; when the HAL is making the request, it must also do it through
+     * a property change event (the main difference is that the request id will be positive in the
+     * former case, and negative in the latter; the SwitchUserMessageType will also be different).
+     *
+     * The format of both request is defined by SwitchUserRequest and the format of the response
+     * (when needed) is defined by SwitchUserResponse. How the HAL (or Android System) should
+     * proceed depends on the message type (which is defined by the SwitchUserMessageType
+     * parameter), as defined below.
+     *
+     * 1.LEGACY_ANDROID_SWITCH
+     * -----------------------
+     *
+     * Called by the Android System to indicate the Android user is about to change, when the change
+     * request was made in a way that is not integrated with the HAL (for example, through
+     * adb shell am switch-user).
+     *
+     * The HAL can switch its internal user once it receives this request, but it doesn't need to
+     * reply back to the Android System. If its internal user cannot be changed for some reason,
+     * then it must wait for the SWITCH_USER(type=ANDROID_POST_SWITCH) call to recover
+     * (for example, it could issue a SWITCH_USER(type=VEHICLE_REQUEST) to switch back to
+     * the previous user), but ideally it should never fail (as switching back could result in a
+     * confusing experience for the end user).
+     *
+     * For example, if the system have users (0, 10, 11) and it's switching from 0 to 11 (where none
+     * of them have any special flag), the request would be:
+     *
+     * int32[0]:  42  // request id
+     * int32[1]:  1   // SwitchUserMessageType::LEGACY_ANDROID_SWITCH
+     * int32[2]:  11  // target user id
+     * int32[3]:  0   // target user flags (none)
+     * int32[4]:  10  // current user
+     * int32[5]:  0   // current user flags (none)
+     * int32[6]:  3   // number of users
+     * int32[7]:  0   // user #0 (Android user id 0)
+     * int32[8]:  0   // flags of user #0 (none)
+     * int32[9]:  10  // user #1 (Android user id 10)
+     * int32[10]: 0   // flags of user #1 (none)
+     * int32[11]: 11  // user #2 (Android user id 11)
+     * int32[12]: 0   // flags of user #2 (none)
+     *
+     * 2.ANDROID_SWITCH
+     * ----------------
+     * Called by the Android System to indicate the Android user is about to change, but Android
+     * will wait for the HAL's response (up to some time) before proceeding.
+     *
+     * The HAL must switch its internal user once it receives this request, then respond back to
+     * Android with a SWITCH_USER(type=VEHICLE_RESPONSE) indicating whether its internal
+     * user was switched or not (through the SwitchUserStatus enum).
+     *
+     * For example, if Android has users (0, 10, 11) and it's switching from 10 to 11 (where
+     * none of them have any special flag), the request would be:
+     *
+     * int32[0]:  42  // request id
+     * int32[1]:  2   // SwitchUserMessageType::ANDROID_SWITCH
+     * int32[2]:  11  // target user id
+     * int32[3]:  0   // target user flags (none)
+     * int32[4]:  10  // current user
+     * int32[5]:  0   // current user flags (none)
+     * int32[6]:  3   // number of users
+     * int32[7]:  0   // 1st user (user 0)
+     * int32[8]:  0   // 1st user flags (none)
+     * int32[9]:  10  // 2nd user (user 10)
+     * int32[10]: 0   // 2nd user flags (none)
+     * int32[11]: 11  // 3rd user (user 11)
+     * int32[12]: 0   // 3rd user flags (none)
+     *
+     * If the request succeeded, the HAL must update the propery with:
+     *
+     * int32[0]: 42  // request id
+     * int32[1]: 3   // messageType = SwitchUserMessageType::VEHICLE_RESPONSE
+     * int32[2]: 1   // status = SwitchUserStatus::SUCCESS
+     *
+     * But if it failed, the response would be something like:
+     *
+     * int32[0]: 42   // request id
+     * int32[1]: 3    // messageType = SwitchUserMessageType::VEHICLE_RESPONSE
+     * int32[2]: 2    // status = SwitchUserStatus::FAILURE
+     * string: "108-D'OH!" // OEM-spefic error message
+     *
+     * 3.VEHICLE_RESPONSE
+     * ------------------
+     * Called by the HAL to indicate whether a request of type ANDROID_SWITCH should proceed or
+     * abort - see the ANDROID_SWITCH section above for more info.
+     *
+     * 4.VEHICLE_REQUEST
+     * ------------------
+     * Called by the HAL to request that the current foreground Android user is switched.
+     *
+     * This is useful in situations where Android started as one user, but the vehicle identified
+     * the driver as another user. For example, user A unlocked the car using the key fob of user B;
+     * the INITIAL_USER_INFO request returned user B, but then a face recognition subsubsystem
+     * identified the user as A.
+     *
+     * The HAL makes this request by a property change event (passing a negative request id), and
+     * the Android system will response by issuye an ANDROID_POST_SWITCH call which the same
+     * request id.
+     *
+     * For example, if the current foreground Android user is 10 and the HAL asked it to switch to
+     * 11, the request would be:
+     *
+     * int32[0]: -108  // request id
+     * int32[1]: 4     // messageType = SwitchUserMessageType::VEHICLE_REQUEST
+     * int32[2]: 11    // Android user id
+     *
+     * If the request succeeded and Android has 3 users (0, 10, 11), the response would be:
+     *
+     * int32[0]: -108 // request id
+     * int32[1]:  5   // messageType = SwitchUserMessageType::ANDROID_SWITCH
+     * int32[2]:  11  // target user id
+     * int32[3]:  11  // target user id flags (none)
+     * int32[4]:  11  // current user
+     * int32[5]:  0   // current user flags (none)
+     * int32[6]:  3   // number of users
+     * int32[7]:  0   // 1st user (user 0)
+     * int32[8]:  0   // 1st user flags (none)
+     * int32[9]:  10  // 2nd user (user 10)
+     * int32[10]: 4   // 2nd user flags (none)
+     * int32[11]: 11  // 3rd user (user 11)
+     * int32[12]: 3   // 3rd user flags (none)
+     *
+     * Notice that both the current and target user ids are the same - if the request failed, then
+     * they would be different (i.e, target user would be 11, but current user would still be 10).
+     *
+     * 5.ANDROID_POST_SWITCH
+     * ---------------------
+     * Called by the Android System after a request to switch a user was made
+     *
+     * This property is called after switch requests of any type (i.e., LEGACY_ANDROID_SWITCH,
+     * ANDROID_SWITCH, or VEHICLE_REQUEST) and can be used to determine if the request succeeded or
+     * failed:
+     *
+     * 1. When it succeeded, it's called when the Android user is in the boot locked state and the
+     *    value of the current and target users ids in the response are different. This would be
+     *    equivalent to receiving an Intent.ACTION_LOCKED_BOOT_COMPLETED in an Android app.
+     * 2. When it failed it's called right away and the value of the current and target users ids
+     *    in the response are the same.
+     *
+     * The HAL can update its internal state once it receives this request, but it doesn't need to
+     * reply back to the Android System.
+     *
+     * Request: the first N values as defined by INITIAL_USER_INFO (where the request-specific
+     * value at index 1 is SwitchUserMessageType::ANDROID_POST_SWITCH), then 2 more values for the
+     * target user id (i.e., the Android user id that was requested to be switched to) and its flags
+     * (as defined by  UserFlags).
+     *
+     * Response: none.
+     *
+     * Example: see VEHICLE_REQUEST section above.
+     *
+     * @change_mode VehiclePropertyChangeMode:ON_CHANGE
+     * @access VehiclePropertyAccess:READ_WRITE
+     */
+    SWITCH_USER = (
+        0x0F08
+        | VehiclePropertyGroup:SYSTEM
+        | VehiclePropertyType:MIXED
+        | VehicleArea:GLOBAL),
 };
 
 /**
@@ -3733,3 +3944,258 @@
 enum VmsPublisherInformationIntegerValuesIndex : VmsBaseMessageIntegerValuesIndex {
     PUBLISHER_ID = 1,
 };
+
+/**
+ * Information about a specific Android user.
+ */
+struct UserInfo {
+
+    UserId userId;
+
+    UserFlags flags;
+};
+
+/**
+ * Id of an Android user.
+ *
+ * Must be > 0 for valid ids, or -1 when it's not used.
+ */
+typedef int32_t UserId;
+
+/**
+ * Flags used to define the characteristics of an Android user.
+ */
+enum UserFlags: int32_t {
+    /**
+     * No flags.
+     */
+    NONE = 0x0,
+
+    /**
+     * System user.
+     * On automotive, that user is always running, although never on foreground (except during
+     * boot or exceptional circumstances).
+     */
+    SYSTEM = 0x01,
+
+    /**
+     * Guest users have restrictions.
+     */
+    GUEST = 0x02,
+
+    /**
+     * Ephemeral users have non-persistent state.
+     */
+    EPHEMERAL = 0x04,
+
+    /**
+     * Admin users have additional privileges such as permission to create other users.
+     */
+    ADMIN = 0x08,
+};
+
+/**
+ * Information about all Android users.
+ *
+ * NOTE: this struct is not used in the HAL properties directly, it's part of other structs, which
+ * in turn are converted to a VehiclePropValue.RawValue through libraries provided by the default
+ * Vehicle HAL implementation.
+ */
+struct UsersInfo {
+
+    /** The current foreground user. */
+    UserInfo currentUser;
+
+    /** Number of existing users (includes the current user). */
+    int32_t numberUsers;
+
+    /** List of existing users (includes the current user). */
+    vec<UserInfo> existingUsers;
+ };
+
+/**
+ * Id of a request related to user management.
+ *
+ * This id can be used by the Android system to map responses sent by the HAL, and vice-versa.
+ *
+ * For requests originated by Android, the value is positive (> 0), while for requests originated by
+ * the HAL it must be negative (< 0).
+ */
+typedef int32_t UserRequestId;
+
+/**
+ * Defines the format of a INITIAL_USER_INFO request made by the Android system.
+ *
+ * NOTE: this struct is not used in the HAL properties directly, it must be converted to
+ * VehiclePropValue.RawValue through libraries provided by the default Vehicle HAL implementation.
+ */
+struct InitialUserInfoRequest {
+    /**
+     * Arbitrary id used to map the HAL response to the request.
+     */
+    UserRequestId requestId;
+
+    /**
+     * Type of request.
+     */
+    InitialUserInfoRequestType requestType;
+
+    /**
+     * Information about the current state of the Android system.
+     */
+    UsersInfo usersInfo;
+};
+
+/**
+ * Defines when a INITIAL_USER_INFO request was made.
+ */
+enum InitialUserInfoRequestType : int32_t {
+  /** At the first time Android was booted (or after a factory reset). */
+  FIRST_BOOT = 1,
+
+  /** At the first time Android was booted after the system was updated. */
+  FIRST_BOOT_AFTER_OTA = 2,
+
+  /** When Android was booted "from scratch". */
+  COLD_BOOT = 3,
+
+  /** When Android was resumed after the system was suspended to memory. */
+  RESUME = 4,
+};
+
+/**
+ * Defines the format of a HAL response to a INITIAL_USER_INFO request.
+ *
+ * NOTE: this struct is not used in the HAL properties directly, it must be converted to
+ * VehiclePropValue.RawValue through libraries provided by the default Vehicle HAL implementation.
+ */
+struct InitialUserInfoResponse {
+    /**
+     * Id of the request being responded.
+     */
+    UserRequestId requestId;
+
+    /**
+     * which action the Android system should take.
+     */
+    InitialUserInfoResponseAction action;
+
+    /**
+     * Information about the user that should be switched to or created.
+     */
+    UserInfo userToSwitchOrCreate;
+
+    /**
+     * Name of the user that should be created.
+     */
+    string userNameToCreate;
+};
+
+/**
+ * Defines which action the Android system should take in an INITIAL_USER_INFO request.
+ */
+enum InitialUserInfoResponseAction : int32_t {
+   /**
+    * Let the Android System decide what to do.
+    *
+    * For example, it might create a new user on first boot, and switch to the last
+    * active user afterwards.
+    */
+   DEFAULT = 0,
+
+   /**
+    * Switch to an existing Android user.
+    */
+   SWITCH = 1,
+
+   /**
+    * Create a new Android user (and switch to it).
+    */
+   CREATE = 2,
+};
+
+/**
+ * Defines the format of a SWITCH_USER property.
+ *
+ * NOTE: this struct is not used in the HAL properties directly, it must be converted to
+ * VehiclePropValue.RawValue through libraries provided by the default Vehicle HAL implementation.
+ */
+struct SwitchUserRequest {
+    /**
+     * Arbitrary id used to map the response to the request.
+     */
+    UserRequestId requestId;
+
+    /**
+     * Type of message.
+     */
+    SwitchUserMessageType messageType;
+
+    /**
+     * Information about the Android user being switched to.
+     *
+     * Only the user id (but not the flags) should be set when the request is made by HAL.
+     */
+    UserInfo targetUser;
+
+    /**
+     * Information about the current state of the Android system.
+     *
+     * Should not be set when the request is made by HAL.
+     */
+    UsersInfo usersInfo;
+};
+
+/**
+ * Defines the reason a SWITCH_USER call was made.
+ *
+ * The meaning of each constant is explained in that property.
+ */
+enum SwitchUserMessageType: int32_t {
+   LEGACY_ANDROID_SWITCH = 1,
+   ANDROID_SWITCH = 2,
+   VEHICLE_RESPONSE = 3,
+   VEHICLE_REQUEST = 4,
+   ANDROID_POST_SWITCH = 5,
+};
+
+/**
+ * Defines the result of a SwitchUserRequest.
+ *
+ * NOTE: this struct is not used in the HAL properties directly, it must be converted to
+ * VehiclePropValue.RawValue through libraries provided by the default Vehicle HAL implementation.
+ */
+struct SwitchUserResponse {
+    /**
+     * Id of the request being responded.
+     */
+    UserRequestId requestId;
+
+    /**
+     * Type of message.
+     */
+    SwitchUserMessageType messageType;
+
+    /**
+     * Status of the request.
+     */
+    SwitchUserStatus status;
+
+    /**
+     * HAL-specific error message.
+     *
+     * This argument is optional, and when defined (and the status is FAILURE), it's passed "as-is"
+     * to the caller. It could be used to show custom error messages to the end user.
+     */
+    string failureMessage;
+};
+
+/**
+ * Status of the response to a SwitchUserRequest.
+ */
+enum SwitchUserStatus : int32_t {
+   /** The request succeeded and the HAL user was switched. */
+   SUCCESS = 1,
+   /** The request failed and the HAL user remained the same. */
+   FAILURE = 2,
+};
diff --git a/camera/common/1.0/default/HandleImporter.cpp b/camera/common/1.0/default/HandleImporter.cpp
index ac32c95..7792b31 100644
--- a/camera/common/1.0/default/HandleImporter.cpp
+++ b/camera/common/1.0/default/HandleImporter.cpp
@@ -182,9 +182,8 @@
     }
 
     Mutex::Autolock lock(mLock);
-    if (mMapperV4 == nullptr && mMapperV3 == nullptr && mMapperV2 == nullptr) {
-        ALOGE("%s: mMapperV4, mMapperV3 and mMapperV2 are all null!", __FUNCTION__);
-        return;
+    if (!mInitialized) {
+        initializeLocked();
     }
 
     if (mMapperV4 != nullptr) {
diff --git a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
index 9ff0d74..5f86742 100644
--- a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
+++ b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
@@ -81,8 +81,6 @@
     return locked;
 }
 
-buffer_handle_t sEmptyBuffer = nullptr;
-
 } // Anonymous namespace
 
 // Static instances
@@ -119,8 +117,8 @@
     std::string make, model;
     if (ret < 0) {
         ALOGW("%s v4l2 QUERYCAP failed", __FUNCTION__);
-        make = "Generic UVC webcam";
-        model = "Generic UVC webcam";
+        mExifMake = "Generic UVC webcam";
+        mExifModel = "Generic UVC webcam";
     } else {
         // capability.card is UTF-8 encoded
         char card[32];
@@ -134,11 +132,11 @@
             }
         }
         if (j == 0 || card[j - 1] != '\0') {
-            make = "Generic UVC webcam";
-            model = "Generic UVC webcam";
+            mExifMake = "Generic UVC webcam";
+            mExifModel = "Generic UVC webcam";
         } else {
-            make = card;
-            model = card;
+            mExifMake = card;
+            mExifModel = card;
         }
     }
 
@@ -147,7 +145,7 @@
         ALOGE("%s: init OutputThread failed!", __FUNCTION__);
         return true;
     }
-    mOutputThread->setExifMakeModel(make, model);
+    mOutputThread->setExifMakeModel(mExifMake, mExifModel);
 
     status_t status = initDefaultRequests();
     if (status != OK) {
@@ -161,7 +159,7 @@
         ALOGE("%s: invalid request fmq", __FUNCTION__);
         return true;
     }
-    mResultMetadataQueue = std::make_shared<RequestMetadataQueue>(
+    mResultMetadataQueue = std::make_shared<ResultMetadataQueue>(
             kMetadataMsgQueueSize, false /* non blocking */);
     if (!mResultMetadataQueue->isValid()) {
         ALOGE("%s: invalid result fmq", __FUNCTION__);
@@ -183,7 +181,7 @@
 }
 
 void ExternalCameraDeviceSession::initOutputThread() {
-    mOutputThread = new OutputThread(this, mCroppingType);
+    mOutputThread = new OutputThread(this, mCroppingType, mCameraCharacteristics);
 }
 
 void ExternalCameraDeviceSession::closeOutputThread() {
@@ -518,35 +516,9 @@
         uint64_t bufId, buffer_handle_t buf,
         /*out*/buffer_handle_t** outBufPtr,
         bool allowEmptyBuf) {
-
-    if (buf == nullptr && bufId == BUFFER_ID_NO_BUFFER) {
-        if (allowEmptyBuf) {
-            *outBufPtr = &sEmptyBuffer;
-            return Status::OK;
-        } else {
-            ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId);
-            return Status::ILLEGAL_ARGUMENT;
-        }
-    }
-
-    CirculatingBuffers& cbs = mCirculatingBuffers[streamId];
-    if (cbs.count(bufId) == 0) {
-        if (buf == nullptr) {
-            ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId);
-            return Status::ILLEGAL_ARGUMENT;
-        }
-        // Register a newly seen buffer
-        buffer_handle_t importedBuf = buf;
-        sHandleImporter.importBuffer(importedBuf);
-        if (importedBuf == nullptr) {
-            ALOGE("%s: output buffer for stream %d is invalid!", __FUNCTION__, streamId);
-            return Status::INTERNAL_ERROR;
-        } else {
-            cbs[bufId] = importedBuf;
-        }
-    }
-    *outBufPtr = &cbs[bufId];
-    return Status::OK;
+    return importBufferImpl(
+            mCirculatingBuffers, sHandleImporter, streamId,
+            bufId, buf, outBufPtr, allowEmptyBuf);
 }
 
 Status ExternalCameraDeviceSession::importRequestLockedImpl(
@@ -791,15 +763,32 @@
 
 //TODO: refactor with processCaptureResult
 Status ExternalCameraDeviceSession::processCaptureRequestError(
-        const std::shared_ptr<HalRequest>& req) {
+        const std::shared_ptr<HalRequest>& req,
+        /*out*/std::vector<NotifyMsg>* outMsgs,
+        /*out*/std::vector<CaptureResult>* outResults) {
     ATRACE_CALL();
     // Return V4L2 buffer to V4L2 buffer queue
-    enqueueV4l2Frame(req->frameIn);
+    sp<V3_4::implementation::V4L2Frame> v4l2Frame =
+            static_cast<V3_4::implementation::V4L2Frame*>(req->frameIn.get());
+    enqueueV4l2Frame(v4l2Frame);
 
-    // NotifyShutter
-    notifyShutter(req->frameNumber, req->shutterTs);
+    if (outMsgs == nullptr) {
+        notifyShutter(req->frameNumber, req->shutterTs);
+        notifyError(/*frameNum*/req->frameNumber, /*stream*/-1, ErrorCode::ERROR_REQUEST);
+    } else {
+        NotifyMsg shutter;
+        shutter.type = MsgType::SHUTTER;
+        shutter.msg.shutter.frameNumber = req->frameNumber;
+        shutter.msg.shutter.timestamp = req->shutterTs;
 
-    notifyError(/*frameNum*/req->frameNumber, /*stream*/-1, ErrorCode::ERROR_REQUEST);
+        NotifyMsg error;
+        error.type = MsgType::ERROR;
+        error.msg.error.frameNumber = req->frameNumber;
+        error.msg.error.errorStreamId = -1;
+        error.msg.error.errorCode = ErrorCode::ERROR_REQUEST;
+        outMsgs->push_back(shutter);
+        outMsgs->push_back(error);
+    }
 
     // Fill output buffers
     hidl_vec<CaptureResult> results;
@@ -826,16 +815,22 @@
         mInflightFrames.erase(req->frameNumber);
     }
 
-    // Callback into framework
-    invokeProcessCaptureResultCallback(results, /* tryWriteFmq */true);
-    freeReleaseFences(results);
+    if (outResults == nullptr) {
+        // Callback into framework
+        invokeProcessCaptureResultCallback(results, /* tryWriteFmq */true);
+        freeReleaseFences(results);
+    } else {
+        outResults->push_back(result);
+    }
     return Status::OK;
 }
 
 Status ExternalCameraDeviceSession::processCaptureResult(std::shared_ptr<HalRequest>& req) {
     ATRACE_CALL();
     // Return V4L2 buffer to V4L2 buffer queue
-    enqueueV4l2Frame(req->frameIn);
+    sp<V3_4::implementation::V4L2Frame> v4l2Frame =
+            static_cast<V3_4::implementation::V4L2Frame*>(req->frameIn.get());
+    enqueueV4l2Frame(v4l2Frame);
 
     // NotifyShutter
     notifyShutter(req->frameNumber, req->shutterTs);
@@ -923,29 +918,10 @@
     mProcessCaptureResultLock.unlock();
 }
 
-void ExternalCameraDeviceSession::freeReleaseFences(hidl_vec<CaptureResult>& results) {
-    for (auto& result : results) {
-        if (result.inputBuffer.releaseFence.getNativeHandle() != nullptr) {
-            native_handle_t* handle = const_cast<native_handle_t*>(
-                    result.inputBuffer.releaseFence.getNativeHandle());
-            native_handle_close(handle);
-            native_handle_delete(handle);
-        }
-        for (auto& buf : result.outputBuffers) {
-            if (buf.releaseFence.getNativeHandle() != nullptr) {
-                native_handle_t* handle = const_cast<native_handle_t*>(
-                        buf.releaseFence.getNativeHandle());
-                native_handle_close(handle);
-                native_handle_delete(handle);
-            }
-        }
-    }
-    return;
-}
-
 ExternalCameraDeviceSession::OutputThread::OutputThread(
-        wp<ExternalCameraDeviceSession> parent,
-        CroppingType ct) : mParent(parent), mCroppingType(ct) {}
+        wp<OutputThreadInterface> parent, CroppingType ct,
+        const common::V1_0::helper::CameraMetadata& chars) :
+        mParent(parent), mCroppingType(ct), mCameraCharacteristics(chars) {}
 
 ExternalCameraDeviceSession::OutputThread::~OutputThread() {}
 
@@ -955,88 +931,6 @@
     mExifModel = model;
 }
 
-uint32_t ExternalCameraDeviceSession::OutputThread::getFourCcFromLayout(
-        const YCbCrLayout& layout) {
-    intptr_t cb = reinterpret_cast<intptr_t>(layout.cb);
-    intptr_t cr = reinterpret_cast<intptr_t>(layout.cr);
-    if (std::abs(cb - cr) == 1 && layout.chromaStep == 2) {
-        // Interleaved format
-        if (layout.cb > layout.cr) {
-            return V4L2_PIX_FMT_NV21;
-        } else {
-            return V4L2_PIX_FMT_NV12;
-        }
-    } else if (layout.chromaStep == 1) {
-        // Planar format
-        if (layout.cb > layout.cr) {
-            return V4L2_PIX_FMT_YVU420; // YV12
-        } else {
-            return V4L2_PIX_FMT_YUV420; // YU12
-        }
-    } else {
-        return FLEX_YUV_GENERIC;
-    }
-}
-
-int ExternalCameraDeviceSession::OutputThread::getCropRect(
-        CroppingType ct, const Size& inSize, const Size& outSize, IMapper::Rect* out) {
-    if (out == nullptr) {
-        ALOGE("%s: out is null", __FUNCTION__);
-        return -1;
-    }
-
-    uint32_t inW = inSize.width;
-    uint32_t inH = inSize.height;
-    uint32_t outW = outSize.width;
-    uint32_t outH = outSize.height;
-
-    // Handle special case where aspect ratio is close to input but scaled
-    // dimension is slightly larger than input
-    float arIn = ASPECT_RATIO(inSize);
-    float arOut = ASPECT_RATIO(outSize);
-    if (isAspectRatioClose(arIn, arOut)) {
-        out->left = 0;
-        out->top = 0;
-        out->width = inW;
-        out->height = inH;
-        return 0;
-    }
-
-    if (ct == VERTICAL) {
-        uint64_t scaledOutH = static_cast<uint64_t>(outH) * inW / outW;
-        if (scaledOutH > inH) {
-            ALOGE("%s: Output size %dx%d cannot be vertically cropped from input size %dx%d",
-                    __FUNCTION__, outW, outH, inW, inH);
-            return -1;
-        }
-        scaledOutH = scaledOutH & ~0x1; // make it multiple of 2
-
-        out->left = 0;
-        out->top = ((inH - scaledOutH) / 2) & ~0x1;
-        out->width = inW;
-        out->height = static_cast<int32_t>(scaledOutH);
-        ALOGV("%s: crop %dx%d to %dx%d: top %d, scaledH %d",
-                __FUNCTION__, inW, inH, outW, outH, out->top, static_cast<int32_t>(scaledOutH));
-    } else {
-        uint64_t scaledOutW = static_cast<uint64_t>(outW) * inH / outH;
-        if (scaledOutW > inW) {
-            ALOGE("%s: Output size %dx%d cannot be horizontally cropped from input size %dx%d",
-                    __FUNCTION__, outW, outH, inW, inH);
-            return -1;
-        }
-        scaledOutW = scaledOutW & ~0x1; // make it multiple of 2
-
-        out->left = ((inW - scaledOutW) / 2) & ~0x1;
-        out->top = 0;
-        out->width = static_cast<int32_t>(scaledOutW);
-        out->height = inH;
-        ALOGV("%s: crop %dx%d to %dx%d: top %d, scaledW %d",
-                __FUNCTION__, inW, inH, outW, outH, out->top, static_cast<int32_t>(scaledOutW));
-    }
-
-    return 0;
-}
-
 int ExternalCameraDeviceSession::OutputThread::cropAndScaleLocked(
         sp<AllocatedFrame>& in, const Size& outSz, YCbCrLayout* out) {
     Size inSz = {in->mWidth, in->mHeight};
@@ -1274,265 +1168,6 @@
     return 0;
 }
 
-int ExternalCameraDeviceSession::OutputThread::formatConvertLocked(
-        const YCbCrLayout& in, const YCbCrLayout& out, Size sz, uint32_t format) {
-    int ret = 0;
-    switch (format) {
-        case V4L2_PIX_FMT_NV21:
-            ret = libyuv::I420ToNV21(
-                    static_cast<uint8_t*>(in.y),
-                    in.yStride,
-                    static_cast<uint8_t*>(in.cb),
-                    in.cStride,
-                    static_cast<uint8_t*>(in.cr),
-                    in.cStride,
-                    static_cast<uint8_t*>(out.y),
-                    out.yStride,
-                    static_cast<uint8_t*>(out.cr),
-                    out.cStride,
-                    sz.width,
-                    sz.height);
-            if (ret != 0) {
-                ALOGE("%s: convert to NV21 buffer failed! ret %d",
-                            __FUNCTION__, ret);
-                return ret;
-            }
-            break;
-        case V4L2_PIX_FMT_NV12:
-            ret = libyuv::I420ToNV12(
-                    static_cast<uint8_t*>(in.y),
-                    in.yStride,
-                    static_cast<uint8_t*>(in.cb),
-                    in.cStride,
-                    static_cast<uint8_t*>(in.cr),
-                    in.cStride,
-                    static_cast<uint8_t*>(out.y),
-                    out.yStride,
-                    static_cast<uint8_t*>(out.cb),
-                    out.cStride,
-                    sz.width,
-                    sz.height);
-            if (ret != 0) {
-                ALOGE("%s: convert to NV12 buffer failed! ret %d",
-                            __FUNCTION__, ret);
-                return ret;
-            }
-            break;
-        case V4L2_PIX_FMT_YVU420: // YV12
-        case V4L2_PIX_FMT_YUV420: // YU12
-            // TODO: maybe we can speed up here by somehow save this copy?
-            ret = libyuv::I420Copy(
-                    static_cast<uint8_t*>(in.y),
-                    in.yStride,
-                    static_cast<uint8_t*>(in.cb),
-                    in.cStride,
-                    static_cast<uint8_t*>(in.cr),
-                    in.cStride,
-                    static_cast<uint8_t*>(out.y),
-                    out.yStride,
-                    static_cast<uint8_t*>(out.cb),
-                    out.cStride,
-                    static_cast<uint8_t*>(out.cr),
-                    out.cStride,
-                    sz.width,
-                    sz.height);
-            if (ret != 0) {
-                ALOGE("%s: copy to YV12 or YU12 buffer failed! ret %d",
-                            __FUNCTION__, ret);
-                return ret;
-            }
-            break;
-        case FLEX_YUV_GENERIC:
-            // TODO: b/72261744 write to arbitrary flexible YUV layout. Slow.
-            ALOGE("%s: unsupported flexible yuv layout"
-                    " y %p cb %p cr %p y_str %d c_str %d c_step %d",
-                    __FUNCTION__, out.y, out.cb, out.cr,
-                    out.yStride, out.cStride, out.chromaStep);
-            return -1;
-        default:
-            ALOGE("%s: unknown YUV format 0x%x!", __FUNCTION__, format);
-            return -1;
-    }
-    return 0;
-}
-
-int ExternalCameraDeviceSession::OutputThread::encodeJpegYU12(
-        const Size & inSz, const YCbCrLayout& inLayout,
-        int jpegQuality, const void *app1Buffer, size_t app1Size,
-        void *out, const size_t maxOutSize, size_t &actualCodeSize)
-{
-    /* libjpeg is a C library so we use C-style "inheritance" by
-     * putting libjpeg's jpeg_destination_mgr first in our custom
-     * struct. This allows us to cast jpeg_destination_mgr* to
-     * CustomJpegDestMgr* when we get it passed to us in a callback */
-    struct CustomJpegDestMgr {
-        struct jpeg_destination_mgr mgr;
-        JOCTET *mBuffer;
-        size_t mBufferSize;
-        size_t mEncodedSize;
-        bool mSuccess;
-    } dmgr;
-
-    jpeg_compress_struct cinfo = {};
-    jpeg_error_mgr jerr;
-
-    /* Initialize error handling with standard callbacks, but
-     * then override output_message (to print to ALOG) and
-     * error_exit to set a flag and print a message instead
-     * of killing the whole process */
-    cinfo.err = jpeg_std_error(&jerr);
-
-    cinfo.err->output_message = [](j_common_ptr cinfo) {
-        char buffer[JMSG_LENGTH_MAX];
-
-        /* Create the message */
-        (*cinfo->err->format_message)(cinfo, buffer);
-        ALOGE("libjpeg error: %s", buffer);
-    };
-    cinfo.err->error_exit = [](j_common_ptr cinfo) {
-        (*cinfo->err->output_message)(cinfo);
-        if(cinfo->client_data) {
-            auto & dmgr =
-                *reinterpret_cast<CustomJpegDestMgr*>(cinfo->client_data);
-            dmgr.mSuccess = false;
-        }
-    };
-    /* Now that we initialized some callbacks, let's create our compressor */
-    jpeg_create_compress(&cinfo);
-
-    /* Initialize our destination manager */
-    dmgr.mBuffer = static_cast<JOCTET*>(out);
-    dmgr.mBufferSize = maxOutSize;
-    dmgr.mEncodedSize = 0;
-    dmgr.mSuccess = true;
-    cinfo.client_data = static_cast<void*>(&dmgr);
-
-    /* These lambdas become C-style function pointers and as per C++11 spec
-     * may not capture anything */
-    dmgr.mgr.init_destination = [](j_compress_ptr cinfo) {
-        auto & dmgr = reinterpret_cast<CustomJpegDestMgr&>(*cinfo->dest);
-        dmgr.mgr.next_output_byte = dmgr.mBuffer;
-        dmgr.mgr.free_in_buffer = dmgr.mBufferSize;
-        ALOGV("%s:%d jpeg start: %p [%zu]",
-              __FUNCTION__, __LINE__, dmgr.mBuffer, dmgr.mBufferSize);
-    };
-
-    dmgr.mgr.empty_output_buffer = [](j_compress_ptr cinfo __unused) {
-        ALOGV("%s:%d Out of buffer", __FUNCTION__, __LINE__);
-        return 0;
-    };
-
-    dmgr.mgr.term_destination = [](j_compress_ptr cinfo) {
-        auto & dmgr = reinterpret_cast<CustomJpegDestMgr&>(*cinfo->dest);
-        dmgr.mEncodedSize = dmgr.mBufferSize - dmgr.mgr.free_in_buffer;
-        ALOGV("%s:%d Done with jpeg: %zu", __FUNCTION__, __LINE__, dmgr.mEncodedSize);
-    };
-    cinfo.dest = reinterpret_cast<struct jpeg_destination_mgr*>(&dmgr);
-
-    /* We are going to be using JPEG in raw data mode, so we are passing
-     * straight subsampled planar YCbCr and it will not touch our pixel
-     * data or do any scaling or anything */
-    cinfo.image_width = inSz.width;
-    cinfo.image_height = inSz.height;
-    cinfo.input_components = 3;
-    cinfo.in_color_space = JCS_YCbCr;
-
-    /* Initialize defaults and then override what we want */
-    jpeg_set_defaults(&cinfo);
-
-    jpeg_set_quality(&cinfo, jpegQuality, 1);
-    jpeg_set_colorspace(&cinfo, JCS_YCbCr);
-    cinfo.raw_data_in = 1;
-    cinfo.dct_method = JDCT_IFAST;
-
-    /* Configure sampling factors. The sampling factor is JPEG subsampling 420
-     * because the source format is YUV420. Note that libjpeg sampling factors
-     * are... a little weird. Sampling of Y=2,U=1,V=1 means there is 1 U and
-     * 1 V value for each 2 Y values */
-    cinfo.comp_info[0].h_samp_factor = 2;
-    cinfo.comp_info[0].v_samp_factor = 2;
-    cinfo.comp_info[1].h_samp_factor = 1;
-    cinfo.comp_info[1].v_samp_factor = 1;
-    cinfo.comp_info[2].h_samp_factor = 1;
-    cinfo.comp_info[2].v_samp_factor = 1;
-
-    /* Let's not hardcode YUV420 in 6 places... 5 was enough */
-    int maxVSampFactor = std::max( {
-        cinfo.comp_info[0].v_samp_factor,
-        cinfo.comp_info[1].v_samp_factor,
-        cinfo.comp_info[2].v_samp_factor
-    });
-    int cVSubSampling = cinfo.comp_info[0].v_samp_factor /
-                        cinfo.comp_info[1].v_samp_factor;
-
-    /* Start the compressor */
-    jpeg_start_compress(&cinfo, TRUE);
-
-    /* Compute our macroblock height, so we can pad our input to be vertically
-     * macroblock aligned.
-     * TODO: Does it need to be horizontally MCU aligned too? */
-
-    size_t mcuV = DCTSIZE*maxVSampFactor;
-    size_t paddedHeight = mcuV * ((inSz.height + mcuV - 1) / mcuV);
-
-    /* libjpeg uses arrays of row pointers, which makes it really easy to pad
-     * data vertically (unfortunately doesn't help horizontally) */
-    std::vector<JSAMPROW> yLines (paddedHeight);
-    std::vector<JSAMPROW> cbLines(paddedHeight/cVSubSampling);
-    std::vector<JSAMPROW> crLines(paddedHeight/cVSubSampling);
-
-    uint8_t *py = static_cast<uint8_t*>(inLayout.y);
-    uint8_t *pcr = static_cast<uint8_t*>(inLayout.cr);
-    uint8_t *pcb = static_cast<uint8_t*>(inLayout.cb);
-
-    for(uint32_t i = 0; i < paddedHeight; i++)
-    {
-        /* Once we are in the padding territory we still point to the last line
-         * effectively replicating it several times ~ CLAMP_TO_EDGE */
-        int li = std::min(i, inSz.height - 1);
-        yLines[i]  = static_cast<JSAMPROW>(py + li * inLayout.yStride);
-        if(i < paddedHeight / cVSubSampling)
-        {
-            crLines[i] = static_cast<JSAMPROW>(pcr + li * inLayout.cStride);
-            cbLines[i] = static_cast<JSAMPROW>(pcb + li * inLayout.cStride);
-        }
-    }
-
-    /* If APP1 data was passed in, use it */
-    if(app1Buffer && app1Size)
-    {
-        jpeg_write_marker(&cinfo, JPEG_APP0 + 1,
-             static_cast<const JOCTET*>(app1Buffer), app1Size);
-    }
-
-    /* While we still have padded height left to go, keep giving it one
-     * macroblock at a time. */
-    while (cinfo.next_scanline < cinfo.image_height) {
-        const uint32_t batchSize = DCTSIZE * maxVSampFactor;
-        const uint32_t nl = cinfo.next_scanline;
-        JSAMPARRAY planes[3]{ &yLines[nl],
-                              &cbLines[nl/cVSubSampling],
-                              &crLines[nl/cVSubSampling] };
-
-        uint32_t done = jpeg_write_raw_data(&cinfo, planes, batchSize);
-
-        if (done != batchSize) {
-            ALOGE("%s: compressed %u lines, expected %u (total %u/%u)",
-              __FUNCTION__, done, batchSize, cinfo.next_scanline,
-              cinfo.image_height);
-            return -1;
-        }
-    }
-
-    /* This will flush everything */
-    jpeg_finish_compress(&cinfo);
-
-    /* Grab the actual code size and set it */
-    actualCodeSize = dmgr.mEncodedSize;
-
-    return 0;
-}
-
 /*
  * TODO: There needs to be a mechanism to discover allocated buffer size
  * in the HAL.
@@ -1555,25 +1190,9 @@
 }
 
 Size ExternalCameraDeviceSession::getMaxThumbResolution() const {
-    Size thumbSize { 0, 0 };
-    camera_metadata_ro_entry entry =
-        mCameraCharacteristics.find(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES);
-    for(uint32_t i = 0; i < entry.count; i += 2) {
-        Size sz { static_cast<uint32_t>(entry.data.i32[i]),
-                  static_cast<uint32_t>(entry.data.i32[i+1]) };
-        if(sz.width * sz.height > thumbSize.width * thumbSize.height) {
-            thumbSize = sz;
-        }
-    }
-
-    if (thumbSize.width * thumbSize.height == 0) {
-        ALOGW("%s: non-zero thumbnail size not available", __FUNCTION__);
-    }
-
-    return thumbSize;
+    return getMaxThumbnailResolution(mCameraCharacteristics);
 }
 
-
 ssize_t ExternalCameraDeviceSession::getJpegBufferSize(
         uint32_t width, uint32_t height) const {
     // Constant from camera3.h
@@ -1616,7 +1235,7 @@
 
 int ExternalCameraDeviceSession::OutputThread::createJpegLocked(
         HalStreamBuffer &halBuf,
-        const std::shared_ptr<HalRequest>& req)
+        const common::V1_0::helper::CameraMetadata& setting)
 {
     ATRACE_CALL();
     int ret;
@@ -1645,17 +1264,17 @@
     Size thumbSize;
     bool outputThumbnail = true;
 
-    if (req->setting.exists(ANDROID_JPEG_QUALITY)) {
-        camera_metadata_entry entry =
-            req->setting.find(ANDROID_JPEG_QUALITY);
+    if (setting.exists(ANDROID_JPEG_QUALITY)) {
+        camera_metadata_ro_entry entry =
+            setting.find(ANDROID_JPEG_QUALITY);
         jpegQuality = entry.data.u8[0];
     } else {
         return lfail("%s: ANDROID_JPEG_QUALITY not set",__FUNCTION__);
     }
 
-    if (req->setting.exists(ANDROID_JPEG_THUMBNAIL_QUALITY)) {
-        camera_metadata_entry entry =
-            req->setting.find(ANDROID_JPEG_THUMBNAIL_QUALITY);
+    if (setting.exists(ANDROID_JPEG_THUMBNAIL_QUALITY)) {
+        camera_metadata_ro_entry entry =
+            setting.find(ANDROID_JPEG_THUMBNAIL_QUALITY);
         thumbQuality = entry.data.u8[0];
     } else {
         return lfail(
@@ -1663,9 +1282,9 @@
             __FUNCTION__);
     }
 
-    if (req->setting.exists(ANDROID_JPEG_THUMBNAIL_SIZE)) {
-        camera_metadata_entry entry =
-            req->setting.find(ANDROID_JPEG_THUMBNAIL_SIZE);
+    if (setting.exists(ANDROID_JPEG_THUMBNAIL_SIZE)) {
+        camera_metadata_ro_entry entry =
+            setting.find(ANDROID_JPEG_THUMBNAIL_SIZE);
         thumbSize = Size { static_cast<uint32_t>(entry.data.i32[0]),
                            static_cast<uint32_t>(entry.data.i32[1])
         };
@@ -1732,8 +1351,8 @@
 
     /* Combine camera characteristics with request settings to form EXIF
      * metadata */
-    common::V1_0::helper::CameraMetadata meta(parent->mCameraCharacteristics);
-    meta.append(req->setting);
+    common::V1_0::helper::CameraMetadata meta(mCameraCharacteristics);
+    meta.append(setting);
 
     /* Generate EXIF object */
     std::unique_ptr<ExifUtils> utils(ExifUtils::create());
@@ -1838,7 +1457,7 @@
     // TODO: see if we can save some computation by converting to YV12 here
     uint8_t* inData;
     size_t inDataSize;
-    if (req->frameIn->map(&inData, &inDataSize) != 0) {
+    if (req->frameIn->getData(&inData, &inDataSize) != 0) {
         lk.unlock();
         return onDeviceError("%s: V4L2 buffer map failed", __FUNCTION__);
     }
@@ -1899,7 +1518,7 @@
         // Gralloc lockYCbCr the buffer
         switch (halBuf.format) {
             case PixelFormat::BLOB: {
-                int ret = createJpegLocked(halBuf, req);
+                int ret = createJpegLocked(halBuf, req->setting);
 
                 if(ret != 0) {
                     lk.unlock();
@@ -1949,8 +1568,8 @@
                 }
 
                 Size sz {halBuf.width, halBuf.height};
-                ATRACE_BEGIN("formatConvertLocked");
-                ret = formatConvertLocked(cropAndScaled, outLayout, sz, outputFourcc);
+                ATRACE_BEGIN("formatConvert");
+                ret = formatConvert(cropAndScaled, outLayout, sz, outputFourcc);
                 ATRACE_END();
                 if (ret != 0) {
                     lk.unlock();
@@ -2055,6 +1674,14 @@
     return Status::OK;
 }
 
+void ExternalCameraDeviceSession::OutputThread::clearIntermediateBuffers() {
+    std::lock_guard<std::mutex> lk(mBufferLock);
+    mYu12Frame.clear();
+    mYu12ThumbFrame.clear();
+    mIntermediateBuffers.clear();
+    mBlobBufferSize = 0;
+}
+
 Status ExternalCameraDeviceSession::OutputThread::submitRequest(
         const std::shared_ptr<HalRequest>& req) {
     std::unique_lock<std::mutex> lk(mRequestListLock);
@@ -2090,6 +1717,32 @@
     }
 }
 
+std::list<std::shared_ptr<HalRequest>>
+ExternalCameraDeviceSession::OutputThread::switchToOffline() {
+    ATRACE_CALL();
+    std::list<std::shared_ptr<HalRequest>> emptyList;
+    auto parent = mParent.promote();
+    if (parent == nullptr) {
+       ALOGE("%s: session has been disconnected!", __FUNCTION__);
+       return emptyList;
+    }
+
+    std::unique_lock<std::mutex> lk(mRequestListLock);
+    std::list<std::shared_ptr<HalRequest>> reqs = std::move(mRequestList);
+    mRequestList.clear();
+    if (mProcessingRequest) {
+        std::chrono::seconds timeout = std::chrono::seconds(kFlushWaitTimeoutSec);
+        auto st = mRequestDoneCond.wait_for(lk, timeout);
+        if (st == std::cv_status::timeout) {
+            ALOGE("%s: wait for inflight request finish timeout!", __FUNCTION__);
+        }
+    }
+    lk.unlock();
+    clearIntermediateBuffers();
+    ALOGV("%s: returning %zu request for offline processing", __FUNCTION__, reqs.size());
+    return reqs;
+}
+
 void ExternalCameraDeviceSession::OutputThread::waitForNextRequest(
         std::shared_ptr<HalRequest>* out) {
     ATRACE_CALL();
@@ -2733,6 +2386,7 @@
         return Status::INTERNAL_ERROR;
     }
 
+    mBlobBufferSize = blobBufferSize;
     status = mOutputThread->allocateIntermediateBuffers(v4lSize,
                 mMaxThumbResolution, config.streams, blobBufferSize);
     if (status != Status::OK) {
@@ -2916,16 +2570,6 @@
 
 status_t ExternalCameraDeviceSession::fillCaptureResult(
         common::V1_0::helper::CameraMetadata &md, nsecs_t timestamp) {
-    // android.control
-    // For USB camera, we don't know the AE state. Set the state to converged to
-    // indicate the frame should be good to use. Then apps don't have to wait the
-    // AE state.
-    const uint8_t aeState = ANDROID_CONTROL_AE_STATE_CONVERGED;
-    UPDATE(md, ANDROID_CONTROL_AE_STATE, &aeState, 1);
-
-    const uint8_t ae_lock = ANDROID_CONTROL_AE_LOCK_OFF;
-    UPDATE(md, ANDROID_CONTROL_AE_LOCK, &ae_lock, 1);
-
     bool afTrigger = false;
     {
         std::lock_guard<std::mutex> lk(mAfTriggerLock);
@@ -2951,46 +2595,10 @@
     }
     UPDATE(md, ANDROID_CONTROL_AF_STATE, &afState, 1);
 
-    // Set AWB state to converged to indicate the frame should be good to use.
-    const uint8_t awbState = ANDROID_CONTROL_AWB_STATE_CONVERGED;
-    UPDATE(md, ANDROID_CONTROL_AWB_STATE, &awbState, 1);
+    camera_metadata_ro_entry activeArraySize =
+            mCameraCharacteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
 
-    const uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
-    UPDATE(md, ANDROID_CONTROL_AWB_LOCK, &awbLock, 1);
-
-    camera_metadata_ro_entry active_array_size =
-        mCameraCharacteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
-
-    if (active_array_size.count == 0) {
-        ALOGE("%s: cannot find active array size!", __FUNCTION__);
-        return -EINVAL;
-    }
-
-    const uint8_t flashState = ANDROID_FLASH_STATE_UNAVAILABLE;
-    UPDATE(md, ANDROID_FLASH_STATE, &flashState, 1);
-
-    // This means pipeline latency of X frame intervals. The maximum number is 4.
-    const uint8_t requestPipelineMaxDepth = 4;
-    UPDATE(md, ANDROID_REQUEST_PIPELINE_DEPTH, &requestPipelineMaxDepth, 1);
-
-    // android.scaler
-    const int32_t crop_region[] = {
-          active_array_size.data.i32[0], active_array_size.data.i32[1],
-          active_array_size.data.i32[2], active_array_size.data.i32[3],
-    };
-    UPDATE(md, ANDROID_SCALER_CROP_REGION, crop_region, ARRAY_SIZE(crop_region));
-
-    // android.sensor
-    UPDATE(md, ANDROID_SENSOR_TIMESTAMP, &timestamp, 1);
-
-    // android.statistics
-    const uint8_t lensShadingMapMode = ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF;
-    UPDATE(md, ANDROID_STATISTICS_LENS_SHADING_MAP_MODE, &lensShadingMapMode, 1);
-
-    const uint8_t sceneFlicker = ANDROID_STATISTICS_SCENE_FLICKER_NONE;
-    UPDATE(md, ANDROID_STATISTICS_SCENE_FLICKER, &sceneFlicker, 1);
-
-    return OK;
+    return fillCaptureResultCommon(md, timestamp, activeArraySize);
 }
 
 #undef ARRAY_SIZE
diff --git a/camera/device/3.4/default/ExternalCameraUtils.cpp b/camera/device/3.4/default/ExternalCameraUtils.cpp
index e25deff..62a4c87 100644
--- a/camera/device/3.4/default/ExternalCameraUtils.cpp
+++ b/camera/device/3.4/default/ExternalCameraUtils.cpp
@@ -18,10 +18,23 @@
 #include <log/log.h>
 
 #include <cmath>
+#include <cstring>
 #include <sys/mman.h>
 #include <linux/videodev2.h>
+
+#define HAVE_JPEG // required for libyuv.h to export MJPEG decode APIs
+#include <libyuv.h>
+
+#include <jpeglib.h>
+
 #include "ExternalCameraUtils.h"
 
+namespace {
+
+buffer_handle_t sEmptyBuffer = nullptr;
+
+} // Anonymous namespace
+
 namespace android {
 namespace hardware {
 namespace camera {
@@ -29,10 +42,13 @@
 namespace V3_4 {
 namespace implementation {
 
+Frame::Frame(uint32_t width, uint32_t height, uint32_t fourcc) :
+        mWidth(width), mHeight(height), mFourcc(fourcc) {}
+
 V4L2Frame::V4L2Frame(
         uint32_t w, uint32_t h, uint32_t fourcc,
         int bufIdx, int fd, uint32_t dataSize, uint64_t offset) :
-        mWidth(w), mHeight(h), mFourcc(fourcc),
+        Frame(w, h, fourcc),
         mBufferIndex(bufIdx), mFd(fd), mDataSize(dataSize), mOffset(offset) {}
 
 int V4L2Frame::map(uint8_t** data, size_t* dataSize) {
@@ -75,9 +91,13 @@
     unmap();
 }
 
+int V4L2Frame::getData(uint8_t** outData, size_t* dataSize) {
+    return map(outData, dataSize);
+}
+
 AllocatedFrame::AllocatedFrame(
         uint32_t w, uint32_t h) :
-        mWidth(w), mHeight(h), mFourcc(V4L2_PIX_FMT_YUV420) {};
+        Frame(w, h, V4L2_PIX_FMT_YUV420) {};
 
 AllocatedFrame::~AllocatedFrame() {}
 
@@ -106,6 +126,17 @@
     return 0;
 }
 
+int AllocatedFrame::getData(uint8_t** outData, size_t* dataSize) {
+    YCbCrLayout layout;
+    int ret = allocate(&layout);
+    if (ret != 0) {
+        return ret;
+    }
+    *outData = mData.data();
+    *dataSize = mData.size();
+    return 0;
+}
+
 int AllocatedFrame::getLayout(YCbCrLayout* out) {
     IMapper::Rect noCrop = {0, 0,
             static_cast<int32_t>(mWidth),
@@ -150,8 +181,521 @@
     return durationDenominator / static_cast<double>(durationNumerator);
 }
 
+::android::hardware::camera::common::V1_0::Status importBufferImpl(
+        /*inout*/std::map<int, CirculatingBuffers>& circulatingBuffers,
+        /*inout*/HandleImporter& handleImporter,
+        int32_t streamId,
+        uint64_t bufId, buffer_handle_t buf,
+        /*out*/buffer_handle_t** outBufPtr,
+        bool allowEmptyBuf) {
+    using ::android::hardware::camera::common::V1_0::Status;
+    if (buf == nullptr && bufId == BUFFER_ID_NO_BUFFER) {
+        if (allowEmptyBuf) {
+            *outBufPtr = &sEmptyBuffer;
+            return Status::OK;
+        } else {
+            ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId);
+            return Status::ILLEGAL_ARGUMENT;
+        }
+    }
+
+    CirculatingBuffers& cbs = circulatingBuffers[streamId];
+    if (cbs.count(bufId) == 0) {
+        if (buf == nullptr) {
+            ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId);
+            return Status::ILLEGAL_ARGUMENT;
+        }
+        // Register a newly seen buffer
+        buffer_handle_t importedBuf = buf;
+        handleImporter.importBuffer(importedBuf);
+        if (importedBuf == nullptr) {
+            ALOGE("%s: output buffer for stream %d is invalid!", __FUNCTION__, streamId);
+            return Status::INTERNAL_ERROR;
+        } else {
+            cbs[bufId] = importedBuf;
+        }
+    }
+    *outBufPtr = &cbs[bufId];
+    return Status::OK;
+}
+
+uint32_t getFourCcFromLayout(const YCbCrLayout& layout) {
+    intptr_t cb = reinterpret_cast<intptr_t>(layout.cb);
+    intptr_t cr = reinterpret_cast<intptr_t>(layout.cr);
+    if (std::abs(cb - cr) == 1 && layout.chromaStep == 2) {
+        // Interleaved format
+        if (layout.cb > layout.cr) {
+            return V4L2_PIX_FMT_NV21;
+        } else {
+            return V4L2_PIX_FMT_NV12;
+        }
+    } else if (layout.chromaStep == 1) {
+        // Planar format
+        if (layout.cb > layout.cr) {
+            return V4L2_PIX_FMT_YVU420; // YV12
+        } else {
+            return V4L2_PIX_FMT_YUV420; // YU12
+        }
+    } else {
+        return FLEX_YUV_GENERIC;
+    }
+}
+
+int getCropRect(
+        CroppingType ct, const Size& inSize, const Size& outSize, IMapper::Rect* out) {
+    if (out == nullptr) {
+        ALOGE("%s: out is null", __FUNCTION__);
+        return -1;
+    }
+
+    uint32_t inW = inSize.width;
+    uint32_t inH = inSize.height;
+    uint32_t outW = outSize.width;
+    uint32_t outH = outSize.height;
+
+    // Handle special case where aspect ratio is close to input but scaled
+    // dimension is slightly larger than input
+    float arIn = ASPECT_RATIO(inSize);
+    float arOut = ASPECT_RATIO(outSize);
+    if (isAspectRatioClose(arIn, arOut)) {
+        out->left = 0;
+        out->top = 0;
+        out->width = inW;
+        out->height = inH;
+        return 0;
+    }
+
+    if (ct == VERTICAL) {
+        uint64_t scaledOutH = static_cast<uint64_t>(outH) * inW / outW;
+        if (scaledOutH > inH) {
+            ALOGE("%s: Output size %dx%d cannot be vertically cropped from input size %dx%d",
+                    __FUNCTION__, outW, outH, inW, inH);
+            return -1;
+        }
+        scaledOutH = scaledOutH & ~0x1; // make it multiple of 2
+
+        out->left = 0;
+        out->top = ((inH - scaledOutH) / 2) & ~0x1;
+        out->width = inW;
+        out->height = static_cast<int32_t>(scaledOutH);
+        ALOGV("%s: crop %dx%d to %dx%d: top %d, scaledH %d",
+                __FUNCTION__, inW, inH, outW, outH, out->top, static_cast<int32_t>(scaledOutH));
+    } else {
+        uint64_t scaledOutW = static_cast<uint64_t>(outW) * inH / outH;
+        if (scaledOutW > inW) {
+            ALOGE("%s: Output size %dx%d cannot be horizontally cropped from input size %dx%d",
+                    __FUNCTION__, outW, outH, inW, inH);
+            return -1;
+        }
+        scaledOutW = scaledOutW & ~0x1; // make it multiple of 2
+
+        out->left = ((inW - scaledOutW) / 2) & ~0x1;
+        out->top = 0;
+        out->width = static_cast<int32_t>(scaledOutW);
+        out->height = inH;
+        ALOGV("%s: crop %dx%d to %dx%d: top %d, scaledW %d",
+                __FUNCTION__, inW, inH, outW, outH, out->top, static_cast<int32_t>(scaledOutW));
+    }
+
+    return 0;
+}
+
+int formatConvert(
+        const YCbCrLayout& in, const YCbCrLayout& out, Size sz, uint32_t format) {
+    int ret = 0;
+    switch (format) {
+        case V4L2_PIX_FMT_NV21:
+            ret = libyuv::I420ToNV21(
+                    static_cast<uint8_t*>(in.y),
+                    in.yStride,
+                    static_cast<uint8_t*>(in.cb),
+                    in.cStride,
+                    static_cast<uint8_t*>(in.cr),
+                    in.cStride,
+                    static_cast<uint8_t*>(out.y),
+                    out.yStride,
+                    static_cast<uint8_t*>(out.cr),
+                    out.cStride,
+                    sz.width,
+                    sz.height);
+            if (ret != 0) {
+                ALOGE("%s: convert to NV21 buffer failed! ret %d",
+                            __FUNCTION__, ret);
+                return ret;
+            }
+            break;
+        case V4L2_PIX_FMT_NV12:
+            ret = libyuv::I420ToNV12(
+                    static_cast<uint8_t*>(in.y),
+                    in.yStride,
+                    static_cast<uint8_t*>(in.cb),
+                    in.cStride,
+                    static_cast<uint8_t*>(in.cr),
+                    in.cStride,
+                    static_cast<uint8_t*>(out.y),
+                    out.yStride,
+                    static_cast<uint8_t*>(out.cb),
+                    out.cStride,
+                    sz.width,
+                    sz.height);
+            if (ret != 0) {
+                ALOGE("%s: convert to NV12 buffer failed! ret %d",
+                            __FUNCTION__, ret);
+                return ret;
+            }
+            break;
+        case V4L2_PIX_FMT_YVU420: // YV12
+        case V4L2_PIX_FMT_YUV420: // YU12
+            // TODO: maybe we can speed up here by somehow save this copy?
+            ret = libyuv::I420Copy(
+                    static_cast<uint8_t*>(in.y),
+                    in.yStride,
+                    static_cast<uint8_t*>(in.cb),
+                    in.cStride,
+                    static_cast<uint8_t*>(in.cr),
+                    in.cStride,
+                    static_cast<uint8_t*>(out.y),
+                    out.yStride,
+                    static_cast<uint8_t*>(out.cb),
+                    out.cStride,
+                    static_cast<uint8_t*>(out.cr),
+                    out.cStride,
+                    sz.width,
+                    sz.height);
+            if (ret != 0) {
+                ALOGE("%s: copy to YV12 or YU12 buffer failed! ret %d",
+                            __FUNCTION__, ret);
+                return ret;
+            }
+            break;
+        case FLEX_YUV_GENERIC:
+            // TODO: b/72261744 write to arbitrary flexible YUV layout. Slow.
+            ALOGE("%s: unsupported flexible yuv layout"
+                    " y %p cb %p cr %p y_str %d c_str %d c_step %d",
+                    __FUNCTION__, out.y, out.cb, out.cr,
+                    out.yStride, out.cStride, out.chromaStep);
+            return -1;
+        default:
+            ALOGE("%s: unknown YUV format 0x%x!", __FUNCTION__, format);
+            return -1;
+    }
+    return 0;
+}
+
+int encodeJpegYU12(
+        const Size & inSz, const YCbCrLayout& inLayout,
+        int jpegQuality, const void *app1Buffer, size_t app1Size,
+        void *out, const size_t maxOutSize, size_t &actualCodeSize)
+{
+    /* libjpeg is a C library so we use C-style "inheritance" by
+     * putting libjpeg's jpeg_destination_mgr first in our custom
+     * struct. This allows us to cast jpeg_destination_mgr* to
+     * CustomJpegDestMgr* when we get it passed to us in a callback */
+    struct CustomJpegDestMgr {
+        struct jpeg_destination_mgr mgr;
+        JOCTET *mBuffer;
+        size_t mBufferSize;
+        size_t mEncodedSize;
+        bool mSuccess;
+    } dmgr;
+
+    jpeg_compress_struct cinfo = {};
+    jpeg_error_mgr jerr;
+
+    /* Initialize error handling with standard callbacks, but
+     * then override output_message (to print to ALOG) and
+     * error_exit to set a flag and print a message instead
+     * of killing the whole process */
+    cinfo.err = jpeg_std_error(&jerr);
+
+    cinfo.err->output_message = [](j_common_ptr cinfo) {
+        char buffer[JMSG_LENGTH_MAX];
+
+        /* Create the message */
+        (*cinfo->err->format_message)(cinfo, buffer);
+        ALOGE("libjpeg error: %s", buffer);
+    };
+    cinfo.err->error_exit = [](j_common_ptr cinfo) {
+        (*cinfo->err->output_message)(cinfo);
+        if(cinfo->client_data) {
+            auto & dmgr =
+                *reinterpret_cast<CustomJpegDestMgr*>(cinfo->client_data);
+            dmgr.mSuccess = false;
+        }
+    };
+    /* Now that we initialized some callbacks, let's create our compressor */
+    jpeg_create_compress(&cinfo);
+
+    /* Initialize our destination manager */
+    dmgr.mBuffer = static_cast<JOCTET*>(out);
+    dmgr.mBufferSize = maxOutSize;
+    dmgr.mEncodedSize = 0;
+    dmgr.mSuccess = true;
+    cinfo.client_data = static_cast<void*>(&dmgr);
+
+    /* These lambdas become C-style function pointers and as per C++11 spec
+     * may not capture anything */
+    dmgr.mgr.init_destination = [](j_compress_ptr cinfo) {
+        auto & dmgr = reinterpret_cast<CustomJpegDestMgr&>(*cinfo->dest);
+        dmgr.mgr.next_output_byte = dmgr.mBuffer;
+        dmgr.mgr.free_in_buffer = dmgr.mBufferSize;
+        ALOGV("%s:%d jpeg start: %p [%zu]",
+              __FUNCTION__, __LINE__, dmgr.mBuffer, dmgr.mBufferSize);
+    };
+
+    dmgr.mgr.empty_output_buffer = [](j_compress_ptr cinfo __unused) {
+        ALOGV("%s:%d Out of buffer", __FUNCTION__, __LINE__);
+        return 0;
+    };
+
+    dmgr.mgr.term_destination = [](j_compress_ptr cinfo) {
+        auto & dmgr = reinterpret_cast<CustomJpegDestMgr&>(*cinfo->dest);
+        dmgr.mEncodedSize = dmgr.mBufferSize - dmgr.mgr.free_in_buffer;
+        ALOGV("%s:%d Done with jpeg: %zu", __FUNCTION__, __LINE__, dmgr.mEncodedSize);
+    };
+    cinfo.dest = reinterpret_cast<struct jpeg_destination_mgr*>(&dmgr);
+
+    /* We are going to be using JPEG in raw data mode, so we are passing
+     * straight subsampled planar YCbCr and it will not touch our pixel
+     * data or do any scaling or anything */
+    cinfo.image_width = inSz.width;
+    cinfo.image_height = inSz.height;
+    cinfo.input_components = 3;
+    cinfo.in_color_space = JCS_YCbCr;
+
+    /* Initialize defaults and then override what we want */
+    jpeg_set_defaults(&cinfo);
+
+    jpeg_set_quality(&cinfo, jpegQuality, 1);
+    jpeg_set_colorspace(&cinfo, JCS_YCbCr);
+    cinfo.raw_data_in = 1;
+    cinfo.dct_method = JDCT_IFAST;
+
+    /* Configure sampling factors. The sampling factor is JPEG subsampling 420
+     * because the source format is YUV420. Note that libjpeg sampling factors
+     * are... a little weird. Sampling of Y=2,U=1,V=1 means there is 1 U and
+     * 1 V value for each 2 Y values */
+    cinfo.comp_info[0].h_samp_factor = 2;
+    cinfo.comp_info[0].v_samp_factor = 2;
+    cinfo.comp_info[1].h_samp_factor = 1;
+    cinfo.comp_info[1].v_samp_factor = 1;
+    cinfo.comp_info[2].h_samp_factor = 1;
+    cinfo.comp_info[2].v_samp_factor = 1;
+
+    /* Let's not hardcode YUV420 in 6 places... 5 was enough */
+    int maxVSampFactor = std::max( {
+        cinfo.comp_info[0].v_samp_factor,
+        cinfo.comp_info[1].v_samp_factor,
+        cinfo.comp_info[2].v_samp_factor
+    });
+    int cVSubSampling = cinfo.comp_info[0].v_samp_factor /
+                        cinfo.comp_info[1].v_samp_factor;
+
+    /* Start the compressor */
+    jpeg_start_compress(&cinfo, TRUE);
+
+    /* Compute our macroblock height, so we can pad our input to be vertically
+     * macroblock aligned.
+     * TODO: Does it need to be horizontally MCU aligned too? */
+
+    size_t mcuV = DCTSIZE*maxVSampFactor;
+    size_t paddedHeight = mcuV * ((inSz.height + mcuV - 1) / mcuV);
+
+    /* libjpeg uses arrays of row pointers, which makes it really easy to pad
+     * data vertically (unfortunately doesn't help horizontally) */
+    std::vector<JSAMPROW> yLines (paddedHeight);
+    std::vector<JSAMPROW> cbLines(paddedHeight/cVSubSampling);
+    std::vector<JSAMPROW> crLines(paddedHeight/cVSubSampling);
+
+    uint8_t *py = static_cast<uint8_t*>(inLayout.y);
+    uint8_t *pcr = static_cast<uint8_t*>(inLayout.cr);
+    uint8_t *pcb = static_cast<uint8_t*>(inLayout.cb);
+
+    for(uint32_t i = 0; i < paddedHeight; i++)
+    {
+        /* Once we are in the padding territory we still point to the last line
+         * effectively replicating it several times ~ CLAMP_TO_EDGE */
+        int li = std::min(i, inSz.height - 1);
+        yLines[i]  = static_cast<JSAMPROW>(py + li * inLayout.yStride);
+        if(i < paddedHeight / cVSubSampling)
+        {
+            li = std::min(i, (inSz.height - 1) / cVSubSampling);
+            crLines[i] = static_cast<JSAMPROW>(pcr + li * inLayout.cStride);
+            cbLines[i] = static_cast<JSAMPROW>(pcb + li * inLayout.cStride);
+        }
+    }
+
+    /* If APP1 data was passed in, use it */
+    if(app1Buffer && app1Size)
+    {
+        jpeg_write_marker(&cinfo, JPEG_APP0 + 1,
+             static_cast<const JOCTET*>(app1Buffer), app1Size);
+    }
+
+    /* While we still have padded height left to go, keep giving it one
+     * macroblock at a time. */
+    while (cinfo.next_scanline < cinfo.image_height) {
+        const uint32_t batchSize = DCTSIZE * maxVSampFactor;
+        const uint32_t nl = cinfo.next_scanline;
+        JSAMPARRAY planes[3]{ &yLines[nl],
+                              &cbLines[nl/cVSubSampling],
+                              &crLines[nl/cVSubSampling] };
+
+        uint32_t done = jpeg_write_raw_data(&cinfo, planes, batchSize);
+
+        if (done != batchSize) {
+            ALOGE("%s: compressed %u lines, expected %u (total %u/%u)",
+              __FUNCTION__, done, batchSize, cinfo.next_scanline,
+              cinfo.image_height);
+            return -1;
+        }
+    }
+
+    /* This will flush everything */
+    jpeg_finish_compress(&cinfo);
+
+    /* Grab the actual code size and set it */
+    actualCodeSize = dmgr.mEncodedSize;
+
+    return 0;
+}
+
+Size getMaxThumbnailResolution(const common::V1_0::helper::CameraMetadata& chars) {
+    Size thumbSize { 0, 0 };
+    camera_metadata_ro_entry entry =
+        chars.find(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES);
+    for(uint32_t i = 0; i < entry.count; i += 2) {
+        Size sz { static_cast<uint32_t>(entry.data.i32[i]),
+                  static_cast<uint32_t>(entry.data.i32[i+1]) };
+        if(sz.width * sz.height > thumbSize.width * thumbSize.height) {
+            thumbSize = sz;
+        }
+    }
+
+    if (thumbSize.width * thumbSize.height == 0) {
+        ALOGW("%s: non-zero thumbnail size not available", __FUNCTION__);
+    }
+
+    return thumbSize;
+}
+
+void freeReleaseFences(hidl_vec<V3_2::CaptureResult>& results) {
+    for (auto& result : results) {
+        if (result.inputBuffer.releaseFence.getNativeHandle() != nullptr) {
+            native_handle_t* handle = const_cast<native_handle_t*>(
+                    result.inputBuffer.releaseFence.getNativeHandle());
+            native_handle_close(handle);
+            native_handle_delete(handle);
+        }
+        for (auto& buf : result.outputBuffers) {
+            if (buf.releaseFence.getNativeHandle() != nullptr) {
+                native_handle_t* handle = const_cast<native_handle_t*>(
+                        buf.releaseFence.getNativeHandle());
+                native_handle_close(handle);
+                native_handle_delete(handle);
+            }
+        }
+    }
+    return;
+}
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+#define UPDATE(md, tag, data, size)               \
+do {                                              \
+    if ((md).update((tag), (data), (size))) {     \
+        ALOGE("Update " #tag " failed!");         \
+        return BAD_VALUE;                         \
+    }                                             \
+} while (0)
+
+status_t fillCaptureResultCommon(
+        common::V1_0::helper::CameraMetadata &md, nsecs_t timestamp,
+        camera_metadata_ro_entry& activeArraySize) {
+    if (activeArraySize.count < 4) {
+        ALOGE("%s: cannot find active array size!", __FUNCTION__);
+        return -EINVAL;
+    }
+    // android.control
+    // For USB camera, we don't know the AE state. Set the state to converged to
+    // indicate the frame should be good to use. Then apps don't have to wait the
+    // AE state.
+    const uint8_t aeState = ANDROID_CONTROL_AE_STATE_CONVERGED;
+    UPDATE(md, ANDROID_CONTROL_AE_STATE, &aeState, 1);
+
+    const uint8_t ae_lock = ANDROID_CONTROL_AE_LOCK_OFF;
+    UPDATE(md, ANDROID_CONTROL_AE_LOCK, &ae_lock, 1);
+
+    // Set AWB state to converged to indicate the frame should be good to use.
+    const uint8_t awbState = ANDROID_CONTROL_AWB_STATE_CONVERGED;
+    UPDATE(md, ANDROID_CONTROL_AWB_STATE, &awbState, 1);
+
+    const uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
+    UPDATE(md, ANDROID_CONTROL_AWB_LOCK, &awbLock, 1);
+
+    const uint8_t flashState = ANDROID_FLASH_STATE_UNAVAILABLE;
+    UPDATE(md, ANDROID_FLASH_STATE, &flashState, 1);
+
+    // This means pipeline latency of X frame intervals. The maximum number is 4.
+    const uint8_t requestPipelineMaxDepth = 4;
+    UPDATE(md, ANDROID_REQUEST_PIPELINE_DEPTH, &requestPipelineMaxDepth, 1);
+
+    // android.scaler
+    const int32_t crop_region[] = {
+          activeArraySize.data.i32[0], activeArraySize.data.i32[1],
+          activeArraySize.data.i32[2], activeArraySize.data.i32[3],
+    };
+    UPDATE(md, ANDROID_SCALER_CROP_REGION, crop_region, ARRAY_SIZE(crop_region));
+
+    // android.sensor
+    UPDATE(md, ANDROID_SENSOR_TIMESTAMP, &timestamp, 1);
+
+    // android.statistics
+    const uint8_t lensShadingMapMode = ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF;
+    UPDATE(md, ANDROID_STATISTICS_LENS_SHADING_MAP_MODE, &lensShadingMapMode, 1);
+
+    const uint8_t sceneFlicker = ANDROID_STATISTICS_SCENE_FLICKER_NONE;
+    UPDATE(md, ANDROID_STATISTICS_SCENE_FLICKER, &sceneFlicker, 1);
+
+    return OK;
+}
+
+#undef ARRAY_SIZE
+#undef UPDATE
+
 }  // namespace implementation
 }  // namespace V3_4
+
+namespace V3_6 {
+namespace implementation {
+
+AllocatedV4L2Frame::AllocatedV4L2Frame(sp<V3_4::implementation::V4L2Frame> frameIn) :
+        Frame(frameIn->mWidth, frameIn->mHeight, frameIn->mFourcc) {
+    uint8_t* dataIn;
+    size_t dataSize;
+    if (frameIn->getData(&dataIn, &dataSize) != 0) {
+        ALOGE("%s: map input V4L2 frame failed!", __FUNCTION__);
+        return;
+    }
+
+    mData.resize(dataSize);
+    std::memcpy(mData.data(), dataIn, dataSize);
+}
+
+int AllocatedV4L2Frame::getData(uint8_t** outData, size_t* dataSize) {
+    if (outData == nullptr || dataSize == nullptr) {
+        ALOGE("%s: outData(%p)/dataSize(%p) must not be null", __FUNCTION__, outData, dataSize);
+        return -1;
+    }
+
+    *outData = mData.data();
+    *dataSize = mData.size();
+    return 0;
+}
+
+AllocatedV4L2Frame::~AllocatedV4L2Frame() {}
+
+}  // namespace implementation
+}  // namespace V3_6
 }  // namespace device
 
 
diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
index 71b7c17..180f0c1 100644
--- a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
+++ b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICE3SESSION_H
-#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICE3SESSION_H
+#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICESESSION_H
+#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICESESSION_H
 
 #include <android/hardware/camera/device/3.2/ICameraDevice.h>
 #include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
@@ -84,7 +84,8 @@
 using ::android::Mutex;
 using ::android::base::unique_fd;
 
-struct ExternalCameraDeviceSession : public virtual RefBase {
+struct ExternalCameraDeviceSession : public virtual RefBase,
+        public virtual OutputThreadInterface {
 
     ExternalCameraDeviceSession(const sp<ICameraDeviceCallback>&,
             const ExternalCameraConfig& cfg,
@@ -110,6 +111,82 @@
     static const int kMaxStallStream = 1;
     static const uint32_t kMaxBytesPerPixel = 2;
 
+    class OutputThread : public android::Thread {
+    public:
+        OutputThread(wp<OutputThreadInterface> parent, CroppingType,
+                const common::V1_0::helper::CameraMetadata&);
+        virtual ~OutputThread();
+
+        Status allocateIntermediateBuffers(
+                const Size& v4lSize, const Size& thumbSize,
+                const hidl_vec<Stream>& streams,
+                uint32_t blobBufferSize);
+        Status submitRequest(const std::shared_ptr<HalRequest>&);
+        void flush();
+        void dump(int fd);
+        virtual bool threadLoop() override;
+
+        void setExifMakeModel(const std::string& make, const std::string& model);
+
+        // The remaining request list is returned for offline processing
+        std::list<std::shared_ptr<HalRequest>> switchToOffline();
+
+    protected:
+        // Methods to request output buffer in parallel
+        // No-op for device@3.4. Implemented in device@3.5
+        virtual int requestBufferStart(const std::vector<HalStreamBuffer>&) { return 0; }
+        virtual int waitForBufferRequestDone(
+                /*out*/std::vector<HalStreamBuffer>*) { return 0; }
+
+        static const int kFlushWaitTimeoutSec = 3; // 3 sec
+        static const int kReqWaitTimeoutMs = 33;   // 33ms
+        static const int kReqWaitTimesMax = 90;    // 33ms * 90 ~= 3 sec
+
+        void waitForNextRequest(std::shared_ptr<HalRequest>* out);
+        void signalRequestDone();
+
+        int cropAndScaleLocked(
+                sp<AllocatedFrame>& in, const Size& outSize,
+                YCbCrLayout* out);
+
+        int cropAndScaleThumbLocked(
+                sp<AllocatedFrame>& in, const Size& outSize,
+                YCbCrLayout* out);
+
+        int createJpegLocked(HalStreamBuffer &halBuf,
+                const common::V1_0::helper::CameraMetadata& settings);
+
+        void clearIntermediateBuffers();
+
+        const wp<OutputThreadInterface> mParent;
+        const CroppingType mCroppingType;
+        const common::V1_0::helper::CameraMetadata mCameraCharacteristics;
+
+        mutable std::mutex mRequestListLock;      // Protect acccess to mRequestList,
+                                                  // mProcessingRequest and mProcessingFrameNumer
+        std::condition_variable mRequestCond;     // signaled when a new request is submitted
+        std::condition_variable mRequestDoneCond; // signaled when a request is done processing
+        std::list<std::shared_ptr<HalRequest>> mRequestList;
+        bool mProcessingRequest = false;
+        uint32_t mProcessingFrameNumer = 0;
+
+        // V4L2 frameIn
+        // (MJPG decode)-> mYu12Frame
+        // (Scale)-> mScaledYu12Frames
+        // (Format convert) -> output gralloc frames
+        mutable std::mutex mBufferLock; // Protect access to intermediate buffers
+        sp<AllocatedFrame> mYu12Frame;
+        sp<AllocatedFrame> mYu12ThumbFrame;
+        std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mIntermediateBuffers;
+        std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mScaledYu12Frames;
+        YCbCrLayout mYu12FrameLayout;
+        YCbCrLayout mYu12ThumbFrameLayout;
+        uint32_t mBlobBufferSize = 0; // 0 -> HAL derive buffer size, else: use given size
+
+        std::string mExifMake;
+        std::string mExifModel;
+    };
+
 protected:
 
     // Methods from ::android::hardware::camera::device::V3_2::ICameraDeviceSession follow
@@ -150,27 +227,22 @@
             ICameraDeviceSession::processCaptureRequest_3_4_cb _hidl_cb);
 
 protected:
-    struct HalStreamBuffer {
-        int32_t streamId;
-        uint64_t bufferId;
-        uint32_t width;
-        uint32_t height;
-        PixelFormat format;
-        V3_2::BufferUsageFlags usage;
-        buffer_handle_t* bufPtr;
-        int acquireFence;
-        bool fenceTimeout;
-    };
+    // Methods from OutputThreadInterface
+    virtual Status importBuffer(int32_t streamId,
+            uint64_t bufId, buffer_handle_t buf,
+            /*out*/buffer_handle_t** outBufPtr,
+            bool allowEmptyBuf) override;
 
-    struct HalRequest {
-        uint32_t frameNumber;
-        common::V1_0::helper::CameraMetadata setting;
-        sp<V4L2Frame> frameIn;
-        nsecs_t shutterTs;
-        std::vector<HalStreamBuffer> buffers;
-    };
+    virtual Status processCaptureResult(std::shared_ptr<HalRequest>&) override;
 
-    static const uint64_t BUFFER_ID_NO_BUFFER = 0;
+    virtual Status processCaptureRequestError(const std::shared_ptr<HalRequest>&,
+        /*out*/std::vector<NotifyMsg>* msgs = nullptr,
+        /*out*/std::vector<CaptureResult>* results = nullptr) override;
+
+    virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const override;
+
+    virtual void notifyError(uint32_t frameNumber, int32_t streamId, ErrorCode ec) override;
+    // End of OutputThreadInterface methods
 
     Status constructDefaultRequestSettingsRaw(RequestTemplate type,
             V3_2::CameraMetadata *outMetadata);
@@ -219,11 +291,6 @@
             // Optional argument for ICameraDeviceSession@3.5 impl
             bool allowEmptyBuf = false);
 
-    Status importBuffer(int32_t streamId,
-            uint64_t bufId, buffer_handle_t buf,
-            /*out*/buffer_handle_t** outBufPtr,
-            bool allowEmptyBuf);
-
     Status importBufferLocked(int32_t streamId,
             uint64_t bufId, buffer_handle_t buf,
             /*out*/buffer_handle_t** outBufPtr,
@@ -236,106 +303,15 @@
 
     Status processOneCaptureRequest(const CaptureRequest& request);
 
-    Status processCaptureResult(std::shared_ptr<HalRequest>&);
-    Status processCaptureRequestError(const std::shared_ptr<HalRequest>&);
     void notifyShutter(uint32_t frameNumber, nsecs_t shutterTs);
-    void notifyError(uint32_t frameNumber, int32_t streamId, ErrorCode ec);
     void invokeProcessCaptureResultCallback(
             hidl_vec<CaptureResult> &results, bool tryWriteFmq);
-    static void freeReleaseFences(hidl_vec<CaptureResult>&);
 
     Size getMaxJpegResolution() const;
     Size getMaxThumbResolution() const;
 
-    ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const;
-
     int waitForV4L2BufferReturnLocked(std::unique_lock<std::mutex>& lk);
 
-    class OutputThread : public android::Thread {
-    public:
-        OutputThread(wp<ExternalCameraDeviceSession> parent, CroppingType);
-        virtual ~OutputThread();
-
-        Status allocateIntermediateBuffers(
-                const Size& v4lSize, const Size& thumbSize,
-                const hidl_vec<Stream>& streams,
-                uint32_t blobBufferSize);
-        Status submitRequest(const std::shared_ptr<HalRequest>&);
-        void flush();
-        void dump(int fd);
-        virtual bool threadLoop() override;
-
-        void setExifMakeModel(const std::string& make, const std::string& model);
-
-    protected:
-        // Methods to request output buffer in parallel
-        // No-op for device@3.4. Implemented in device@3.5
-        virtual int requestBufferStart(const std::vector<HalStreamBuffer>&) { return 0; }
-        virtual int waitForBufferRequestDone(
-                /*out*/std::vector<HalStreamBuffer>*) { return 0; }
-
-        static const uint32_t FLEX_YUV_GENERIC = static_cast<uint32_t>('F') |
-                static_cast<uint32_t>('L') << 8 | static_cast<uint32_t>('E') << 16 |
-                static_cast<uint32_t>('X') << 24;
-        // returns FLEX_YUV_GENERIC for formats other than YV12/YU12/NV12/NV21
-        static uint32_t getFourCcFromLayout(const YCbCrLayout&);
-        static int getCropRect(
-                CroppingType ct, const Size& inSize, const Size& outSize, IMapper::Rect* out);
-
-        static const int kFlushWaitTimeoutSec = 3; // 3 sec
-        static const int kReqWaitTimeoutMs = 33;   // 33ms
-        static const int kReqWaitTimesMax = 90;    // 33ms * 90 ~= 3 sec
-
-        void waitForNextRequest(std::shared_ptr<HalRequest>* out);
-        void signalRequestDone();
-
-        int cropAndScaleLocked(
-                sp<AllocatedFrame>& in, const Size& outSize,
-                YCbCrLayout* out);
-
-        int cropAndScaleThumbLocked(
-                sp<AllocatedFrame>& in, const Size& outSize,
-                YCbCrLayout* out);
-
-        int formatConvertLocked(const YCbCrLayout& in, const YCbCrLayout& out,
-                Size sz, uint32_t format);
-
-        static int encodeJpegYU12(const Size &inSz,
-                const YCbCrLayout& inLayout, int jpegQuality,
-                const void *app1Buffer, size_t app1Size,
-                void *out, size_t maxOutSize,
-                size_t &actualCodeSize);
-
-        int createJpegLocked(HalStreamBuffer &halBuf, const std::shared_ptr<HalRequest>& req);
-
-        const wp<ExternalCameraDeviceSession> mParent;
-        const CroppingType mCroppingType;
-
-        mutable std::mutex mRequestListLock;      // Protect acccess to mRequestList,
-                                                  // mProcessingRequest and mProcessingFrameNumer
-        std::condition_variable mRequestCond;     // signaled when a new request is submitted
-        std::condition_variable mRequestDoneCond; // signaled when a request is done processing
-        std::list<std::shared_ptr<HalRequest>> mRequestList;
-        bool mProcessingRequest = false;
-        uint32_t mProcessingFrameNumer = 0;
-
-        // V4L2 frameIn
-        // (MJPG decode)-> mYu12Frame
-        // (Scale)-> mScaledYu12Frames
-        // (Format convert) -> output gralloc frames
-        mutable std::mutex mBufferLock; // Protect access to intermediate buffers
-        sp<AllocatedFrame> mYu12Frame;
-        sp<AllocatedFrame> mYu12ThumbFrame;
-        std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mIntermediateBuffers;
-        std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mScaledYu12Frames;
-        YCbCrLayout mYu12FrameLayout;
-        YCbCrLayout mYu12ThumbFrameLayout;
-        uint32_t mBlobBufferSize = 0; // 0 -> HAL derive buffer size, else: use given size
-
-        std::string mExifMake;
-        std::string mExifModel;
-    };
-
     // Protect (most of) HIDL interface methods from synchronized-entering
     mutable Mutex mInterfaceLock;
 
@@ -345,7 +321,7 @@
     const common::V1_0::helper::CameraMetadata mCameraCharacteristics;
     const std::vector<SupportedV4L2Format> mSupportedFormats;
     const CroppingType mCroppingType;
-    const std::string& mCameraId;
+    const std::string mCameraId;
 
     // Not protected by mLock, this is almost a const.
     // Setup in constructor, reset in close() after OutputThread is joined
@@ -381,12 +357,6 @@
     std::mutex mInflightFramesLock; // protect mInflightFrames
     std::unordered_set<uint32_t>  mInflightFrames;
 
-    // buffers currently circulating between HAL and camera service
-    // key: bufferId sent via HIDL interface
-    // value: imported buffer_handle_t
-    // Buffer will be imported during processCaptureRequest and will be freed
-    // when the its stream is deleted or camera device session is closed
-    typedef std::unordered_map<uint64_t, buffer_handle_t> CirculatingBuffers;
     // Stream ID -> circulating buffers map
     std::map<int, CirculatingBuffers> mCirculatingBuffers;
     // Protect mCirculatingBuffers, must not lock mLock after acquiring this lock
@@ -395,6 +365,8 @@
     std::mutex mAfTriggerLock; // protect mAfTrigger
     bool mAfTrigger = false;
 
+    uint32_t mBlobBufferSize = 0;
+
     static HandleImporter sHandleImporter;
 
     /* Beginning of members not changed after initialize() */
@@ -410,6 +382,9 @@
 
     const Size mMaxThumbResolution;
     const Size mMaxJpegResolution;
+
+    std::string mExifMake;
+    std::string mExifModel;
     /* End of members not changed after initialize() */
 
 private:
@@ -484,4 +459,4 @@
 }  // namespace hardware
 }  // namespace android
 
-#endif  // ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICE3SESSION_H
+#endif  // ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICESESSION_H
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 341c622..74f75eb 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
@@ -17,16 +17,27 @@
 #ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMUTIL_H
 #define ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMUTIL_H
 
+#include <android/hardware/camera/common/1.0/types.h>
+#include <android/hardware/camera/device/3.2/types.h>
+#include <android/hardware/graphics/common/1.0/types.h>
 #include <android/hardware/graphics/mapper/2.0/IMapper.h>
 #include <inttypes.h>
 #include <mutex>
+#include <unordered_map>
 #include <unordered_set>
 #include <vector>
 #include "tinyxml2.h"  // XML parsing
 #include "utils/LightRefBase.h"
+#include "utils/Timers.h"
+#include <CameraMetadata.h>
+#include <HandleImporter.h>
 
-using android::hardware::graphics::mapper::V2_0::IMapper;
-using android::hardware::graphics::mapper::V2_0::YCbCrLayout;
+
+using ::android::hardware::graphics::mapper::V2_0::IMapper;
+using ::android::hardware::graphics::mapper::V2_0::YCbCrLayout;
+using ::android::hardware::camera::common::V1_0::helper::HandleImporter;
+using ::android::hardware::camera::common::V1_0::Status;
+using ::android::hardware::camera::device::V3_2::ErrorCode;
 
 namespace android {
 namespace hardware {
@@ -113,16 +124,28 @@
     std::vector<FrameRate> frameRates;
 };
 
+// A Base class with basic information about a frame
+struct Frame : public VirtualLightRefBase {
+public:
+    Frame(uint32_t width, uint32_t height, uint32_t fourcc);
+    const uint32_t mWidth;
+    const uint32_t mHeight;
+    const uint32_t mFourcc;
+
+    // getData might involve map/allocation
+    virtual int getData(uint8_t** outData, size_t* dataSize) = 0;
+};
+
 // A class provide access to a dequeued V4L2 frame buffer (mostly in MJPG format)
 // Also contains necessary information to enqueue the buffer back to V4L2 buffer queue
-class V4L2Frame : public virtual VirtualLightRefBase {
+class V4L2Frame : public Frame {
 public:
     V4L2Frame(uint32_t w, uint32_t h, uint32_t fourcc, int bufIdx, int fd,
               uint32_t dataSize, uint64_t offset);
     ~V4L2Frame() override;
-    const uint32_t mWidth;
-    const uint32_t mHeight;
-    const uint32_t mFourcc;
+
+    virtual int getData(uint8_t** outData, size_t* dataSize) override;
+
     const int mBufferIndex; // for later enqueue
     int map(uint8_t** data, size_t* dataSize);
     int unmap();
@@ -137,13 +160,13 @@
 
 // A RAII class representing a CPU allocated YUV frame used as intermeidate buffers
 // when generating output images.
-class AllocatedFrame : public virtual VirtualLightRefBase {
+class AllocatedFrame : public Frame {
 public:
-    AllocatedFrame(uint32_t w, uint32_t h); // TODO: use Size?
+    AllocatedFrame(uint32_t w, uint32_t h); // only support V4L2_PIX_FMT_YUV420 for now
     ~AllocatedFrame() override;
-    const uint32_t mWidth;
-    const uint32_t mHeight;
-    const uint32_t mFourcc; // Only support YU12 format for now
+
+    virtual int getData(uint8_t** outData, size_t* dataSize) override;
+
     int allocate(YCbCrLayout* out = nullptr);
     int getLayout(YCbCrLayout* out);
     int getCroppedLayout(const IMapper::Rect&, YCbCrLayout* out); // return non-zero for bad input
@@ -165,8 +188,110 @@
 
 bool isAspectRatioClose(float ar1, float ar2);
 
+struct HalStreamBuffer {
+    int32_t streamId;
+    uint64_t bufferId;
+    uint32_t width;
+    uint32_t height;
+    ::android::hardware::graphics::common::V1_0::PixelFormat format;
+    ::android::hardware::camera::device::V3_2::BufferUsageFlags usage;
+    buffer_handle_t* bufPtr;
+    int acquireFence;
+    bool fenceTimeout;
+};
+
+struct HalRequest {
+    uint32_t frameNumber;
+    common::V1_0::helper::CameraMetadata setting;
+    sp<Frame> frameIn;
+    nsecs_t shutterTs;
+    std::vector<HalStreamBuffer> buffers;
+};
+
+static const uint64_t BUFFER_ID_NO_BUFFER = 0;
+
+// buffers currently circulating between HAL and camera service
+// key: bufferId sent via HIDL interface
+// value: imported buffer_handle_t
+// Buffer will be imported during processCaptureRequest (or requestStreamBuffer
+// in the case of HAL buffer manager is enabled) and will be freed
+// when the stream is deleted or camera device session is closed
+typedef std::unordered_map<uint64_t, buffer_handle_t> CirculatingBuffers;
+
+::android::hardware::camera::common::V1_0::Status importBufferImpl(
+        /*inout*/std::map<int, CirculatingBuffers>& circulatingBuffers,
+        /*inout*/HandleImporter& handleImporter,
+        int32_t streamId,
+        uint64_t bufId, buffer_handle_t buf,
+        /*out*/buffer_handle_t** outBufPtr,
+        bool allowEmptyBuf);
+
+static const uint32_t FLEX_YUV_GENERIC = static_cast<uint32_t>('F') |
+        static_cast<uint32_t>('L') << 8 | static_cast<uint32_t>('E') << 16 |
+        static_cast<uint32_t>('X') << 24;
+
+// returns FLEX_YUV_GENERIC for formats other than YV12/YU12/NV12/NV21
+uint32_t getFourCcFromLayout(const YCbCrLayout&);
+
+using ::android::hardware::camera::external::common::Size;
+int getCropRect(CroppingType ct, const Size& inSize,
+        const Size& outSize, IMapper::Rect* out);
+
+int formatConvert(const YCbCrLayout& in, const YCbCrLayout& out, Size sz, uint32_t format);
+
+int encodeJpegYU12(const Size &inSz,
+        const YCbCrLayout& inLayout, int jpegQuality,
+        const void *app1Buffer, size_t app1Size,
+        void *out, size_t maxOutSize,
+        size_t &actualCodeSize);
+
+Size getMaxThumbnailResolution(const common::V1_0::helper::CameraMetadata&);
+
+void freeReleaseFences(hidl_vec<V3_2::CaptureResult>&);
+
+status_t fillCaptureResultCommon(common::V1_0::helper::CameraMetadata& md, nsecs_t timestamp,
+        camera_metadata_ro_entry& activeArraySize);
+
+// Interface for OutputThread calling back to parent
+struct OutputThreadInterface : public virtual RefBase {
+    virtual ::android::hardware::camera::common::V1_0::Status importBuffer(
+            int32_t streamId, uint64_t bufId, buffer_handle_t buf,
+            /*out*/buffer_handle_t** outBufPtr, bool allowEmptyBuf) = 0;
+
+    virtual void notifyError(uint32_t frameNumber, int32_t streamId, ErrorCode ec) = 0;
+
+    // Callbacks are fired within the method if msgs/results are nullptr.
+    // Otherwise the callbacks will be returned and caller is responsible to
+    // fire the callback later
+    virtual ::android::hardware::camera::common::V1_0::Status processCaptureRequestError(
+            const std::shared_ptr<HalRequest>&,
+            /*out*/std::vector<V3_2::NotifyMsg>* msgs = nullptr,
+            /*out*/std::vector<V3_2::CaptureResult>* results = nullptr) = 0;
+
+    virtual ::android::hardware::camera::common::V1_0::Status processCaptureResult(
+            std::shared_ptr<HalRequest>&) = 0;
+
+    virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const = 0;
+};
+
 }  // namespace implementation
 }  // namespace V3_4
+
+namespace V3_6 {
+namespace implementation {
+
+// A CPU copy of a mapped V4L2Frame. Will map the input V4L2 frame.
+class AllocatedV4L2Frame : public V3_4::implementation::Frame {
+public:
+    AllocatedV4L2Frame(sp<V3_4::implementation::V4L2Frame> frameIn);
+    ~AllocatedV4L2Frame() override;
+    virtual int getData(uint8_t** outData, size_t* dataSize) override;
+private:
+    std::vector<uint8_t> mData;
+};
+
+} // namespace implementation
+} // namespace V3_6
 }  // namespace device
 }  // namespace camera
 }  // namespace hardware
diff --git a/camera/device/3.5/default/ExternalCameraDeviceSession.cpp b/camera/device/3.5/default/ExternalCameraDeviceSession.cpp
index 00c1d0d..287ac32 100644
--- a/camera/device/3.5/default/ExternalCameraDeviceSession.cpp
+++ b/camera/device/3.5/default/ExternalCameraDeviceSession.cpp
@@ -80,7 +80,7 @@
 
 
 ExternalCameraDeviceSession::BufferRequestThread::BufferRequestThread(
-        wp<ExternalCameraDeviceSession> parent,
+        wp<OutputThreadInterface> parent,
         sp<V3_5::ICameraDeviceCallback> callbacks) :
         mParent(parent),
         mCallbacks(callbacks) {}
@@ -254,7 +254,8 @@
         mBufferRequestThread = new BufferRequestThread(this, mCallback_3_5);
         mBufferRequestThread->run("ExtCamBufReq", PRIORITY_DISPLAY);
     }
-    mOutputThread = new OutputThread(this, mCroppingType, mBufferRequestThread);
+    mOutputThread = new OutputThread(
+            this, mCroppingType, mCameraCharacteristics, mBufferRequestThread);
 }
 
 void ExternalCameraDeviceSession::closeOutputThreadImpl() {
@@ -271,10 +272,11 @@
 }
 
 ExternalCameraDeviceSession::OutputThread::OutputThread(
-        wp<ExternalCameraDeviceSession> parent,
+        wp<OutputThreadInterface> parent,
         CroppingType ct,
+        const common::V1_0::helper::CameraMetadata& chars,
         sp<BufferRequestThread> bufReqThread) :
-        V3_4::implementation::ExternalCameraDeviceSession::OutputThread(parent, ct),
+        V3_4::implementation::ExternalCameraDeviceSession::OutputThread(parent, ct, chars),
         mBufferRequestThread(bufReqThread) {}
 
 ExternalCameraDeviceSession::OutputThread::~OutputThread() {}
diff --git a/camera/device/3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h b/camera/device/3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h
index 281f93a..e89ef45 100644
--- a/camera/device/3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h
+++ b/camera/device/3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_5_EXTCAMERADEVICE3SESSION_H
-#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_5_EXTCAMERADEVICE3SESSION_H
+#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_5_EXTCAMERADEVICESESSION_H
+#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_5_EXTCAMERADEVICESESSION_H
 
 #include <android/hardware/camera/device/3.5/ICameraDeviceCallback.h>
 #include <android/hardware/camera/device/3.5/ICameraDeviceSession.h>
@@ -72,6 +72,7 @@
 
 using ::android::hardware::camera::device::V3_4::implementation::SupportedV4L2Format;
 using ::android::hardware::camera::device::V3_4::implementation::CroppingType;
+using ::android::hardware::camera::device::V3_4::implementation::HalStreamBuffer;
 
 struct ExternalCameraDeviceSession : public V3_4::implementation::ExternalCameraDeviceSession {
 
@@ -97,6 +98,62 @@
                 config, supportedFormats, devCfg);
     }
 
+    class BufferRequestThread : public android::Thread {
+    public:
+        BufferRequestThread(
+                wp<OutputThreadInterface> parent,
+                sp<V3_5::ICameraDeviceCallback> callbacks);
+
+        int requestBufferStart(const std::vector<HalStreamBuffer>&);
+        int waitForBufferRequestDone(
+                /*out*/std::vector<HalStreamBuffer>*);
+
+        virtual bool threadLoop() override;
+
+    private:
+        void waitForNextRequest();
+
+        const wp<OutputThreadInterface> mParent;
+        const sp<V3_5::ICameraDeviceCallback> mCallbacks;
+
+        std::mutex mLock;
+        bool mRequestingBuffer = false;
+
+        std::vector<HalStreamBuffer> mBufferReqs;
+        std::vector<HalStreamBuffer> mPendingReturnBufferReqs;
+        // mHalBufferReqs is not under mLock protection during the HIDL transaction
+        hidl_vec<BufferRequest>      mHalBufferReqs;
+
+        // request buffers takes much less time in steady state, but can take much longer
+        // when requesting 1st buffer from a stream.
+        // TODO: consider a separate timeout for new vs. steady state?
+        // TODO: or make sure framework is warming up the pipeline during configure new stream?
+        static const int kReqProcTimeoutMs = 66;
+
+        static const int kReqWaitTimeoutMs = 33;
+        static const int kReqWaitTimesWarn = 90;  // 33ms * 90 ~= 3 sec
+        std::condition_variable mRequestCond;     // signaled when a new buffer request incoming
+        std::condition_variable mRequestDoneCond; // signaled when a request is done
+    };
+
+    class OutputThread :
+            public V3_4::implementation::ExternalCameraDeviceSession::OutputThread {
+    public:
+        // TODO: pass buffer request thread to OutputThread ctor
+        OutputThread(wp<OutputThreadInterface> parent, CroppingType,
+                const common::V1_0::helper::CameraMetadata&,
+                sp<BufferRequestThread> bufReqThread);
+        virtual ~OutputThread();
+
+    protected:
+        // Methods to request output buffer in parallel
+        virtual int requestBufferStart(const std::vector<HalStreamBuffer>&) override;
+        virtual int waitForBufferRequestDone(
+                /*out*/std::vector<HalStreamBuffer>*) override;
+
+        const sp<BufferRequestThread> mBufferRequestThread;
+    };
+
 protected:
     // Methods from v3.4 and earlier will trampoline to inherited implementation
     Return<void> configureStreams_3_5(
@@ -120,63 +177,8 @@
             hidl_vec<buffer_handle_t*>& allBufPtrs,
             hidl_vec<int>& allFences) override;
 
-    class BufferRequestThread : public android::Thread {
-    public:
-        BufferRequestThread(
-                wp<ExternalCameraDeviceSession> parent,
-                sp<V3_5::ICameraDeviceCallback> callbacks);
-
-        int requestBufferStart(const std::vector<HalStreamBuffer>&);
-        int waitForBufferRequestDone(
-                /*out*/std::vector<HalStreamBuffer>*);
-
-        virtual bool threadLoop() override;
-
-    private:
-        void waitForNextRequest();
-
-        const wp<ExternalCameraDeviceSession> mParent;
-        const sp<V3_5::ICameraDeviceCallback> mCallbacks;
-
-        std::mutex mLock;
-        bool mRequestingBuffer = false;
-
-        std::vector<HalStreamBuffer> mBufferReqs;
-        std::vector<HalStreamBuffer> mPendingReturnBufferReqs;
-        // mHalBufferReqs is not under mLock protection during the HIDL transaction
-        hidl_vec<BufferRequest>      mHalBufferReqs;
-
-        // request buffers takes much less time in steady state, but can take much longer
-        // when requesting 1st buffer from a stream.
-        // TODO: consider a separate timeout for new vs. steady state?
-        // TODO: or make sure framework is warming up the pipeline during configure new stream?
-        static const int kReqProcTimeoutMs = 66;
-
-        static const int kReqWaitTimeoutMs = 33;
-        static const int kReqWaitTimesWarn = 90;  // 33ms * 90 ~= 3 sec
-        std::condition_variable mRequestCond;     // signaled when a new buffer request incoming
-        std::condition_variable mRequestDoneCond; // signaled when a request is done
-    };
-
     sp<BufferRequestThread> mBufferRequestThread;
 
-    class OutputThread :
-            public V3_4::implementation::ExternalCameraDeviceSession::OutputThread {
-    public:
-        // TODO: pass buffer request thread to OutputThread ctor
-        OutputThread(wp<ExternalCameraDeviceSession> parent, CroppingType,
-                sp<BufferRequestThread> bufReqThread);
-        virtual ~OutputThread();
-
-    protected:
-        // Methods to request output buffer in parallel
-        virtual int requestBufferStart(const std::vector<HalStreamBuffer>&) override;
-        virtual int waitForBufferRequestDone(
-                /*out*/std::vector<HalStreamBuffer>*) override;
-
-        const sp<BufferRequestThread> mBufferRequestThread;
-    };
-
     sp<V3_5::ICameraDeviceCallback> mCallback_3_5;
     bool mSupportBufMgr;
 
@@ -270,4 +272,4 @@
 }  // namespace hardware
 }  // namespace android
 
-#endif  // ANDROID_HARDWARE_CAMERA_DEVICE_V3_5_EXTCAMERADEVICE3SESSION_H
+#endif  // ANDROID_HARDWARE_CAMERA_DEVICE_V3_5_EXTCAMERADEVICESESSION_H
diff --git a/camera/device/3.6/default/Android.bp b/camera/device/3.6/default/Android.bp
index ce51185..a2ddebd 100644
--- a/camera/device/3.6/default/Android.bp
+++ b/camera/device/3.6/default/Android.bp
@@ -28,6 +28,7 @@
     srcs: [
         "ExternalCameraDevice.cpp",
         "ExternalCameraDeviceSession.cpp",
+        "ExternalCameraOfflineSession.cpp",
     ],
     shared_libs: [
         "libhidlbase",
diff --git a/camera/device/3.6/default/ExternalCameraDeviceSession.cpp b/camera/device/3.6/default/ExternalCameraDeviceSession.cpp
index e14ae99..60a1a10 100644
--- a/camera/device/3.6/default/ExternalCameraDeviceSession.cpp
+++ b/camera/device/3.6/default/ExternalCameraDeviceSession.cpp
@@ -73,12 +73,8 @@
 
     Status status = configureStreams(config_v32, &outStreams_v33, blobBufferSize);
 
-    outStreams.streams.resize(outStreams_v33.streams.size());
-    for (size_t i = 0; i < outStreams.streams.size(); i++) {
-        outStreams.streams[i].v3_4.v3_3 = outStreams_v33.streams[i];
-        // TODO: implement it later
-        outStreams.streams[i].supportOffline = false;
-    }
+    fillOutputStream3_6(outStreams_v33, &outStreams);
+
     _hidl_cb(status, outStreams);
     return Void();
 }
@@ -86,12 +82,275 @@
 Return<void> ExternalCameraDeviceSession::switchToOffline(
         const hidl_vec<int32_t>& streamsToKeep,
         ICameraDeviceSession::switchToOffline_cb _hidl_cb) {
-    // TODO: implement this
-    (void) streamsToKeep;
-    (void) _hidl_cb;
+    std::vector<NotifyMsg> msgs;
+    std::vector<CaptureResult> results;
+    CameraOfflineSessionInfo info;
+    sp<ICameraOfflineSession> session;
+
+    Status st = switchToOffline(streamsToKeep, &msgs, &results, &info, &session);
+
+    mCallback->notify(msgs);
+    hidl_vec<CaptureResult> hidlResults(std::move(results));
+    invokeProcessCaptureResultCallback(hidlResults, /* tryWriteFmq */true);
+    V3_4::implementation::freeReleaseFences(hidlResults);
+
+    _hidl_cb(st, info, session);
     return Void();
 }
 
+void ExternalCameraDeviceSession::fillOutputStream3_6(
+        const V3_3::HalStreamConfiguration& outStreams_v33,
+        /*out*/V3_6::HalStreamConfiguration* outStreams_v36) {
+    if (outStreams_v36 == nullptr) {
+        ALOGE("%s: outStreams_v36 must not be null!", __FUNCTION__);
+        return;
+    }
+    Mutex::Autolock _l(mLock);
+    outStreams_v36->streams.resize(outStreams_v33.streams.size());
+    for (size_t i = 0; i < outStreams_v36->streams.size(); i++) {
+        outStreams_v36->streams[i].v3_4.v3_3 = outStreams_v33.streams[i];
+        outStreams_v36->streams[i].supportOffline =
+                supportOfflineLocked(outStreams_v33.streams[i].v3_2.id);
+    }
+}
+
+bool ExternalCameraDeviceSession::supportOfflineLocked(int32_t streamId) {
+    const Stream& stream = mStreamMap[streamId];
+    if (stream.format == PixelFormat::BLOB &&
+            stream.dataSpace == static_cast<int32_t>(Dataspace::V0_JFIF)) {
+        return true;
+    }
+    // TODO: support YUV output stream?
+    return false;
+}
+
+bool ExternalCameraDeviceSession::canDropRequest(const hidl_vec<int32_t>& offlineStreams,
+        std::shared_ptr<V3_4::implementation::HalRequest> halReq) {
+    for (const auto& buffer : halReq->buffers) {
+        for (auto offlineStreamId : offlineStreams) {
+            if (buffer.streamId == offlineStreamId) {
+                return false;
+            }
+        }
+    }
+    // Only drop a request completely if it has no offline output
+    return true;
+}
+
+void ExternalCameraDeviceSession::fillOfflineSessionInfo(const hidl_vec<int32_t>& offlineStreams,
+        std::deque<std::shared_ptr<HalRequest>>& offlineReqs,
+        const std::map<int, CirculatingBuffers>& circulatingBuffers,
+        /*out*/CameraOfflineSessionInfo* info) {
+    if (info == nullptr) {
+        ALOGE("%s: output info must not be null!", __FUNCTION__);
+        return;
+    }
+
+    info->offlineStreams.resize(offlineStreams.size());
+    info->offlineRequests.resize(offlineReqs.size());
+
+    // Fill in offline reqs and count outstanding buffers
+    for (size_t i = 0; i < offlineReqs.size(); i++) {
+        info->offlineRequests[i].frameNumber = offlineReqs[i]->frameNumber;
+        info->offlineRequests[i].pendingStreams.resize(offlineReqs[i]->buffers.size());
+        for (size_t bIdx = 0; bIdx < offlineReqs[i]->buffers.size(); bIdx++) {
+            int32_t streamId = offlineReqs[i]->buffers[bIdx].streamId;
+            info->offlineRequests[i].pendingStreams[bIdx] = streamId;
+        }
+    }
+
+    for (size_t i = 0; i < offlineStreams.size(); i++) {
+        int32_t streamId = offlineStreams[i];
+        info->offlineStreams[i].id = streamId;
+        // outstanding buffers are 0 since we are doing hal buffer management and
+        // offline session will ask for those buffers later
+        info->offlineStreams[i].numOutstandingBuffers = 0;
+        const CirculatingBuffers& bufIdMap = circulatingBuffers.at(streamId);
+        info->offlineStreams[i].circulatingBufferIds.resize(bufIdMap.size());
+        size_t bIdx = 0;
+        for (const auto& pair : bufIdMap) {
+            // Fill in bufferId
+            info->offlineStreams[i].circulatingBufferIds[bIdx++] = pair.first;
+        }
+
+    }
+}
+
+Status ExternalCameraDeviceSession::switchToOffline(const hidl_vec<int32_t>& offlineStreams,
+        /*out*/std::vector<NotifyMsg>* msgs,
+        /*out*/std::vector<CaptureResult>* results,
+        /*out*/CameraOfflineSessionInfo* info,
+        /*out*/sp<ICameraOfflineSession>* session) {
+    ATRACE_CALL();
+    if (offlineStreams.size() > 1) {
+        ALOGE("%s: more than one offline stream is not supported", __FUNCTION__);
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    if (msgs == nullptr || results == nullptr || info == nullptr || session == nullptr) {
+        ALOGE("%s: output arguments (%p, %p, %p, %p) must not be null", __FUNCTION__,
+                msgs, results, info, session);
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    msgs->clear();
+    results->clear();
+
+    Mutex::Autolock _il(mInterfaceLock);
+    Status status = initStatus();
+    if (status != Status::OK) {
+        return status;
+    }
+
+    Mutex::Autolock _l(mLock);
+    for (auto streamId : offlineStreams) {
+        if (!supportOfflineLocked(streamId)) {
+            return Status::ILLEGAL_ARGUMENT;
+        }
+    }
+
+    // pause output thread and get all remaining inflight requests
+    auto remainingReqs = mOutputThread->switchToOffline();
+    std::vector<std::shared_ptr<V3_4::implementation::HalRequest>> halReqs;
+
+    // Send out buffer/request error for remaining requests and filter requests
+    // to be handled in offline mode
+    for (auto& halReq : remainingReqs) {
+        bool dropReq = canDropRequest(offlineStreams, halReq);
+        if (dropReq) {
+            // Request is dropped completely. Just send request error and
+            // there is no need to send the request to offline session
+            processCaptureRequestError(halReq, msgs, results);
+            continue;
+        }
+
+        // All requests reach here must have at least one offline stream output
+        NotifyMsg shutter;
+        shutter.type = MsgType::SHUTTER;
+        shutter.msg.shutter.frameNumber = halReq->frameNumber;
+        shutter.msg.shutter.timestamp = halReq->shutterTs;
+        msgs->push_back(shutter);
+
+        std::vector<V3_4::implementation::HalStreamBuffer> offlineBuffers;
+        for (const auto& buffer : halReq->buffers) {
+            bool dropBuffer = true;
+            for (auto offlineStreamId : offlineStreams) {
+                if (buffer.streamId == offlineStreamId) {
+                    dropBuffer = false;
+                    break;
+                }
+            }
+            if (dropBuffer) {
+                NotifyMsg error;
+                error.type = MsgType::ERROR;
+                error.msg.error.frameNumber = halReq->frameNumber;
+                error.msg.error.errorStreamId = buffer.streamId;
+                error.msg.error.errorCode = ErrorCode::ERROR_BUFFER;
+                msgs->push_back(error);
+
+                CaptureResult result;
+                result.frameNumber = halReq->frameNumber;
+                result.partialResult = 0; // buffer only result
+                result.inputBuffer.streamId = -1;
+                result.outputBuffers.resize(1);
+                result.outputBuffers[0].streamId = buffer.streamId;
+                result.outputBuffers[0].bufferId = buffer.bufferId;
+                result.outputBuffers[0].status = BufferStatus::ERROR;
+                if (buffer.acquireFence >= 0) {
+                    native_handle_t* handle = native_handle_create(/*numFds*/1, /*numInts*/0);
+                    handle->data[0] = buffer.acquireFence;
+                    result.outputBuffers[0].releaseFence.setTo(handle, /*shouldOwn*/false);
+                }
+                results->push_back(result);
+            } else {
+                offlineBuffers.push_back(buffer);
+            }
+        }
+        halReq->buffers = offlineBuffers;
+        halReqs.push_back(halReq);
+    }
+
+    // convert hal requests to offline request
+    std::deque<std::shared_ptr<HalRequest>> offlineReqs(halReqs.size());
+    for (auto& v4lReq : halReqs) {
+        std::shared_ptr<HalRequest> halReq = std::make_shared<HalRequest>();
+        halReq->frameNumber = v4lReq->frameNumber;
+        halReq->setting = v4lReq->setting;
+        halReq->shutterTs = v4lReq->shutterTs;
+        halReq->buffers = v4lReq->buffers;
+        sp<V3_4::implementation::V4L2Frame> v4l2Frame =
+                static_cast<V3_4::implementation::V4L2Frame*>(v4lReq->frameIn.get());
+        halReq->frameIn = new AllocatedV4L2Frame(v4l2Frame);
+        offlineReqs.push_back(halReq);
+        // enqueue V4L2 frame
+        enqueueV4l2Frame(v4l2Frame);
+    }
+
+    // Collect buffer caches/streams
+    hidl_vec<Stream> streamInfos;
+    streamInfos.resize(offlineStreams.size());
+    std::map<int, CirculatingBuffers> circulatingBuffers;
+    {
+        Mutex::Autolock _l(mCbsLock);
+        size_t idx = 0;
+        for(auto streamId : offlineStreams) {
+            circulatingBuffers[streamId] = mCirculatingBuffers.at(streamId);
+            mCirculatingBuffers.erase(streamId);
+            streamInfos[idx++] = mStreamMap.at(streamId);
+            mStreamMap.erase(streamId);
+        }
+    }
+
+    fillOfflineSessionInfo(offlineStreams, offlineReqs, circulatingBuffers, info);
+
+    // create the offline session object
+    bool afTrigger;
+    {
+        std::lock_guard<std::mutex> lk(mAfTriggerLock);
+        afTrigger = mAfTrigger;
+    }
+    sp<ExternalCameraOfflineSession> sessionImpl = new ExternalCameraOfflineSession(
+            mCroppingType, mCameraCharacteristics, mCameraId,
+            mExifMake, mExifModel, mBlobBufferSize, afTrigger,
+            streamInfos, offlineReqs, circulatingBuffers);
+
+    bool initFailed = sessionImpl->initialize();
+    if (initFailed) {
+        ALOGE("%s: offline session initialize failed!", __FUNCTION__);
+        return Status::INTERNAL_ERROR;
+    }
+
+    // cleanup stream and buffer caches
+    {
+        Mutex::Autolock _l(mCbsLock);
+        for(auto pair : mStreamMap) {
+            cleanupBuffersLocked(/*Stream ID*/pair.first);
+        }
+        mCirculatingBuffers.clear();
+    }
+    mStreamMap.clear();
+
+    // update inflight records
+    {
+        std::lock_guard<std::mutex> lk(mInflightFramesLock);
+        mInflightFrames.clear();
+    }
+
+    // stop v4l2 streaming
+    if (v4l2StreamOffLocked() !=0) {
+        ALOGE("%s: stop V4L2 streaming failed!", __FUNCTION__);
+        return Status::INTERNAL_ERROR;
+    }
+
+    // No need to return session if there is no offline requests left
+    if (offlineReqs.size() != 0) {
+        *session = sessionImpl->getInterface();
+    } else {
+        *session = nullptr;
+    }
+    return Status::OK;
+}
+
 } // namespace implementation
 }  // namespace V3_6
 }  // namespace device
diff --git a/camera/device/3.6/default/ExternalCameraOfflineSession.cpp b/camera/device/3.6/default/ExternalCameraOfflineSession.cpp
new file mode 100644
index 0000000..e606fda
--- /dev/null
+++ b/camera/device/3.6/default/ExternalCameraOfflineSession.cpp
@@ -0,0 +1,554 @@
+/*
+ * Copyright (C) 2020 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 "ExtCamOfflnSsn@3.6"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+#include <android/log.h>
+
+#include <linux/videodev2.h>
+#include <sync/sync.h>
+
+#define HAVE_JPEG // required for libyuv.h to export MJPEG decode APIs
+#include <libyuv.h>
+
+#include <utils/Trace.h>
+#include "ExternalCameraOfflineSession.h"
+
+namespace {
+
+// Size of request/result metadata fast message queue. Change to 0 to always use hwbinder buffer.
+static constexpr size_t kMetadataMsgQueueSize = 1 << 18 /* 256kB */;
+
+} // anonymous namespace
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace V3_6 {
+namespace implementation {
+
+// static instance
+HandleImporter ExternalCameraOfflineSession::sHandleImporter;
+
+using V3_5::implementation::ExternalCameraDeviceSession;
+
+ExternalCameraOfflineSession::ExternalCameraOfflineSession(
+        const CroppingType& croppingType,
+        const common::V1_0::helper::CameraMetadata& chars,
+        const std::string& cameraId,
+        const std::string& exifMake,
+        const std::string& exifModel,
+        const uint32_t blobBufferSize,
+        const bool afTrigger,
+        const hidl_vec<Stream>& offlineStreams,
+        std::deque<std::shared_ptr<HalRequest>>& offlineReqs,
+        const std::map<int, CirculatingBuffers>& circulatingBuffers) :
+        mCroppingType(croppingType), mChars(chars), mCameraId(cameraId),
+        mExifMake(exifMake), mExifModel(exifModel), mBlobBufferSize(blobBufferSize),
+        mAfTrigger(afTrigger), mOfflineStreams(offlineStreams), mOfflineReqs(offlineReqs),
+        mCirculatingBuffers(circulatingBuffers) {}
+
+ExternalCameraOfflineSession::~ExternalCameraOfflineSession() {
+    close();
+}
+
+bool ExternalCameraOfflineSession::initialize() {
+    mResultMetadataQueue = std::make_shared<ResultMetadataQueue>(
+            kMetadataMsgQueueSize, false /* non blocking */);
+    if (!mResultMetadataQueue->isValid()) {
+        ALOGE("%s: invalid result fmq", __FUNCTION__);
+        return true;
+    }
+    return false;
+}
+
+void ExternalCameraOfflineSession::initOutputThread() {
+    if (mOutputThread != nullptr) {
+        ALOGE("%s: OutputThread already exist!", __FUNCTION__);
+        return;
+    }
+
+    mBufferRequestThread = new ExternalCameraDeviceSession::BufferRequestThread(
+            this, mCallback);
+    mBufferRequestThread->run("ExtCamBufReq", PRIORITY_DISPLAY);
+
+    mOutputThread = new OutputThread(this, mCroppingType, mChars,
+            mBufferRequestThread, mOfflineReqs);
+
+    mOutputThread->setExifMakeModel(mExifMake, mExifModel);
+
+    Size inputSize = { mOfflineReqs[0]->frameIn->mWidth, mOfflineReqs[0]->frameIn->mHeight};
+    Size maxThumbSize = V3_4::implementation::getMaxThumbnailResolution(mChars);
+    mOutputThread->allocateIntermediateBuffers(
+            inputSize, maxThumbSize, mOfflineStreams, mBlobBufferSize);
+
+    mOutputThread->run("ExtCamOfflnOut", PRIORITY_DISPLAY);
+}
+
+bool ExternalCameraOfflineSession::OutputThread::threadLoop() {
+    auto parent = mParent.promote();
+    if (parent == nullptr) {
+       ALOGE("%s: session has been disconnected!", __FUNCTION__);
+       return false;
+    }
+
+    if (mOfflineReqs.empty()) {
+        ALOGI("%s: all offline requests are processed. Stopping.", __FUNCTION__);
+        return false;
+    }
+
+    std::shared_ptr<HalRequest> req = mOfflineReqs.front();
+    mOfflineReqs.pop_front();
+
+    auto onDeviceError = [&](auto... args) {
+        ALOGE(args...);
+        parent->notifyError(
+                req->frameNumber, /*stream*/-1, ErrorCode::ERROR_DEVICE);
+        signalRequestDone();
+        return false;
+    };
+
+    if (req->frameIn->mFourcc != V4L2_PIX_FMT_MJPEG && req->frameIn->mFourcc != V4L2_PIX_FMT_Z16) {
+        return onDeviceError("%s: do not support V4L2 format %c%c%c%c", __FUNCTION__,
+                req->frameIn->mFourcc & 0xFF,
+                (req->frameIn->mFourcc >> 8) & 0xFF,
+                (req->frameIn->mFourcc >> 16) & 0xFF,
+                (req->frameIn->mFourcc >> 24) & 0xFF);
+    }
+
+    int res = requestBufferStart(req->buffers);
+    if (res != 0) {
+        ALOGE("%s: send BufferRequest failed! res %d", __FUNCTION__, res);
+        return onDeviceError("%s: failed to send buffer request!", __FUNCTION__);
+    }
+
+    std::unique_lock<std::mutex> lk(mBufferLock);
+    // Convert input V4L2 frame to YU12 of the same size
+    // TODO: see if we can save some computation by converting to YV12 here
+    uint8_t* inData;
+    size_t inDataSize;
+    if (req->frameIn->getData(&inData, &inDataSize) != 0) {
+        lk.unlock();
+        return onDeviceError("%s: V4L2 buffer map failed", __FUNCTION__);
+    }
+
+    // TODO: in some special case maybe we can decode jpg directly to gralloc output?
+    if (req->frameIn->mFourcc == V4L2_PIX_FMT_MJPEG) {
+        ATRACE_BEGIN("MJPGtoI420");
+        int res = libyuv::MJPGToI420(
+            inData, inDataSize, static_cast<uint8_t*>(mYu12FrameLayout.y), mYu12FrameLayout.yStride,
+            static_cast<uint8_t*>(mYu12FrameLayout.cb), mYu12FrameLayout.cStride,
+            static_cast<uint8_t*>(mYu12FrameLayout.cr), mYu12FrameLayout.cStride,
+            mYu12Frame->mWidth, mYu12Frame->mHeight, mYu12Frame->mWidth, mYu12Frame->mHeight);
+        ATRACE_END();
+
+        if (res != 0) {
+            // For some webcam, the first few V4L2 frames might be malformed...
+            ALOGE("%s: Convert V4L2 frame to YU12 failed! res %d", __FUNCTION__, res);
+            lk.unlock();
+            Status st = parent->processCaptureRequestError(req);
+            if (st != Status::OK) {
+                return onDeviceError("%s: failed to process capture request error!", __FUNCTION__);
+            }
+            signalRequestDone();
+            return true;
+        }
+    }
+
+    ATRACE_BEGIN("Wait for BufferRequest done");
+    res = waitForBufferRequestDone(&req->buffers);
+    ATRACE_END();
+
+    if (res != 0) {
+        ALOGE("%s: wait for BufferRequest done failed! res %d", __FUNCTION__, res);
+        lk.unlock();
+        return onDeviceError("%s: failed to process buffer request error!", __FUNCTION__);
+    }
+
+    ALOGV("%s processing new request", __FUNCTION__);
+    const int kSyncWaitTimeoutMs = 500;
+    for (auto& halBuf : req->buffers) {
+        if (*(halBuf.bufPtr) == nullptr) {
+            ALOGW("%s: buffer for stream %d missing", __FUNCTION__, halBuf.streamId);
+            halBuf.fenceTimeout = true;
+        } else if (halBuf.acquireFence >= 0) {
+            int ret = sync_wait(halBuf.acquireFence, kSyncWaitTimeoutMs);
+            if (ret) {
+                halBuf.fenceTimeout = true;
+            } else {
+                ::close(halBuf.acquireFence);
+                halBuf.acquireFence = -1;
+            }
+        }
+
+        if (halBuf.fenceTimeout) {
+            continue;
+        }
+
+        // Gralloc lockYCbCr the buffer
+        switch (halBuf.format) {
+            case PixelFormat::BLOB: {
+                int ret = createJpegLocked(halBuf, req->setting);
+
+                if(ret != 0) {
+                    lk.unlock();
+                    return onDeviceError("%s: createJpegLocked failed with %d",
+                          __FUNCTION__, ret);
+                }
+            } break;
+            case PixelFormat::Y16: {
+                void* outLayout = sHandleImporter.lock(*(halBuf.bufPtr), halBuf.usage, inDataSize);
+
+                std::memcpy(outLayout, inData, inDataSize);
+
+                int relFence = sHandleImporter.unlock(*(halBuf.bufPtr));
+                if (relFence >= 0) {
+                    halBuf.acquireFence = relFence;
+                }
+            } break;
+            case PixelFormat::YCBCR_420_888:
+            case PixelFormat::YV12: {
+                IMapper::Rect outRect {0, 0,
+                        static_cast<int32_t>(halBuf.width),
+                        static_cast<int32_t>(halBuf.height)};
+                YCbCrLayout outLayout = sHandleImporter.lockYCbCr(
+                        *(halBuf.bufPtr), halBuf.usage, outRect);
+                ALOGV("%s: outLayout y %p cb %p cr %p y_str %d c_str %d c_step %d",
+                        __FUNCTION__, outLayout.y, outLayout.cb, outLayout.cr,
+                        outLayout.yStride, outLayout.cStride, outLayout.chromaStep);
+
+                // Convert to output buffer size/format
+                uint32_t outputFourcc = V3_4::implementation::getFourCcFromLayout(outLayout);
+                ALOGV("%s: converting to format %c%c%c%c", __FUNCTION__,
+                        outputFourcc & 0xFF,
+                        (outputFourcc >> 8) & 0xFF,
+                        (outputFourcc >> 16) & 0xFF,
+                        (outputFourcc >> 24) & 0xFF);
+
+                YCbCrLayout cropAndScaled;
+                ATRACE_BEGIN("cropAndScaleLocked");
+                int ret = cropAndScaleLocked(
+                        mYu12Frame,
+                        Size { halBuf.width, halBuf.height },
+                        &cropAndScaled);
+                ATRACE_END();
+                if (ret != 0) {
+                    lk.unlock();
+                    return onDeviceError("%s: crop and scale failed!", __FUNCTION__);
+                }
+
+                Size sz {halBuf.width, halBuf.height};
+                ATRACE_BEGIN("formatConvert");
+                ret = V3_4::implementation::formatConvert(cropAndScaled, outLayout, sz, outputFourcc);
+                ATRACE_END();
+                if (ret != 0) {
+                    lk.unlock();
+                    return onDeviceError("%s: format coversion failed!", __FUNCTION__);
+                }
+                int relFence = sHandleImporter.unlock(*(halBuf.bufPtr));
+                if (relFence >= 0) {
+                    halBuf.acquireFence = relFence;
+                }
+            } break;
+            default:
+                lk.unlock();
+                return onDeviceError("%s: unknown output format %x", __FUNCTION__, halBuf.format);
+        }
+    } // for each buffer
+    mScaledYu12Frames.clear();
+
+    // Don't hold the lock while calling back to parent
+    lk.unlock();
+    Status st = parent->processCaptureResult(req);
+    if (st != Status::OK) {
+        return onDeviceError("%s: failed to process capture result!", __FUNCTION__);
+    }
+    signalRequestDone();
+    return true;
+}
+
+Status ExternalCameraOfflineSession::importBuffer(int32_t streamId,
+        uint64_t bufId, buffer_handle_t buf,
+        /*out*/buffer_handle_t** outBufPtr,
+        bool allowEmptyBuf) {
+    Mutex::Autolock _l(mCbsLock);
+    return V3_4::implementation::importBufferImpl(
+            mCirculatingBuffers, sHandleImporter, streamId,
+            bufId, buf, outBufPtr, allowEmptyBuf);
+    return Status::OK;
+};
+
+#define UPDATE(md, tag, data, size)               \
+do {                                              \
+    if ((md).update((tag), (data), (size))) {     \
+        ALOGE("Update " #tag " failed!");         \
+        return BAD_VALUE;                         \
+    }                                             \
+} while (0)
+
+status_t ExternalCameraOfflineSession::fillCaptureResult(
+        common::V1_0::helper::CameraMetadata &md, nsecs_t timestamp) {
+    bool afTrigger = false;
+    {
+        std::lock_guard<std::mutex> lk(mAfTriggerLock);
+        afTrigger = mAfTrigger;
+        if (md.exists(ANDROID_CONTROL_AF_TRIGGER)) {
+            camera_metadata_entry entry = md.find(ANDROID_CONTROL_AF_TRIGGER);
+            if (entry.data.u8[0] == ANDROID_CONTROL_AF_TRIGGER_START) {
+                mAfTrigger = afTrigger = true;
+            } else if (entry.data.u8[0] == ANDROID_CONTROL_AF_TRIGGER_CANCEL) {
+                mAfTrigger = afTrigger = false;
+            }
+        }
+    }
+
+    // For USB camera, the USB camera handles everything and we don't have control
+    // over AF. We only simply fake the AF metadata based on the request
+    // received here.
+    uint8_t afState;
+    if (afTrigger) {
+        afState = ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED;
+    } else {
+        afState = ANDROID_CONTROL_AF_STATE_INACTIVE;
+    }
+    UPDATE(md, ANDROID_CONTROL_AF_STATE, &afState, 1);
+
+    camera_metadata_ro_entry activeArraySize =
+            mChars.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+
+    return V3_4::implementation::fillCaptureResultCommon(md, timestamp, activeArraySize);
+}
+
+#undef UPDATE
+
+Status ExternalCameraOfflineSession::processCaptureResult(std::shared_ptr<HalRequest>& req) {
+    ATRACE_CALL();
+    // Fill output buffers
+    hidl_vec<CaptureResult> results;
+    results.resize(1);
+    CaptureResult& result = results[0];
+    result.frameNumber = req->frameNumber;
+    result.partialResult = 1;
+    result.inputBuffer.streamId = -1;
+    result.outputBuffers.resize(req->buffers.size());
+    for (size_t i = 0; i < req->buffers.size(); i++) {
+        result.outputBuffers[i].streamId = req->buffers[i].streamId;
+        result.outputBuffers[i].bufferId = req->buffers[i].bufferId;
+        if (req->buffers[i].fenceTimeout) {
+            result.outputBuffers[i].status = BufferStatus::ERROR;
+            if (req->buffers[i].acquireFence >= 0) {
+                native_handle_t* handle = native_handle_create(/*numFds*/1, /*numInts*/0);
+                handle->data[0] = req->buffers[i].acquireFence;
+                result.outputBuffers[i].releaseFence.setTo(handle, /*shouldOwn*/false);
+            }
+            notifyError(req->frameNumber, req->buffers[i].streamId, ErrorCode::ERROR_BUFFER);
+        } else {
+            result.outputBuffers[i].status = BufferStatus::OK;
+            // TODO: refactor
+            if (req->buffers[i].acquireFence >= 0) {
+                native_handle_t* handle = native_handle_create(/*numFds*/1, /*numInts*/0);
+                handle->data[0] = req->buffers[i].acquireFence;
+                result.outputBuffers[i].releaseFence.setTo(handle, /*shouldOwn*/false);
+            }
+        }
+    }
+
+    // Fill capture result metadata
+    fillCaptureResult(req->setting, req->shutterTs);
+    const camera_metadata_t *rawResult = req->setting.getAndLock();
+    V3_2::implementation::convertToHidl(rawResult, &result.result);
+    req->setting.unlock(rawResult);
+
+    // Callback into framework
+    invokeProcessCaptureResultCallback(results, /* tryWriteFmq */true);
+    V3_4::implementation::freeReleaseFences(results);
+    return Status::OK;
+};
+
+void ExternalCameraOfflineSession::invokeProcessCaptureResultCallback(
+        hidl_vec<CaptureResult> &results, bool tryWriteFmq) {
+    if (mProcessCaptureResultLock.tryLock() != OK) {
+        const nsecs_t NS_TO_SECOND = 1000000000;
+        ALOGV("%s: previous call is not finished! waiting 1s...", __FUNCTION__);
+        if (mProcessCaptureResultLock.timedLock(/* 1s */NS_TO_SECOND) != OK) {
+            ALOGE("%s: cannot acquire lock in 1s, cannot proceed",
+                    __FUNCTION__);
+            return;
+        }
+    }
+    if (tryWriteFmq && mResultMetadataQueue->availableToWrite() > 0) {
+        for (CaptureResult &result : results) {
+            if (result.result.size() > 0) {
+                if (mResultMetadataQueue->write(result.result.data(), result.result.size())) {
+                    result.fmqResultSize = result.result.size();
+                    result.result.resize(0);
+                } else {
+                    ALOGW("%s: couldn't utilize fmq, fall back to hwbinder", __FUNCTION__);
+                    result.fmqResultSize = 0;
+                }
+            } else {
+                result.fmqResultSize = 0;
+            }
+        }
+    }
+    auto status = mCallback->processCaptureResult(results);
+    if (!status.isOk()) {
+        ALOGE("%s: processCaptureResult ERROR : %s", __FUNCTION__,
+              status.description().c_str());
+    }
+
+    mProcessCaptureResultLock.unlock();
+}
+
+Status ExternalCameraOfflineSession::processCaptureRequestError(
+        const std::shared_ptr<HalRequest>& req,
+        /*out*/std::vector<NotifyMsg>* outMsgs,
+        /*out*/std::vector<CaptureResult>* outResults) {
+    ATRACE_CALL();
+
+    if (outMsgs == nullptr) {
+        notifyError(/*frameNum*/req->frameNumber, /*stream*/-1, ErrorCode::ERROR_REQUEST);
+    } else {
+        NotifyMsg shutter;
+        shutter.type = MsgType::SHUTTER;
+        shutter.msg.shutter.frameNumber = req->frameNumber;
+        shutter.msg.shutter.timestamp = req->shutterTs;
+
+        NotifyMsg error;
+        error.type = MsgType::ERROR;
+        error.msg.error.frameNumber = req->frameNumber;
+        error.msg.error.errorStreamId = -1;
+        error.msg.error.errorCode = ErrorCode::ERROR_REQUEST;
+        outMsgs->push_back(shutter);
+        outMsgs->push_back(error);
+    }
+
+    // Fill output buffers
+    hidl_vec<CaptureResult> results;
+    results.resize(1);
+    CaptureResult& result = results[0];
+    result.frameNumber = req->frameNumber;
+    result.partialResult = 1;
+    result.inputBuffer.streamId = -1;
+    result.outputBuffers.resize(req->buffers.size());
+    for (size_t i = 0; i < req->buffers.size(); i++) {
+        result.outputBuffers[i].streamId = req->buffers[i].streamId;
+        result.outputBuffers[i].bufferId = req->buffers[i].bufferId;
+        result.outputBuffers[i].status = BufferStatus::ERROR;
+        if (req->buffers[i].acquireFence >= 0) {
+            native_handle_t* handle = native_handle_create(/*numFds*/1, /*numInts*/0);
+            handle->data[0] = req->buffers[i].acquireFence;
+            result.outputBuffers[i].releaseFence.setTo(handle, /*shouldOwn*/false);
+        }
+    }
+
+    if (outResults == nullptr) {
+        // Callback into framework
+        invokeProcessCaptureResultCallback(results, /* tryWriteFmq */true);
+        V3_4::implementation::freeReleaseFences(results);
+    } else {
+        outResults->push_back(result);
+    }
+    return Status::OK;
+};
+
+ssize_t ExternalCameraOfflineSession::getJpegBufferSize(
+        uint32_t /*width*/, uint32_t /*height*/) const {
+    // Empty implementation here as the jpeg buffer size is passed in by ctor
+    return 0;
+};
+
+void ExternalCameraOfflineSession::notifyError(uint32_t frameNumber, int32_t streamId, ErrorCode ec) {
+    NotifyMsg msg;
+    msg.type = MsgType::ERROR;
+    msg.msg.error.frameNumber = frameNumber;
+    msg.msg.error.errorStreamId = streamId;
+    msg.msg.error.errorCode = ec;
+    mCallback->notify({msg});
+};
+
+Return<void> ExternalCameraOfflineSession::setCallback(const sp<ICameraDeviceCallback>& cb) {
+    Mutex::Autolock _il(mInterfaceLock);
+    if (mCallback != nullptr && cb != nullptr) {
+        ALOGE("%s: callback must not be set twice!", __FUNCTION__);
+        return Void();
+    }
+    mCallback = cb;
+
+    initOutputThread();
+
+    if (mOutputThread == nullptr) {
+        ALOGE("%s: init OutputThread failed!", __FUNCTION__);
+    }
+    return Void();
+}
+
+Return<void> ExternalCameraOfflineSession::getCaptureResultMetadataQueue(
+        V3_3::ICameraDeviceSession::getCaptureResultMetadataQueue_cb _hidl_cb) {
+    Mutex::Autolock _il(mInterfaceLock);
+    _hidl_cb(*mResultMetadataQueue->getDesc());
+    return Void();
+}
+
+void ExternalCameraOfflineSession::cleanupBuffersLocked(int id) {
+    for (auto& pair : mCirculatingBuffers.at(id)) {
+        sHandleImporter.freeBuffer(pair.second);
+    }
+    mCirculatingBuffers[id].clear();
+    mCirculatingBuffers.erase(id);
+}
+
+Return<void> ExternalCameraOfflineSession::close() {
+    Mutex::Autolock _il(mInterfaceLock);
+    {
+        Mutex::Autolock _l(mLock);
+        if (mClosed) {
+            ALOGW("%s: offline session already closed!", __FUNCTION__);
+            return Void();
+        }
+    }
+    if (mBufferRequestThread) {
+        mBufferRequestThread->requestExit();
+        mBufferRequestThread->join();
+        mBufferRequestThread.clear();
+    }
+    if (mOutputThread) {
+        mOutputThread->flush();
+        mOutputThread->requestExit();
+        mOutputThread->join();
+        mOutputThread.clear();
+    }
+
+    Mutex::Autolock _l(mLock);
+    // free all buffers
+    {
+        Mutex::Autolock _cbl(mCbsLock);
+        for(auto stream : mOfflineStreams) {
+            cleanupBuffersLocked(stream.id);
+        }
+    }
+    mCallback.clear();
+    mClosed = true;
+    return Void();
+}
+
+} // namespace implementation
+}  // namespace V3_6
+}  // namespace device
+}  // namespace camera
+}  // namespace hardware
+}  // namespace android
diff --git a/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraDeviceSession.h b/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraDeviceSession.h
index 0e57c4c..db0d9a5 100644
--- a/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraDeviceSession.h
+++ b/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraDeviceSession.h
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_6_EXTCAMERADEVICE3SESSION_H
-#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_6_EXTCAMERADEVICE3SESSION_H
+#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_6_EXTCAMERADEVICESESSION_H
+#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_6_EXTCAMERADEVICESESSION_H
 
 #include <android/hardware/camera/device/3.5/ICameraDeviceCallback.h>
 #include <android/hardware/camera/device/3.6/ICameraDeviceSession.h>
 #include <../../3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h>
+#include "ExternalCameraOfflineSession.h"
 
 namespace android {
 namespace hardware {
@@ -37,6 +38,7 @@
 using ::android::hardware::camera::device::V3_2::Stream;
 using ::android::hardware::camera::device::V3_5::StreamConfiguration;
 using ::android::hardware::camera::device::V3_6::ICameraDeviceSession;
+using ::android::hardware::camera::device::V3_6::ICameraOfflineSession;
 using ::android::hardware::camera::common::V1_0::Status;
 using ::android::hardware::camera::external::common::ExternalCameraConfig;
 using ::android::hardware::graphics::common::V1_0::PixelFormat;
@@ -69,13 +71,6 @@
         return new TrampolineSessionInterface_3_6(this);
     }
 
-    static Status isStreamCombinationSupported(const V3_2::StreamConfiguration& config,
-            const std::vector<SupportedV4L2Format>& supportedFormats,
-            const ExternalCameraConfig& devCfg) {
-        return V3_4::implementation::ExternalCameraDeviceSession::isStreamCombinationSupported(
-                config, supportedFormats, devCfg);
-    }
-
 protected:
     // Methods from v3.5 and earlier will trampoline to inherited implementation
     Return<void> configureStreams_3_6(
@@ -86,6 +81,28 @@
             const hidl_vec<int32_t>& streamsToKeep,
             ICameraDeviceSession::switchToOffline_cb _hidl_cb);
 
+    void fillOutputStream3_6(const V3_3::HalStreamConfiguration& outStreams_v33,
+            /*out*/V3_6::HalStreamConfiguration* outStreams_v36);
+    bool supportOfflineLocked(int32_t streamId);
+
+    // Main body of switchToOffline. This method does not invoke any callbacks
+    // but instead returns the necessary callbacks in output arguments so callers
+    // can callback later without holding any locks
+    Status switchToOffline(const hidl_vec<int32_t>& offlineStreams,
+            /*out*/std::vector<NotifyMsg>* msgs,
+            /*out*/std::vector<CaptureResult>* results,
+            /*out*/CameraOfflineSessionInfo* info,
+            /*out*/sp<ICameraOfflineSession>* session);
+
+    // Whether a request can be completely dropped when switching to offline
+    bool canDropRequest(const hidl_vec<int32_t>& offlineStreams,
+            std::shared_ptr<V3_4::implementation::HalRequest> halReq);
+
+    void fillOfflineSessionInfo(const hidl_vec<int32_t>& offlineStreams,
+            std::deque<std::shared_ptr<HalRequest>>& offlineReqs,
+            const std::map<int, CirculatingBuffers>& circulatingBuffers,
+            /*out*/CameraOfflineSessionInfo* info);
+
 private:
 
     struct TrampolineSessionInterface_3_6 : public ICameraDeviceSession {
@@ -188,4 +205,4 @@
 }  // namespace hardware
 }  // namespace android
 
-#endif  // ANDROID_HARDWARE_CAMERA_DEVICE_V3_6_EXTCAMERADEVICE3SESSION_H
+#endif  // ANDROID_HARDWARE_CAMERA_DEVICE_V3_6_EXTCAMERADEVICESESSION_H
diff --git a/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraOfflineSession.h b/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraOfflineSession.h
new file mode 100644
index 0000000..230b67c
--- /dev/null
+++ b/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraOfflineSession.h
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2020 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_CAMERA_DEVICE_V3_6_EXTCAMERAOFFLINESESSION_H
+#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_6_EXTCAMERAOFFLINESESSION_H
+
+#include <android/hardware/camera/device/3.5/ICameraDeviceCallback.h>
+#include <android/hardware/camera/device/3.6/ICameraDeviceSession.h>
+#include <android/hardware/camera/device/3.6/ICameraOfflineSession.h>
+#include <android/hardware/camera/common/1.0/types.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <deque>
+#include <../../3.4/default/include/ext_device_v3_4_impl/ExternalCameraUtils.h>
+#include <../../3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h>
+#include <HandleImporter.h>
+#include <Exif.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace V3_6 {
+namespace implementation {
+
+using ::android::hardware::camera::device::V3_2::BufferCache;
+using ::android::hardware::camera::device::V3_5::BufferRequest;
+using ::android::hardware::camera::device::V3_5::BufferRequestStatus;
+using ::android::hardware::camera::device::V3_2::BufferStatus;
+using ::android::hardware::camera::device::V3_2::CameraMetadata;
+using ::android::hardware::camera::device::V3_2::CaptureRequest;
+using ::android::hardware::camera::device::V3_2::CaptureResult;
+using ::android::hardware::camera::device::V3_2::ErrorCode;
+using ::android::hardware::camera::device::V3_5::ICameraDeviceCallback;
+using ::android::hardware::camera::device::V3_2::MsgType;
+using ::android::hardware::camera::device::V3_2::NotifyMsg;
+using ::android::hardware::camera::device::V3_2::RequestTemplate;
+using ::android::hardware::camera::device::V3_2::Stream;
+using ::android::hardware::camera::device::V3_5::StreamConfiguration;
+using ::android::hardware::camera::device::V3_2::StreamConfigurationMode;
+using ::android::hardware::camera::device::V3_2::StreamRotation;
+using ::android::hardware::camera::device::V3_2::StreamType;
+using ::android::hardware::camera::device::V3_2::DataspaceFlags;
+using ::android::hardware::camera::device::V3_2::CameraBlob;
+using ::android::hardware::camera::device::V3_2::CameraBlobId;
+using ::android::hardware::camera::device::V3_4::HalStreamConfiguration;
+using ::android::hardware::camera::device::V3_6::ICameraOfflineSession;
+using ::android::hardware::camera::common::V1_0::Status;
+using ::android::hardware::camera::common::V1_0::helper::HandleImporter;
+using ::android::hardware::camera::common::V1_0::helper::ExifUtils;
+using ::android::hardware::camera::external::common::ExternalCameraConfig;
+using ::android::hardware::camera::external::common::Size;
+using ::android::hardware::camera::external::common::SizeHasher;
+using ::android::hardware::graphics::common::V1_0::BufferUsage;
+using ::android::hardware::graphics::common::V1_0::Dataspace;
+using ::android::hardware::graphics::common::V1_0::PixelFormat;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::MQDescriptorSync;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::hidl_string;
+using ::android::sp;
+using ::android::Mutex;
+using ::android::base::unique_fd;
+
+using ::android::hardware::camera::device::V3_4::implementation::SupportedV4L2Format;
+using ::android::hardware::camera::device::V3_4::implementation::CroppingType;
+using ::android::hardware::camera::device::V3_4::implementation::CirculatingBuffers;
+using ::android::hardware::camera::device::V3_4::implementation::HalRequest;
+using ::android::hardware::camera::device::V3_4::implementation::OutputThreadInterface;
+
+struct ExternalCameraOfflineSession : public virtual RefBase,
+        public virtual OutputThreadInterface {
+
+    ExternalCameraOfflineSession(
+            const CroppingType& croppingType,
+            const common::V1_0::helper::CameraMetadata& chars,
+            const std::string& cameraId,
+            const std::string& exifMake,
+            const std::string& exifModel,
+            uint32_t blobBufferSize,
+            bool afTrigger,
+            const hidl_vec<Stream>& offlineStreams,
+            std::deque<std::shared_ptr<HalRequest>>& offlineReqs,
+            const std::map<int, CirculatingBuffers>& circulatingBuffers);
+
+    bool initialize();
+
+    virtual ~ExternalCameraOfflineSession();
+
+    // Retrieve the HIDL interface, split into its own class to avoid inheritance issues when
+    // dealing with minor version revs and simultaneous implementation and interface inheritance
+    virtual sp<V3_6::ICameraOfflineSession> getInterface() {
+        return new TrampolineSessionInterface_3_6(this);
+    }
+
+protected:
+
+    // Methods from OutputThreadInterface
+    virtual Status importBuffer(int32_t streamId,
+            uint64_t bufId, buffer_handle_t buf,
+            /*out*/buffer_handle_t** outBufPtr,
+            bool allowEmptyBuf) override;
+
+    virtual Status processCaptureResult(std::shared_ptr<HalRequest>&) override;
+
+    virtual Status processCaptureRequestError(const std::shared_ptr<HalRequest>&,
+            /*out*/std::vector<NotifyMsg>* msgs = nullptr,
+            /*out*/std::vector<CaptureResult>* results = nullptr) override;
+
+    virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const override;
+
+    virtual void notifyError(uint32_t frameNumber, int32_t streamId, ErrorCode ec) override;
+    // End of OutputThreadInterface methods
+
+    class OutputThread : public V3_5::implementation::ExternalCameraDeviceSession::OutputThread {
+    public:
+        OutputThread(
+                wp<OutputThreadInterface> parent, CroppingType ct,
+                const common::V1_0::helper::CameraMetadata& chars,
+                sp<V3_5::implementation::ExternalCameraDeviceSession::BufferRequestThread> bufReqThread,
+                std::deque<std::shared_ptr<HalRequest>>& offlineReqs) :
+                V3_5::implementation::ExternalCameraDeviceSession::OutputThread(
+                        parent, ct, chars, bufReqThread),
+                mOfflineReqs(offlineReqs) {}
+
+        virtual bool threadLoop() override;
+
+    protected:
+        std::deque<std::shared_ptr<HalRequest>> mOfflineReqs;
+    }; // OutputThread
+
+
+    Return<void> setCallback(const sp<ICameraDeviceCallback>& cb);
+
+    Return<void> getCaptureResultMetadataQueue(
+            V3_3::ICameraDeviceSession::getCaptureResultMetadataQueue_cb _hidl_cb);
+
+    Return<void> close();
+
+    void initOutputThread();
+
+    void invokeProcessCaptureResultCallback(
+            hidl_vec<CaptureResult> &results, bool tryWriteFmq);
+
+    status_t fillCaptureResult(common::V1_0::helper::CameraMetadata& md, nsecs_t timestamp);
+
+    void cleanupBuffersLocked(int id);
+
+    // Protect (most of) HIDL interface methods from synchronized-entering
+    mutable Mutex mInterfaceLock;
+
+    mutable Mutex mLock; // Protect all data members except otherwise noted
+
+    bool mClosed = false;
+    const CroppingType mCroppingType;
+    const common::V1_0::helper::CameraMetadata mChars;
+    const std::string mCameraId;
+    const std::string mExifMake;
+    const std::string mExifModel;
+    const uint32_t mBlobBufferSize;
+
+    std::mutex mAfTriggerLock; // protect mAfTrigger
+    bool mAfTrigger;
+
+    const hidl_vec<Stream> mOfflineStreams;
+    std::deque<std::shared_ptr<HalRequest>> mOfflineReqs;
+
+    // Protect mCirculatingBuffers, must not lock mLock after acquiring this lock
+    mutable Mutex mCbsLock;
+    std::map<int, CirculatingBuffers> mCirculatingBuffers;
+
+    static HandleImporter sHandleImporter;
+
+    using ResultMetadataQueue = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+    std::shared_ptr<ResultMetadataQueue> mResultMetadataQueue;
+
+    // Protect against invokeProcessCaptureResultCallback()
+    Mutex mProcessCaptureResultLock;
+
+    sp<ICameraDeviceCallback> mCallback;
+
+    sp<V3_5::implementation::ExternalCameraDeviceSession::BufferRequestThread> mBufferRequestThread;
+    sp<OutputThread> mOutputThread;
+private:
+
+    struct TrampolineSessionInterface_3_6 : public ICameraOfflineSession {
+        TrampolineSessionInterface_3_6(sp<ExternalCameraOfflineSession> parent) :
+                mParent(parent) {}
+
+        virtual Return<void> setCallback(const sp<ICameraDeviceCallback>& cb) override {
+            return mParent->setCallback(cb);
+        }
+
+        virtual Return<void> getCaptureResultMetadataQueue(
+                V3_3::ICameraDeviceSession::getCaptureResultMetadataQueue_cb _hidl_cb) override {
+            return mParent->getCaptureResultMetadataQueue(_hidl_cb);
+        }
+
+        virtual Return<void> close() override {
+            return mParent->close();
+        }
+
+    private:
+        sp<ExternalCameraOfflineSession> mParent;
+    };
+};
+
+}  // namespace implementation
+}  // namespace V3_6
+}  // namespace device
+}  // namespace camera
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_CAMERA_DEVICE_V3_6_EXTCAMERAOFFLINESESSION_H
diff --git a/camera/provider/2.4/vts/functional/Android.bp b/camera/provider/2.4/vts/functional/Android.bp
index 15f5c9a..4b9d6f1 100644
--- a/camera/provider/2.4/vts/functional/Android.bp
+++ b/camera/provider/2.4/vts/functional/Android.bp
@@ -38,7 +38,8 @@
         "android.hardware.camera.device@3.3",
         "android.hardware.camera.device@3.4",
         "android.hardware.camera.device@3.5",
-	"android.hardware.camera.metadata@3.4",
+        "android.hardware.camera.device@3.6",
+        "android.hardware.camera.metadata@3.4",
         "android.hardware.camera.provider@2.4",
         "android.hardware.camera.provider@2.5",
         "android.hardware.camera.provider@2.6",
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index b4092ca..e3e53dd 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -37,6 +37,7 @@
 #include <android/hardware/camera/device/3.5/ICameraDevice.h>
 #include <android/hardware/camera/device/3.5/ICameraDeviceCallback.h>
 #include <android/hardware/camera/device/3.5/ICameraDeviceSession.h>
+#include <android/hardware/camera/device/3.6/ICameraDeviceSession.h>
 #include <android/hardware/camera/metadata/3.4/types.h>
 #include <android/hardware/camera/provider/2.4/ICameraProvider.h>
 #include <android/hardware/camera/provider/2.5/ICameraProvider.h>
@@ -133,6 +134,8 @@
 
 const uint32_t kMaxPreviewWidth = 1920;
 const uint32_t kMaxPreviewHeight = 1080;
+const uint32_t kMaxStillWidth = 2048;
+const uint32_t kMaxStillHeight = 1536;
 const uint32_t kMaxVideoWidth = 4096;
 const uint32_t kMaxVideoHeight = 2160;
 const int64_t kStreamBufferTimeoutSec = 3;
@@ -162,11 +165,13 @@
 namespace {
     // "device@<version>/legacy/<id>"
     const char *kDeviceNameRE = "device@([0-9]+\\.[0-9]+)/%s/(.+)";
+    const int CAMERA_DEVICE_API_VERSION_3_6 = 0x306;
     const int CAMERA_DEVICE_API_VERSION_3_5 = 0x305;
     const int CAMERA_DEVICE_API_VERSION_3_4 = 0x304;
     const int CAMERA_DEVICE_API_VERSION_3_3 = 0x303;
     const int CAMERA_DEVICE_API_VERSION_3_2 = 0x302;
     const int CAMERA_DEVICE_API_VERSION_1_0 = 0x100;
+    const char *kHAL3_6 = "3.6";
     const char *kHAL3_5 = "3.5";
     const char *kHAL3_4 = "3.4";
     const char *kHAL3_3 = "3.3";
@@ -202,7 +207,9 @@
             return -1;
         }
 
-        if (version.compare(kHAL3_5) == 0) {
+        if (version.compare(kHAL3_6) == 0) {
+            return CAMERA_DEVICE_API_VERSION_3_6;
+        } else if (version.compare(kHAL3_5) == 0) {
             return CAMERA_DEVICE_API_VERSION_3_5;
         } else if (version.compare(kHAL3_4) == 0) {
             return CAMERA_DEVICE_API_VERSION_3_4;
@@ -717,7 +724,8 @@
     void castSession(const sp<ICameraDeviceSession> &session, int32_t deviceVersion,
             sp<device::V3_3::ICameraDeviceSession> *session3_3 /*out*/,
             sp<device::V3_4::ICameraDeviceSession> *session3_4 /*out*/,
-            sp<device::V3_5::ICameraDeviceSession> *session3_5 /*out*/);
+            sp<device::V3_5::ICameraDeviceSession> *session3_5 /*out*/,
+            sp<device::V3_6::ICameraDeviceSession> *session3_6 /*out*/);
     void castDevice(const sp<device::V3_2::ICameraDevice> &device, int32_t deviceVersion,
             sp<device::V3_5::ICameraDevice> *device3_5/*out*/);
     void createStreamConfiguration(const ::android::hardware::hidl_vec<V3_2::Stream>& streams3_2,
@@ -727,6 +735,17 @@
             ::android::hardware::camera::device::V3_5::StreamConfiguration *config3_5,
             uint32_t jpegBufferSize = 0);
 
+    void configureOfflineStillStream(const std::string &name, int32_t deviceVersion,
+            sp<ICameraProvider> provider,
+            const AvailableStream *threshold,
+            sp<device::V3_6::ICameraDeviceSession> *session/*out*/,
+            V3_2::Stream *stream /*out*/,
+            device::V3_6::HalStreamConfiguration *halStreamConfig /*out*/,
+            bool *supportsPartialResults /*out*/,
+            uint32_t *partialResultCount /*out*/,
+            sp<DeviceCb> *outCb /*out*/,
+            uint32_t *jpegBufferSize /*out*/,
+            bool *useHalBufManager /*out*/);
     void configurePreviewStreams3_4(const std::string &name, int32_t deviceVersion,
             sp<ICameraProvider> provider,
             const AvailableStream *previewThreshold,
@@ -792,6 +811,7 @@
             uint32_t* outBufSize);
     static Status isConstrainedModeAvailable(camera_metadata_t *staticMeta);
     static Status isLogicalMultiCamera(const camera_metadata_t *staticMeta);
+    static Status isOfflineSessionSupported(const camera_metadata_t *staticMeta);
     static Status getPhysicalCameraIds(const camera_metadata_t *staticMeta,
             std::unordered_set<std::string> *physicalIds/*out*/);
     static Status getSupportedKeys(camera_metadata_t *staticMeta,
@@ -818,6 +838,9 @@
             CameraParameters &cameraParams, const char *mode) ;
     static Status isMonochromeCamera(const camera_metadata_t *staticMeta);
 
+    // Used by switchToOffline where a new result queue is created for offline reqs
+    void updateInflightResultQueue(std::shared_ptr<ResultMetadataQueue> resultQueue);
+
 protected:
 
     // In-flight queue for tracking completion of capture requests.
@@ -1442,7 +1465,13 @@
         hidl_vec<StreamBuffer> tmpRetBuffers(bufReq.numBuffersRequested);
         for (size_t j = 0; j < bufReq.numBuffersRequested; j++) {
             hidl_handle buffer_handle;
-            mParent->allocateGraphicBuffer(stream.v3_2.width, stream.v3_2.height,
+            uint32_t w = stream.v3_2.width;
+            uint32_t h = stream.v3_2.height;
+            if (stream.v3_2.format == PixelFormat::BLOB) {
+                w = stream.bufferSize;
+                h = 1;
+            }
+            mParent->allocateGraphicBuffer(w, h,
                     android_convertGralloc1To0Usage(
                             halStream.producerUsage, halStream.consumerUsage),
                     halStream.overrideFormat, &buffer_handle);
@@ -1706,6 +1735,7 @@
     for (const auto& name : cameraDeviceNames) {
         int deviceVersion = getCameraDeviceVersion(name, mProviderType);
         switch (deviceVersion) {
+            case CAMERA_DEVICE_API_VERSION_3_6:
             case CAMERA_DEVICE_API_VERSION_3_5:
             case CAMERA_DEVICE_API_VERSION_3_4:
             case CAMERA_DEVICE_API_VERSION_3_3:
@@ -1748,6 +1778,7 @@
     for (const auto& name : cameraDeviceNames) {
         int deviceVersion = getCameraDeviceVersion(name, mProviderType);
         switch (deviceVersion) {
+            case CAMERA_DEVICE_API_VERSION_3_6:
             case CAMERA_DEVICE_API_VERSION_3_5:
             case CAMERA_DEVICE_API_VERSION_3_4:
             case CAMERA_DEVICE_API_VERSION_3_3:
@@ -2489,6 +2520,7 @@
     for (const auto& name : cameraDeviceNames) {
         int deviceVersion = getCameraDeviceVersion(name, mProviderType);
         switch (deviceVersion) {
+            case CAMERA_DEVICE_API_VERSION_3_6:
             case CAMERA_DEVICE_API_VERSION_3_5:
             case CAMERA_DEVICE_API_VERSION_3_4:
             case CAMERA_DEVICE_API_VERSION_3_3:
@@ -2568,6 +2600,7 @@
     for (const auto& name : cameraDeviceNames) {
         int deviceVersion = getCameraDeviceVersion(name, mProviderType);
         switch (deviceVersion) {
+            case CAMERA_DEVICE_API_VERSION_3_6:
             case CAMERA_DEVICE_API_VERSION_3_5:
             case CAMERA_DEVICE_API_VERSION_3_4:
             case CAMERA_DEVICE_API_VERSION_3_3:
@@ -2694,6 +2727,7 @@
     for (const auto& name : cameraDeviceNames) {
         int deviceVersion = getCameraDeviceVersion(name, mProviderType);
         switch (deviceVersion) {
+            case CAMERA_DEVICE_API_VERSION_3_6:
             case CAMERA_DEVICE_API_VERSION_3_5:
             case CAMERA_DEVICE_API_VERSION_3_4:
             case CAMERA_DEVICE_API_VERSION_3_3:
@@ -2759,6 +2793,7 @@
     for (const auto& name : cameraDeviceNames) {
         int deviceVersion = getCameraDeviceVersion(name, mProviderType);
         switch (deviceVersion) {
+            case CAMERA_DEVICE_API_VERSION_3_6:
             case CAMERA_DEVICE_API_VERSION_3_5:
             case CAMERA_DEVICE_API_VERSION_3_4:
             case CAMERA_DEVICE_API_VERSION_3_3:
@@ -2788,8 +2823,12 @@
                 sp<device::V3_3::ICameraDeviceSession> sessionV3_3;
                 sp<device::V3_4::ICameraDeviceSession> sessionV3_4;
                 sp<device::V3_5::ICameraDeviceSession> sessionV3_5;
-                castSession(session, deviceVersion, &sessionV3_3, &sessionV3_4, &sessionV3_5);
-                if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_5) {
+                sp<device::V3_6::ICameraDeviceSession> sessionV3_6;
+                castSession(session, deviceVersion, &sessionV3_3,
+                        &sessionV3_4, &sessionV3_5, &sessionV3_6);
+                if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_6) {
+                    ASSERT_TRUE(sessionV3_6.get() != nullptr);
+                } else if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_5) {
                     ASSERT_TRUE(sessionV3_5.get() != nullptr);
                 } else if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_4) {
                     ASSERT_TRUE(sessionV3_4.get() != nullptr);
@@ -2851,6 +2890,7 @@
     for (const auto& name : cameraDeviceNames) {
         int deviceVersion = getCameraDeviceVersion(name, mProviderType);
         switch (deviceVersion) {
+            case CAMERA_DEVICE_API_VERSION_3_6:
             case CAMERA_DEVICE_API_VERSION_3_5:
             case CAMERA_DEVICE_API_VERSION_3_4:
             case CAMERA_DEVICE_API_VERSION_3_3:
@@ -2949,11 +2989,12 @@
         sp<device::V3_3::ICameraDeviceSession> session3_3;
         sp<device::V3_4::ICameraDeviceSession> session3_4;
         sp<device::V3_5::ICameraDeviceSession> session3_5;
+        sp<device::V3_6::ICameraDeviceSession> session3_6;
         sp<device::V3_2::ICameraDevice> cameraDevice;
         sp<device::V3_5::ICameraDevice> cameraDevice3_5;
         openEmptyDeviceSession(name, mProvider,
                 &session /*out*/, &staticMeta /*out*/, &cameraDevice /*out*/);
-        castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
+        castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5, &session3_6);
         castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
 
         outputStreams.clear();
@@ -3057,11 +3098,12 @@
         sp<device::V3_3::ICameraDeviceSession> session3_3;
         sp<device::V3_4::ICameraDeviceSession> session3_4;
         sp<device::V3_5::ICameraDeviceSession> session3_5;
+        sp<device::V3_6::ICameraDeviceSession> session3_6;
         sp<device::V3_2::ICameraDevice> cameraDevice;
         sp<device::V3_5::ICameraDevice> cameraDevice3_5;
         openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
                 &cameraDevice /*out*/);
-        castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
+        castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5, &session3_6);
         castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
 
         outputStreams.clear();
@@ -3254,11 +3296,12 @@
         sp<device::V3_3::ICameraDeviceSession> session3_3;
         sp<device::V3_4::ICameraDeviceSession> session3_4;
         sp<device::V3_5::ICameraDeviceSession> session3_5;
+        sp<device::V3_6::ICameraDeviceSession> session3_6;
         sp<device::V3_2::ICameraDevice> cameraDevice;
         sp<device::V3_5::ICameraDevice> cameraDevice3_5;
         openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
                 &cameraDevice /*out*/);
-        castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
+        castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5, &session3_6);
         castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
 
         Status rc = isZSLModeAvailable(staticMeta);
@@ -3421,8 +3464,9 @@
         sp<device::V3_3::ICameraDeviceSession> session3_3;
         sp<device::V3_4::ICameraDeviceSession> session3_4;
         sp<device::V3_5::ICameraDeviceSession> session3_5;
+        sp<device::V3_6::ICameraDeviceSession> session3_6;
         openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMetaBuffer /*out*/);
-        castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
+        castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5, &session3_6);
         if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_4) {
             ASSERT_NE(session3_4, nullptr);
         } else {
@@ -3543,11 +3587,12 @@
         sp<device::V3_3::ICameraDeviceSession> session3_3;
         sp<device::V3_4::ICameraDeviceSession> session3_4;
         sp<device::V3_5::ICameraDeviceSession> session3_5;
+        sp<device::V3_6::ICameraDeviceSession> session3_6;
         sp<device::V3_2::ICameraDevice> cameraDevice;
         sp<device::V3_5::ICameraDevice> cameraDevice3_5;
         openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
                 &cameraDevice /*out*/);
-        castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
+        castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5, &session3_6);
         castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
 
         // Check if camera support depth only
@@ -3660,11 +3705,12 @@
         sp<device::V3_3::ICameraDeviceSession> session3_3;
         sp<device::V3_4::ICameraDeviceSession> session3_4;
         sp<device::V3_5::ICameraDeviceSession> session3_5;
+        sp<device::V3_6::ICameraDeviceSession> session3_6;
         sp<device::V3_2::ICameraDevice> cameraDevice;
         sp<device::V3_5::ICameraDevice> cameraDevice3_5;
         openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
                 &cameraDevice /*out*/);
-        castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
+        castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5, &session3_6);
         castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
 
         Status rc = isConstrainedModeAvailable(staticMeta);
@@ -3871,11 +3917,12 @@
         sp<device::V3_3::ICameraDeviceSession> session3_3;
         sp<device::V3_4::ICameraDeviceSession> session3_4;
         sp<device::V3_5::ICameraDeviceSession> session3_5;
+        sp<device::V3_6::ICameraDeviceSession> session3_6;
         sp<device::V3_2::ICameraDevice> cameraDevice;
         sp<device::V3_5::ICameraDevice> cameraDevice3_5;
         openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
                 &cameraDevice /*out*/);
-        castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
+        castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5, &session3_6);
         castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
 
         // Check if camera support depth only
@@ -4613,6 +4660,207 @@
     }
 }
 
+// Verify camera offline session behavior
+TEST_P(CameraHidlTest, switchToOffline) {
+    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
+    AvailableStream threshold = {kMaxStillWidth, kMaxStillHeight,
+                                        static_cast<int32_t>(PixelFormat::BLOB)};
+    uint64_t bufferId = 1;
+    uint32_t frameNumber = 1;
+    ::android::hardware::hidl_vec<uint8_t> settings;
+
+    for (const auto& name : cameraDeviceNames) {
+        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
+        if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
+            continue;
+        } else if (deviceVersion <= 0) {
+            ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
+            ADD_FAILURE();
+            return;
+        }
+
+        camera_metadata_t* staticMetaBuffer;
+        {
+            Return<void> ret;
+            sp<ICameraDeviceSession> session;
+            openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMetaBuffer /*out*/);
+            ::android::hardware::camera::common::V1_0::helper::CameraMetadata staticMeta(
+                    staticMetaBuffer);
+
+            if (isOfflineSessionSupported(staticMetaBuffer) != Status::OK) {
+                ret = session->close();
+                ASSERT_TRUE(ret.isOk());
+                continue;
+            }
+            ret = session->close();
+            ASSERT_TRUE(ret.isOk());
+        }
+
+        bool supportsPartialResults = false;
+        uint32_t partialResultCount = 0;
+        V3_2::Stream stream;
+        V3_6::HalStreamConfiguration halStreamConfig;
+        sp<V3_6::ICameraDeviceSession> session;
+        sp<DeviceCb> cb;
+        uint32_t jpegBufferSize;
+        bool useHalBufManager;
+        configureOfflineStillStream(name, deviceVersion, mProvider, &threshold,
+                &session /*out*/, &stream /*out*/, &halStreamConfig /*out*/,
+                &supportsPartialResults /*out*/, &partialResultCount /*out*/, &cb /*out*/,
+                &jpegBufferSize /*out*/, &useHalBufManager /*out*/);
+
+        auto ret = session->constructDefaultRequestSettings(RequestTemplate::STILL_CAPTURE,
+            [&](auto status, const auto& req) {
+                ASSERT_EQ(Status::OK, status);
+                settings = req; });
+        ASSERT_TRUE(ret.isOk());
+
+        std::shared_ptr<ResultMetadataQueue> resultQueue;
+        auto resultQueueRet =
+            session->getCaptureResultMetadataQueue(
+                [&resultQueue](const auto& descriptor) {
+                    resultQueue = std::make_shared<ResultMetadataQueue>(
+                            descriptor);
+                    if (!resultQueue->isValid() ||
+                            resultQueue->availableToWrite() <= 0) {
+                        ALOGE("%s: HAL returns empty result metadata fmq,"
+                                " not use it", __func__);
+                        resultQueue = nullptr;
+                        // Don't use the queue onwards.
+                    }
+                });
+        ASSERT_TRUE(resultQueueRet.isOk());
+
+        ::android::hardware::camera::common::V1_0::helper::CameraMetadata requestMeta;
+        StreamBuffer emptyInputBuffer = {-1, 0, nullptr, BufferStatus::ERROR, nullptr, nullptr};
+        hidl_handle buffers[kBurstFrameCount];
+        StreamBuffer outputBuffers[kBurstFrameCount];
+        CaptureRequest requests[kBurstFrameCount];
+        InFlightRequest inflightReqs[kBurstFrameCount];
+        hidl_vec<uint8_t> requestSettings[kBurstFrameCount];
+        auto halStreamConfig3_2 = halStreamConfig.streams[0].v3_4.v3_3.v3_2;
+        for (uint32_t i = 0; i < kBurstFrameCount; i++) {
+            std::unique_lock<std::mutex> l(mLock);
+
+            if (useHalBufManager) {
+                outputBuffers[i] = {halStreamConfig3_2.id, /*bufferId*/ 0,
+                        buffers[i], BufferStatus::OK, nullptr, nullptr};
+            } else {
+                // jpeg buffer (w,h) = (blobLen, 1)
+                allocateGraphicBuffer(jpegBufferSize, /*height*/1,
+                        android_convertGralloc1To0Usage(halStreamConfig3_2.producerUsage,
+                            halStreamConfig3_2.consumerUsage),
+                        halStreamConfig3_2.overrideFormat, &buffers[i]);
+                outputBuffers[i] = {halStreamConfig3_2.id, bufferId + i,
+                    buffers[i], BufferStatus::OK, nullptr, nullptr};
+            }
+
+            requestMeta.clear();
+            requestMeta.append(reinterpret_cast<camera_metadata_t *> (settings.data()));
+
+            camera_metadata_t *metaBuffer = requestMeta.release();
+            requestSettings[i].setToExternal(reinterpret_cast<uint8_t *> (metaBuffer),
+                    get_camera_metadata_size(metaBuffer), true);
+
+            requests[i] = {frameNumber + i, 0 /* fmqSettingsSize */, requestSettings[i],
+                emptyInputBuffer, {outputBuffers[i]}};
+
+            inflightReqs[i] = {1, false, supportsPartialResults, partialResultCount,
+                    resultQueue};
+            mInflightMap.add(frameNumber + i, &inflightReqs[i]);
+        }
+
+        Status status = Status::INTERNAL_ERROR;
+        uint32_t numRequestProcessed = 0;
+        hidl_vec<BufferCache> cachesToRemove;
+        hidl_vec<CaptureRequest> burstRequest;
+        burstRequest.setToExternal(requests, kBurstFrameCount);
+        Return<void> returnStatus = session->processCaptureRequest(burstRequest, cachesToRemove,
+                [&status, &numRequestProcessed] (auto s, uint32_t n) {
+                    status = s;
+                    numRequestProcessed = n;
+                });
+        ASSERT_TRUE(returnStatus.isOk());
+        ASSERT_EQ(Status::OK, status);
+        ASSERT_EQ(numRequestProcessed, kBurstFrameCount);
+
+        hidl_vec<int32_t> offlineStreamIds = {halStreamConfig3_2.id};
+        V3_6::CameraOfflineSessionInfo offlineSessionInfo;
+        sp<device::V3_6::ICameraOfflineSession> offlineSession;
+        returnStatus = session->switchToOffline(offlineStreamIds,
+                [&status, &offlineSessionInfo, &offlineSession] (auto stat, auto info,
+                    auto offSession) {
+                    status = stat;
+                    offlineSessionInfo = info;
+                    offlineSession = offSession;
+                });
+        ASSERT_TRUE(returnStatus.isOk());
+
+        if (!halStreamConfig.streams[0].supportOffline) {
+            ASSERT_EQ(status, Status::ILLEGAL_ARGUMENT);
+            ret = session->close();
+            ASSERT_TRUE(ret.isOk());
+            continue;
+        }
+
+        ASSERT_EQ(status, Status::OK);
+        // Hal might be unable to find any requests qualified for offline mode.
+        if (offlineSession == nullptr) {
+            ret = session->close();
+            ASSERT_TRUE(ret.isOk());
+            continue;
+        }
+
+        ASSERT_EQ(offlineSessionInfo.offlineStreams.size(), 1u);
+        ASSERT_EQ(offlineSessionInfo.offlineStreams[0].id, halStreamConfig3_2.id);
+        ASSERT_NE(offlineSessionInfo.offlineRequests.size(), 0u);
+
+        // close device session to make sure offline session does not rely on it
+        ret = session->close();
+        ASSERT_TRUE(ret.isOk());
+
+        std::shared_ptr<ResultMetadataQueue> offlineResultQueue;
+        auto offlineResultQueueRet =
+            offlineSession->getCaptureResultMetadataQueue(
+                [&offlineResultQueue](const auto& descriptor) {
+                    offlineResultQueue = std::make_shared<ResultMetadataQueue>(
+                            descriptor);
+                    if (!offlineResultQueue->isValid() ||
+                            offlineResultQueue->availableToWrite() <= 0) {
+                        ALOGE("%s: offline session returns empty result metadata fmq,"
+                                " not use it", __func__);
+                        offlineResultQueue = nullptr;
+                        // Don't use the queue onwards.
+                    }
+                });
+        ASSERT_TRUE(offlineResultQueueRet.isOk());
+
+        updateInflightResultQueue(offlineResultQueue);
+
+        ret = offlineSession->setCallback(cb);
+        ASSERT_TRUE(ret.isOk());
+
+        for (size_t i = 0; i < kBurstFrameCount; i++) {
+            std::unique_lock<std::mutex> l(mLock);
+            while (!inflightReqs[i].errorCodeValid && ((0 < inflightReqs[i].numBuffersLeft) ||
+                            (!inflightReqs[i].haveResultMetadata))) {
+                auto timeout = std::chrono::system_clock::now() +
+                        std::chrono::seconds(kStreamBufferTimeoutSec);
+                ASSERT_NE(std::cv_status::timeout, mResultCondition.wait_until(l, timeout));
+            }
+
+            ASSERT_FALSE(inflightReqs[i].errorCodeValid);
+            ASSERT_NE(inflightReqs[i].resultOutputBuffers.size(), 0u);
+            ASSERT_EQ(stream.id, inflightReqs[i].resultOutputBuffers[0].streamId);
+            ASSERT_FALSE(inflightReqs[i].collectedResult.isEmpty());
+        }
+
+
+        ret = offlineSession->close();
+        ASSERT_TRUE(ret.isOk());
+    }
+}
+
 // Check whether an invalid capture request with missing output buffers
 // will be reported correctly.
 TEST_P(CameraHidlTest, processCaptureRequestInvalidBuffer) {
@@ -4972,6 +5220,30 @@
     return ret;
 }
 
+// Check if the camera device has logical multi-camera capability.
+Status CameraHidlTest::isOfflineSessionSupported(const camera_metadata_t *staticMeta) {
+    Status ret = Status::METHOD_NOT_SUPPORTED;
+    if (nullptr == staticMeta) {
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    camera_metadata_ro_entry entry;
+    int rc = find_camera_metadata_ro_entry(staticMeta,
+            ANDROID_REQUEST_AVAILABLE_CAPABILITIES, &entry);
+    if (0 != rc) {
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    for (size_t i = 0; i < entry.count; i++) {
+        if (ANDROID_REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING == entry.data.u8[i]) {
+            ret = Status::OK;
+            break;
+        }
+    }
+
+    return ret;
+}
+
 // Generate a list of physical camera ids backing a logical multi-camera.
 Status CameraHidlTest::getPhysicalCameraIds(const camera_metadata_t *staticMeta,
         std::unordered_set<std::string> *physicalIds) {
@@ -5330,7 +5602,8 @@
     *outCb = cb;
 
     sp<device::V3_3::ICameraDeviceSession> session3_3;
-    castSession(session, deviceVersion, &session3_3, session3_4, session3_5);
+    sp<device::V3_6::ICameraDeviceSession> session3_6;
+    castSession(session, deviceVersion, &session3_3, session3_4, session3_5, &session3_6);
     ASSERT_NE(nullptr, (*session3_4).get());
 
     *useHalBufManager = false;
@@ -5423,6 +5696,142 @@
     ASSERT_TRUE(ret.isOk());
 }
 
+// Configure preview stream with possible offline session support
+void CameraHidlTest::configureOfflineStillStream(const std::string &name,
+        int32_t deviceVersion,
+        sp<ICameraProvider> provider,
+        const AvailableStream *threshold,
+        sp<device::V3_6::ICameraDeviceSession> *session/*out*/,
+        V3_2::Stream *stream /*out*/,
+        device::V3_6::HalStreamConfiguration *halStreamConfig /*out*/,
+        bool *supportsPartialResults /*out*/,
+        uint32_t *partialResultCount /*out*/,
+        sp<DeviceCb> *outCb /*out*/,
+        uint32_t *jpegBufferSize /*out*/,
+        bool *useHalBufManager /*out*/) {
+    ASSERT_NE(nullptr, session);
+    ASSERT_NE(nullptr, halStreamConfig);
+    ASSERT_NE(nullptr, stream);
+    ASSERT_NE(nullptr, supportsPartialResults);
+    ASSERT_NE(nullptr, partialResultCount);
+    ASSERT_NE(nullptr, outCb);
+    ASSERT_NE(nullptr, jpegBufferSize);
+    ASSERT_NE(nullptr, useHalBufManager);
+
+    std::vector<AvailableStream> outputStreams;
+    ::android::sp<ICameraDevice> cameraDevice;
+    ALOGI("configureStreams: Testing camera device %s", name.c_str());
+    Return<void> ret;
+    ret = provider->getCameraDeviceInterface_V3_x(
+        name,
+        [&](auto status, const auto& device) {
+            ALOGI("getCameraDeviceInterface_V3_x returns status:%d",
+                  (int)status);
+            ASSERT_EQ(Status::OK, status);
+            ASSERT_NE(device, nullptr);
+            cameraDevice = device;
+        });
+    ASSERT_TRUE(ret.isOk());
+
+    camera_metadata_t *staticMeta;
+    ret = cameraDevice->getCameraCharacteristics([&] (Status s,
+            CameraMetadata metadata) {
+        ASSERT_EQ(Status::OK, s);
+        staticMeta = clone_camera_metadata(
+                reinterpret_cast<const camera_metadata_t*>(metadata.data()));
+        ASSERT_NE(nullptr, staticMeta);
+    });
+    ASSERT_TRUE(ret.isOk());
+
+    camera_metadata_ro_entry entry;
+    auto status = find_camera_metadata_ro_entry(staticMeta,
+            ANDROID_REQUEST_PARTIAL_RESULT_COUNT, &entry);
+    if ((0 == status) && (entry.count > 0)) {
+        *partialResultCount = entry.data.i32[0];
+        *supportsPartialResults = (*partialResultCount > 1);
+    }
+
+    *useHalBufManager = false;
+    status = find_camera_metadata_ro_entry(staticMeta,
+            ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION, &entry);
+    if ((0 == status) && (entry.count == 1)) {
+        *useHalBufManager = (entry.data.u8[0] ==
+            ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5);
+    }
+
+    auto st = getJpegBufferSize(staticMeta, jpegBufferSize);
+    ASSERT_EQ(st, Status::OK);
+
+    sp<DeviceCb> cb = new DeviceCb(this, deviceVersion, staticMeta);
+    ret = cameraDevice->open(cb, [&session](auto status, const auto& newSession) {
+            ALOGI("device::open returns status:%d", (int)status);
+            ASSERT_EQ(Status::OK, status);
+            ASSERT_NE(newSession, nullptr);
+            auto castResult = device::V3_6::ICameraDeviceSession::castFrom(newSession);
+            ASSERT_TRUE(castResult.isOk());
+            *session = castResult;
+        });
+    ASSERT_TRUE(ret.isOk());
+    *outCb = cb;
+
+    outputStreams.clear();
+    auto rc = getAvailableOutputStreams(staticMeta,
+            outputStreams, threshold);
+    size_t idx = 0;
+    int currLargest = outputStreams[0].width * outputStreams[0].height;
+    for (size_t i = 0; i < outputStreams.size(); i++) {
+        int area = outputStreams[i].width * outputStreams[i].height;
+        if (area > currLargest) {
+            idx = i;
+            currLargest = area;
+        }
+    }
+    free_camera_metadata(staticMeta);
+    ASSERT_EQ(Status::OK, rc);
+    ASSERT_FALSE(outputStreams.empty());
+
+    V3_2::DataspaceFlags dataspaceFlag = 0;
+    switch (static_cast<PixelFormat>(outputStreams[idx].format)) {
+        case PixelFormat::BLOB:
+            dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::V0_JFIF);
+            break;
+        case PixelFormat::Y16:
+            dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::DEPTH);
+            break;
+        default:
+            dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::UNKNOWN);
+    }
+
+    ::android::hardware::hidl_vec<V3_4::Stream> streams3_4(/*size*/1);
+    V3_4::Stream stream3_4 = {{ 0 /*streamId*/, StreamType::OUTPUT,
+        static_cast<uint32_t> (outputStreams[idx].width),
+        static_cast<uint32_t> (outputStreams[idx].height),
+        static_cast<PixelFormat> (outputStreams[idx].format),
+        GRALLOC1_CONSUMER_USAGE_CPU_READ, dataspaceFlag, StreamRotation::ROTATION_0},
+        nullptr /*physicalId*/, /*bufferSize*/ *jpegBufferSize};
+    streams3_4[0] = stream3_4;
+
+    ::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
+    ::android::hardware::camera::device::V3_5::StreamConfiguration config3_5;
+    config3_4 = {streams3_4, StreamConfigurationMode::NORMAL_MODE, {}};
+
+    config3_5.v3_4 = config3_4;
+    config3_5.streamConfigCounter = 0;
+    ret = (*session)->configureStreams_3_6(config3_5,
+            [&] (Status s, device::V3_6::HalStreamConfiguration halConfig) {
+                ASSERT_EQ(Status::OK, s);
+                *halStreamConfig = halConfig;
+
+                if (*useHalBufManager) {
+                    hidl_vec<V3_2::HalStream> halStreams3_2(1);
+                    halStreams3_2[0] = halConfig.streams[0].v3_4.v3_3.v3_2;
+                    cb->setCurrentStreamConfig(streams3_4, halStreams3_2);
+                }
+            });
+    *stream = streams3_4[0].v3_2;
+    ASSERT_TRUE(ret.isOk());
+}
+
 bool CameraHidlTest::isDepthOnly(camera_metadata_t* staticMeta) {
     camera_metadata_ro_entry scalarEntry;
     camera_metadata_ro_entry depthEntry;
@@ -5454,6 +5863,14 @@
     return false;
 }
 
+void CameraHidlTest::updateInflightResultQueue(std::shared_ptr<ResultMetadataQueue> resultQueue) {
+    std::unique_lock<std::mutex> l(mLock);
+    for (size_t i = 0; i < mInflightMap.size(); i++) {
+        auto& req = mInflightMap.editValueAt(i);
+        req->resultQueue = resultQueue;
+    }
+}
+
 // Open a device session and configure a preview stream.
 void CameraHidlTest::configurePreviewStream(const std::string &name, int32_t deviceVersion,
         sp<ICameraProvider> provider,
@@ -5522,7 +5939,8 @@
     sp<device::V3_3::ICameraDeviceSession> session3_3;
     sp<device::V3_4::ICameraDeviceSession> session3_4;
     sp<device::V3_5::ICameraDeviceSession> session3_5;
-    castSession(*session, deviceVersion, &session3_3, &session3_4, &session3_5);
+    sp<device::V3_6::ICameraDeviceSession> session3_6;
+    castSession(*session, deviceVersion, &session3_3, &session3_4, &session3_5, &session3_6);
 
     *useHalBufManager = false;
     status = find_camera_metadata_ro_entry(staticMeta,
@@ -5657,12 +6075,20 @@
 void CameraHidlTest::castSession(const sp<ICameraDeviceSession> &session, int32_t deviceVersion,
         sp<device::V3_3::ICameraDeviceSession> *session3_3 /*out*/,
         sp<device::V3_4::ICameraDeviceSession> *session3_4 /*out*/,
-        sp<device::V3_5::ICameraDeviceSession> *session3_5 /*out*/) {
+        sp<device::V3_5::ICameraDeviceSession> *session3_5 /*out*/,
+        sp<device::V3_6::ICameraDeviceSession> *session3_6 /*out*/) {
     ASSERT_NE(nullptr, session3_3);
     ASSERT_NE(nullptr, session3_4);
     ASSERT_NE(nullptr, session3_5);
+    ASSERT_NE(nullptr, session3_6);
 
     switch (deviceVersion) {
+        case CAMERA_DEVICE_API_VERSION_3_6: {
+            auto castResult = device::V3_6::ICameraDeviceSession::castFrom(session);
+            ASSERT_TRUE(castResult.isOk());
+            *session3_6 = castResult;
+        }
+        [[fallthrough]];
         case CAMERA_DEVICE_API_VERSION_3_5: {
             auto castResult = device::V3_5::ICameraDeviceSession::castFrom(session);
             ASSERT_TRUE(castResult.isOk());
@@ -6261,7 +6687,8 @@
     sp<device::V3_3::ICameraDeviceSession> session3_3;
     sp<device::V3_4::ICameraDeviceSession> session3_4;
     sp<device::V3_5::ICameraDeviceSession> session3_5;
-    castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
+    sp<device::V3_6::ICameraDeviceSession> session3_6;
+    castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5, &session3_6);
     ASSERT_NE(nullptr, session3_5.get());
 
     hidl_vec<int32_t> streamIds(1);
diff --git a/camera/provider/2.5/default/Android.bp b/camera/provider/2.5/default/Android.bp
index 4563362..9ddf651 100644
--- a/camera/provider/2.5/default/Android.bp
+++ b/camera/provider/2.5/default/Android.bp
@@ -52,6 +52,8 @@
         "android.hardware.camera.provider@2.4-external",
         "android.hardware.camera.provider@2.5",
         "android.hardware.graphics.mapper@2.0",
+        "android.hardware.graphics.mapper@3.0",
+        "android.hardware.graphics.mapper@4.0",
         "android.hidl.allocator@1.0",
         "android.hidl.memory@1.0",
         "camera.device@3.3-impl",
@@ -72,7 +74,8 @@
     ],
     header_libs: [
         "camera.device@3.4-external-impl_headers",
-        "camera.device@3.5-external-impl_headers"
+        "camera.device@3.5-external-impl_headers",
+        "camera.device@3.6-external-impl_headers"
     ],
     export_include_dirs: ["."],
 }
@@ -165,7 +168,10 @@
         "android.hardware.camera.provider@2.5",
         "android.hardware.camera.provider@2.5-external",
         "android.hardware.graphics.mapper@2.0",
+        "android.hardware.graphics.mapper@3.0",
+        "android.hardware.graphics.mapper@4.0",
         "libbinder",
+        "libcamera_metadata",
         "libhidlbase",
         "liblog",
         "libtinyxml2",
@@ -179,5 +185,6 @@
         "camera.device@3.4-impl_headers",
         "camera.device@3.5-external-impl_headers",
         "camera.device@3.5-impl_headers",
+        "camera.device@3.6-external-impl_headers",
     ],
 }
diff --git a/cas/1.1/vts/functional/VtsHalCasV1_1TargetTest.cpp b/cas/1.1/vts/functional/VtsHalCasV1_1TargetTest.cpp
index 7e5a61a..1b5797b 100644
--- a/cas/1.1/vts/functional/VtsHalCasV1_1TargetTest.cpp
+++ b/cas/1.1/vts/functional/VtsHalCasV1_1TargetTest.cpp
@@ -16,8 +16,6 @@
 
 #define LOG_TAG "mediacas_hidl_hal_test"
 
-#include <VtsHalHidlTargetTestBase.h>
-#include <VtsHalHidlTargetTestEnvBase.h>
 #include <android-base/logging.h>
 #include <android/hardware/cas/1.0/IDescramblerBase.h>
 #include <android/hardware/cas/1.0/types.h>
diff --git a/current.txt b/current.txt
index f1c588d..bb48cd3 100644
--- a/current.txt
+++ b/current.txt
@@ -600,6 +600,8 @@
 a785a57447a81e9c130eef6904c3a5c256076c6a04588c40620ebd6fa2660d77 android.hardware.radio@1.2::types
 1a6e2bd289f22931c526b21916910f1d4c436b7acb9556e4243de4ce8e6cc2e4 android.hardware.soundtrigger@2.0::ISoundTriggerHwCallback
 fd65298e1e09e0e3c781ab18305920d757dbe55a3b459ce17814ec5cf6dfee99 android.hardware.wifi@1.0::IWifiP2pIface
+ff5dd821c2c7a9c78607159c4d788960b725487263c49d956ca5fa3d37008b45 android.hardware.wifi@1.2::IWifiStaIface
+5751f230e86a36111e7c5b995577cbf89d8df76c8e6c7641199198f3db3a93f7 android.hardware.wifi@1.3::IWifiStaIface
 
 # HALs released in Android R
 e966a3437d6a98d9d9e14e9d672088771716031900c0deb55a0946c751a03a44 android.hardware.audio@6.0::types
@@ -634,6 +636,8 @@
 40ab2c6866c18d32baf6e49e3053949e79601f56963a791e93e68b9ee18f718d android.hardware.bluetooth@1.1::IBluetoothHciCallbacks
 07d0a252b2d8fa35887908a996ba395cf392968395fc30afab791f46e0c22a52 android.hardware.boot@1.1::IBootControl
 74049a402be913963edfdd80828a53736570e9d8124a1bf18166b6ed46a6b0ab android.hardware.boot@1.1::types
+e88840e0558439cb54837514ddccd43877094951758f367e9c638084eb7455a6 android.hardware.camera.provider@2.6::ICameraProvider
+8f8d9463508ff9cae88eb35c429fd0e2dbca0ca8f5de7fdf836cc0c4370becb6 android.hardware.camera.provider@2.6::ICameraProviderCallback
 c1aa508d00b66ed5feefea398fd5edf28fa651ac89773adad7dfda4e0a73a952 android.hardware.cas@1.2::ICas
 9811f867def49b420d8c707f7e38d3bdd64f835244e1d2a5e9762ab9835672dc android.hardware.cas@1.2::ICasListener
 f18695dd36ee205640b8326a17453858a7b4596653aaa6ef0016b0aef1bd4dac android.hardware.cas@1.2::IMediaCasService
@@ -671,20 +675,20 @@
 3646950b10f7cacdafca13609b0e18496cea942f3bdfe920494661856eff48bb android.hardware.neuralnetworks@1.3::types
 3e01d4446cd69fd1c48f8572efd97487bc179564b32bd795800b97bbe10be37b android.hardware.wifi@1.4::IWifi
 c67aaf26a7a40d14ea61e70e20afacbd0bb906df1704d585ac8599fbb69dd44b android.hardware.wifi.hostapd@1.2::IHostapd
-11f6448d15336361180391c8ebcdfd2d7cf77b3782d577e594d583aadc9c2877 android.hardware.wifi.hostapd@1.2::types
+2b5a7ea572b736030c64a3b4043af244425477c4672301780fe15aba5ed393d9 android.hardware.wifi.hostapd@1.2::types
 a64467bae843569f0d465c5be7f0c7a5b987985b55a3ef4794dd5afc68538650 android.hardware.wifi.supplicant@1.3::ISupplicant
 8aed0a8e03e7a67bfdfb78ad7529a9ae95bea36e6060473b204c89d772522126 android.hardware.wifi.supplicant@1.3::ISupplicantStaIface
 def77c7db95d374f11a111bfc4ed60f92451303642a43276c4e291988fcee625 android.hardware.wifi.supplicant@1.3::ISupplicantStaIfaceCallback
 62cf050c593c1ec34b49178b5bdde72dd9b80d9bad3eb184e4f0cd564d28678c android.hardware.wifi.supplicant@1.3::ISupplicantStaNetwork
 98592d193a717066facf91428426e5abe211e3bd718bc372e29fb944ddbe6e7c android.hardware.wifi.supplicant@1.3::types
-50e22cd55ad5499e68e81541bbc67bd10e59c1b9f3ff8cc7ba70dcb0d2918381 android.hardware.radio@1.5::types
-8cc3306e8cd755d04521d1611b217b9d13a2a76d2af57cbea8f875333b3363f7 android.hardware.radio@1.5::IRadio
+e1d34b83188a8ef3c507ec53c0ebcf714863c746da7f4a05460453f7c4c09389 android.hardware.radio@1.5::types
+8062d0a1a03594dd8b448adcf6f08856b5720f7e33f9b785a21d3ef74a4f211d android.hardware.radio@1.5::IRadio
 e96ae1c3a9c0689002ec2318e9c587f4f607c16a75a3cd38788b77eb91072021 android.hardware.radio@1.5::IRadioIndication
-7b77721a7716e163f5cc5f2830ed5616b953fcf0e5406f69de0fde5ce95e4184 android.hardware.radio@1.5::IRadioResponse
-2fd107f3de1b7e36825e241a88dfae8edf3a77c166cb746f00ddf6440ab78db1 android.hardware.radio.config@1.3::types
+7f2439b48bda2961c6d629d0415eee66d519142cf9537f05e9d285153c70ca85 android.hardware.radio@1.5::IRadioResponse
+dcc8872337f0135e81970e1d8d5fd7139160dc80e9be76f0ae05290fa7e472b8 android.hardware.radio.config@1.3::types
 a2977755bc5f1ef47f04b7f2400632efda6218e1515dba847da487145cfabc4f android.hardware.radio.config@1.3::IRadioConfig
 742360c775313438b0f82256eac62fb5bbc76a6ae6f388573f3aa142fb2c1eea android.hardware.radio.config@1.3::IRadioConfigIndication
 0006ab8e8b0910cbd3bbb08d5f17d5fac7d65a2bdad5f2334e4851db9d1e6fa8 android.hardware.radio.config@1.3::IRadioConfigResponse
-51d1c8d285e0456da2a3fdfbf4700c6277165d5e83219894d651c8ea0e39aa8b android.hardware.soundtrigger@2.3::types
+4a6517ea4ad807855428b0101d8e1a486497bd88ab4300ba3b2be43d46d32580 android.hardware.soundtrigger@2.3::types
 12d7533ff0754f45bf59ab300799074570a99a676545652c2c23abc73cb4515d android.hardware.soundtrigger@2.3::ISoundTriggerHw
 7746fda1fbf9c7c132bae701cc5a161309e4f5e7f3e8065811045975ee86196d android.hardware.usb.gadget@1.1::IUsbGadget
diff --git a/drm/1.3/vts/OWNERS b/drm/1.3/vts/OWNERS
new file mode 100644
index 0000000..3a0672e
--- /dev/null
+++ b/drm/1.3/vts/OWNERS
@@ -0,0 +1,9 @@
+conglin@google.com
+edwinwong@google.com
+fredgc@google.com
+jtinker@google.com
+juce@google.com
+kylealexander@google.com
+rfrias@google.com
+robertshih@google.com
+sigquit@google.com
diff --git a/drm/1.3/vts/functional/Android.bp b/drm/1.3/vts/functional/Android.bp
new file mode 100644
index 0000000..4be1575
--- /dev/null
+++ b/drm/1.3/vts/functional/Android.bp
@@ -0,0 +1,78 @@
+//
+// Copyright (C) 2020 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.drm@1.3-vts",
+    defaults: ["VtsHalTargetTestDefaults"],
+    local_include_dirs: [
+        "include",
+    ],
+    srcs: [
+        "drm_hal_test.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.drm@1.0",
+        "android.hardware.drm@1.1",
+        "android.hardware.drm@1.2",
+        "android.hardware.drm@1.3",
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
+        "libhidlmemory",
+        "libnativehelper",
+    ],
+    static_libs: [
+        "android.hardware.drm@1.0-helper",
+        "libcrypto_static",
+        "libdrmvtshelper",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+}
+
+cc_test {
+    name: "VtsHalDrmV1_3TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    include_dirs: ["hardware/interfaces/drm/1.0/vts/functional"],
+    srcs: [
+        "drm_hal_test_main.cpp",
+    ],
+    whole_static_libs: [
+        "android.hardware.drm@1.0-vts",
+        "android.hardware.drm@1.1-vts",
+        "android.hardware.drm@1.2-vts",
+        "android.hardware.drm@1.3-vts",
+    ],
+    shared_libs: [
+        "android.hardware.drm@1.0",
+        "android.hardware.drm@1.1",
+        "android.hardware.drm@1.2",
+        "android.hardware.drm@1.3",
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
+        "libhidlmemory",
+        "libnativehelper",
+    ],
+    static_libs: [
+        "android.hardware.drm@1.0-helper",
+        "libcrypto_static",
+        "libdrmvtshelper",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts-core",
+    ],
+}
diff --git a/drm/1.3/vts/functional/drm_hal_test.cpp b/drm/1.3/vts/functional/drm_hal_test.cpp
new file mode 100644
index 0000000..738f5b2
--- /dev/null
+++ b/drm/1.3/vts/functional/drm_hal_test.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 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 "drm_hal_test@1.3"
+
+#include "android/hardware/drm/1.3/vts/drm_hal_test.h"
+
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_3 {
+namespace vts {
+
+TEST_P(DrmHalTestV1_3, SchemeSupported) {
+    EXPECT_TRUE(drmFactory_->isCryptoSchemeSupported(GetParam().scheme_));
+}
+
+TEST_P(DrmHalTestV1_3, SignRsaNotAllowed) {
+    hidl_array<uint8_t, 16> kWidevineUUID ({
+        0xED,0xEF,0x8B,0xA9,0x79,0xD6,0x4A,0xCE,
+        0xA3,0xC8,0x27,0xDC,0xD5,0x1D,0x21,0xED
+    });
+
+    if (!drmFactory_->isCryptoSchemeSupported(kWidevineUUID)) {
+        GTEST_SKIP() << "Widevine only test";
+    }
+
+    // signRSA
+    const hidl_vec<uint8_t>& sessionId{};
+    const hidl_string& algorithm{};
+    const hidl_vec<uint8_t>& message{};
+    const hidl_vec<uint8_t>& wrappedKey{};
+    auto res = drmPlugin_->signRSA(
+        sessionId, algorithm, message, wrappedKey,
+        [&](StatusV1_0 status, const hidl_vec<uint8_t>& signature) {
+            EXPECT_EQ(status, StatusV1_0::ERROR_DRM_UNKNOWN);
+            EXPECT_EQ(signature.size(), 0);
+        }
+    );
+    EXPECT_TRUE(res.isOk());
+}
+
+}  // namespace vts
+}  // namespace V1_3
+}  // namespace drm
+}  // namespace hardware
+}  // namespace android
diff --git a/drm/1.3/vts/functional/drm_hal_test_main.cpp b/drm/1.3/vts/functional/drm_hal_test_main.cpp
new file mode 100644
index 0000000..02b45ea
--- /dev/null
+++ b/drm/1.3/vts/functional/drm_hal_test_main.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+/**
+ * Instantiate the set of test cases for each vendor module
+ */
+
+#define LOG_TAG "drm_hal_test@1.3"
+
+#include <android/hardware/drm/1.3/ICryptoFactory.h>
+#include <android/hardware/drm/1.3/IDrmFactory.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/ServiceManagement.h>
+#include <log/log.h>
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "android/hardware/drm/1.0/vts/drm_hal_clearkey_test.h" // V1_0 tests
+#include "android/hardware/drm/1.0/vts/drm_hal_vendor_test.h"   // V1_0 tests
+#include "android/hardware/drm/1.1/vts/drm_hal_clearkey_test.h" // V1_1 tests
+#include "android/hardware/drm/1.2/vts/drm_hal_common.h"        // V1_2 tests
+#include "android/hardware/drm/1.3/vts/drm_hal_test.h"          // V1_3 tests
+
+using drm_vts::DrmHalTestParam;
+using drm_vts::PrintParamInstanceToString;
+
+using android::hardware::drm::V1_0::vts::DrmHalVendorFactoryTest;
+using android::hardware::drm::V1_0::vts::DrmHalVendorPluginTest;
+using android::hardware::drm::V1_0::vts::DrmHalVendorDecryptTest;
+using android::hardware::drm::V1_0::vts::DrmHalClearkeyFactoryTest;
+using android::hardware::drm::V1_0::vts::DrmHalClearkeyPluginTest;
+using android::hardware::drm::V1_0::vts::DrmHalClearkeyDecryptTest;
+using android::hardware::drm::V1_1::vts::DrmHalClearkeyTest;
+using android::hardware::drm::V1_2::vts::DrmHalTest;
+using android::hardware::drm::V1_2::vts::DrmHalClearkeyTestV1_2;
+using android::hardware::drm::V1_3::vts::DrmHalTestV1_3;
+
+static const std::vector<DrmHalTestParam> kAllInstances = [] {
+    using ::android::hardware::drm::V1_3::ICryptoFactory;
+    using ::android::hardware::drm::V1_3::IDrmFactory;
+
+    std::vector<std::string> drmInstances =
+            android::hardware::getAllHalInstanceNames(IDrmFactory::descriptor);
+    std::vector<std::string> cryptoInstances =
+            android::hardware::getAllHalInstanceNames(ICryptoFactory::descriptor);
+    std::set<std::string> allInstances;
+    allInstances.insert(drmInstances.begin(), drmInstances.end());
+    allInstances.insert(cryptoInstances.begin(), cryptoInstances.end());
+
+    std::vector<DrmHalTestParam> allInstanceUuidCombos;
+    for (const auto &instance : allInstances) {
+        auto drmFactory = IDrmFactory::getService(instance);
+        if (drmFactory == nullptr) {
+            continue;
+        }
+        drmFactory->getSupportedCryptoSchemes(
+            [&](const hidl_vec<hidl_array<uint8_t, 16>>& schemes) {
+                for (const auto &scheme : schemes) {
+                    allInstanceUuidCombos.push_back(DrmHalTestParam(instance, scheme));
+                }
+            });
+    }
+    return allInstanceUuidCombos;
+}();
+
+INSTANTIATE_TEST_CASE_P(PerInstanceUuidV1_0, DrmHalVendorFactoryTest,
+                        testing::ValuesIn(kAllInstances),
+                        drm_vts::PrintParamInstanceToString);
+INSTANTIATE_TEST_CASE_P(PerInstanceUuidV1_0, DrmHalVendorPluginTest,
+                        testing::ValuesIn(kAllInstances),
+                        drm_vts::PrintParamInstanceToString);
+INSTANTIATE_TEST_CASE_P(PerInstanceUuidV1_0, DrmHalVendorDecryptTest,
+                        testing::ValuesIn(kAllInstances),
+                        drm_vts::PrintParamInstanceToString);
+
+INSTANTIATE_TEST_SUITE_P(PerInstanceUuidV1_0, DrmHalClearkeyFactoryTest,
+                         testing::ValuesIn(kAllInstances),
+                         drm_vts::PrintParamInstanceToString);
+INSTANTIATE_TEST_SUITE_P(PerInstanceUuidV1_0, DrmHalClearkeyPluginTest,
+                         testing::ValuesIn(kAllInstances),
+                         drm_vts::PrintParamInstanceToString);
+INSTANTIATE_TEST_SUITE_P(PerInstanceUuidV1_0, DrmHalClearkeyDecryptTest,
+                         testing::ValuesIn(kAllInstances),
+                         drm_vts::PrintParamInstanceToString);
+
+INSTANTIATE_TEST_SUITE_P(PerInstanceUuidV1_1, DrmHalClearkeyTest,
+                         testing::ValuesIn(kAllInstances),
+                         PrintParamInstanceToString);
+
+INSTANTIATE_TEST_SUITE_P(PerInstanceUuidV1_2, DrmHalTest,
+                         testing::ValuesIn(kAllInstances),
+                         PrintParamInstanceToString);
+INSTANTIATE_TEST_SUITE_P(PerInstanceUuidV1_2, DrmHalClearkeyTestV1_2,
+                         testing::ValuesIn(kAllInstances),
+                         PrintParamInstanceToString);
+
+INSTANTIATE_TEST_SUITE_P(PerInstanceUuidV1_3, DrmHalTestV1_3,
+                         testing::ValuesIn(kAllInstances),
+                         PrintParamInstanceToString);
+
+int main(int argc, char** argv) {
+#if defined(__LP64__)
+    const char* kModulePath = "/data/local/tmp/64/lib";
+#else
+    const char* kModulePath = "/data/local/tmp/32/lib";
+#endif
+    DrmHalTest::gVendorModules = new drm_vts::VendorModules(kModulePath);
+    if (DrmHalTest::gVendorModules->getPathList().size() == 0) {
+        std::cerr << "WARNING: No vendor modules found in " << kModulePath <<
+                ", all vendor tests will be skipped" << std::endl;
+    }
+    ::testing::InitGoogleTest(&argc, argv);
+    int status = RUN_ALL_TESTS();
+    ALOGI("Test result = %d", status);
+    return status;
+}
diff --git a/drm/1.3/vts/functional/include/android/hardware/drm/1.3/vts/drm_hal_test.h b/drm/1.3/vts/functional/include/android/hardware/drm/1.3/vts/drm_hal_test.h
new file mode 100644
index 0000000..d8f5277
--- /dev/null
+++ b/drm/1.3/vts/functional/include/android/hardware/drm/1.3/vts/drm_hal_test.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 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 DRM_HAL_TEST_V1_3_H
+#define DRM_HAL_TEST_V1_3_H
+
+#include <android/hardware/drm/1.3/ICryptoFactory.h>
+#include <android/hardware/drm/1.3/IDrmFactory.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/ServiceManagement.h>
+#include <log/log.h>
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "drm_hal_vendor_module_api.h"
+#include "drm_vts_helper.h"
+#include "vendor_modules.h"
+#include "VtsHalHidlTargetCallbackBase.h"
+
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_3 {
+namespace vts {
+
+using android::hardware::hidl_array;
+using android::hardware::hidl_string;
+
+using drm_vts::DrmHalTestParam;
+
+using IDrmFactoryV1_3 = android::hardware::drm::V1_3::IDrmFactory;
+using IDrmPluginV1_0 = android::hardware::drm::V1_0::IDrmPlugin;
+using StatusV1_0 = android::hardware::drm::V1_0::Status;
+
+class DrmHalTestV1_3 : public ::testing::TestWithParam<DrmHalTestParam> {
+public:
+    DrmHalTestV1_3()
+        : drmFactory_(IDrmFactoryV1_3::getService(GetParam().instance_)) {}
+
+    virtual void SetUp() override {
+        ASSERT_NE(drmFactory_, nullptr);
+
+        // create plugin
+        hidl_string packageName("android.hardware.drm.V1_3.vts");
+        auto res = drmFactory_->createPlugin(
+            GetParam().scheme_, packageName,
+            [&](StatusV1_0 status, const sp<IDrmPluginV1_0>& pluginV1_0) {
+                EXPECT_EQ(StatusV1_0::OK, status);
+                drmPlugin_ = pluginV1_0;
+            });
+        EXPECT_TRUE(res.isOk());
+        ASSERT_NE(drmPlugin_, nullptr);
+    }
+
+    virtual void TearDown() override {}
+
+protected:
+    sp<IDrmFactoryV1_3> drmFactory_;
+    sp<IDrmPluginV1_0> drmPlugin_;
+};
+
+}  // namespace vts
+}  // namespace V1_3
+}  // namespace drm
+}  // namespace hardware
+}  // namespace android
+
+#endif  // DRM_HAL_TEST_V1_3_H
diff --git a/gnss/1.0/vts/functional/VtsHalGnssV1_0TargetTest.cpp b/gnss/1.0/vts/functional/VtsHalGnssV1_0TargetTest.cpp
index 6183a1a..c04b4d0 100644
--- a/gnss/1.0/vts/functional/VtsHalGnssV1_0TargetTest.cpp
+++ b/gnss/1.0/vts/functional/VtsHalGnssV1_0TargetTest.cpp
@@ -26,6 +26,8 @@
 #include <condition_variable>
 #include <mutex>
 
+#include <cutils/properties.h>
+
 using android::hardware::Return;
 using android::hardware::Void;
 
@@ -37,6 +39,12 @@
 using android::hardware::gnss::V1_0::IGnssMeasurement;
 using android::sp;
 
+static bool IsAutomotiveDevice() {
+    char buffer[PROPERTY_VALUE_MAX] = {0};
+    property_get("ro.hardware.type", buffer, "");
+    return strncmp(buffer, "automotive", PROPERTY_VALUE_MAX) == 0;
+}
+
 #define TIMEOUT_SEC 2  // for basic commands/responses
 
 // for command line argument on how strictly to run the test
@@ -473,9 +481,9 @@
 
   auto gnssDebug = gnss_hal_->getExtensionGnssDebug();
   ASSERT_TRUE(gnssDebug.isOk());
-  if (info_called_count_ > 0 && last_info_.yearOfHw >= 2017) {
-    sp<IGnssDebug> iGnssDebug = gnssDebug;
-    EXPECT_NE(iGnssDebug, nullptr);
+  if (!IsAutomotiveDevice() && info_called_count_ > 0 && last_info_.yearOfHw >= 2017) {
+      sp<IGnssDebug> iGnssDebug = gnssDebug;
+      EXPECT_NE(iGnssDebug, nullptr);
   }
 }
 
diff --git a/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp b/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp
index e0d8b54..8530ffb 100644
--- a/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp
+++ b/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp
@@ -19,6 +19,7 @@
 #include <gnss_hal_test.h>
 
 #include <android/hardware/gnss/1.1/IGnssConfiguration.h>
+#include <cutils/properties.h>
 #include <gtest/gtest.h>
 
 using android::hardware::hidl_vec;
@@ -32,6 +33,12 @@
 using android::hardware::gnss::V1_1::IGnssConfiguration;
 using android::hardware::gnss::V1_1::IGnssMeasurement;
 
+static bool IsAutomotiveDevice() {
+    char buffer[PROPERTY_VALUE_MAX] = {0};
+    property_get("ro.hardware.type", buffer, "");
+    return strncmp(buffer, "automotive", PROPERTY_VALUE_MAX) == 0;
+}
+
 /*
  * SetupTeardownCreateCleanup:
  * Requests the gnss HAL then calls cleanup
@@ -524,7 +531,8 @@
 TEST_P(GnssHalTest, GnssDebugValuesSanityTest) {
     auto gnssDebug = gnss_hal_->getExtensionGnssDebug();
     ASSERT_TRUE(gnssDebug.isOk());
-    if (gnss_cb_->info_cbq_.calledCount() > 0 && gnss_cb_->last_info_.yearOfHw >= 2017) {
+    if (!IsAutomotiveDevice() && gnss_cb_->info_cbq_.calledCount() > 0 &&
+        gnss_cb_->last_info_.yearOfHw >= 2017) {
         sp<IGnssDebug> iGnssDebug = gnssDebug;
         EXPECT_NE(iGnssDebug, nullptr);
 
diff --git a/graphics/composer/2.1/utils/vts/Android.bp b/graphics/composer/2.1/utils/vts/Android.bp
index cdc0f35..3b0911f 100644
--- a/graphics/composer/2.1/utils/vts/Android.bp
+++ b/graphics/composer/2.1/utils/vts/Android.bp
@@ -23,14 +23,13 @@
         "TestCommandReader.cpp",
     ],
     static_libs: [
-        "VtsHalHidlTargetTestBase",
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.mapper@2.0-vts",
         "android.hardware.graphics.mapper@3.0-vts",
         "android.hardware.graphics.mapper@4.0-vts",
+        "libgtest",
     ],
     export_static_lib_headers: [
-        "VtsHalHidlTargetTestBase",
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.mapper@2.0-vts",
         "android.hardware.graphics.mapper@3.0-vts",
diff --git a/graphics/composer/2.1/utils/vts/ComposerVts.cpp b/graphics/composer/2.1/utils/vts/ComposerVts.cpp
index a8e1480..4b6b7c8 100644
--- a/graphics/composer/2.1/utils/vts/ComposerVts.cpp
+++ b/graphics/composer/2.1/utils/vts/ComposerVts.cpp
@@ -16,8 +16,6 @@
 
 #include <composer-vts/2.1/ComposerVts.h>
 
-#include <VtsHalHidlTargetTestBase.h>
-
 namespace android {
 namespace hardware {
 namespace graphics {
@@ -25,11 +23,6 @@
 namespace V2_1 {
 namespace vts {
 
-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) : mComposer(composer) {
     // ASSERT_* can only be used in functions returning void.
     [this] {
diff --git a/graphics/composer/2.2/utils/vts/Android.bp b/graphics/composer/2.2/utils/vts/Android.bp
index 5432882..a8bb1a2 100644
--- a/graphics/composer/2.2/utils/vts/Android.bp
+++ b/graphics/composer/2.2/utils/vts/Android.bp
@@ -26,11 +26,11 @@
         "libui",
     ],
     static_libs: [
-        "VtsHalHidlTargetTestBase",
         "android.hardware.graphics.composer@2.1-vts",
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.mapper@2.1-vts",
         "libarect",
+        "libgtest",
         "libmath",
         "libnativewindow",
         "librenderengine",
@@ -40,7 +40,6 @@
         "android.hardware.graphics.mapper@4.0-vts",
     ],
     export_static_lib_headers: [
-        "VtsHalHidlTargetTestBase",
         "android.hardware.graphics.composer@2.1-vts",
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.mapper@2.1-vts",
diff --git a/graphics/composer/2.2/utils/vts/ComposerVts.cpp b/graphics/composer/2.2/utils/vts/ComposerVts.cpp
index 93b67f0..a526137 100644
--- a/graphics/composer/2.2/utils/vts/ComposerVts.cpp
+++ b/graphics/composer/2.2/utils/vts/ComposerVts.cpp
@@ -16,7 +16,6 @@
 
 #include <composer-vts/2.2/ComposerVts.h>
 
-#include <VtsHalHidlTargetTestBase.h>
 #include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
 #include <hidl/HidlTransportUtils.h>
 
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 5d22305..6bc2732 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
@@ -22,7 +22,6 @@
 #include <unordered_set>
 #include <vector>
 
-#include <VtsHalHidlTargetTestBase.h>
 #include <android/hardware/graphics/composer/2.2/IComposer.h>
 #include <android/hardware/graphics/composer/2.2/IComposerClient.h>
 #include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
diff --git a/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ReadbackVts.h b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ReadbackVts.h
index 7519a64..d5eedf1 100644
--- a/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ReadbackVts.h
+++ b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ReadbackVts.h
@@ -142,7 +142,7 @@
     const native_handle_t* mBufferHandle = nullptr;
 };
 
-class ReadbackHelper : public ::testing::VtsHalHidlTargetTestBase {
+class ReadbackHelper {
   public:
     static std::string getColorModeString(ColorMode mode);
 
diff --git a/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/RenderEngineVts.h b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/RenderEngineVts.h
index b936cab..4eb4bb7 100644
--- a/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/RenderEngineVts.h
+++ b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/RenderEngineVts.h
@@ -24,8 +24,6 @@
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
-#include <VtsHalHidlTargetTestBase.h>
-
 namespace android {
 namespace hardware {
 namespace graphics {
diff --git a/graphics/composer/2.2/vts/functional/Android.bp b/graphics/composer/2.2/vts/functional/Android.bp
index f987516..e38af00 100644
--- a/graphics/composer/2.2/vts/functional/Android.bp
+++ b/graphics/composer/2.2/vts/functional/Android.bp
@@ -19,7 +19,7 @@
     defaults: ["VtsHalTargetTestDefaults"],
     srcs: [
         "VtsHalGraphicsComposerV2_2ReadbackTest.cpp",
-        "VtsHalGraphicsComposerV2_2TargetTest.cpp"
+        "VtsHalGraphicsComposerV2_2TargetTest.cpp",
     ],
 
     // TODO(b/64437680): Assume these libs are always available on the device.
@@ -51,12 +51,16 @@
         "android.hardware.graphics.mapper@3.0-vts",
         "android.hardware.graphics.mapper@4.0",
         "android.hardware.graphics.mapper@4.0-vts",
-        "librenderengine"
+        "libgtest",
+        "librenderengine",
     ],
     header_libs: [
         "android.hardware.graphics.composer@2.1-command-buffer",
         "android.hardware.graphics.composer@2.2-command-buffer",
     ],
     disable_framework: true,
-    test_suites: ["general-tests", "vts-core"],
+    test_suites: [
+        "general-tests",
+        "vts-core",
+    ],
 }
diff --git a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp
index 044bd96..cb43e64 100644
--- a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp
+++ b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp
@@ -932,7 +932,7 @@
 
 class GraphicsBlendModeCompositionTest
     : public GraphicsCompositionTestBase,
-      public testing::WithParamInterface<std::tuple<string, string>> {
+      public testing::WithParamInterface<std::tuple<std::string, std::string>> {
   public:
     void SetUp() override {
         SetUpBase(std::get<0>(GetParam()));
diff --git a/graphics/composer/2.3/utils/vts/Android.bp b/graphics/composer/2.3/utils/vts/Android.bp
index f65a9c4..3d81e8f 100644
--- a/graphics/composer/2.3/utils/vts/Android.bp
+++ b/graphics/composer/2.3/utils/vts/Android.bp
@@ -23,6 +23,7 @@
     static_libs: [
         "android.hardware.graphics.composer@2.2-vts",
         "android.hardware.graphics.composer@2.3",
+        "libgtest",
     ],
     export_static_lib_headers: [
         "android.hardware.graphics.composer@2.2-vts",
diff --git a/graphics/composer/2.3/utils/vts/ComposerVts.cpp b/graphics/composer/2.3/utils/vts/ComposerVts.cpp
index d4f5b3a..d73a3b0 100644
--- a/graphics/composer/2.3/utils/vts/ComposerVts.cpp
+++ b/graphics/composer/2.3/utils/vts/ComposerVts.cpp
@@ -16,8 +16,6 @@
 
 #include <composer-vts/2.3/ComposerVts.h>
 
-#include <VtsHalHidlTargetTestBase.h>
-
 namespace android {
 namespace hardware {
 namespace graphics {
@@ -27,11 +25,6 @@
 
 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) {}
 
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
index e5ac842..dae9ab4 100644
--- 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
@@ -19,7 +19,6 @@
 #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>
diff --git a/graphics/composer/2.4/utils/vts/Android.bp b/graphics/composer/2.4/utils/vts/Android.bp
index 673c15e..e42223d 100644
--- a/graphics/composer/2.4/utils/vts/Android.bp
+++ b/graphics/composer/2.4/utils/vts/Android.bp
@@ -22,12 +22,12 @@
         "GraphicsComposerCallback.cpp",
     ],
     static_libs: [
-        "VtsHalHidlTargetTestBase",
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer@2.3-vts",
         "android.hardware.graphics.composer@2.3",
         "android.hardware.graphics.composer@2.4",
+        "libgtest",
     ],
     header_libs: [
         "android.hardware.graphics.composer@2.1-command-buffer",
diff --git a/graphics/composer/2.4/utils/vts/ComposerVts.cpp b/graphics/composer/2.4/utils/vts/ComposerVts.cpp
index 8cdc452..c5f3b5e 100644
--- a/graphics/composer/2.4/utils/vts/ComposerVts.cpp
+++ b/graphics/composer/2.4/utils/vts/ComposerVts.cpp
@@ -16,8 +16,6 @@
 
 #include <composer-vts/2.4/ComposerVts.h>
 
-#include <VtsHalHidlTargetTestBase.h>
-
 namespace android {
 namespace hardware {
 namespace graphics {
@@ -27,11 +25,6 @@
 
 using V2_4::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_3::vts::Composer(composer), mComposer(composer) {}
 
diff --git a/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h b/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h
index 3dd8e50..fd59eb9 100644
--- a/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h
+++ b/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h
@@ -19,7 +19,6 @@
 #include <memory>
 #include <vector>
 
-#include <VtsHalHidlTargetTestBase.h>
 #include <android/hardware/graphics/composer/2.4/IComposer.h>
 #include <android/hardware/graphics/composer/2.4/IComposerClient.h>
 #include <composer-vts/2.3/ComposerVts.h>
diff --git a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
index f6b46ff..a7c3fd7 100644
--- a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
+++ b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
@@ -42,6 +42,8 @@
 namespace vts {
 namespace {
 
+using namespace std::chrono_literals;
+
 using common::V1_0::BufferUsage;
 using common::V1_1::RenderIntent;
 using common::V1_2::ColorMode;
@@ -526,7 +528,7 @@
             EXPECT_EQ(Error::UNSUPPORTED,
                       mComposerClient->setAutoLowLatencyMode(mPrimaryDisplay, false));
             GTEST_SUCCEED() << "Auto Low Latency Mode is not supported on display "
-                            << to_string(display) << ", skipping test";
+                            << std::to_string(display) << ", skipping test";
             return;
         }
 
@@ -580,7 +582,7 @@
     if (!contentTypeSupport) {
         EXPECT_EQ(Error::UNSUPPORTED, mComposerClient->setContentType(display, contentType));
         GTEST_SUCCEED() << contentTypeStr << " content type is not supported on display "
-                        << to_string(display) << ", skipping test";
+                        << std::to_string(display) << ", skipping test";
         return;
     }
 
@@ -626,7 +628,7 @@
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IComposer::descriptor)),
         android::hardware::PrintInstanceNameToString);
 
-TEST_F(GraphicsComposerHidlCommandTest, getLayerGenericMetadataKeys) {
+TEST_P(GraphicsComposerHidlCommandTest, getLayerGenericMetadataKeys) {
     std::vector<IComposerClient::LayerGenericMetadataKey> keys;
     mComposerClient->getLayerGenericMetadataKeys(&keys);
 
diff --git a/health/2.1/README.md b/health/2.1/README.md
index bfcf13b..8390570 100644
--- a/health/2.1/README.md
+++ b/health/2.1/README.md
@@ -24,6 +24,9 @@
         ```mk
         # Install default passthrough implementation to vendor.
         PRODUCT_PACKAGES += android.hardware.health@2.1-impl
+
+        # For non-A/B devices, install default passthrough implementation to recovery.
+        PRODUCT_PACKAGES += android.hardware.health@2.1-impl.recovery
         ```
 
         You are done. Otherwise, go to the next step.
@@ -42,6 +45,8 @@
               implementation, See
               [Upgrading from Health HAL 2.0](#update-from-2-0).
 
+        1. [Install the implementation](#install).
+
         1. [Update necessary SELinux permissions](#selinux).
 
         1. [Fix `/charger` symlink](#charger-symlink).
@@ -95,15 +100,22 @@
   `HealthImpl::getHealthInfo` or `HealthImpl::getHealthInfo_2_1` because they call
   `getDiskStats` and `getStorageInfo` to retrieve storage information.
 
+# Install the implementation {#install}
+
+In `device.mk`:
+
+```mk
+# Install the passthrough implementation to vendor.
+PRODUCT_PACKAGES += android.hardware.health@2.1-impl-<device>
+
+# For non-A/B devices, also install the passthrough implementation to recovery.
+PRODUCT_PACKAGES += android.hardware.health@2.1-impl-<device>.recovery
+```
+
 # Update necessary SELinux permissions {#selinux}
 
 For example (replace `<device>` with the device name):
 ```
-# device/<manufacturer>/<device>/sepolicy/vendor/file_contexts
-# Required for charger to open passthrough implementation. Replace <device> with the proper device
-# name. File name must be consistent with `stem` of the implementation module.
-/vendor/lib(64)?/hw/android\.hardware\.health@2\.0-impl-2\.1-<device>\.so u:object_r:same_process_hal_file:s0
-
 # device/<manufacturer>/<device>/sepolicy/vendor/hal_health_default.te
 # Add device specific permissions to hal_health_default domain, especially
 # if a device-specific libhealthd is used and/or device-specific storage related
diff --git a/health/2.1/default/android.hardware.health@2.1-service.rc b/health/2.1/default/android.hardware.health@2.1-service.rc
index 917f1c2..b6d9e3b 100644
--- a/health/2.1/default/android.hardware.health@2.1-service.rc
+++ b/health/2.1/default/android.hardware.health@2.1-service.rc
@@ -1,5 +1,5 @@
 service health-hal-2-1 /vendor/bin/hw/android.hardware.health@2.1-service
-    class hal
+    class hal charger
     user system
     group system
     capabilities WAKE_ALARM
diff --git a/neuralnetworks/1.3/types.t b/neuralnetworks/1.3/types.t
index a973923..d4351ec 100644
--- a/neuralnetworks/1.3/types.t
+++ b/neuralnetworks/1.3/types.t
@@ -553,6 +553,19 @@
 };
 
 /**
+ * Optional timeout duration measured in nanoseconds.
+ */
+safe_union OptionalTimeoutDuration {
+    /** No time point provided. */
+    Monostate none;
+
+    /**
+     * Timeout duration measured in nanoseconds.
+     */
+    uint64_t nanoseconds;
+};
+
+/**
  * Return status of a function.
  */
 enum ErrorStatus : @1.0::ErrorStatus {
diff --git a/radio/1.5/IRadio.hal b/radio/1.5/IRadio.hal
index bc40500..2ec92e5 100644
--- a/radio/1.5/IRadio.hal
+++ b/radio/1.5/IRadio.hal
@@ -18,10 +18,8 @@
 
 import @1.0::CdmaSmsMessage;
 import @1.2::DataRequestReason;
-import @1.4::DataProfileInfo;
 import @1.4::IRadio;
 import @1.5::AccessNetwork;
-import @1.5::BarringInfo;
 import @1.5::DataProfileInfo;
 import @1.5::IndicationFilter;
 import @1.5::LinkAddress;
@@ -118,13 +116,13 @@
             vec<RadioAccessSpecifier> specifiers);
 
     /**
-     * Starts a network scan
+     * Starts a network scan.
      *
      * @param serial Serial number of request.
      * @param request Defines the radio networks/bands/channels which need to be scanned.
      *
-     * Same API as @1.4::IRadio.startNetworkScan_1_4, except using
-     * 1.5 version of NetworkScanRequest
+     * Same API as @1.4::IRadio.startNetworkScan_1_4, except using the
+     * 1.5 NetworkScanRequest as the input param.
      */
     oneway startNetworkScan_1_5(int32_t serial, NetworkScanRequest request);
 
@@ -166,14 +164,14 @@
      * Response function is IRadioResponse.setupDataCallResponse_1_5()
      *
      * Note this API is the same as the 1.4 version except using the
-     * 1.5 AccessNetwork, DataProfileInto, and link addresses as the input param.
+     * 1.5 AccessNetwork, DataProfileInto, and LinkAddress as the input param.
      */
     oneway setupDataCall_1_5(int32_t serial, AccessNetwork accessNetwork,
             DataProfileInfo dataProfileInfo, bool roamingAllowed,
             DataRequestReason reason, vec<LinkAddress> addresses, vec<string> dnses);
 
     /**
-     * Set an apn to initial attach network
+     * Set an APN to initial attach network.
      *
      * @param serial Serial number of request.
      * @param dataProfileInfo data profile containing APN settings
@@ -189,7 +187,7 @@
      * Send data profiles of the current carrier to the modem.
      *
      * @param serial Serial number of request.
-     * @param profiles Array of DataProfile to set.
+     * @param profiles Array of DataProfileInfo to set.
      *
      * Response callback is IRadioResponse.setDataProfileResponse_1_5()
      *
@@ -230,7 +228,7 @@
      *
      * Prevents the reporting of specified unsolicited indications from the radio. This is used
      * for power saving in instances when those indications are not needed. If unset, defaults to
-     * @1.2::IndicationFilter:ALL.
+     * @1.5::IndicationFilter:ALL.
      *
      * @param serial Serial number of request.
      * @param indicationFilter 32-bit bitmap of IndicationFilter. Bits set to 1 indicate the
@@ -250,7 +248,7 @@
     oneway getBarringInfo(int32_t serial);
 
     /**
-     * Request current voice registration state
+     * Request current voice registration state.
      *
      * @param serial Serial number of request.
      *
@@ -259,7 +257,7 @@
     oneway getVoiceRegistrationState_1_5(int32_t serial);
 
     /**
-     * Request current data registration state
+     * Request current data registration state.
      *
      * @param serial Serial number of request.
      *
diff --git a/radio/1.5/IRadioResponse.hal b/radio/1.5/IRadioResponse.hal
index 6a2187f..aa8b526 100644
--- a/radio/1.5/IRadioResponse.hal
+++ b/radio/1.5/IRadioResponse.hal
@@ -21,10 +21,9 @@
 import @1.4::IRadioResponse;
 import @1.5::BarringInfo;
 import @1.5::CellInfo;
+import @1.5::PersoSubstate;
 import @1.5::RegStateResult;
 import @1.5::SetupDataCallResult;
-import @1.4::SetupDataCallResult;
-import @1.5::PersoSubstate;
 
 /**
  * Interface declaring response functions to solicited radio requests.
@@ -177,6 +176,7 @@
     oneway getBarringInfoResponse(RadioResponseInfo info, vec<BarringInfo> barringInfos);
 
     /**
+     * @param info Response info struct containing response type, serial no. and error
      * @param voiceRegResponse Current Voice registration response as defined by RegStateResult
      *        in types.hal
      *
@@ -215,7 +215,6 @@
      */
     oneway getCellInfoListResponse_1_5(RadioResponseInfo info, vec<CellInfo> cellInfo);
 
-
     /**
      * @param info Response info struct containing response type, serial no. and error
      *
diff --git a/radio/1.5/types.hal b/radio/1.5/types.hal
index cf195cc..448e5c8 100644
--- a/radio/1.5/types.hal
+++ b/radio/1.5/types.hal
@@ -19,19 +19,15 @@
 import @1.0::ApnAuthType;
 import @1.0::DataProfileId;
 import @1.0::DataProfileInfoType;
-import @1.0::CdmaSignalStrength;
-import @1.0::EvdoSignalStrength;
 import @1.0::GsmSignalStrength;
 import @1.0::LteSignalStrength;
 import @1.0::PersoSubstate;
-import @1.0::RadioAccessFamily;
 import @1.0::RadioError;
 import @1.0::RegState;
 import @1.0::TimeStampType;
 import @1.1::EutranBands;
 import @1.1::GeranBands;
 import @1.1::RadioAccessNetworks;
-import @1.1::RadioAccessSpecifier;
 import @1.1::ScanStatus;
 import @1.1::ScanType;
 import @1.1::UtranBands;
@@ -43,7 +39,6 @@
 import @1.2::CellIdentityLte;
 import @1.2::CellInfoCdma;
 import @1.2::IndicationFilter;
-import @1.2::NetworkScanRequest;
 import @1.2::TdscdmaSignalStrength;
 import @1.2::WcdmaSignalStrength;
 import @1.4::AccessNetwork;
@@ -51,11 +46,11 @@
 import @1.4::CellIdentityNr;
 import @1.4::DataCallFailCause;
 import @1.4::DataConnActiveStatus;
-import @1.4::DataProfileInfo;
 import @1.4::LteVopsInfo;
 import @1.4::NrIndicators;
 import @1.4::NrSignalStrength;
 import @1.4::PdpProtocolType;
+import @1.4::RadioAccessFamily;
 import @1.4::RadioTechnology;
 
 import android.hidl.safe_union@1.0::Monostate;
@@ -131,7 +126,7 @@
     /** Signal Measurement Type */
     SignalMeasurementType signalMeasurement;
 
-    /** A hysteresis time in milliseconds to prevent flapping. A value of 0 disables hysteresis */
+    /** A hysteresis time in milliseconds to prevent flapping. A value of 0 disables hysteresis. */
     int32_t hysteresisMs;
 
     /**
@@ -175,7 +170,7 @@
 };
 
 /**
- * Overwritten from @1.1::RadioAccessSpecifier to add NGRAN and NgranBands
+ * Overwritten from @1.1::RadioAccessSpecifier to add NGRAN and NgranBands.
  */
 struct RadioAccessSpecifier {
     /**
@@ -264,8 +259,7 @@
 };
 
 /**
- * Overwritten from @1.2::NetworkScanRequest to update
- * RadioAccessSpecifier to 1.5 version
+ * Overwritten from @1.2::NetworkScanRequest to update RadioAccessSpecifier to 1.5 version.
  */
 struct NetworkScanRequest {
     ScanType type;
@@ -328,11 +322,10 @@
 };
 
 /**
- * Extended from @1.4::DataProfileInfo to update ApnTypes to 1.5 version and replace mtu with
+ * Overwritten from @1.4::DataProfileInfo to update ApnTypes to 1.5 version and replace mtu with
  * mtuV4 and mtuV6. In the future, this must be extended instead of overwritten.
  */
 struct DataProfileInfo {
-
     /** ID of the data profile. */
     DataProfileId profileId;
 
@@ -445,8 +438,8 @@
 };
 
 /**
- * Overwritten from @1.4::SetupDataCallResult in order to update the addresses to 1.5
- * version. In 1.5 the type of addresses changes to vector of LinkAddress, and mtu is replaced by
+ * Overwritten from @1.4::SetupDataCallResult in order to update the addresses to 1.5 version.
+ * In 1.5 the type of addresses changes to vector of LinkAddress, and mtu is replaced by
  * mtuV4 and mtuV6.
  */
 struct SetupDataCallResult {
@@ -687,7 +680,7 @@
     } ratSpecificInfo;
 };
 
-/** A union representing the CellIdentity of a single cell */
+/** A union representing the CellIdentity of a single cell. */
 safe_union CellIdentity {
     Monostate noinit;
 
@@ -699,125 +692,116 @@
     CellIdentityNr nr;
 };
 
-/**
- * Combined list of barring services for UTRAN, EUTRAN, and NGRAN.
- *
- * Barring information is defined in:
- * -UTRAN - 3gpp 25.331 Sec 10.2.48.8.6.
- * -EUTRAN - 3gpp 36.331 Sec 6.3.1 SystemInformationBlockType2
- * -NGRAN - 3gpp 38.331 Sec 6.3.2 UAC-BarringInfo and 22.261 Sec 6.22.2.[2-3]
- */
-enum BarringServiceType : int32_t {
-    /** Applicabe to UTRAN */
-    /** Barring for all CS services, including registration */
-    CS_SERVICE,
-    /** Barring for all PS services, including registration */
-    PS_SERVICE,
-    /** Barring for mobile-originated circuit-switched voice calls */
-    CS_VOICE,
-
-    /** Applicable to EUTRAN, NGRAN */
-    /** Barring for mobile-originated signalling for any purpose */
-    MO_SIGNALLING,
-    /** Barring for mobile-originated internet or other interactive data */
-    MO_DATA,
-    /** Barring for circuit-switched fallback calling */
-    CS_FALLBACK,
-    /** Barring for IMS voice calling */
-    MMTEL_VOICE,
-    /** Barring for IMS video calling */
-    MMTEL_VIDEO,
-
-    /** Applicable to UTRAN, EUTRAN, NGRAN */
-    /** Barring for emergency services, either CS or emergency MMTEL */
-    EMERGENCY,
-    /** Barring for short message services */
-    SMS,
-
-    /** Operator-specific barring codes; applicable to NGRAN */
-    OPERATOR_1 = 1001,
-    OPERATOR_2 = 1002,
-    OPERATOR_3 = 1003,
-    OPERATOR_4 = 1004,
-    OPERATOR_5 = 1005,
-    OPERATOR_6 = 1006,
-    OPERATOR_7 = 1007,
-    OPERATOR_8 = 1008,
-    OPERATOR_9 = 1009,
-    OPERATOR_10 = 1010,
-    OPERATOR_11 = 1011,
-    OPERATOR_12 = 1012,
-    OPERATOR_13 = 1013,
-    OPERATOR_14 = 1014,
-    OPERATOR_15 = 1015,
-    OPERATOR_16 = 1016,
-    OPERATOR_17 = 1017,
-    OPERATOR_18 = 1018,
-    OPERATOR_19 = 1019,
-    OPERATOR_20 = 1020,
-    OPERATOR_21 = 1021,
-    OPERATOR_22 = 1022,
-    OPERATOR_23 = 1023,
-    OPERATOR_24 = 1024,
-    OPERATOR_25 = 1025,
-    OPERATOR_26 = 1026,
-    OPERATOR_27 = 1027,
-    OPERATOR_28 = 1028,
-    OPERATOR_29 = 1029,
-    OPERATOR_30 = 1030,
-    OPERATOR_31 = 1031,
-    OPERATOR_32 = 1032,
-};
-
-enum BarringType : int32_t {
-    /** Device is not barred for the given service */
-    NONE,
-    /** Device may be barred based on time and probability factors */
-    CONDITIONAL,
-    /* Device is unconditionally barred */
-    UNCONDITIONAL,
-};
-
-struct ConditionalBarringInfo {
-    /** The barring factor as a percentage 0-100 */
-    int32_t barringFactor;
-
-    /** The number of seconds between re-evaluations of barring */
-    int32_t barringTimeSeconds;
-
-    /**
-     * Indicates whether barring is currently being applied.
-     *
-     * <p>True if the UE applies barring to a conditionally barred
-     * service based on the conditional barring parameters.
-     *
-     * <p>False if the service is conditionally barred but barring
-     * is not currently applied, which could be due to either the
-     * barring criteria not having been evaluated (if the UE has not
-     * attempted to use the service) or due to the criteria being
-     * evaluated and the UE being permitted to use the service
-     * despite conditional barring.
-     */
-    bool isBarred;
-};
-
-safe_union BarringTypeSpecificInfo {
-    /** Barring type is either none or unconditional */
-    Monostate noinit;
-
-    /** Must be included if barring is conditional */
-    ConditionalBarringInfo conditionalBarringInfo;
-};
-
 struct BarringInfo {
-    /** Barring service */
-    BarringServiceType service;
+    /**
+     * Combined list of barring services for UTRAN, EUTRAN, and NGRAN.
+     *
+     * Barring information is defined in:
+     * -UTRAN - 3gpp 25.331 Sec 10.2.48.8.6.
+     * -EUTRAN - 3gpp 36.331 Sec 6.3.1 SystemInformationBlockType2
+     * -NGRAN - 3gpp 38.331 Sec 6.3.2 UAC-BarringInfo and 22.261 Sec 6.22.2.[2-3]
+     */
+    enum ServiceType : int32_t {
+        /** Applicable to UTRAN */
+        /** Barring for all CS services, including registration */
+        CS_SERVICE,
+        /** Barring for all PS services, including registration */
+        PS_SERVICE,
+        /** Barring for mobile-originated circuit-switched voice calls */
+        CS_VOICE,
+
+        /** Applicable to EUTRAN, NGRAN */
+        /** Barring for mobile-originated signalling for any purpose */
+        MO_SIGNALLING,
+        /** Barring for mobile-originated internet or other interactive data */
+        MO_DATA,
+        /** Barring for circuit-switched fallback calling */
+        CS_FALLBACK,
+        /** Barring for IMS voice calling */
+        MMTEL_VOICE,
+        /** Barring for IMS video calling */
+        MMTEL_VIDEO,
+
+        /** Applicable to UTRAN, EUTRAN, NGRAN */
+        /** Barring for emergency services, either CS or emergency MMTEL */
+        EMERGENCY,
+        /** Barring for short message services */
+        SMS,
+
+        /** Operator-specific barring codes; applicable to NGRAN */
+        OPERATOR_1 = 1001,
+        OPERATOR_2 = 1002,
+        OPERATOR_3 = 1003,
+        OPERATOR_4 = 1004,
+        OPERATOR_5 = 1005,
+        OPERATOR_6 = 1006,
+        OPERATOR_7 = 1007,
+        OPERATOR_8 = 1008,
+        OPERATOR_9 = 1009,
+        OPERATOR_10 = 1010,
+        OPERATOR_11 = 1011,
+        OPERATOR_12 = 1012,
+        OPERATOR_13 = 1013,
+        OPERATOR_14 = 1014,
+        OPERATOR_15 = 1015,
+        OPERATOR_16 = 1016,
+        OPERATOR_17 = 1017,
+        OPERATOR_18 = 1018,
+        OPERATOR_19 = 1019,
+        OPERATOR_20 = 1020,
+        OPERATOR_21 = 1021,
+        OPERATOR_22 = 1022,
+        OPERATOR_23 = 1023,
+        OPERATOR_24 = 1024,
+        OPERATOR_25 = 1025,
+        OPERATOR_26 = 1026,
+        OPERATOR_27 = 1027,
+        OPERATOR_28 = 1028,
+        OPERATOR_29 = 1029,
+        OPERATOR_30 = 1030,
+        OPERATOR_31 = 1031,
+        OPERATOR_32 = 1032,
+    } serviceType;
 
     /** The type of barring applied to the service */
-    BarringType type;
+    enum BarringType : int32_t {
+        /** Device is not barred for the given service */
+        NONE,
+        /** Device may be barred based on time and probability factors */
+        CONDITIONAL,
+        /* Device is unconditionally barred */
+        UNCONDITIONAL,
+    } barringType;
 
     /** Type-specific barring info if applicable */
-    BarringTypeSpecificInfo typeSpecificInfo;
+    safe_union BarringTypeSpecificInfo {
+        /** Barring type is either none or unconditional */
+        Monostate noinit;
+
+        /** Must be included if barring is conditional */
+        struct Conditional {
+            /** The barring factor as a percentage 0-100 */
+            int32_t factor;
+
+            /** The number of seconds between re-evaluations of barring */
+            int32_t timeSeconds;
+
+            /**
+             * Indicates whether barring is currently being applied.
+             *
+             * <p>True if the UE applies barring to a conditionally barred
+             * service based on the conditional barring parameters.
+             *
+             * <p>False if the service is conditionally barred but barring
+             * is not currently applied, which could be due to either the
+             * barring criteria not having been evaluated (if the UE has not
+             * attempted to use the service) or due to the criteria being
+             * evaluated and the UE being permitted to use the service
+             * despite conditional barring.
+             */
+            bool isBarred;
+        } conditional;
+    } barringTypeSpecificInfo;
 };
 
 enum IndicationFilter : @1.2::IndicationFilter {
@@ -963,14 +947,14 @@
     string registeredPlmn;
 
     /**
-     * Access-technology-specific registration information, such as for Cdma2000.
+     * Access-technology-specific registration information, such as for CDMA2000.
      */
     safe_union AccessTechnologySpecificInfo {
         Monostate noinit;
 
         struct Cdma2000RegistrationInfo {
             /**
-             * concurrent services support indicator. if registered on a CDMA system.
+             * Concurrent services support indicator. if registered on a CDMA system.
              * false - Concurrent services not supported,
              * true - Concurrent services supported
              */
@@ -999,10 +983,10 @@
         struct EutranRegistrationInfo {
             /**
              * Network capabilities for voice over PS services. This info is valid only on LTE
-             * network and must be present when device is camped on LTE. vopsInfo must be empty when
+             * network and must be present when device is camped on LTE. VopsInfo must be empty when
              * device is camped only on 2G/3G.
              */
-            LteVopsInfo lteVopsInfo; // LTE network capability
+            LteVopsInfo lteVopsInfo;
 
             /**
              * The parameters of NR 5G Non-Standalone. This value is only valid on E-UTRAN,
@@ -1032,17 +1016,20 @@
 };
 
 /**
- * Additional personalization categories in addition to those specified in 3GPP TS 22.022 and 3GPP2 C.S0068-0.
+ * Additional personalization categories in addition to those specified in 3GPP TS 22.022 and
+ * 3GPP2 C.S0068-0.
  */
 enum PersoSubstate : @1.0::PersoSubstate {
     SIM_SPN,
     SIM_SPN_PUK,
-    SIM_SP_EHPLMN,              // Equivalent Home PLMN
+    /** Equivalent Home PLMN */
+    SIM_SP_EHPLMN,
     SIM_SP_EHPLMN_PUK,
     SIM_ICCID,
     SIM_ICCID_PUK,
     SIM_IMPI,
     SIM_IMPI_PUK,
-    SIM_NS_SP,                  // Network subset service provider
+    /** Network subset service provider */
+    SIM_NS_SP,
     SIM_NS_SP_PUK,
 };
diff --git a/radio/1.5/vts/functional/radio_hidl_hal_api.cpp b/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
index 09305de..621825f 100644
--- a/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
@@ -969,7 +969,10 @@
     }
 }
 
-TEST_F(RadioHidlTest_v1_5, setRadioPower_1_5_emergencyCall_cancalled) {
+/*
+ * Test IRadio.setRadioPower_1_5() for the response returned.
+ */
+TEST_F(RadioHidlTest_v1_5, setRadioPower_1_5_emergencyCall_cancelled) {
     // Set radio power to off.
     serial = GetRandomSerialNumber();
     radio_v1_5->setRadioPower_1_5(serial, false, false, false);
diff --git a/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h b/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h
index d1c17e6..ce7b1ab 100644
--- a/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h
+++ b/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h
@@ -114,9 +114,6 @@
     Return<void> supplyNetworkDepersonalizationResponse(const RadioResponseInfo& info,
                                                         int32_t remainingRetries);
 
-    Return<void> supplySimDepersonalizationResponse(const RadioResponseInfo& info,
-            ::android::hardware::radio::V1_5::PersoSubstate persoType, int32_t remainingRetries);
-
     Return<void> getCurrentCallsResponse(
             const RadioResponseInfo& info,
             const ::android::hardware::hidl_vec<::android::hardware::radio::V1_0::Call>& calls);
@@ -579,6 +576,10 @@
 
     Return<void> sendCdmaSmsExpectMoreResponse(const RadioResponseInfo& info,
                                                const SendSmsResult& sms);
+
+    Return<void> supplySimDepersonalizationResponse(
+            const RadioResponseInfo& info,
+            ::android::hardware::radio::V1_5::PersoSubstate persoType, int32_t remainingRetries);
 };
 
 /* Callback class for radio indication */
diff --git a/radio/1.5/vts/functional/radio_response.cpp b/radio/1.5/vts/functional/radio_response.cpp
index a62d086..26401eb 100644
--- a/radio/1.5/vts/functional/radio_response.cpp
+++ b/radio/1.5/vts/functional/radio_response.cpp
@@ -62,13 +62,6 @@
     return Void();
 }
 
-Return<void> RadioResponse_v1_5::supplySimDepersonalizationResponse(
-        const RadioResponseInfo& /*info*/,
-        ::android::hardware::radio::V1_5::PersoSubstate /*persoType*/,
-        int32_t /*remainingRetries*/) {
-    return Void();
-}
-
 Return<void> RadioResponse_v1_5::getCurrentCallsResponse(
         const RadioResponseInfo& /*info*/,
         const ::android::hardware::hidl_vec<::android::hardware::radio::V1_0::Call>& /*calls*/) {
@@ -1011,3 +1004,10 @@
                                                                const SendSmsResult& /*sms*/) {
     return Void();
 }
+
+Return<void> RadioResponse_v1_5::supplySimDepersonalizationResponse(
+        const RadioResponseInfo& /*info*/,
+        ::android::hardware::radio::V1_5::PersoSubstate /*persoType*/,
+        int32_t /*remainingRetries*/) {
+    return Void();
+}
diff --git a/radio/config/1.3/types.hal b/radio/config/1.3/types.hal
index 7860006..aef0ff8 100644
--- a/radio/config/1.3/types.hal
+++ b/radio/config/1.3/types.hal
@@ -50,17 +50,17 @@
     /** 3GPP capability. */
     THREE_GPP_REG             = 1 << 1,
     /** CDMA 2000 with EHRPD capability. */
-    CDMA2000_EHRPD_REG        = 1 << 2,
-    /** GSM capability. */
-    GERAN_REG                 = 1 << 3,
+    CDMA2000_EHRPD            = 1 << 2,
+    /** GSM/EDGE capability. */
+    GERAN                     = 1 << 3,
     /** UMTS capability. */
-    UTRAN_REG                 = 1 << 4,
+    UTRAN                     = 1 << 4,
     /** LTE capability. */
-    EUTRAN_REG                = 1 << 5,
+    EUTRAN                    = 1 << 5,
     /** 5G capability. */
-    NGRAN_REG                 = 1 << 6,
-    /** Dual Connectivity capability. */
-    EN_DC_REG                 = 1 << 7,
+    NGRAN                     = 1 << 6,
+    /** 5G dual connectivity capability. */
+    EN_DC                     = 1 << 7,
     /** VoLTE capability (IMS registered). */
     PS_VOICE_REG              = 1 << 8,
     /** CS voice call capability. */
@@ -72,7 +72,7 @@
     /** Network scanning capability. */
     NETWORK_SCAN              = 1 << 12,
     /** CDMA capability for SIM associated with modem. */
-    CSIM                      = 1 << 13,
+    CSIM_APP                  = 1 << 13,
 };
 
 struct ConcurrentModemFeatures {
diff --git a/sensors/2.0/multihal/HalProxy.cpp b/sensors/2.0/multihal/HalProxy.cpp
index 7c52661..ac6f17a 100644
--- a/sensors/2.0/multihal/HalProxy.cpp
+++ b/sensors/2.0/multihal/HalProxy.cpp
@@ -290,6 +290,8 @@
     stream << "  Wakelock ref count: " << mWakelockRefCount << std::endl;
     stream << "  # of events on pending write writes queue: " << mSizePendingWriteEventsQueue
            << std::endl;
+    stream << " Most events seen on pending write events queue: "
+           << mMostEventsObservedPendingWriteEventsQueue << std::endl;
     if (!mPendingWriteEventsQueue.empty()) {
         stream << "  Size of events list on front of pending writes queue: "
                << mPendingWriteEventsQueue.front().first.size() << std::endl;
@@ -571,6 +573,8 @@
         std::vector<Event> eventsLeft(events.begin() + numToWrite, events.end());
         mPendingWriteEventsQueue.push({eventsLeft, numWakeupEvents});
         mSizePendingWriteEventsQueue += numLeft;
+        mMostEventsObservedPendingWriteEventsQueue =
+                std::max(mMostEventsObservedPendingWriteEventsQueue, mSizePendingWriteEventsQueue);
         mEventQueueWriteCV.notify_one();
     }
 }
diff --git a/sensors/2.0/multihal/include/HalProxy.h b/sensors/2.0/multihal/include/HalProxy.h
index ce28e67..978f7cf 100644
--- a/sensors/2.0/multihal/include/HalProxy.h
+++ b/sensors/2.0/multihal/include/HalProxy.h
@@ -200,6 +200,9 @@
      */
     std::queue<std::pair<std::vector<Event>, size_t>> mPendingWriteEventsQueue;
 
+    //! The most events observed on the pending write events queue for debug purposes.
+    size_t mMostEventsObservedPendingWriteEventsQueue = 0;
+
     //! The max number of events allowed in the pending write events queue
     static constexpr size_t kMaxSizePendingWriteEventsQueue = 100000;
 
diff --git a/soundtrigger/2.3/types.hal b/soundtrigger/2.3/types.hal
index 730f969..10fc34e 100644
--- a/soundtrigger/2.3/types.hal
+++ b/soundtrigger/2.3/types.hal
@@ -66,7 +66,7 @@
      * Bit field encoding of the AudioCapabilities
      * supported by the firmware.
      */
-    uint32_t audioCapabilities;
+    bitfield<AudioCapabilities> audioCapabilities;
 };
 
 /**
diff --git a/wifi/1.2/IWifiStaIface.hal b/wifi/1.2/IWifiStaIface.hal
index 3a7f777..d65b33b 100644
--- a/wifi/1.2/IWifiStaIface.hal
+++ b/wifi/1.2/IWifiStaIface.hal
@@ -23,7 +23,7 @@
 /**
  * Interface used to represent a single STA iface.
  *
- * IWifiChip.createStaIface() may return a @1.2::IWifiStaIface when supported.
+ * IWifiChip.createStaIface() must return a @1.2::IWifiStaIface when supported.
  */
 interface IWifiStaIface extends @1.0::IWifiStaIface {
     /**
diff --git a/wifi/1.3/IWifiStaIface.hal b/wifi/1.3/IWifiStaIface.hal
index 81c0c38..3a75509 100644
--- a/wifi/1.3/IWifiStaIface.hal
+++ b/wifi/1.3/IWifiStaIface.hal
@@ -23,7 +23,7 @@
 /**
  * Interface used to represent a single STA iface.
  *
- * IWifiChip.createStaIface() may return a @1.3::IWifiStaIface when supported.
+ * IWifiChip.createStaIface() must return a @1.3::IWifiStaIface when supported.
  */
 interface IWifiStaIface extends @1.2::IWifiStaIface {
     /**
diff --git a/wifi/1.4/default/hidl_struct_util.cpp b/wifi/1.4/default/hidl_struct_util.cpp
index 4996e35..fd1d5b1 100644
--- a/wifi/1.4/default/hidl_struct_util.cpp
+++ b/wifi/1.4/default/hidl_struct_util.cpp
@@ -2715,6 +2715,21 @@
     }
     return true;
 }
+
+legacy_hal::wifi_interface_type convertHidlIfaceTypeToLegacy(
+    IfaceType hidl_interface_type) {
+    switch (hidl_interface_type) {
+        case IfaceType::STA:
+            return legacy_hal::WIFI_INTERFACE_TYPE_STA;
+        case IfaceType::AP:
+            return legacy_hal::WIFI_INTERFACE_TYPE_AP;
+        case IfaceType::P2P:
+            return legacy_hal::WIFI_INTERFACE_TYPE_P2P;
+        case IfaceType::NAN:
+            return legacy_hal::WIFI_INTERFACE_TYPE_NAN;
+    }
+    CHECK(false);
+}
 }  // namespace hidl_struct_util
 }  // namespace implementation
 }  // namespace V1_4
diff --git a/wifi/1.4/default/hidl_struct_util.h b/wifi/1.4/default/hidl_struct_util.h
index d040c1f..929f877 100644
--- a/wifi/1.4/default/hidl_struct_util.h
+++ b/wifi/1.4/default/hidl_struct_util.h
@@ -65,6 +65,8 @@
 bool convertLegacyWifiMacInfosToHidl(
     const std::vector<legacy_hal::WifiMacInfo>& legacy_mac_infos,
     std::vector<IWifiChipEventCallback::RadioModeInfo>* hidl_radio_mode_infos);
+legacy_hal::wifi_interface_type convertHidlIfaceTypeToLegacy(
+    IfaceType hidl_interface_type);
 
 // STA iface conversion methods.
 bool convertLegacyFeaturesToHidlStaCapabilities(
diff --git a/wifi/1.4/default/wifi_chip.cpp b/wifi/1.4/default/wifi_chip.cpp
index 3498510..4c9fad1 100644
--- a/wifi/1.4/default/wifi_chip.cpp
+++ b/wifi/1.4/default/wifi_chip.cpp
@@ -797,6 +797,15 @@
         return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
     }
     std::string ifname = allocateApIfaceName();
+    legacy_hal::wifi_error legacy_status =
+        legacy_hal_.lock()->createVirtualInterface(
+            ifname,
+            hidl_struct_util::convertHidlIfaceTypeToLegacy(IfaceType::AP));
+    if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+        LOG(ERROR) << "Failed to add interface: " << ifname << " "
+                   << legacyErrorToString(legacy_status);
+        return {createWifiStatusFromLegacyError(legacy_status), {}};
+    }
     sp<WifiApIface> iface = new WifiApIface(ifname, legacy_hal_, iface_util_);
     ap_ifaces_.push_back(iface);
     for (const auto& callback : event_cb_handler_.getCallbacks()) {
@@ -835,6 +844,12 @@
     // nan/rtt objects over AP iface. But, there is no harm to do it
     // here and not make that assumption all over the place.
     invalidateAndRemoveDependencies(ifname);
+    legacy_hal::wifi_error legacy_status =
+        legacy_hal_.lock()->deleteVirtualInterface(ifname);
+    if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+        LOG(ERROR) << "Failed to remove interface: " << ifname << " "
+                   << legacyErrorToString(legacy_status);
+    }
     invalidateAndClear(ap_ifaces_, iface);
     for (const auto& callback : event_cb_handler_.getCallbacks()) {
         if (!callback->onIfaceRemoved(IfaceType::AP, ifname).isOk()) {
@@ -944,6 +959,15 @@
         return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
     }
     std::string ifname = allocateStaIfaceName();
+    legacy_hal::wifi_error legacy_status =
+        legacy_hal_.lock()->createVirtualInterface(
+            ifname,
+            hidl_struct_util::convertHidlIfaceTypeToLegacy(IfaceType::STA));
+    if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+        LOG(ERROR) << "Failed to add interface: " << ifname << " "
+                   << legacyErrorToString(legacy_status);
+        return {createWifiStatusFromLegacyError(legacy_status), {}};
+    }
     sp<WifiStaIface> iface = new WifiStaIface(ifname, legacy_hal_, iface_util_);
     sta_ifaces_.push_back(iface);
     for (const auto& callback : event_cb_handler_.getCallbacks()) {
@@ -979,6 +1003,12 @@
     }
     // Invalidate & remove any dependent objects first.
     invalidateAndRemoveDependencies(ifname);
+    legacy_hal::wifi_error legacy_status =
+        legacy_hal_.lock()->deleteVirtualInterface(ifname);
+    if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+        LOG(ERROR) << "Failed to remove interface: " << ifname << " "
+                   << legacyErrorToString(legacy_status);
+    }
     invalidateAndClear(sta_ifaces_, iface);
     for (const auto& callback : event_cb_handler_.getCallbacks()) {
         if (!callback->onIfaceRemoved(IfaceType::STA, ifname).isOk()) {
diff --git a/wifi/1.4/default/wifi_legacy_hal.cpp b/wifi/1.4/default/wifi_legacy_hal.cpp
index 3ca3226..a040c89 100644
--- a/wifi/1.4/default/wifi_legacy_hal.cpp
+++ b/wifi/1.4/default/wifi_legacy_hal.cpp
@@ -19,6 +19,7 @@
 
 #include <android-base/logging.h>
 #include <cutils/properties.h>
+#include <net/if.h>
 
 #include "hidl_sync_util.h"
 #include "wifi_legacy_hal.h"
@@ -1355,6 +1356,7 @@
         LOG(ERROR) << "Failed to enumerate interface handles";
         return status;
     }
+    iface_name_to_handle_.clear();
     for (int i = 0; i < num_iface_handles; ++i) {
         std::array<char, IFNAMSIZ> iface_name_arr = {};
         status = global_func_table_.wifi_get_iface_name(
@@ -1421,6 +1423,39 @@
     return {status, std::move(cached_scan_results)};
 }
 
+wifi_error WifiLegacyHal::createVirtualInterface(const std::string& ifname,
+                                                 wifi_interface_type iftype) {
+    // Create the interface if it doesn't exist. If interface already exist,
+    // Vendor Hal should return WIFI_SUCCESS.
+    wifi_error status = global_func_table_.wifi_virtual_interface_create(
+        global_handle_, ifname.c_str(), iftype);
+    return handleVirtualInterfaceCreateOrDeleteStatus(ifname, status);
+}
+
+wifi_error WifiLegacyHal::deleteVirtualInterface(const std::string& ifname) {
+    // Delete the interface if it was created dynamically.
+    wifi_error status = global_func_table_.wifi_virtual_interface_delete(
+        global_handle_, ifname.c_str());
+    return handleVirtualInterfaceCreateOrDeleteStatus(ifname, status);
+}
+
+wifi_error WifiLegacyHal::handleVirtualInterfaceCreateOrDeleteStatus(
+    const std::string& ifname, wifi_error status) {
+    if (status == WIFI_SUCCESS) {
+        // refresh list of handlers now.
+        status = retrieveIfaceHandles();
+    } else if (status == WIFI_ERROR_NOT_SUPPORTED) {
+        // Vendor hal does not implement this API. Such vendor implementations
+        // are expected to create / delete interface by other means.
+
+        // check if interface exists.
+        if (if_nametoindex(ifname.c_str())) {
+            status = retrieveIfaceHandles();
+        }
+    }
+    return status;
+}
+
 void WifiLegacyHal::invalidate() {
     global_handle_ = nullptr;
     iface_name_to_handle_.clear();
diff --git a/wifi/1.4/default/wifi_legacy_hal.h b/wifi/1.4/default/wifi_legacy_hal.h
index a7b40a0..72cf197 100644
--- a/wifi/1.4/default/wifi_legacy_hal.h
+++ b/wifi/1.4/default/wifi_legacy_hal.h
@@ -369,6 +369,11 @@
     wifi_error setCountryCode(const std::string& iface_name,
                               std::array<int8_t, 2> code);
 
+    // interface functions.
+    wifi_error createVirtualInterface(const std::string& ifname,
+                                      wifi_interface_type iftype);
+    wifi_error deleteVirtualInterface(const std::string& ifname);
+
    private:
     // Retrieve interface handles for all the available interfaces.
     wifi_error retrieveIfaceHandles();
@@ -380,6 +385,9 @@
     std::pair<wifi_error, std::vector<wifi_cached_scan_results>>
     getGscanCachedResults(const std::string& iface_name);
     void invalidate();
+    // Handles wifi (error) status of Virtual interface create/delete
+    wifi_error handleVirtualInterfaceCreateOrDeleteStatus(
+        const std::string& ifname, wifi_error status);
 
     // Global function table of legacy HAL.
     wifi_hal_fn global_func_table_;
diff --git a/wifi/1.4/default/wifi_legacy_hal_stubs.cpp b/wifi/1.4/default/wifi_legacy_hal_stubs.cpp
index bbe470e..6945b4c 100644
--- a/wifi/1.4/default/wifi_legacy_hal_stubs.cpp
+++ b/wifi/1.4/default/wifi_legacy_hal_stubs.cpp
@@ -139,6 +139,8 @@
     populateStubFor(&hal_fn->wifi_set_radio_mode_change_handler);
     populateStubFor(&hal_fn->wifi_set_latency_mode);
     populateStubFor(&hal_fn->wifi_set_thermal_mitigation_mode);
+    populateStubFor(&hal_fn->wifi_virtual_interface_create);
+    populateStubFor(&hal_fn->wifi_virtual_interface_delete);
     return true;
 }
 }  // namespace legacy_hal
diff --git a/wifi/hostapd/1.2/types.hal b/wifi/hostapd/1.2/types.hal
index 54e6529..9c187fa 100644
--- a/wifi/hostapd/1.2/types.hal
+++ b/wifi/hostapd/1.2/types.hal
@@ -38,6 +38,9 @@
     WLAN_REASON_DISASSOC_AP_BUSY = 5,
 };
 
+/**
+ * Mac Address type. 6 octets representing physical address of a device.
+ */
 typedef uint8_t[6] MacAddress;
 
 /**
diff --git a/wifi/supplicant/1.2/vts/functional/supplicant_sta_iface_hidl_test.cpp b/wifi/supplicant/1.2/vts/functional/supplicant_sta_iface_hidl_test.cpp
index 8116c3f..f38dda4 100644
--- a/wifi/supplicant/1.2/vts/functional/supplicant_sta_iface_hidl_test.cpp
+++ b/wifi/supplicant/1.2/vts/functional/supplicant_sta_iface_hidl_test.cpp
@@ -111,9 +111,14 @@
         // If DPP is not supported, we just pass the test.
         sta_iface_->getKeyMgmtCapabilities(
             [&](const SupplicantStatus& status, uint32_t keyMgmtMaskInternal) {
-                EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+                // Since getKeyMgmtCapabilities() is overridden by an
+                // upgraded API in newer HAL versions, allow for
+                // FAILURE_UNKNOWN and return DPP is not supported.
+                if (status.code != SupplicantStatusCode::FAILURE_UNKNOWN) {
+                    EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
 
-                keyMgmtMask = keyMgmtMaskInternal;
+                    keyMgmtMask = keyMgmtMaskInternal;
+                }
             });
 
         if (!(keyMgmtMask & ISupplicantStaNetwork::KeyMgmtMask::DPP)) {
@@ -268,8 +273,12 @@
  * GetKeyMgmtCapabilities
  */
 TEST_P(SupplicantStaIfaceHidlTest, GetKeyMgmtCapabilities) {
-    sta_iface_->getKeyMgmtCapabilities(
-        [&](const SupplicantStatus& status, uint32_t keyMgmtMask) {
+    sta_iface_->getKeyMgmtCapabilities([&](const SupplicantStatus& status,
+                                           uint32_t keyMgmtMask) {
+        // Since this API is overridden by an upgraded API in newer HAL
+        // versions, allow FAILURE_UNKNOWN to indicate that the test is no
+        // longer supported on newer HAL.
+        if (status.code != SupplicantStatusCode::FAILURE_UNKNOWN) {
             EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
 
             // Even though capabilities vary, these two are always set in HAL
@@ -277,7 +286,8 @@
             EXPECT_TRUE(keyMgmtMask & ISupplicantStaNetwork::KeyMgmtMask::NONE);
             EXPECT_TRUE(keyMgmtMask &
                         ISupplicantStaNetwork::KeyMgmtMask::IEEE8021X);
-        });
+        }
+    });
 }
 
 /*
@@ -455,4 +465,4 @@
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(
             android::hardware::wifi::supplicant::V1_2::ISupplicant::
                 descriptor))),
-    android::hardware::PrintInstanceTupleNameToString<>);
\ No newline at end of file
+    android::hardware::PrintInstanceTupleNameToString<>);
diff --git a/wifi/supplicant/1.2/vts/functional/supplicant_sta_network_hidl_test.cpp b/wifi/supplicant/1.2/vts/functional/supplicant_sta_network_hidl_test.cpp
index 4c3d808..54ceb20 100644
--- a/wifi/supplicant/1.2/vts/functional/supplicant_sta_network_hidl_test.cpp
+++ b/wifi/supplicant/1.2/vts/functional/supplicant_sta_network_hidl_test.cpp
@@ -112,13 +112,23 @@
     uint32_t keyMgmt = (uint32_t)ISupplicantStaNetwork::KeyMgmtMask::SAE;
 
     sta_network_->setKeyMgmt_1_2(keyMgmt, [](const SupplicantStatus &status) {
-        EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+        // Since this API is overridden by an upgraded API in newer HAL
+        // versions, allow FAILURE_UNKNOWN to indicate that the test is no
+        // longer supported on newer HALs.
+        if (status.code != SupplicantStatusCode::FAILURE_UNKNOWN) {
+            EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+        }
     });
 
     sta_network_->getKeyMgmt_1_2(
         [&keyMgmt](const SupplicantStatus &status, uint32_t keyMgmtOut) {
-            EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
-            EXPECT_EQ(keyMgmtOut, keyMgmt);
+            // Since this API is overridden by an upgraded API in newer HAL
+            // versions, allow FAILURE_UNKNOWN to indicate that the test is no
+            // longer supported on newer HALs.
+            if (status.code != SupplicantStatusCode::FAILURE_UNKNOWN) {
+                EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+                EXPECT_EQ(keyMgmtOut, keyMgmt);
+            }
         });
 }
 
@@ -131,14 +141,24 @@
 
     sta_network_->setGroupCipher_1_2(
         groupCipher, [](const SupplicantStatus &status) {
-            EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+            // Since this API is overridden by an upgraded API in newer HAL
+            // versions, allow FAILURE_UNKNOWN to indicate that the test is no
+            // longer supported on newer HALs.
+            if (status.code != SupplicantStatusCode::FAILURE_UNKNOWN) {
+                EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+            }
         });
 
     sta_network_->getGroupCipher_1_2(
         [&groupCipher](const SupplicantStatus &status,
                        uint32_t groupCipherOut) {
-            EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
-            EXPECT_EQ(groupCipherOut, groupCipher);
+            // Since this API is overridden by an upgraded API in newer HAL
+            // versions, allow FAILURE_UNKNOWN to indicate that the test is no
+            // longer supported on newer HALs.
+            if (status.code != SupplicantStatusCode::FAILURE_UNKNOWN) {
+                EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+                EXPECT_EQ(groupCipherOut, groupCipher);
+            }
         });
 }
 
@@ -151,14 +171,24 @@
 
     sta_network_->setPairwiseCipher_1_2(
         pairwiseCipher, [](const SupplicantStatus &status) {
-            EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+            // Since this API is overridden by an upgraded API in newer HAL
+            // versions, allow FAILURE_UNKNOWN to indicate that the test is no
+            // longer supported on newer HALs.
+            if (status.code != SupplicantStatusCode::FAILURE_UNKNOWN) {
+                EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+            }
         });
 
     sta_network_->getPairwiseCipher_1_2(
         [&pairwiseCipher](const SupplicantStatus &status,
                           uint32_t pairwiseCipherOut) {
-            EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
-            EXPECT_EQ(pairwiseCipherOut, pairwiseCipher);
+            // Since this API is overridden by an upgraded API in newer HAL
+            // versions, allow FAILURE_UNKNOWN to indicate that the test is no
+            // longer supported on newer HALs.
+            if (status.code != SupplicantStatusCode::FAILURE_UNKNOWN) {
+                EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+                EXPECT_EQ(pairwiseCipherOut, pairwiseCipher);
+            }
         });
 }
 
diff --git a/wifi/supplicant/1.3/vts/functional/supplicant_sta_network_hidl_test.cpp b/wifi/supplicant/1.3/vts/functional/supplicant_sta_network_hidl_test.cpp
index 13f3366..93eabd6 100644
--- a/wifi/supplicant/1.3/vts/functional/supplicant_sta_network_hidl_test.cpp
+++ b/wifi/supplicant/1.3/vts/functional/supplicant_sta_network_hidl_test.cpp
@@ -17,7 +17,6 @@
 #include <android-base/logging.h>
 
 #include <VtsCoreUtil.h>
-#include <VtsHalHidlTargetTestBase.h>
 #include <android/hardware/wifi/1.0/IWifi.h>
 #include <android/hardware/wifi/1.1/IWifi.h>
 #include <android/hardware/wifi/supplicant/1.3/ISupplicant.h>