Merge "VtsHalGraphicsComposer3_TargetTest test fixes."
diff --git a/audio/common/all-versions/default/service/Android.bp b/audio/common/all-versions/default/service/Android.bp
index 0d4775c6a..1bd6abe 100644
--- a/audio/common/all-versions/default/service/Android.bp
+++ b/audio/common/all-versions/default/service/Android.bp
@@ -7,17 +7,40 @@
     default_applicable_licenses: ["hardware_interfaces_license"],
 }
 
+soong_config_module_type {
+    name: "android_hardware_audio_config_default",
+    module_type: "cc_defaults",
+    config_namespace: "android_hardware_audio",
+    bool_variables: [
+        "run_64bit",
+    ],
+    properties: ["compile_multilib"],
+}
+
+android_hardware_audio_config_default {
+    name: "android_hardware_audio_config_defaults",
+
+    soong_config_variables: {
+        run_64bit: {
+            conditions_default: {
+                // Prefer 32 bit as the binary must always be installed at the same
+                // location for init to start it and the build system does not support
+                // having two binaries installable to the same location even if they are
+                // not installed in the same build.
+                compile_multilib: "prefer32",
+            },
+            compile_multilib: "64",
+        },
+    },
+}
+
 cc_binary {
     name: "android.hardware.audio.service",
 
     init_rc: ["android.hardware.audio.service.rc"],
     relative_install_path: "hw",
     vendor: true,
-    // Prefer 32 bit as the binary must always be installed at the same
-    // location for init to start it and the build system does not support
-    // having two binaries installable to the same location even if they are
-    // not installed in the same build.
-    compile_multilib: "prefer32",
+
     srcs: ["service.cpp"],
 
     cflags: [
@@ -34,6 +57,10 @@
         "libutils",
         "libhardware",
     ],
+
+    defaults: [
+        "android_hardware_audio_config_defaults",
+    ],
 }
 
 // Legacy service name, use android.hardware.audio.service instead
diff --git a/automotive/vehicle/TEST_MAPPING b/automotive/vehicle/TEST_MAPPING
index 3696351..ff6f3be 100644
--- a/automotive/vehicle/TEST_MAPPING
+++ b/automotive/vehicle/TEST_MAPPING
@@ -20,6 +20,9 @@
     },
     {
       "name": "FakeUserHalTest"
+    },
+    {
+      "name": "DefaultVehicleHalTest"
     }
   ]
 }
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/Android.bp b/automotive/vehicle/aidl/impl/fake_impl/hardware/Android.bp
index dfc2efc..dcd9208 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/Android.bp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/Android.bp
@@ -22,10 +22,17 @@
     name: "FakeVehicleHardware",
     vendor: true,
     srcs: ["src/*.cpp"],
-    cflags: ["-DENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING"],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
-    defaults: ["VehicleHalDefaults"],
+    defaults: [
+        "VehicleHalDefaults",
+        "FakeVehicleHardwareDefaults",
+    ],
+}
+
+cc_defaults {
+    name: "FakeVehicleHardwareDefaults",
+    cflags: ["-DENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING"],
     header_libs: [
         "IVehicleHardware",
         "VehicleHalDefaultConfig",
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
index 104147a..5b2003e 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
@@ -62,27 +62,6 @@
 const char* VENDOR_OVERRIDE_DIR = "/vendor/etc/automotive/vhaloverride/";
 const char* OVERRIDE_PROPERTY = "persist.vendor.vhal_init_value_override";
 
-template <class T>
-StatusCode getErrorCode(const Result<T>& result) {
-    if (result.ok()) {
-        return StatusCode::OK;
-    }
-    return static_cast<StatusCode>(result.error().code());
-}
-
-template <class T>
-int getIntErrorCode(const Result<T>& result) {
-    return toInt(getErrorCode(result));
-}
-
-template <class T>
-std::string getErrorMsg(const Result<T>& result) {
-    if (result.ok()) {
-        return "";
-    }
-    return result.error().message();
-}
-
 }  // namespace
 
 void FakeVehicleHardware::storePropInitialValue(const defaultconfig::ConfigDeclaration& config) {
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h b/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h
index 2b36c72..013d177 100644
--- a/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h
+++ b/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h
@@ -25,6 +25,7 @@
 #include <aidl/android/hardware/automotive/vehicle/FuelType.h>
 #include <aidl/android/hardware/automotive/vehicle/GetValueRequest.h>
 #include <aidl/android/hardware/automotive/vehicle/GetValueResult.h>
+#include <aidl/android/hardware/automotive/vehicle/GetValueResults.h>
 #include <aidl/android/hardware/automotive/vehicle/Obd2CommonIgnitionMonitors.h>
 #include <aidl/android/hardware/automotive/vehicle/Obd2FuelSystemStatus.h>
 #include <aidl/android/hardware/automotive/vehicle/Obd2FuelType.h>
@@ -34,6 +35,7 @@
 #include <aidl/android/hardware/automotive/vehicle/PortLocationType.h>
 #include <aidl/android/hardware/automotive/vehicle/SetValueRequest.h>
 #include <aidl/android/hardware/automotive/vehicle/SetValueResult.h>
+#include <aidl/android/hardware/automotive/vehicle/SetValueResults.h>
 #include <aidl/android/hardware/automotive/vehicle/StatusCode.h>
 #include <aidl/android/hardware/automotive/vehicle/VehicleApPowerStateReport.h>
 #include <aidl/android/hardware/automotive/vehicle/VehicleApPowerStateReq.h>
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h b/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h
index 95e58c6..63eb747 100644
--- a/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h
+++ b/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h
@@ -18,6 +18,9 @@
 #define android_hardware_automotive_vehicle_aidl_impl_utils_common_include_VehicleUtils_H_
 
 #include <VehicleHalTypes.h>
+
+#include <android-base/format.h>
+#include <android-base/result.h>
 #include <utils/Log.h>
 
 namespace android {
@@ -183,6 +186,60 @@
     return size;
 }
 
+template <class T>
+::aidl::android::hardware::automotive::vehicle::StatusCode getErrorCode(
+        const ::android::base::Result<T>& result) {
+    if (result.ok()) {
+        return ::aidl::android::hardware::automotive::vehicle::StatusCode::OK;
+    }
+    return static_cast<::aidl::android::hardware::automotive::vehicle::StatusCode>(
+            result.error().code());
+}
+
+template <class T>
+int getIntErrorCode(const ::android::base::Result<T>& result) {
+    return toInt(getErrorCode(result));
+}
+
+template <class T>
+std::string getErrorMsg(const ::android::base::Result<T>& result) {
+    if (result.ok()) {
+        return "";
+    }
+    return result.error().message();
+}
+
+template <class T>
+::ndk::ScopedAStatus toScopedAStatus(
+        const ::android::base::Result<T>& result,
+        ::aidl::android::hardware::automotive::vehicle::StatusCode status,
+        std::string additionalErrorMsg) {
+    if (result.ok()) {
+        return ::ndk::ScopedAStatus::ok();
+    }
+    return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+            toInt(status),
+            fmt::format("{}, error: {}", additionalErrorMsg, getErrorMsg(result)).c_str());
+}
+
+template <class T>
+::ndk::ScopedAStatus toScopedAStatus(
+        const ::android::base::Result<T>& result,
+        ::aidl::android::hardware::automotive::vehicle::StatusCode status) {
+    return toScopedAStatus(result, status, "");
+}
+
+template <class T>
+::ndk::ScopedAStatus toScopedAStatus(const ::android::base::Result<T>& result) {
+    return toScopedAStatus(result, getErrorCode(result));
+}
+
+template <class T>
+::ndk::ScopedAStatus toScopedAStatus(const ::android::base::Result<T>& result,
+                                     std::string additionalErrorMsg) {
+    return toScopedAStatus(result, getErrorCode(result), additionalErrorMsg);
+}
+
 }  // namespace vehicle
 }  // namespace automotive
 }  // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/vhal/Android.bp b/automotive/vehicle/aidl/impl/vhal/Android.bp
index 0704107..79d3ebd 100644
--- a/automotive/vehicle/aidl/impl/vhal/Android.bp
+++ b/automotive/vehicle/aidl/impl/vhal/Android.bp
@@ -1,37 +1,68 @@
-// Copyright (C) 2021 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.
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "hardware_interfaces_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["hardware_interfaces_license"],
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 cc_binary {
     name: "android.hardware.automotive.vehicle-aidl-default-service",
-    defaults: ["VehicleHalDefaults"],
-    local_include_dirs: ["include"],
+    vendor: true,
+    defaults: [
+        "FakeVehicleHardwareDefaults",
+        "VehicleHalDefaults",
+        "android-automotive-large-parcelable-defaults",
+    ],
     vintf_fragments: ["vhal-default-service.xml"],
     init_rc: ["vhal-default-service.rc"],
-    vendor: true,
     relative_install_path: "hw",
-    srcs: ["src/*.cpp"],
+    srcs: ["src/VehicleService.cpp"],
+    static_libs: [
+        "DefaultVehicleHal",
+        "FakeVehicleHardware",
+        "VehicleHalUtils",
+        "android-automotive-large-parcelable-vendor-lib",
+    ],
+    header_libs: [
+        "IVehicleHardware",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+    ],
+}
+
+cc_library {
+    name: "DefaultVehicleHal",
+    vendor: true,
+    defaults: [
+        "VehicleHalDefaults",
+        "android-automotive-large-parcelable-defaults",
+    ],
+    local_include_dirs: ["include"],
+    export_include_dirs: ["include"],
+    srcs: [
+        "src/ConnectedClient.cpp",
+        "src/DefaultVehicleHal.cpp",
+    ],
     static_libs: [
         "VehicleHalUtils",
+        "android-automotive-large-parcelable-vendor-lib",
+    ],
+    header_libs: [
+        "IVehicleHardware",
     ],
     shared_libs: [
         "libbinder_ndk",
diff --git a/automotive/vehicle/aidl/impl/vhal/include/ConnectedClient.h b/automotive/vehicle/aidl/impl/vhal/include/ConnectedClient.h
new file mode 100644
index 0000000..43a9603
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/vhal/include/ConnectedClient.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef android_hardware_automotive_vehicle_aidl_impl_vhal_include_ConnectedClient_H_
+#define android_hardware_automotive_vehicle_aidl_impl_vhal_include_ConnectedClient_H_
+
+#include <VehicleHalTypes.h>
+
+#include <aidl/android/hardware/automotive/vehicle/IVehicleCallback.h>
+#include <android-base/result.h>
+
+#include <memory>
+#include <unordered_set>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+// A class to represent a binder client with a callback interface. Each callback function, e.g.
+// GetValues or SetValues for a specific binder client is a separate {@code ConnectedClient}.
+// For one {@code ConnectedClient}, we use one pending request pool to manage all pending requests,
+// so the request IDs must be unique for one client. We also manage a set of callback functions
+// for one client, e.g. timeoutCallback which could be passed to hardware.
+// This class is thread-safe.
+class ConnectedClient {
+  public:
+    ConnectedClient(
+            std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>
+                    callback);
+
+    virtual ~ConnectedClient() = default;
+
+  protected:
+    const std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>
+            mCallback;
+};
+
+// A class to represent a client that calls {@code IVehicle.setValues} or {@code
+// IVehicle.getValues}.
+template <class ResultType, class ResultsType>
+class GetSetValuesClient final : public ConnectedClient {
+  public:
+    GetSetValuesClient(
+            std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>
+                    callback);
+
+    // Sends the results to this client.
+    void sendResults(const std::vector<ResultType>& results);
+
+    // Sends each result separately to this client. Each result would be sent through one callback
+    // invocation.
+    void sendResultsSeparately(const std::vector<ResultType>& results);
+
+    // Gets the callback to be called when the request for this client has finished.
+    std::shared_ptr<const std::function<void(std::vector<ResultType>)>> getResultCallback();
+
+  private:
+    // The following members are only initialized during construction.
+    std::shared_ptr<const std::function<void(std::vector<ResultType>)>> mResultCallback;
+};
+
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_automotive_vehicle_aidl_impl_vhal_include_ConnectedClient_H_
diff --git a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
index 49c501e..43bdca2 100644
--- a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
+++ b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
@@ -17,14 +17,68 @@
 #ifndef android_hardware_automotive_vehicle_aidl_impl_vhal_include_DefaultVehicleHal_H_
 #define android_hardware_automotive_vehicle_aidl_impl_vhal_include_DefaultVehicleHal_H_
 
+#include <IVehicleHardware.h>
+#include <LargeParcelableBase.h>
+#include <VehicleUtils.h>
 #include <aidl/android/hardware/automotive/vehicle/BnVehicle.h>
+#include <android/binder_auto_utils.h>
+
+#include <memory>
+#include <unordered_map>
+#include <vector>
 
 namespace android {
 namespace hardware {
 namespace automotive {
 namespace vehicle {
 
+// private namespace
+namespace defaultvehiclehal_impl {
+
+constexpr int INVALID_MEMORY_FD = -1;
+
+template <class T>
+::ndk::ScopedAStatus toScopedAStatus(
+        const ::android::base::Result<T>& result,
+        ::aidl::android::hardware::automotive::vehicle::StatusCode status) {
+    if (result.ok()) {
+        return ::ndk::ScopedAStatus::ok();
+    }
+    return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(toInt(status),
+                                                                     getErrorMsg(result).c_str());
+}
+
+template <class T>
+::ndk::ScopedAStatus toScopedAStatus(const ::android::base::Result<T>& result) {
+    return toScopedAStatus(result, getErrorCode(result));
+}
+
+template <class T1, class T2>
+::ndk::ScopedAStatus vectorToStableLargeParcelable(std::vector<T1>& values, T2* output) {
+    auto result = ::android::automotive::car_binder_lib::LargeParcelableBase::
+            parcelableVectorToStableLargeParcelable(values);
+    if (!result.ok()) {
+        return toScopedAStatus(
+                result, ::aidl::android::hardware::automotive::vehicle::StatusCode::INTERNAL_ERROR);
+    }
+    auto& fd = result.value();
+    if (fd == nullptr) {
+        output->payloads = values;
+    } else {
+        // Move the returned ScopedFileDescriptor pointer to ScopedFileDescriptor value in
+        // 'sharedMemoryFd' field.
+        output->sharedMemoryFd.set(fd->get());
+        *(fd->getR()) = INVALID_MEMORY_FD;
+    }
+    return ::ndk::ScopedAStatus::ok();
+}
+
+}  // namespace defaultvehiclehal_impl
+
 class DefaultVehicleHal final : public ::aidl::android::hardware::automotive::vehicle::BnVehicle {
+  public:
+    explicit DefaultVehicleHal(std::unique_ptr<IVehicleHardware> hardware);
+
     ::ndk::ScopedAStatus getAllPropConfigs(
             ::aidl::android::hardware::automotive::vehicle::VehiclePropConfigs* returnConfigs)
             override;
@@ -56,6 +110,14 @@
             const std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>&
                     callback,
             int64_t sharedMemoryId) override;
+
+    IVehicleHardware* getHardware();
+
+  private:
+    const std::unique_ptr<IVehicleHardware> mVehicleHardware;
+    std::unordered_map<int32_t, ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig>
+            mConfigsByPropId;
+    std::unique_ptr<::ndk::ScopedFileDescriptor> mConfigFile;
 };
 
 }  // namespace vehicle
diff --git a/automotive/vehicle/aidl/impl/vhal/include/ParcelableUtils.h b/automotive/vehicle/aidl/impl/vhal/include/ParcelableUtils.h
new file mode 100644
index 0000000..dcb15b9
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/vhal/include/ParcelableUtils.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef android_hardware_automotive_vehicle_aidl_impl_vhal_include_ParcelableUtils_H_
+#define android_hardware_automotive_vehicle_aidl_impl_vhal_include_ParcelableUtils_H_
+
+#include <LargeParcelableBase.h>
+#include <VehicleHalTypes.h>
+#include <VehicleUtils.h>
+
+#include <memory>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+template <class T1, class T2>
+::ndk::ScopedAStatus vectorToStableLargeParcelable(std::vector<T1>&& values, T2* output) {
+    auto result = ::android::automotive::car_binder_lib::LargeParcelableBase::
+            parcelableVectorToStableLargeParcelable(values);
+    if (!result.ok()) {
+        return toScopedAStatus(
+                result, ::aidl::android::hardware::automotive::vehicle::StatusCode::INTERNAL_ERROR);
+    }
+    auto& fd = result.value();
+    if (fd == nullptr) {
+        // If we no longer needs values, move it inside the payloads to avoid copying.
+        output->payloads = std::move(values);
+    } else {
+        // Move the returned ScopedFileDescriptor pointer to ScopedFileDescriptor value in
+        // 'sharedMemoryFd' field.
+        output->sharedMemoryFd = std::move(*fd);
+    }
+    return ::ndk::ScopedAStatus::ok();
+}
+
+template <class T1, class T2>
+::ndk::ScopedAStatus vectorToStableLargeParcelable(const std::vector<T1>& values, T2* output) {
+    // Because 'values' is passed in as const reference, we have to do a copy here.
+    std::vector<T1> valuesCopy = values;
+
+    return vectorToStableLargeParcelable(std::move(valuesCopy), output);
+}
+
+template <class T1, class T2>
+::android::base::expected<std::vector<T1>, ::ndk::ScopedAStatus> stableLargeParcelableToVector(
+        const T2& largeParcelable) {
+    ::android::base::Result<std::optional<std::vector<T1>>> result =
+            ::android::automotive::car_binder_lib::LargeParcelableBase::
+                    stableLargeParcelableToParcelableVector<T1>(largeParcelable.sharedMemoryFd);
+
+    if (!result.ok()) {
+        return ::android::base::unexpected(toScopedAStatus(
+                result, ::aidl::android::hardware::automotive::vehicle::StatusCode::INVALID_ARG,
+                "failed to parse large parcelable"));
+    }
+
+    if (!result.value().has_value()) {
+        return ::android::base::unexpected(
+                ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                        toInt(::aidl::android::hardware::automotive::vehicle::StatusCode::
+                                      INVALID_ARG),
+                        "empty request"));
+    }
+
+    return std::move(result.value().value());
+}
+
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_automotive_vehicle_aidl_impl_vhal_include_ParcelableUtils_H_
diff --git a/automotive/vehicle/aidl/impl/vhal/src/ConnectedClient.cpp b/automotive/vehicle/aidl/impl/vhal/src/ConnectedClient.cpp
new file mode 100644
index 0000000..656dfaf
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/vhal/src/ConnectedClient.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ConnectedClient.h"
+#include "ParcelableUtils.h"
+
+#include <VehicleHalTypes.h>
+
+#include <utils/Log.h>
+
+#include <inttypes.h>
+#include <unordered_set>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+namespace {
+
+using ::aidl::android::hardware::automotive::vehicle::GetValueResult;
+using ::aidl::android::hardware::automotive::vehicle::GetValueResults;
+using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback;
+using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
+using ::aidl::android::hardware::automotive::vehicle::SetValueResults;
+using ::aidl::android::hardware::automotive::vehicle::StatusCode;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropValues;
+using ::android::base::Result;
+using ::ndk::ScopedAStatus;
+
+// A function to call the specific callback based on results type.
+template <class T>
+ScopedAStatus callCallback(std::shared_ptr<IVehicleCallback> callback, const T& results);
+
+template <>
+ScopedAStatus callCallback<GetValueResults>(std::shared_ptr<IVehicleCallback> callback,
+                                            const GetValueResults& results) {
+    return callback->onGetValues(results);
+}
+
+template <>
+ScopedAStatus callCallback<SetValueResults>(std::shared_ptr<IVehicleCallback> callback,
+                                            const SetValueResults& results) {
+    return callback->onSetValues(results);
+}
+
+// Send a single GetValue/SetValue result through the callback.
+template <class ResultType, class ResultsType>
+void sendGetOrSetValueResult(std::shared_ptr<IVehicleCallback> callback, const ResultType& result) {
+    ResultsType parcelableResults;
+    parcelableResults.payloads.resize(1);
+    parcelableResults.payloads[0] = result;
+    if (ScopedAStatus callbackStatus = callCallback(callback, parcelableResults);
+        !callbackStatus.isOk()) {
+        ALOGE("failed to call callback, error: %s, code: %d", callbackStatus.getMessage(),
+              callbackStatus.getServiceSpecificError());
+    }
+}
+
+// Send all the GetValue/SetValue results through callback, one result in each callback invocation.
+template <class ResultType, class ResultsType>
+void sendGetOrSetValueResultsSeparately(std::shared_ptr<IVehicleCallback> callback,
+                                        const std::vector<ResultType>& results) {
+    for (const auto& result : results) {
+        sendGetOrSetValueResult<ResultType, ResultsType>(callback, result);
+    }
+}
+
+// Send all the GetValue/SetValue results through callback in a single callback invocation.
+template <class ResultType, class ResultsType>
+void sendGetOrSetValueResults(std::shared_ptr<IVehicleCallback> callback,
+                              const std::vector<ResultType>& results) {
+    ResultsType parcelableResults;
+    ScopedAStatus status = vectorToStableLargeParcelable(results, &parcelableResults);
+    if (status.isOk()) {
+        if (ScopedAStatus callbackStatus = callCallback(callback, parcelableResults);
+            !callbackStatus.isOk()) {
+            ALOGE("failed to call callback, error: %s, code: %d", status.getMessage(),
+                  status.getServiceSpecificError());
+        }
+        return;
+    }
+    int statusCode = status.getServiceSpecificError();
+    ALOGE("failed to marshal result into large parcelable, error: "
+          "%s, code: %d",
+          status.getMessage(), statusCode);
+    sendGetOrSetValueResultsSeparately<ResultType, ResultsType>(callback, results);
+}
+
+// Specify the functions for GetValues and SetValues types.
+template void sendGetOrSetValueResult<GetValueResult, GetValueResults>(
+        std::shared_ptr<IVehicleCallback> callback, const GetValueResult& result);
+template void sendGetOrSetValueResult<SetValueResult, SetValueResults>(
+        std::shared_ptr<IVehicleCallback> callback, const SetValueResult& result);
+
+template void sendGetOrSetValueResults<GetValueResult, GetValueResults>(
+        std::shared_ptr<IVehicleCallback> callback, const std::vector<GetValueResult>& results);
+template void sendGetOrSetValueResults<SetValueResult, SetValueResults>(
+        std::shared_ptr<IVehicleCallback> callback, const std::vector<SetValueResult>& results);
+
+template void sendGetOrSetValueResultsSeparately<GetValueResult, GetValueResults>(
+        std::shared_ptr<IVehicleCallback> callback, const std::vector<GetValueResult>& results);
+template void sendGetOrSetValueResultsSeparately<SetValueResult, SetValueResults>(
+        std::shared_ptr<IVehicleCallback> callback, const std::vector<SetValueResult>& results);
+
+}  // namespace
+
+ConnectedClient::ConnectedClient(std::shared_ptr<IVehicleCallback> callback)
+    : mCallback(callback) {}
+
+template <class ResultType, class ResultsType>
+GetSetValuesClient<ResultType, ResultsType>::GetSetValuesClient(
+        std::shared_ptr<IVehicleCallback> callback)
+    : ConnectedClient(callback) {
+    mResultCallback = std::make_shared<const std::function<void(std::vector<ResultType>)>>(
+            [callback](std::vector<ResultType> results) {
+                return sendGetOrSetValueResults<ResultType, ResultsType>(callback, results);
+            });
+}
+
+template <class ResultType, class ResultsType>
+std::shared_ptr<const std::function<void(std::vector<ResultType>)>>
+GetSetValuesClient<ResultType, ResultsType>::getResultCallback() {
+    return mResultCallback;
+}
+
+template <class ResultType, class ResultsType>
+void GetSetValuesClient<ResultType, ResultsType>::sendResults(
+        const std::vector<ResultType>& results) {
+    return sendGetOrSetValueResults<ResultType, ResultsType>(mCallback, results);
+}
+
+template <class ResultType, class ResultsType>
+void GetSetValuesClient<ResultType, ResultsType>::sendResultsSeparately(
+        const std::vector<ResultType>& results) {
+    return sendGetOrSetValueResultsSeparately<ResultType, ResultsType>(mCallback, results);
+}
+
+template class GetSetValuesClient<GetValueResult, GetValueResults>;
+template class GetSetValuesClient<SetValueResult, SetValueResults>;
+
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
index 5a31643..fd9e331 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
@@ -18,7 +18,11 @@
 
 #include <DefaultVehicleHal.h>
 
+#include <LargeParcelableBase.h>
 #include <VehicleHalTypes.h>
+#include <VehicleUtils.h>
+#include <android-base/result.h>
+#include <utils/Log.h>
 
 namespace android {
 namespace hardware {
@@ -28,12 +32,41 @@
 using ::aidl::android::hardware::automotive::vehicle::GetValueRequests;
 using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback;
 using ::aidl::android::hardware::automotive::vehicle::SetValueRequests;
+using ::aidl::android::hardware::automotive::vehicle::StatusCode;
 using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfigs;
+using ::android::automotive::car_binder_lib::LargeParcelableBase;
+using ::android::base::Result;
 using ::ndk::ScopedAStatus;
 
-ScopedAStatus DefaultVehicleHal::getAllPropConfigs(VehiclePropConfigs*) {
-    // TODO(b/200737967): implement this.
+DefaultVehicleHal::DefaultVehicleHal(std::unique_ptr<IVehicleHardware> hardware)
+    : mVehicleHardware(std::move(hardware)) {
+    auto configs = mVehicleHardware->getAllPropertyConfigs();
+    for (auto& config : configs) {
+        mConfigsByPropId[config.prop] = config;
+    }
+    auto result = LargeParcelableBase::parcelableVectorToStableLargeParcelable(configs);
+    if (!result.ok()) {
+        ALOGE("failed to convert configs to shared memory file, error: %s, code: %d",
+              getErrorMsg(result).c_str(), getIntErrorCode(result));
+        return;
+    }
+
+    if (result.value() != nullptr) {
+        mConfigFile = std::move(result.value());
+    }
+}
+
+ScopedAStatus DefaultVehicleHal::getAllPropConfigs(VehiclePropConfigs* output) {
+    if (mConfigFile != nullptr) {
+        output->sharedMemoryFd.set(dup(mConfigFile->get()));
+        return ScopedAStatus::ok();
+    }
+    output->payloads.reserve(mConfigsByPropId.size());
+    for (const auto& [_, config] : mConfigsByPropId) {
+        output->payloads.push_back(config);
+    }
     return ScopedAStatus::ok();
 }
 
@@ -49,9 +82,15 @@
     return ScopedAStatus::ok();
 }
 
-ScopedAStatus DefaultVehicleHal::getPropConfigs(const std::vector<int32_t>&, VehiclePropConfigs*) {
-    // TODO(b/200737967): implement this.
-    return ScopedAStatus::ok();
+ScopedAStatus DefaultVehicleHal::getPropConfigs(const std::vector<int32_t>& props,
+                                                VehiclePropConfigs* output) {
+    std::vector<VehiclePropConfig> configs;
+    for (int32_t prop : props) {
+        if (mConfigsByPropId.find(prop) != mConfigsByPropId.end()) {
+            configs.push_back(mConfigsByPropId[prop]);
+        }
+    }
+    return defaultvehiclehal_impl::vectorToStableLargeParcelable(configs, output);
 }
 
 ScopedAStatus DefaultVehicleHal::subscribe(const std::shared_ptr<IVehicleCallback>&,
@@ -72,6 +111,10 @@
     return ScopedAStatus::ok();
 }
 
+IVehicleHardware* DefaultVehicleHal::getHardware() {
+    return mVehicleHardware.get();
+}
+
 }  // namespace vehicle
 }  // namespace automotive
 }  // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/vhal/src/VehicleService.cpp b/automotive/vehicle/aidl/impl/vhal/src/VehicleService.cpp
index 7c623ea..14224a5 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/VehicleService.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/VehicleService.cpp
@@ -17,15 +17,19 @@
 #define LOG_TAG "VehicleService"
 
 #include <DefaultVehicleHal.h>
+#include <FakeVehicleHardware.h>
 
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
 #include <utils/Log.h>
 
 using ::android::hardware::automotive::vehicle::DefaultVehicleHal;
+using ::android::hardware::automotive::vehicle::fake::FakeVehicleHardware;
 
 int main(int /* argc */, char* /* argv */[]) {
-    std::shared_ptr<DefaultVehicleHal> vhal = ndk::SharedRefBase::make<DefaultVehicleHal>();
+    std::unique_ptr<FakeVehicleHardware> hardware = std::make_unique<FakeVehicleHardware>();
+    std::shared_ptr<DefaultVehicleHal> vhal =
+            ::ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
 
     ALOGI("Registering as service...");
     binder_exception_t err = AServiceManager_addService(vhal->asBinder().get(),
diff --git a/automotive/vehicle/aidl/impl/vhal/test/Android.bp b/automotive/vehicle/aidl/impl/vhal/test/Android.bp
new file mode 100644
index 0000000..bf16475
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/vhal/test/Android.bp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+    name: "DefaultVehicleHalTest",
+    vendor: true,
+    srcs: ["*.cpp"],
+    static_libs: [
+        "DefaultVehicleHal",
+        "VehicleHalUtils",
+        "android-automotive-large-parcelable-vendor-lib",
+        "libgtest",
+        "libgmock",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder_ndk",
+        "liblog",
+        "libutils",
+    ],
+    header_libs: [
+        "IVehicleHardware",
+    ],
+    defaults: [
+        "VehicleHalDefaults",
+        "android-automotive-large-parcelable-defaults",
+    ],
+    test_suites: ["device-tests"],
+}
diff --git a/automotive/vehicle/aidl/impl/vhal/test/ConnectedClientTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/ConnectedClientTest.cpp
new file mode 100644
index 0000000..ddd0c65
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/vhal/test/ConnectedClientTest.cpp
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ConnectedClient.h"
+#include "MockVehicleCallback.h"
+
+#include <aidl/android/hardware/automotive/vehicle/IVehicleCallback.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+using ::aidl::android::hardware::automotive::vehicle::GetValueResult;
+using ::aidl::android::hardware::automotive::vehicle::GetValueResults;
+using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback;
+using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
+using ::aidl::android::hardware::automotive::vehicle::SetValueResults;
+using ::aidl::android::hardware::automotive::vehicle::StatusCode;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
+
+class ConnectedClientTest : public ::testing::Test {
+  public:
+    void SetUp() override {
+        mCallback = ndk::SharedRefBase::make<MockVehicleCallback>();
+        mCallbackClient = IVehicleCallback::fromBinder(mCallback->asBinder());
+    }
+
+    std::shared_ptr<IVehicleCallback> getCallbackClient() { return mCallbackClient; }
+
+    MockVehicleCallback* getCallback() { return mCallback.get(); }
+
+  protected:
+    using GetValuesClient = GetSetValuesClient<GetValueResult, GetValueResults>;
+    using SetValuesClient = GetSetValuesClient<SetValueResult, SetValueResults>;
+
+  private:
+    std::shared_ptr<MockVehicleCallback> mCallback;
+    std::shared_ptr<IVehicleCallback> mCallbackClient;
+};
+
+TEST_F(ConnectedClientTest, testSendGetValueResults) {
+    std::vector<GetValueResult> results = {{
+                                                   .requestId = 0,
+                                                   .status = StatusCode::OK,
+                                                   .prop =
+                                                           VehiclePropValue{
+                                                                   .prop = 0,
+                                                           },
+                                           },
+                                           {
+                                                   .requestId = 1,
+                                                   .status = StatusCode::OK,
+                                                   .prop =
+                                                           VehiclePropValue{
+                                                                   .prop = 1,
+                                                           },
+                                           }};
+
+    GetValuesClient client(getCallbackClient());
+
+    client.sendResults(results);
+
+    auto maybeGetValueResults = getCallback()->nextGetValueResults();
+    ASSERT_TRUE(maybeGetValueResults.has_value());
+    ASSERT_EQ(maybeGetValueResults.value().payloads, results);
+}
+
+TEST_F(ConnectedClientTest, testSendGetValueResultsSeparately) {
+    std::vector<GetValueResult> results = {{
+                                                   .requestId = 0,
+                                                   .status = StatusCode::OK,
+                                                   .prop =
+                                                           VehiclePropValue{
+                                                                   .prop = 0,
+                                                           },
+                                           },
+                                           {
+                                                   .requestId = 1,
+                                                   .status = StatusCode::OK,
+                                                   .prop =
+                                                           VehiclePropValue{
+                                                                   .prop = 1,
+                                                           },
+                                           }};
+
+    GetValuesClient client(getCallbackClient());
+
+    client.sendResultsSeparately(results);
+
+    for (auto& result : results) {
+        auto maybeGetValueResults = getCallback()->nextGetValueResults();
+        EXPECT_TRUE(maybeGetValueResults.has_value());
+        if (!maybeGetValueResults.has_value()) {
+            continue;
+        }
+        EXPECT_EQ(maybeGetValueResults.value().payloads, std::vector<GetValueResult>({result}));
+    }
+}
+
+TEST_F(ConnectedClientTest, testGetValuesGnResultCallback) {
+    std::vector<GetValueResult> results = {{
+                                                   .requestId = 0,
+                                                   .status = StatusCode::OK,
+                                                   .prop =
+                                                           VehiclePropValue{
+                                                                   .prop = 0,
+                                                           },
+                                           },
+                                           {
+                                                   .requestId = 1,
+                                                   .status = StatusCode::OK,
+                                                   .prop =
+                                                           VehiclePropValue{
+                                                                   .prop = 1,
+                                                           },
+                                           }};
+
+    GetValuesClient client(getCallbackClient());
+
+    (*(client.getResultCallback()))(results);
+
+    auto maybeGetValueResults = getCallback()->nextGetValueResults();
+    ASSERT_TRUE(maybeGetValueResults.has_value());
+    ASSERT_EQ(maybeGetValueResults.value().payloads, results);
+}
+
+TEST_F(ConnectedClientTest, testSendSetValueResults) {
+    std::vector<SetValueResult> results = {{
+                                                   .requestId = 0,
+                                                   .status = StatusCode::OK,
+                                           },
+                                           {
+                                                   .requestId = 1,
+                                                   .status = StatusCode::OK,
+                                           }};
+
+    SetValuesClient client(getCallbackClient());
+
+    client.sendResults(results);
+
+    auto maybeSetValueResults = getCallback()->nextSetValueResults();
+    ASSERT_TRUE(maybeSetValueResults.has_value());
+    ASSERT_EQ(maybeSetValueResults.value().payloads, results);
+}
+
+TEST_F(ConnectedClientTest, testSendSetValueResultsSeparately) {
+    std::vector<SetValueResult> results = {{
+                                                   .requestId = 0,
+                                                   .status = StatusCode::OK,
+                                           },
+                                           {
+                                                   .requestId = 1,
+                                                   .status = StatusCode::OK,
+                                           }};
+
+    SetValuesClient client(getCallbackClient());
+
+    client.sendResultsSeparately(results);
+
+    for (auto& result : results) {
+        auto maybeSetValueResults = getCallback()->nextSetValueResults();
+        EXPECT_TRUE(maybeSetValueResults.has_value());
+        if (!maybeSetValueResults.has_value()) {
+            continue;
+        }
+        EXPECT_EQ(maybeSetValueResults.value().payloads, std::vector<SetValueResult>({result}));
+    }
+}
+
+TEST_F(ConnectedClientTest, testSetValuesGetResultCallback) {
+    std::vector<SetValueResult> results = {{
+                                                   .requestId = 0,
+                                                   .status = StatusCode::OK,
+                                           },
+                                           {
+                                                   .requestId = 1,
+                                                   .status = StatusCode::OK,
+                                           }};
+
+    SetValuesClient client(getCallbackClient());
+
+    (*(client.getResultCallback()))(results);
+
+    auto maybeSetValueResults = getCallback()->nextSetValueResults();
+    ASSERT_TRUE(maybeSetValueResults.has_value());
+    ASSERT_EQ(maybeSetValueResults.value().payloads, results);
+}
+
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
new file mode 100644
index 0000000..62a7098
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DefaultVehicleHal.h"
+
+#include <IVehicleHardware.h>
+#include <LargeParcelableBase.h>
+#include <aidl/android/hardware/automotive/vehicle/IVehicle.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <memory>
+#include <optional>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+namespace {
+
+using ::aidl::android::hardware::automotive::vehicle::GetValueRequest;
+using ::aidl::android::hardware::automotive::vehicle::GetValueResult;
+using ::aidl::android::hardware::automotive::vehicle::IVehicle;
+using ::aidl::android::hardware::automotive::vehicle::SetValueRequest;
+using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
+using ::aidl::android::hardware::automotive::vehicle::StatusCode;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfigs;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
+
+using ::android::automotive::car_binder_lib::LargeParcelableBase;
+using ::android::base::Result;
+
+using ::testing::Eq;
+using ::testing::WhenSortedBy;
+
+class MockVehicleHardware final : public IVehicleHardware {
+  public:
+    std::vector<VehiclePropConfig> getAllPropertyConfigs() const override {
+        return mPropertyConfigs;
+    }
+
+    StatusCode setValues(std::function<void(const std::vector<SetValueResult>&)>&&,
+                         const std::vector<SetValueRequest>&) override {
+        // TODO(b/200737967): mock this.
+        return StatusCode::OK;
+    }
+
+    StatusCode getValues(std::function<void(const std::vector<GetValueResult>&)>&&,
+                         const std::vector<GetValueRequest>&) const override {
+        // TODO(b/200737967): mock this.
+        return StatusCode::OK;
+    }
+
+    DumpResult dump(const std::vector<std::string>&) override {
+        // TODO(b/200737967): mock this.
+        return DumpResult{};
+    }
+
+    StatusCode checkHealth() override {
+        // TODO(b/200737967): mock this.
+        return StatusCode::OK;
+    }
+
+    void registerOnPropertyChangeEvent(
+            std::function<void(const std::vector<VehiclePropValue>&)>&&) override {
+        // TODO(b/200737967): mock this.
+    }
+
+    void registerOnPropertySetErrorEvent(
+            std::function<void(const std::vector<SetValueErrorEvent>&)>&&) override {
+        // TODO(b/200737967): mock this.
+    }
+
+    // Test functions.
+    void setPropertyConfigs(const std::vector<VehiclePropConfig>& configs) {
+        mPropertyConfigs = configs;
+    }
+
+  private:
+    std::vector<VehiclePropConfig> mPropertyConfigs;
+};
+
+struct PropConfigCmp {
+    bool operator()(const VehiclePropConfig& a, const VehiclePropConfig& b) const {
+        return (a.prop < b.prop);
+    }
+} propConfigCmp;
+
+}  // namespace
+
+TEST(DefaultVehicleHalTest, testGetAllPropConfigsSmall) {
+    auto testConfigs = std::vector<VehiclePropConfig>({
+            VehiclePropConfig{
+                    .prop = 1,
+            },
+            VehiclePropConfig{
+                    .prop = 2,
+            },
+    });
+
+    auto hardware = std::make_unique<MockVehicleHardware>();
+    hardware->setPropertyConfigs(testConfigs);
+    auto vhal = ::ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+    std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
+
+    VehiclePropConfigs output;
+    auto status = client->getAllPropConfigs(&output);
+
+    ASSERT_TRUE(status.isOk());
+    ASSERT_THAT(output.payloads, WhenSortedBy(propConfigCmp, Eq(testConfigs)));
+}
+
+TEST(DefaultVehicleHalTest, testGetAllPropConfigsLarge) {
+    std::vector<VehiclePropConfig> testConfigs;
+    // 10000 VehiclePropConfig exceeds 4k memory limit, so it would be sent through shared memory.
+    for (size_t i = 0; i < 10000; i++) {
+        testConfigs.push_back(VehiclePropConfig{
+                .prop = static_cast<int32_t>(i),
+        });
+    }
+
+    auto hardware = std::make_unique<MockVehicleHardware>();
+    hardware->setPropertyConfigs(testConfigs);
+    auto vhal = ::ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+    std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
+
+    VehiclePropConfigs output;
+    auto status = client->getAllPropConfigs(&output);
+
+    ASSERT_TRUE(status.isOk());
+    ASSERT_TRUE(output.payloads.empty());
+    Result<std::optional<std::vector<VehiclePropConfig>>> result =
+            LargeParcelableBase::stableLargeParcelableToParcelableVector<VehiclePropConfig>(
+                    output.sharedMemoryFd);
+    ASSERT_TRUE(result.ok());
+    ASSERT_TRUE(result.value().has_value());
+    ASSERT_EQ(result.value().value(), testConfigs);
+}
+
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.cpp b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.cpp
new file mode 100644
index 0000000..ca366cd
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MockVehicleCallback.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+namespace {
+
+using ::aidl::android::hardware::automotive::vehicle::GetValueResults;
+using ::aidl::android::hardware::automotive::vehicle::SetValueResults;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropErrors;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropValues;
+using ::ndk::ScopedAStatus;
+using ::ndk::ScopedFileDescriptor;
+
+template <class T>
+std::optional<T> pop(std::list<T>& items) {
+    if (items.size() > 0) {
+        auto item = std::move(items.front());
+        items.pop_front();
+        return item;
+    }
+    return std::nullopt;
+}
+
+template <class T>
+static ScopedAStatus storeResults(const T& results, std::list<T>* storedResults) {
+    T resultsCopy{
+            .payloads = results.payloads,
+    };
+    int fd = results.sharedMemoryFd.get();
+    if (fd != -1) {
+        resultsCopy.sharedMemoryFd = ScopedFileDescriptor(dup(fd));
+    }
+    storedResults->push_back(std::move(resultsCopy));
+    return ScopedAStatus::ok();
+}
+
+}  // namespace
+
+ScopedAStatus MockVehicleCallback::onGetValues(const GetValueResults& results) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    return storeResults(results, &mGetValueResults);
+}
+
+ScopedAStatus MockVehicleCallback::onSetValues(const SetValueResults& results) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    return storeResults(results, &mSetValueResults);
+}
+
+ScopedAStatus MockVehicleCallback::onPropertyEvent(const VehiclePropValues&, int32_t) {
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus MockVehicleCallback::onPropertySetError(const VehiclePropErrors&) {
+    return ScopedAStatus::ok();
+}
+
+std::optional<GetValueResults> MockVehicleCallback::nextGetValueResults() {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    return pop(mGetValueResults);
+}
+
+std::optional<SetValueResults> MockVehicleCallback::nextSetValueResults() {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    return pop(mSetValueResults);
+}
+
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.h b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.h
new file mode 100644
index 0000000..916575a
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef android_hardware_automotive_vehicle_aidl_impl_vhal_test_MockVehicleCallback_H_
+#define android_hardware_automotive_vehicle_aidl_impl_vhal_test_MockVehicleCallback_H_
+
+#include <VehicleHalTypes.h>
+
+#include <aidl/android/hardware/automotive/vehicle/BnVehicleCallback.h>
+#include <android-base/thread_annotations.h>
+
+#include <list>
+#include <mutex>
+#include <optional>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+// MockVehicleCallback is a mock VehicleCallback implementation that simply stores the results.
+class MockVehicleCallback final
+    : public ::aidl::android::hardware::automotive::vehicle::BnVehicleCallback {
+  public:
+    ::ndk::ScopedAStatus onGetValues(
+            const ::aidl::android::hardware::automotive::vehicle::GetValueResults& results)
+            override;
+    ::ndk::ScopedAStatus onSetValues(
+            const ::aidl::android::hardware::automotive::vehicle::SetValueResults& results)
+            override;
+    ::ndk::ScopedAStatus onPropertyEvent(
+            const ::aidl::android::hardware::automotive::vehicle::VehiclePropValues&,
+            int32_t) override;
+    ::ndk::ScopedAStatus onPropertySetError(
+            const ::aidl::android::hardware::automotive::vehicle::VehiclePropErrors&) override;
+
+    // Test functions
+    std::optional<::aidl::android::hardware::automotive::vehicle::GetValueResults>
+    nextGetValueResults();
+    std::optional<::aidl::android::hardware::automotive::vehicle::SetValueResults>
+    nextSetValueResults();
+
+  private:
+    std::mutex mLock;
+    std::list<::aidl::android::hardware::automotive::vehicle::GetValueResults> mGetValueResults
+            GUARDED_BY(mLock);
+    std::list<::aidl::android::hardware::automotive::vehicle::SetValueResults> mSetValueResults
+            GUARDED_BY(mLock);
+};
+
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_automotive_vehicle_aidl_impl_vhal_test_MockVehicleCallback_H_
diff --git a/camera/device/3.8/Android.bp b/camera/device/3.8/Android.bp
new file mode 100644
index 0000000..2a1f215
--- /dev/null
+++ b/camera/device/3.8/Android.bp
@@ -0,0 +1,38 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+
+hidl_interface {
+    name: "android.hardware.camera.device@3.8",
+    root: "android.hardware",
+    srcs: [
+        "types.hal",
+        "ICameraDevice.hal",
+        "ICameraDeviceCallback.hal",
+    ],
+    interfaces: [
+        "android.hardware.camera.common@1.0",
+        "android.hardware.camera.device@3.2",
+        "android.hardware.camera.device@3.3",
+        "android.hardware.camera.device@3.4",
+        "android.hardware.camera.device@3.5",
+        "android.hardware.camera.device@3.6",
+        "android.hardware.camera.device@3.7",
+        "android.hardware.camera.metadata@3.2",
+        "android.hardware.camera.metadata@3.3",
+        "android.hardware.camera.metadata@3.4",
+        "android.hardware.camera.metadata@3.5",
+        "android.hardware.camera.metadata@3.6",
+        "android.hardware.graphics.common@1.0",
+        "android.hidl.base@1.0",
+    ],
+    gen_java: false,
+}
diff --git a/camera/device/3.8/ICameraDevice.hal b/camera/device/3.8/ICameraDevice.hal
new file mode 100644
index 0000000..448f176
--- /dev/null
+++ b/camera/device/3.8/ICameraDevice.hal
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera.device@3.8;
+
+import @3.7::ICameraDevice;
+
+/**
+ * Camera device interface
+ *
+ * Supports the android.hardware.Camera API, and the android.hardware.camera2
+ * API at LIMITED or better hardware level.
+ *
+ * ICameraDevice.open() must return @3.2::ICameraDeviceSession,
+ * @3.5::ICameraDeviceSession, @3.6::ICameraDeviceSession, or
+ * @3.7::ICameraDeviceSession.
+ */
+interface ICameraDevice extends @3.7::ICameraDevice {
+};
diff --git a/camera/device/3.8/ICameraDeviceCallback.hal b/camera/device/3.8/ICameraDeviceCallback.hal
new file mode 100644
index 0000000..de0775d
--- /dev/null
+++ b/camera/device/3.8/ICameraDeviceCallback.hal
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera.device@3.8;
+
+import @3.5::ICameraDeviceCallback;
+
+/**
+ * Callback methods for the HAL to call into the framework.
+ */
+interface ICameraDeviceCallback extends @3.5::ICameraDeviceCallback {
+    /**
+     * Identical to @3.5::ICameraDeviceCallback.notify, except that it takes a
+     * list of @3.8::NotifyMsg which contain readout timestamp in addition
+     * to exposure start timestamp for shutter.
+     *
+     * The readout timestamp is used for the framework to re-time the viewfinder
+     * frames targeted for SurfaceView so that preview jitter can be reduced.
+     */
+    notify_3_8(vec<NotifyMsg> msgs);
+};
diff --git a/camera/device/3.8/types.hal b/camera/device/3.8/types.hal
new file mode 100644
index 0000000..6daa0e1
--- /dev/null
+++ b/camera/device/3.8/types.hal
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera.device@3.8;
+
+import @3.2::ErrorMsg;
+import @3.2::MsgType;
+import @3.2::ShutterMsg;
+
+/**
+ * ShutterMsg:
+ *
+ * Message contents for MsgType::SHUTTER
+ *
+ * This version extends the @3.2 ShutterMsg with the readout timestamp.
+ */
+struct ShutterMsg {
+    /**
+     * The definition of ShutterMsg from prior version.
+     */
+    @3.2::ShutterMsg v3_2;
+
+    /**
+     * Timestamp for the capture readout. This must be in the same time domain
+     * as v3_2.timestamp, and the value must be v3_2.timestamp + exposureTime
+     * for a rolling shutter sensor.
+     */
+    uint64_t readoutTimestamp;
+};
+
+/**
+ * NotifyMsg:
+ *
+ * The message structure sent to ICameraDevice3Callback::notify()
+ *
+ * This version extends the @3.2 NotifyMsg with the @3.8 version of ShutterMsg.
+ */
+struct NotifyMsg {
+    /**
+     * The message type.
+     */
+    @3.2::MsgType type;
+
+    union Message {
+        /**
+         * Error message contents. Valid if type is MsgType::ERROR
+         */
+        @3.2::ErrorMsg error;
+
+        /**
+         * Shutter message contents. Valid if type is MsgType::SHUTTER
+         */
+        ShutterMsg shutter;
+    } msg;
+};
diff --git a/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp b/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp
index 64a51f6..4fc7437 100644
--- a/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp
+++ b/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp
@@ -65,10 +65,10 @@
 
 } // anonymous namespace
 
-ExternalCameraProviderImpl_2_4::ExternalCameraProviderImpl_2_4() :
-        mCfg(ExternalCameraConfig::loadFromCfg()),
-        mHotPlugThread(this) {
-    mHotPlugThread.run("ExtCamHotPlug", PRIORITY_BACKGROUND);
+ExternalCameraProviderImpl_2_4::ExternalCameraProviderImpl_2_4()
+    : mCfg(ExternalCameraConfig::loadFromCfg()) {
+    mHotPlugThread = sp<HotplugThread>::make(this);
+    mHotPlugThread->run("ExtCamHotPlug", PRIORITY_BACKGROUND);
 
     mPreferredHal3MinorVersion =
         property_get_int32("ro.vendor.camera.external.hal3TrebleMinorVersion", 4);
@@ -88,7 +88,7 @@
 }
 
 ExternalCameraProviderImpl_2_4::~ExternalCameraProviderImpl_2_4() {
-    mHotPlugThread.requestExit();
+    mHotPlugThread->requestExit();
 }
 
 
diff --git a/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.h b/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.h
index 8c79f68..f1d8003 100644
--- a/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.h
+++ b/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.h
@@ -102,7 +102,7 @@
     sp<ICameraProviderCallback> mCallbacks = nullptr;
     std::unordered_map<std::string, CameraDeviceStatus> mCameraStatusMap; // camera id -> status
     const ExternalCameraConfig mCfg;
-    HotplugThread mHotPlugThread;
+    sp<HotplugThread> mHotPlugThread;
     int mPreferredHal3MinorVersion;
 };
 
diff --git a/camera/provider/2.4/vts/functional/Android.bp b/camera/provider/2.4/vts/functional/Android.bp
index 8886ee1..2c141ee 100644
--- a/camera/provider/2.4/vts/functional/Android.bp
+++ b/camera/provider/2.4/vts/functional/Android.bp
@@ -49,6 +49,7 @@
         "android.hardware.camera.device@3.5",
         "android.hardware.camera.device@3.6",
         "android.hardware.camera.device@3.7",
+        "android.hardware.camera.device@3.8",
         "android.hardware.camera.metadata@3.4",
         "android.hardware.camera.provider@2.4",
         "android.hardware.camera.provider@2.5",
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index d02547c..ff8cd49 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -43,6 +43,7 @@
 #include <android/hardware/camera/device/3.7/ICameraDevice.h>
 #include <android/hardware/camera/device/3.7/ICameraDeviceSession.h>
 #include <android/hardware/camera/device/3.7/ICameraInjectionSession.h>
+#include <android/hardware/camera/device/3.8/ICameraDeviceCallback.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>
@@ -194,6 +195,7 @@
 namespace {
     // "device@<version>/legacy/<id>"
     const char *kDeviceNameRE = "device@([0-9]+\\.[0-9]+)/%s/(.+)";
+    const int CAMERA_DEVICE_API_VERSION_3_8 = 0x308;
     const int CAMERA_DEVICE_API_VERSION_3_7 = 0x307;
     const int CAMERA_DEVICE_API_VERSION_3_6 = 0x306;
     const int CAMERA_DEVICE_API_VERSION_3_5 = 0x305;
@@ -201,6 +203,7 @@
     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_8 = "3.8";
     const char *kHAL3_7 = "3.7";
     const char *kHAL3_6 = "3.6";
     const char *kHAL3_5 = "3.5";
@@ -238,7 +241,9 @@
             return -1;
         }
 
-        if (version.compare(kHAL3_7) == 0) {
+        if (version.compare(kHAL3_8) == 0) {
+            return CAMERA_DEVICE_API_VERSION_3_8;
+        } else if (version.compare(kHAL3_7) == 0) {
             return CAMERA_DEVICE_API_VERSION_3_7;
         } else if (version.compare(kHAL3_6) == 0) {
             return CAMERA_DEVICE_API_VERSION_3_6;
@@ -638,7 +643,7 @@
      }
  };
 
-    struct DeviceCb : public V3_5::ICameraDeviceCallback {
+    struct DeviceCb : public V3_8::ICameraDeviceCallback {
         DeviceCb(CameraHidlTest *parent, int deviceVersion, const camera_metadata_t *staticMeta) :
                 mParent(parent), mDeviceVersion(deviceVersion) {
             mStaticMetadata = staticMeta;
@@ -648,6 +653,7 @@
                 const hidl_vec<V3_4::CaptureResult>& results) override;
         Return<void> processCaptureResult(const hidl_vec<CaptureResult>& results) override;
         Return<void> notify(const hidl_vec<NotifyMsg>& msgs) override;
+        Return<void> notify_3_8(const hidl_vec<V3_8::NotifyMsg>& msgs) override;
 
         Return<void> requestStreamBuffers(
                 const hidl_vec<V3_5::BufferRequest>& bufReqs,
@@ -663,6 +669,8 @@
      private:
         bool processCaptureResultLocked(const CaptureResult& results,
                 hidl_vec<PhysicalCameraMetadata> physicalCameraMetadata);
+        Return<void> notifyHelper(const hidl_vec<NotifyMsg>& msgs,
+                const std::vector<std::pair<bool, nsecs_t>>& readoutTimestamps);
 
         CameraHidlTest *mParent; // Parent object
         int mDeviceVersion;
@@ -956,6 +964,9 @@
         // Set by notify() SHUTTER call.
         nsecs_t shutterTimestamp;
 
+        bool shutterReadoutTimestampValid;
+        nsecs_t shutterReadoutTimestamp;
+
         bool errorCodeValid;
         ErrorCode errorCode;
 
@@ -1001,6 +1012,8 @@
 
         InFlightRequest() :
                 shutterTimestamp(0),
+                shutterReadoutTimestampValid(false),
+                shutterReadoutTimestamp(0),
                 errorCodeValid(false),
                 errorCode(ErrorCode::ERROR_BUFFER),
                 usePartialResult(false),
@@ -1018,6 +1031,8 @@
                 bool partialResults, uint32_t partialCount,
                 std::shared_ptr<ResultMetadataQueue> queue = nullptr) :
                 shutterTimestamp(0),
+                shutterReadoutTimestampValid(false),
+                shutterReadoutTimestamp(0),
                 errorCodeValid(false),
                 errorCode(ErrorCode::ERROR_BUFFER),
                 usePartialResult(partialResults),
@@ -1036,6 +1051,8 @@
                 const std::unordered_set<std::string>& extraPhysicalResult,
                 std::shared_ptr<ResultMetadataQueue> queue = nullptr) :
                 shutterTimestamp(0),
+                shutterReadoutTimestampValid(false),
+                shutterReadoutTimestamp(0),
                 errorCodeValid(false),
                 errorCode(ErrorCode::ERROR_BUFFER),
                 usePartialResult(partialResults),
@@ -1462,8 +1479,46 @@
     }
 }
 
+Return<void> CameraHidlTest::DeviceCb::notify_3_8(
+        const hidl_vec<V3_8::NotifyMsg>& msgs) {
+    hidl_vec<NotifyMsg> msgs3_2;
+    std::vector<std::pair<bool, nsecs_t>> readoutTimestamps;
+
+    nsecs_t count = msgs.size();
+    msgs3_2.resize(count);
+    readoutTimestamps.resize(count);
+
+    for (size_t i = 0; i < count; i++) {
+        msgs3_2[i].type = msgs[i].type;
+        switch (msgs[i].type) {
+            case MsgType::ERROR:
+                msgs3_2[i].msg.error = msgs[i].msg.error;
+                readoutTimestamps[i] = {false, 0};
+                break;
+            case MsgType::SHUTTER:
+                msgs3_2[i].msg.shutter = msgs[i].msg.shutter.v3_2;
+                readoutTimestamps[i] = {true, msgs[i].msg.shutter.readoutTimestamp};
+                break;
+        }
+    }
+
+    return notifyHelper(msgs3_2, readoutTimestamps);
+}
+
 Return<void> CameraHidlTest::DeviceCb::notify(
         const hidl_vec<NotifyMsg>& messages) {
+    std::vector<std::pair<bool, nsecs_t>> readoutTimestamps;
+    readoutTimestamps.resize(messages.size());
+    for (size_t i = 0; i < messages.size(); i++) {
+        readoutTimestamps[i] = {false, 0};
+    }
+
+    return notifyHelper(messages, readoutTimestamps);
+}
+
+Return<void> CameraHidlTest::DeviceCb::notifyHelper(
+        const hidl_vec<NotifyMsg>& messages,
+        const std::vector<std::pair<bool, nsecs_t>>& readoutTimestamps) {
     std::lock_guard<std::mutex> l(mParent->mLock);
 
     for (size_t i = 0; i < messages.size(); i++) {
@@ -1526,6 +1581,8 @@
                 }
                 InFlightRequest *r = mParent->mInflightMap.editValueAt(idx);
                 r->shutterTimestamp = messages[i].msg.shutter.timestamp;
+                r->shutterReadoutTimestampValid = readoutTimestamps[i].first;
+                r->shutterReadoutTimestamp = readoutTimestamps[i].second;
             }
                 break;
             default:
@@ -1940,6 +1997,7 @@
     for (const auto& name : cameraDeviceNames) {
         int deviceVersion = getCameraDeviceVersion(name, mProviderType);
         switch (deviceVersion) {
+            case CAMERA_DEVICE_API_VERSION_3_8:
             case CAMERA_DEVICE_API_VERSION_3_7:
             case CAMERA_DEVICE_API_VERSION_3_6:
             case CAMERA_DEVICE_API_VERSION_3_5:
@@ -1984,6 +2042,7 @@
     for (const auto& name : cameraDeviceNames) {
         int deviceVersion = getCameraDeviceVersion(name, mProviderType);
         switch (deviceVersion) {
+            case CAMERA_DEVICE_API_VERSION_3_8:
             case CAMERA_DEVICE_API_VERSION_3_7:
             case CAMERA_DEVICE_API_VERSION_3_6:
             case CAMERA_DEVICE_API_VERSION_3_5:
@@ -2725,6 +2784,7 @@
     for (const auto& name : cameraDeviceNames) {
         int deviceVersion = getCameraDeviceVersion(name, mProviderType);
         switch (deviceVersion) {
+            case CAMERA_DEVICE_API_VERSION_3_8:
             case CAMERA_DEVICE_API_VERSION_3_7:
             case CAMERA_DEVICE_API_VERSION_3_6:
             case CAMERA_DEVICE_API_VERSION_3_5:
@@ -2812,6 +2872,7 @@
     for (const auto& name : cameraDeviceNames) {
         int deviceVersion = getCameraDeviceVersion(name, mProviderType);
         switch (deviceVersion) {
+            case CAMERA_DEVICE_API_VERSION_3_8:
             case CAMERA_DEVICE_API_VERSION_3_7:
             case CAMERA_DEVICE_API_VERSION_3_6:
             case CAMERA_DEVICE_API_VERSION_3_5:
@@ -2893,6 +2954,7 @@
     for (const auto& name : cameraDeviceNames) {
         int deviceVersion = getCameraDeviceVersion(name, mProviderType);
         switch (deviceVersion) {
+            case CAMERA_DEVICE_API_VERSION_3_8:
             case CAMERA_DEVICE_API_VERSION_3_7:
             case CAMERA_DEVICE_API_VERSION_3_6:
             case CAMERA_DEVICE_API_VERSION_3_5:
@@ -3021,6 +3083,7 @@
     for (const auto& name : cameraDeviceNames) {
         int deviceVersion = getCameraDeviceVersion(name, mProviderType);
         switch (deviceVersion) {
+            case CAMERA_DEVICE_API_VERSION_3_8:
             case CAMERA_DEVICE_API_VERSION_3_7:
             case CAMERA_DEVICE_API_VERSION_3_6:
             case CAMERA_DEVICE_API_VERSION_3_5:
@@ -3088,6 +3151,7 @@
     for (const auto& name : cameraDeviceNames) {
         int deviceVersion = getCameraDeviceVersion(name, mProviderType);
         switch (deviceVersion) {
+            case CAMERA_DEVICE_API_VERSION_3_8:
             case CAMERA_DEVICE_API_VERSION_3_7:
             case CAMERA_DEVICE_API_VERSION_3_6:
             case CAMERA_DEVICE_API_VERSION_3_5:
@@ -3124,7 +3188,7 @@
                 castSession(session, deviceVersion, &sessionV3_3,
                         &sessionV3_4, &sessionV3_5, &sessionV3_6,
                         &sessionV3_7);
-                if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_7) {
+                if (deviceVersion >= CAMERA_DEVICE_API_VERSION_3_7) {
                     ASSERT_TRUE(sessionV3_7.get() != nullptr);
                 } else if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_6) {
                     ASSERT_TRUE(sessionV3_6.get() != nullptr);
@@ -3190,6 +3254,7 @@
     for (const auto& name : cameraDeviceNames) {
         int deviceVersion = getCameraDeviceVersion(name, mProviderType);
         switch (deviceVersion) {
+            case CAMERA_DEVICE_API_VERSION_3_8:
             case CAMERA_DEVICE_API_VERSION_3_7:
             case CAMERA_DEVICE_API_VERSION_3_6:
             case CAMERA_DEVICE_API_VERSION_3_5:
@@ -4730,6 +4795,19 @@
             ASSERT_NE(inflightReq.resultOutputBuffers.size(), 0u);
             ASSERT_EQ(testStream.id, inflightReq.resultOutputBuffers[0].streamId);
 
+            // For camera device 3.8 or newer, shutterReadoutTimestamp must be
+            // available, and it must be shutterTimestamp + exposureTime.
+            if (deviceVersion >= CAMERA_DEVICE_API_VERSION_3_8) {
+                ASSERT_TRUE(inflightReq.shutterReadoutTimestampValid);
+                ASSERT_FALSE(inflightReq.collectedResult.isEmpty());
+                if (inflightReq.collectedResult.exists(ANDROID_SENSOR_EXPOSURE_TIME)) {
+                    camera_metadata_entry_t exposureTimeResult = inflightReq.collectedResult.find(
+                            ANDROID_SENSOR_EXPOSURE_TIME);
+                    ASSERT_EQ(inflightReq.shutterReadoutTimestamp - inflightReq.shutterTimestamp,
+                            exposureTimeResult.data.i64[0]);
+                }
+            }
+
             request.frameNumber++;
             // Empty settings should be supported after the first call
             // for repeating requests.
@@ -6222,6 +6300,7 @@
         std::string cameraId;
         int deviceVersion = getCameraDeviceVersionAndId(name, mProviderType, &cameraId);
         switch (deviceVersion) {
+            case CAMERA_DEVICE_API_VERSION_3_8:
             case CAMERA_DEVICE_API_VERSION_3_7:
             case CAMERA_DEVICE_API_VERSION_3_6:
             case CAMERA_DEVICE_API_VERSION_3_5:
@@ -7651,6 +7730,7 @@
     ASSERT_NE(nullptr, device3_7);
 
     switch (deviceVersion) {
+        case CAMERA_DEVICE_API_VERSION_3_8:
         case CAMERA_DEVICE_API_VERSION_3_7: {
             auto castResult = device::V3_7::ICameraDevice::castFrom(device);
             ASSERT_TRUE(castResult.isOk());
@@ -7707,6 +7787,7 @@
     ASSERT_NE(nullptr, session3_7);
 
     switch (deviceVersion) {
+        case CAMERA_DEVICE_API_VERSION_3_8:
         case CAMERA_DEVICE_API_VERSION_3_7: {
             auto castResult = device::V3_7::ICameraDeviceSession::castFrom(session);
             ASSERT_TRUE(castResult.isOk());
diff --git a/compatibility_matrices/Android.bp b/compatibility_matrices/Android.bp
index a59be21..193fd2b 100644
--- a/compatibility_matrices/Android.bp
+++ b/compatibility_matrices/Android.bp
@@ -81,5 +81,6 @@
     ],
     kernel_configs: [
         "kernel_config_current_5.10",
+        "kernel_config_current_5.15",
     ],
 }
diff --git a/health/aidl/README.md b/health/aidl/README.md
index 53a4f91..0d7c4c9 100644
--- a/health/aidl/README.md
+++ b/health/aidl/README.md
@@ -80,7 +80,9 @@
 for your device, install it with
 
 ```mk
-PRODUCT_PACKAGES += android.hardware.health-service.example
+PRODUCT_PACKAGES += \
+    android.hardware.health-service.example \
+    android.hardware.health-service.example_recovery \
 ```
 
 Then, delete any existing `service` with `class charger` in your device-specific
@@ -156,15 +158,42 @@
 for charger (`ro.charger.no_ui=true`), skip the invocation of
 `ChargerModeMain()` in `main()`.
 
+### Build system changes
+
+Install both the platform and recovery variant of the service. For example:
+
+```mk
+PRODUCT_PACKAGES += \
+    android.hardware.health-service.cuttlefish \
+    android.hardware.health-service.cuttlefish_recovery \
+```
+
 ### SELinux rules
 
 Add device specific permissions to the domain where the health HAL
 process is executed, especially if a device-specific `libhealthd` is used
 and/or device-specific storage related APIs are implemented.
 
+Example (assuming that your health AIDL service runs in domain
+`hal_health_tuna`:
+
+```text
+type hal_health_tuna, domain;
+hal_server_domain(hal_health_tuna, hal_health)
+type hal_health_tuna_exec, exec_type, vendor_file_type, file_type;
+
+# allow hal_health_tuna ...;
+```
+
 If you did not define a separate domain, the domain is likely
 `hal_health_default`. The device-specific rules for it is likely at
 `device/<manufacturer>/<device>/sepolicy/vendor/hal_health_default.te`.
+In this case, the aforementioned SELinux rules and types has already been
+defined. You only need to add device-specific permissions.
+
+```text
+# allow hal_health_default ...;
+```
 
 ### Implementing charger {#charger}
 
@@ -289,5 +318,3 @@
 type hal_health_tuna, charger_type, domain;
 hal_server_domain(hal_health_default, hal_health)
 ```
-
-[comment: TODO(b/170338625): explain recovery]: #
diff --git a/health/aidl/default/Android.bp b/health/aidl/default/Android.bp
index a13c677..8aa7638 100644
--- a/health/aidl/default/Android.bp
+++ b/health/aidl/default/Android.bp
@@ -23,8 +23,6 @@
 
 cc_defaults {
     name: "libhealth_aidl_common_defaults",
-    recovery_available: true,
-    vendor: true,
     shared_libs: [
         "libbase",
         "libbinder_ndk",
@@ -100,6 +98,8 @@
         "libhealth_aidl_common_defaults",
         "libhealth_aidl_charger_defaults",
     ],
+    vendor: true,
+    recovery_available: true,
     export_include_dirs: ["include"],
     export_static_lib_headers: [
         "libbatterymonitor",
@@ -122,10 +122,9 @@
 
 // AIDL version of android.hardware.health@2.1-service.
 // Default binder service of the health HAL.
-cc_binary {
-    name: "android.hardware.health-service.example",
+cc_defaults {
+    name: "android.hardware.health-service.example-defaults",
     relative_install_path: "hw",
-    init_rc: ["android.hardware.health-service.example.rc"],
     vintf_fragments: ["android.hardware.health-service.example.xml"],
     defaults: [
         "libhealth_aidl_common_defaults",
@@ -135,7 +134,20 @@
         "libhealth_aidl_impl",
     ],
     srcs: ["main.cpp"],
-    overrides: [
-        "charger",
-    ],
+}
+
+cc_binary {
+    name: "android.hardware.health-service.example",
+    vendor: true,
+    defaults: ["android.hardware.health-service.example-defaults"],
+    init_rc: ["android.hardware.health-service.example.rc"],
+    overrides: ["charger"],
+}
+
+cc_binary {
+    name: "android.hardware.health-service.example_recovery",
+    recovery: true,
+    defaults: ["android.hardware.health-service.example-defaults"],
+    init_rc: ["android.hardware.health-service.example_recovery.rc"],
+    overrides: ["charger.recovery"],
 }
diff --git a/health/aidl/default/android.hardware.health-service.example_recovery.rc b/health/aidl/default/android.hardware.health-service.example_recovery.rc
new file mode 100644
index 0000000..0001170
--- /dev/null
+++ b/health/aidl/default/android.hardware.health-service.example_recovery.rc
@@ -0,0 +1,7 @@
+service vendor.health-default /system/bin/hw/android.hardware.health-service.example_recovery
+    class hal
+    seclabel u:r:hal_health_default:s0
+    user system
+    group system
+    capabilities WAKE_ALARM BLOCK_SUSPEND
+    file /dev/kmsg w
diff --git a/health/aidl/default/main.cpp b/health/aidl/default/main.cpp
index 76c6ba0..03b2ecb 100644
--- a/health/aidl/default/main.cpp
+++ b/health/aidl/default/main.cpp
@@ -39,14 +39,16 @@
 static constexpr std::string_view gChargerArg{"--charger"};
 
 int main(int argc, char** argv) {
+#ifdef __ANDROID_RECOVERY__
+    android::base::InitLogging(argv, android::base::KernelLogger);
+#endif
+
     // make a default health service
     auto config = std::make_unique<healthd_config>();
     ::android::hardware::health::InitHealthdConfig(config.get());
     auto binder = ndk::SharedRefBase::make<Health>(gInstanceName, std::move(config));
 
     if (argc >= 2 && argv[1] == gChargerArg) {
-        android::base::InitLogging(argv, &android::base::KernelLogger);
-
 #if !CHARGER_FORCE_NO_UI
         // If charger shouldn't have UI for your device, simply drop the line below
         // for your service implementation. This corresponds to
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Callbacks.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Callbacks.h
index 1ab9dcb..244001f 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Callbacks.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Callbacks.h
@@ -41,7 +41,7 @@
 
 // Converts the results of IDevice::prepareModel* to the NN canonical format. On success, this
 // function returns with a non-null nn::SharedPreparedModel with a feature level of
-// nn::Version::ANDROID_OC_MR1. On failure, this function returns with the appropriate
+// nn::kVersionFeatureLevel1. On failure, this function returns with the appropriate
 // nn::GeneralError.
 nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
         ErrorStatus status, const sp<IPreparedModel>& preparedModel);
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h
index 5c1480e..7710a7e 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h
@@ -28,7 +28,7 @@
 
 namespace android::hardware::neuralnetworks::V1_0::utils {
 
-constexpr auto kVersion = nn::Version::ANDROID_OC_MR1;
+constexpr auto kVersion = nn::kVersionFeatureLevel1;
 
 template <typename Type>
 nn::Result<void> validate(const Type& halObject) {
@@ -51,7 +51,7 @@
 template <typename Type>
 nn::Result<void> compliantVersion(const Type& canonical) {
     const auto version = NN_TRY(nn::validate(canonical));
-    if (version > kVersion) {
+    if (!nn::isCompliantVersion(version, kVersion)) {
         return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
     }
     return {};
diff --git a/neuralnetworks/1.0/utils/src/Device.cpp b/neuralnetworks/1.0/utils/src/Device.cpp
index 49913a2..b0c236e 100644
--- a/neuralnetworks/1.0/utils/src/Device.cpp
+++ b/neuralnetworks/1.0/utils/src/Device.cpp
@@ -99,7 +99,7 @@
 }
 
 nn::Version Device::getFeatureLevel() const {
-    return nn::Version::ANDROID_OC_MR1;
+    return kVersion;
 }
 
 nn::DeviceType Device::getType() const {
diff --git a/neuralnetworks/1.0/utils/test/DeviceTest.cpp b/neuralnetworks/1.0/utils/test/DeviceTest.cpp
index e881da2..83e555f 100644
--- a/neuralnetworks/1.0/utils/test/DeviceTest.cpp
+++ b/neuralnetworks/1.0/utils/test/DeviceTest.cpp
@@ -233,7 +233,7 @@
     const auto featureLevel = device->getFeatureLevel();
 
     // verify result
-    EXPECT_EQ(featureLevel, nn::Version::ANDROID_OC_MR1);
+    EXPECT_EQ(featureLevel, nn::kVersionFeatureLevel1);
 }
 
 TEST(DeviceTest, getCachedData) {
diff --git a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h
index 4660ff7..ff06739 100644
--- a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h
+++ b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h
@@ -30,7 +30,7 @@
 namespace android::hardware::neuralnetworks::V1_1::utils {
 
 constexpr auto kDefaultExecutionPreference = ExecutionPreference::FAST_SINGLE_ANSWER;
-constexpr auto kVersion = nn::Version::ANDROID_P;
+constexpr auto kVersion = nn::kVersionFeatureLevel2;
 
 template <typename Type>
 nn::Result<void> validate(const Type& halObject) {
@@ -53,7 +53,7 @@
 template <typename Type>
 nn::Result<void> compliantVersion(const Type& canonical) {
     const auto version = NN_TRY(nn::validate(canonical));
-    if (version > kVersion) {
+    if (!nn::isCompliantVersion(version, kVersion)) {
         return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
     }
     return {};
diff --git a/neuralnetworks/1.1/utils/src/Device.cpp b/neuralnetworks/1.1/utils/src/Device.cpp
index 7d54cab..3effa84 100644
--- a/neuralnetworks/1.1/utils/src/Device.cpp
+++ b/neuralnetworks/1.1/utils/src/Device.cpp
@@ -99,7 +99,7 @@
 }
 
 nn::Version Device::getFeatureLevel() const {
-    return nn::Version::ANDROID_P;
+    return kVersion;
 }
 
 nn::DeviceType Device::getType() const {
diff --git a/neuralnetworks/1.1/utils/test/DeviceTest.cpp b/neuralnetworks/1.1/utils/test/DeviceTest.cpp
index 41e0e30..2248da6 100644
--- a/neuralnetworks/1.1/utils/test/DeviceTest.cpp
+++ b/neuralnetworks/1.1/utils/test/DeviceTest.cpp
@@ -243,7 +243,7 @@
     const auto featureLevel = device->getFeatureLevel();
 
     // verify result
-    EXPECT_EQ(featureLevel, nn::Version::ANDROID_P);
+    EXPECT_EQ(featureLevel, nn::kVersionFeatureLevel2);
 }
 
 TEST(DeviceTest, getCachedData) {
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Callbacks.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Callbacks.h
index 6dd8138..fc04303 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Callbacks.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Callbacks.h
@@ -38,7 +38,8 @@
 
 // Converts the results of IDevice::prepareModel* to the NN canonical format. On success, this
 // function returns with a non-null nn::SharedPreparedModel with a feature level of
-// nn::Version::ANDROID_Q. On failure, this function returns with the appropriate nn::GeneralError.
+// nn::kVersionFeatureLevel3. On failure, this function returns with the appropriate
+// nn::GeneralError.
 nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
         V1_0::ErrorStatus status, const sp<IPreparedModel>& preparedModel);
 
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
index 23e336a..a06f2ac 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
@@ -39,7 +39,7 @@
 constexpr auto kDefaultMesaureTiming = MeasureTiming::NO;
 constexpr auto kNoTiming = Timing{.timeOnDevice = std::numeric_limits<uint64_t>::max(),
                                   .timeInDriver = std::numeric_limits<uint64_t>::max()};
-constexpr auto kVersion = nn::Version::ANDROID_Q;
+constexpr auto kVersion = nn::kVersionFeatureLevel3;
 
 template <typename Type>
 nn::Result<void> validate(const Type& halObject) {
@@ -62,7 +62,7 @@
 template <typename Type>
 nn::Result<void> compliantVersion(const Type& canonical) {
     const auto version = NN_TRY(nn::validate(canonical));
-    if (version > kVersion) {
+    if (!nn::isCompliantVersion(version, kVersion)) {
         return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
     }
     return {};
diff --git a/neuralnetworks/1.2/utils/src/Burst.cpp b/neuralnetworks/1.2/utils/src/Burst.cpp
index e0a23f1..911fbfa 100644
--- a/neuralnetworks/1.2/utils/src/Burst.cpp
+++ b/neuralnetworks/1.2/utils/src/Burst.cpp
@@ -315,7 +315,7 @@
 
     // if the request is valid but of a higher version than what's supported in burst execution,
     // fall back to another execution path
-    if (const auto version = NN_TRY(nn::validate(request)); version > nn::Version::ANDROID_Q) {
+    if (!compliantVersion(request).ok()) {
         // fallback to another execution path if the packet could not be sent
         return kPreparedModel->execute(request, measure, deadline, loopTimeoutDuration);
     }
@@ -359,7 +359,7 @@
 
     // if the request is valid but of a higher version than what's supported in burst execution,
     // fall back to another execution path
-    if (const auto version = NN_TRY(nn::validate(request)); version > nn::Version::ANDROID_Q) {
+    if (!compliantVersion(request).ok()) {
         // fallback to another execution path if the packet could not be sent
         return kPreparedModel->createReusableExecution(request, measure, loopTimeoutDuration);
     }
diff --git a/neuralnetworks/1.2/utils/src/Device.cpp b/neuralnetworks/1.2/utils/src/Device.cpp
index f12669a..e7acecd 100644
--- a/neuralnetworks/1.2/utils/src/Device.cpp
+++ b/neuralnetworks/1.2/utils/src/Device.cpp
@@ -192,7 +192,7 @@
 }
 
 nn::Version Device::getFeatureLevel() const {
-    return nn::Version::ANDROID_Q;
+    return kVersion;
 }
 
 nn::DeviceType Device::getType() const {
diff --git a/neuralnetworks/1.2/utils/test/DeviceTest.cpp b/neuralnetworks/1.2/utils/test/DeviceTest.cpp
index 215d44c..1dc6285 100644
--- a/neuralnetworks/1.2/utils/test/DeviceTest.cpp
+++ b/neuralnetworks/1.2/utils/test/DeviceTest.cpp
@@ -483,7 +483,7 @@
     const auto featureLevel = device->getFeatureLevel();
 
     // verify result
-    EXPECT_EQ(featureLevel, nn::Version::ANDROID_Q);
+    EXPECT_EQ(featureLevel, nn::kVersionFeatureLevel3);
 }
 
 TEST(DeviceTest, getCachedData) {
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Callbacks.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Callbacks.h
index 4b8ddc1..10892bc 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Callbacks.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Callbacks.h
@@ -47,7 +47,8 @@
 
 // Converts the results of IDevice::prepareModel* to the NN canonical format. On success, this
 // function returns with a non-null nn::SharedPreparedModel with a feature level of
-// nn::Version::ANDROID_R. On failure, this function returns with the appropriate nn::GeneralError.
+// nn::kVersionFeatureLevel4. On failure, this function returns with the appropriate
+// nn::GeneralError.
 nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
         ErrorStatus status, const sp<IPreparedModel>& preparedModel);
 
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h
index 2812db2..594d727 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h
@@ -39,7 +39,7 @@
 using V1_2::utils::kNoTiming;
 
 constexpr auto kDefaultPriority = Priority::MEDIUM;
-constexpr auto kVersion = nn::Version::ANDROID_R;
+constexpr auto kVersion = nn::kVersionFeatureLevel4;
 
 template <typename Type>
 nn::Result<void> validate(const Type& halObject) {
@@ -62,7 +62,7 @@
 template <typename Type>
 nn::Result<void> compliantVersion(const Type& canonical) {
     const auto version = NN_TRY(nn::validate(canonical));
-    if (version > kVersion) {
+    if (!nn::isCompliantVersion(version, kVersion)) {
         return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
     }
     return {};
diff --git a/neuralnetworks/1.3/utils/src/Device.cpp b/neuralnetworks/1.3/utils/src/Device.cpp
index a73ce82..9517fda 100644
--- a/neuralnetworks/1.3/utils/src/Device.cpp
+++ b/neuralnetworks/1.3/utils/src/Device.cpp
@@ -143,7 +143,7 @@
 }
 
 nn::Version Device::getFeatureLevel() const {
-    return nn::Version::ANDROID_R;
+    return kVersion;
 }
 
 nn::DeviceType Device::getType() const {
diff --git a/neuralnetworks/1.3/utils/test/DeviceTest.cpp b/neuralnetworks/1.3/utils/test/DeviceTest.cpp
index 2d1b2f2..7eba4bc 100644
--- a/neuralnetworks/1.3/utils/test/DeviceTest.cpp
+++ b/neuralnetworks/1.3/utils/test/DeviceTest.cpp
@@ -505,7 +505,7 @@
     const auto featureLevel = device->getFeatureLevel();
 
     // verify result
-    EXPECT_EQ(featureLevel, nn::Version::ANDROID_R);
+    EXPECT_EQ(featureLevel, nn::kVersionFeatureLevel4);
 }
 
 TEST(DeviceTest, getCachedData) {
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h
index 1fb694b..b4e747e 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h
@@ -33,9 +33,9 @@
 constexpr std::optional<nn::Version> aidlVersionToCanonicalVersion(int aidlVersion) {
     switch (aidlVersion) {
         case 1:
-            return nn::Version::ANDROID_S;
+            return nn::kVersionFeatureLevel5;
         case 2:
-            return nn::Version::FEATURE_LEVEL_6;
+            return nn::kVersionFeatureLevel6;
         default:
             return std::nullopt;
     }
@@ -64,7 +64,7 @@
 template <typename Type>
 nn::Result<void> compliantVersion(const Type& canonical) {
     const auto version = NN_TRY(nn::validate(canonical));
-    if (version > kVersion) {
+    if (!nn::isCompliantVersion(version, kVersion)) {
         return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
     }
     return {};
diff --git a/neuralnetworks/aidl/utils/src/Callbacks.cpp b/neuralnetworks/aidl/utils/src/Callbacks.cpp
index a321477..8084970 100644
--- a/neuralnetworks/aidl/utils/src/Callbacks.cpp
+++ b/neuralnetworks/aidl/utils/src/Callbacks.cpp
@@ -35,7 +35,8 @@
 
 // Converts the results of IDevice::prepareModel* to the NN canonical format. On success, this
 // function returns with a non-null nn::SharedPreparedModel with a feature level of
-// nn::Version::ANDROID_S. On failure, this function returns with the appropriate nn::GeneralError.
+// nn::kVersionFeatureLevel5. On failure, this function returns with the appropriate
+// nn::GeneralError.
 nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
         ErrorStatus status, const std::shared_ptr<IPreparedModel>& preparedModel) {
     HANDLE_STATUS_AIDL(status) << "model preparation failed with " << toString(status);
diff --git a/neuralnetworks/aidl/utils/test/DeviceTest.cpp b/neuralnetworks/aidl/utils/test/DeviceTest.cpp
index 79abe1b..4e9fc46 100644
--- a/neuralnetworks/aidl/utils/test/DeviceTest.cpp
+++ b/neuralnetworks/aidl/utils/test/DeviceTest.cpp
@@ -152,13 +152,15 @@
 };
 
 std::string printDeviceTest(const testing::TestParamInfo<nn::Version>& info) {
-    switch (info.param) {
-        case nn::Version::ANDROID_S:
+    const nn::Version version = info.param;
+    CHECK(!version.runtimeOnlyFeatures);
+    switch (version.level) {
+        case nn::Version::Level::FEATURE_LEVEL_5:
             return "v1";
-        case nn::Version::FEATURE_LEVEL_6:
+        case nn::Version::Level::FEATURE_LEVEL_6:
             return "v2";
         default:
-            LOG(FATAL) << "Invalid AIDL version: " << info.param;
+            LOG(FATAL) << "Invalid AIDL version: " << version;
             return "invalid";
     }
 }
@@ -891,7 +893,7 @@
 }
 
 INSTANTIATE_TEST_SUITE_P(TestDevice, DeviceTest,
-                         ::testing::Values(nn::Version::ANDROID_S, nn::Version::FEATURE_LEVEL_6),
+                         ::testing::Values(nn::kVersionFeatureLevel5, nn::kVersionFeatureLevel6),
                          printDeviceTest);
 
 }  // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/test/ResilientDeviceTest.cpp b/neuralnetworks/utils/common/test/ResilientDeviceTest.cpp
index 3abd724..0488b63 100644
--- a/neuralnetworks/utils/common/test/ResilientDeviceTest.cpp
+++ b/neuralnetworks/utils/common/test/ResilientDeviceTest.cpp
@@ -28,7 +28,6 @@
 namespace {
 
 using ::testing::_;
-using ::testing::InvokeWithoutArgs;
 using ::testing::Return;
 
 using SharedMockDevice = std::shared_ptr<const nn::MockDevice>;
@@ -54,7 +53,7 @@
     // Setup default actions for each relevant call.
     constexpr auto getName_ret = []() -> const std::string& { return kName; };
     constexpr auto getVersionString_ret = []() -> const std::string& { return kVersionString; };
-    constexpr auto kFeatureLevel = nn::Version::ANDROID_OC_MR1;
+    constexpr auto kFeatureLevel = nn::kVersionFeatureLevel1;
     constexpr auto kDeviceType = nn::DeviceType::ACCELERATOR;
     constexpr auto getSupportedExtensions_ret = []() -> const std::vector<nn::Extension>& {
         return kExtensions;
@@ -142,7 +141,7 @@
 TEST(ResilientDeviceTest, getFeatureLevel) {
     // setup call
     const auto [mockDevice, mockDeviceFactory, device] = setup();
-    constexpr auto kFeatureLevel = nn::Version::ANDROID_OC_MR1;
+    constexpr auto kFeatureLevel = nn::kVersionFeatureLevel1;
     EXPECT_CALL(*mockDevice, getFeatureLevel()).Times(1).WillOnce(Return(kFeatureLevel));
 
     // run test
@@ -592,7 +591,7 @@
     const auto recoveredMockDevice = createConfiguredMockDevice();
     EXPECT_CALL(*recoveredMockDevice, getFeatureLevel())
             .Times(1)
-            .WillOnce(Return(nn::Version::ANDROID_P));
+            .WillOnce(Return(nn::kVersionFeatureLevel2));
     EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
 
     // run test
diff --git a/radio/aidl/aidl_api/android.hardware.radio.config/current/android/hardware/radio/config/SimSlotStatus.aidl b/radio/aidl/aidl_api/android.hardware.radio.config/current/android/hardware/radio/config/SimSlotStatus.aidl
index 60eabc7..bc7f63c 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.config/current/android/hardware/radio/config/SimSlotStatus.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.config/current/android/hardware/radio/config/SimSlotStatus.aidl
@@ -34,7 +34,6 @@
 package android.hardware.radio.config;
 @VintfStability
 parcelable SimSlotStatus {
-  boolean cardActive;
   int cardState;
   String atr;
   String eid;
diff --git a/radio/aidl/aidl_api/android.hardware.radio.voice/current/android/hardware/radio/voice/IRadioVoice.aidl b/radio/aidl/aidl_api/android.hardware.radio.voice/current/android/hardware/radio/voice/IRadioVoice.aidl
index 4cac560..68c82fa 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.voice/current/android/hardware/radio/voice/IRadioVoice.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.voice/current/android/hardware/radio/voice/IRadioVoice.aidl
@@ -53,6 +53,7 @@
   oneway void hangup(in int serial, in int gsmIndex);
   oneway void hangupForegroundResumeBackground(in int serial);
   oneway void hangupWaitingOrBackground(in int serial);
+  oneway void isVoNrEnabled(in int serial);
   oneway void rejectCall(in int serial);
   oneway void responseAcknowledgement();
   oneway void sendBurstDtmf(in int serial, in String dtmf, in int on, in int off);
@@ -66,6 +67,7 @@
   oneway void setPreferredVoicePrivacy(in int serial, in boolean enable);
   oneway void setResponseFunctions(in android.hardware.radio.voice.IRadioVoiceResponse radioVoiceResponse, in android.hardware.radio.voice.IRadioVoiceIndication radioVoiceIndication);
   oneway void setTtyMode(in int serial, in android.hardware.radio.voice.TtyMode mode);
+  oneway void setVoNrEnabled(in int serial, in boolean enable);
   oneway void startDtmf(in int serial, in String s);
   oneway void stopDtmf(in int serial);
   oneway void switchWaitingOrHoldingAndActive(in int serial);
diff --git a/radio/aidl/aidl_api/android.hardware.radio.voice/current/android/hardware/radio/voice/IRadioVoiceResponse.aidl b/radio/aidl/aidl_api/android.hardware.radio.voice/current/android/hardware/radio/voice/IRadioVoiceResponse.aidl
index 9f490a8..a3b5e58 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.voice/current/android/hardware/radio/voice/IRadioVoiceResponse.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.voice/current/android/hardware/radio/voice/IRadioVoiceResponse.aidl
@@ -54,6 +54,7 @@
   oneway void hangupConnectionResponse(in android.hardware.radio.RadioResponseInfo info);
   oneway void hangupForegroundResumeBackgroundResponse(in android.hardware.radio.RadioResponseInfo info);
   oneway void hangupWaitingOrBackgroundResponse(in android.hardware.radio.RadioResponseInfo info);
+  oneway void isVoNrEnabledResponse(in android.hardware.radio.RadioResponseInfo info, in boolean enable);
   oneway void rejectCallResponse(in android.hardware.radio.RadioResponseInfo info);
   oneway void sendBurstDtmfResponse(in android.hardware.radio.RadioResponseInfo info);
   oneway void sendCdmaFeatureCodeResponse(in android.hardware.radio.RadioResponseInfo info);
@@ -65,6 +66,7 @@
   oneway void setMuteResponse(in android.hardware.radio.RadioResponseInfo info);
   oneway void setPreferredVoicePrivacyResponse(in android.hardware.radio.RadioResponseInfo info);
   oneway void setTtyModeResponse(in android.hardware.radio.RadioResponseInfo info);
+  oneway void setVoNrEnabledResponse(in android.hardware.radio.RadioResponseInfo info);
   oneway void startDtmfResponse(in android.hardware.radio.RadioResponseInfo info);
   oneway void stopDtmfResponse(in android.hardware.radio.RadioResponseInfo info);
   oneway void switchWaitingOrHoldingAndActiveResponse(in android.hardware.radio.RadioResponseInfo info);
diff --git a/radio/aidl/android/hardware/radio/config/SimSlotStatus.aidl b/radio/aidl/android/hardware/radio/config/SimSlotStatus.aidl
index 4ab955a..a1c3c27 100644
--- a/radio/aidl/android/hardware/radio/config/SimSlotStatus.aidl
+++ b/radio/aidl/android/hardware/radio/config/SimSlotStatus.aidl
@@ -20,7 +20,6 @@
 
 @VintfStability
 parcelable SimSlotStatus {
-    boolean cardActive;
     /**
      * Card state in the physical slot. Values are CardStatus.[STATE_ABSENT, STATE_PRESENT,
      * STATE_ERROR, STATE_RESTRICTED].
diff --git a/radio/aidl/android/hardware/radio/voice/IRadioVoice.aidl b/radio/aidl/android/hardware/radio/voice/IRadioVoice.aidl
index 1e60de5..a012be4 100644
--- a/radio/aidl/android/hardware/radio/voice/IRadioVoice.aidl
+++ b/radio/aidl/android/hardware/radio/voice/IRadioVoice.aidl
@@ -267,6 +267,15 @@
     void hangupWaitingOrBackground(in int serial);
 
     /**
+     * Query current Voice NR enable state
+     *
+     * @param serial Serial number of request.
+     *
+     * Response function is IRadioVoiceResponse.isVoNrEnabledResponse()
+     */
+    void isVoNrEnabled(in int serial);
+
+    /**
      * Send UDUB (user determined user busy) to ringing or waiting call answer)
      *
      * @param serial Serial number of request.
@@ -404,6 +413,16 @@
     void setTtyMode(in int serial, in TtyMode mode);
 
     /**
+     * Set Voice NR enable state
+     *
+     * @param serial Serial number of request.
+     * @param enable true for "enable vonr" and false for "disable vonr"
+     *
+     * Response function is IRadioVoiceResponse.setVoNrEnabledResponse()
+     */
+    void setVoNrEnabled(in int serial, in boolean enable);
+
+    /**
      * Start playing a DTMF tone. Continue playing DTMF tone until stopDtmf is received. If a
      * startDtmf() is received while a tone is currently playing, it must cancel the previous tone
      * and play the new one.
diff --git a/radio/aidl/android/hardware/radio/voice/IRadioVoiceResponse.aidl b/radio/aidl/android/hardware/radio/voice/IRadioVoiceResponse.aidl
index f3cf5fb..d126fc1 100644
--- a/radio/aidl/android/hardware/radio/voice/IRadioVoiceResponse.aidl
+++ b/radio/aidl/android/hardware/radio/voice/IRadioVoiceResponse.aidl
@@ -468,6 +468,20 @@
 
     /**
      * @param info Response info struct containing response type, serial no. and error
+     * @param enable true for "vonr enabled" and false for "vonr disabled"
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:MODEM_ERR
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:NO_MEMORY
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     */
+    void isVoNrEnabledResponse(in RadioResponseInfo info, in boolean enable);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
      *   RadioError:NONE
@@ -694,6 +708,20 @@
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
+     *   RadioError:MODEM_ERR
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:NO_MEMORY
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     */
+    void setVoNrEnabledResponse(in RadioResponseInfo info);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INVALID_ARGUMENTS
      *   RadioError:NO_RESOURCES
      *   RadioError:NO_MEMORY
      *   RadioError:SYSTEM_ERR
diff --git a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
index 26ed344..64550ef 100644
--- a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
+++ b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
@@ -50,7 +50,7 @@
         vector<KeyCharacteristics> attest_key_characteristics;
         vector<Certificate> attest_key_cert_chain;
         ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
-                                                     .RsaSigningKey(size, 65537)
+                                                     .RsaKey(size, 65537)
                                                      .AttestKey()
                                                      .SetDefaultValidity(),
                                              {} /* attestation signing key */, &attest_key.keyBlob,
@@ -200,7 +200,7 @@
     vector<Certificate> attest_key_cert_chain;
     ASSERT_EQ(ErrorCode::OK,
               GenerateKey(AuthorizationSetBuilder()
-                                  .RsaSigningKey(2048, 65537)
+                                  .RsaKey(2048, 65537)
                                   .AttestKey()
                                   .AttestationChallenge(challenge)
                                   .AttestationApplicationId(app_id)
@@ -299,7 +299,7 @@
 
         EXPECT_EQ(ErrorCode::OK,
                   GenerateKey(AuthorizationSetBuilder()
-                                      .RsaSigningKey(2048, 65537)
+                                      .RsaKey(2048, 65537)
                                       .AttestKey()
                                       .AttestationChallenge("foo")
                                       .AttestationApplicationId("bar")
@@ -371,7 +371,7 @@
 
         EXPECT_EQ(ErrorCode::OK,
                   GenerateKey(AuthorizationSetBuilder()
-                                      .EcdsaSigningKey(EcCurve::P_256)
+                                      .EcdsaKey(EcCurve::P_256)
                                       .AttestKey()
                                       .AttestationChallenge("foo")
                                       .AttestationApplicationId("bar")
@@ -446,7 +446,7 @@
         if ((i & 0x1) == 1) {
             EXPECT_EQ(ErrorCode::OK,
                       GenerateKey(AuthorizationSetBuilder()
-                                          .EcdsaSigningKey(EcCurve::P_256)
+                                          .EcdsaKey(EcCurve::P_256)
                                           .AttestKey()
                                           .AttestationChallenge("foo")
                                           .AttestationApplicationId("bar")
@@ -459,7 +459,7 @@
         } else {
             EXPECT_EQ(ErrorCode::OK,
                       GenerateKey(AuthorizationSetBuilder()
-                                          .RsaSigningKey(2048, 65537)
+                                          .RsaKey(2048, 65537)
                                           .AttestKey()
                                           .AttestationChallenge("foo")
                                           .AttestationApplicationId("bar")
@@ -509,7 +509,7 @@
         vector<KeyCharacteristics> attest_key_characteristics;
         vector<Certificate> attest_key_cert_chain;
         ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
-                                                     .RsaSigningKey(size, 65537)
+                                                     .RsaKey(size, 65537)
                                                      .AttestKey()
                                                      .SetDefaultValidity(),
                                              {} /* attestation signing key */, &attest_key.keyBlob,
@@ -555,12 +555,12 @@
         AttestationKey attest_key;
         vector<KeyCharacteristics> attest_key_characteristics;
         vector<Certificate> attest_key_cert_chain;
-        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
-                                                     .EcdsaSigningKey(curve)
-                                                     .AttestKey()
-                                                     .SetDefaultValidity(),
-                                             {} /* attestation signing key */, &attest_key.keyBlob,
-                                             &attest_key_characteristics, &attest_key_cert_chain));
+        ASSERT_EQ(
+                ErrorCode::OK,
+                GenerateKey(
+                        AuthorizationSetBuilder().EcdsaKey(curve).AttestKey().SetDefaultValidity(),
+                        {} /* attestation signing key */, &attest_key.keyBlob,
+                        &attest_key_characteristics, &attest_key_cert_chain));
 
         ASSERT_GT(attest_key_cert_chain.size(), 0);
         EXPECT_EQ(attest_key_cert_chain.size(), 1);
@@ -671,7 +671,7 @@
     vector<KeyCharacteristics> attest_key_characteristics;
     vector<Certificate> attest_key_cert_chain;
     ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
-                                                 .EcdsaSigningKey(EcCurve::P_256)
+                                                 .EcdsaKey(EcCurve::P_256)
                                                  .AttestKey()
                                                  .SetDefaultValidity(),
                                          {} /* attestation signing key */, &attest_key.keyBlob,
@@ -735,7 +735,7 @@
     vector<KeyCharacteristics> attest_key_characteristics;
     vector<Certificate> attest_key_cert_chain;
     ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
-                                                 .EcdsaSigningKey(EcCurve::P_256)
+                                                 .EcdsaKey(EcCurve::P_256)
                                                  .AttestKey()
                                                  .SetDefaultValidity(),
                                          {} /* attestation signing key */, &attest_key.keyBlob,