Merge changes from topic "revert-10501254-multihal_2_1-FNHFKNQMKB" into rvc-dev

* changes:
  Revert "Set up Multi-HAL for Sensors HAL 2.1"
  Revert "Create Multi-HAL 2.1 service"
  Revert "Update tests for multihal to test HAL 2.1"
diff --git a/automotive/audiocontrol/2.0/default/AudioControl.cpp b/automotive/audiocontrol/2.0/default/AudioControl.cpp
index 5bde839..b7c11cd 100644
--- a/automotive/audiocontrol/2.0/default/AudioControl.cpp
+++ b/automotive/audiocontrol/2.0/default/AudioControl.cpp
@@ -69,7 +69,7 @@
 }
 
 Return<void> AudioControl::setFadeTowardFront(float value) {
-    if (!isValidValue(value)) {
+    if (isValidValue(value)) {
         // Just log in this default mock implementation
         LOG(INFO) << "Fader set to " << value;
     } else {
diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp
index 21b410a..872b35b 100644
--- a/automotive/vehicle/2.0/default/Android.bp
+++ b/automotive/vehicle/2.0/default/Android.bp
@@ -75,7 +75,10 @@
     ],
     local_include_dirs: ["common/include/vhal_v2_0"],
     export_include_dirs: ["impl"],
-    whole_static_libs: ["android.hardware.automotive.vehicle@2.0-manager-lib"],
+    whole_static_libs: [
+        "android.hardware.automotive.vehicle@2.0-emulated-user-hal-lib",
+        "android.hardware.automotive.vehicle@2.0-manager-lib",
+    ],
     shared_libs: [
         "libbase",
         "libjsoncpp",
@@ -87,6 +90,16 @@
     ],
 }
 
+// Library used  to emulate User HAL behavior through lshal debug requests.
+cc_library_static {
+    name: "android.hardware.automotive.vehicle@2.0-emulated-user-hal-lib",
+    vendor: true,
+    defaults: ["vhal_v2_0_defaults"],
+    srcs: [
+        "impl/vhal_v2_0/EmulatedUserHal.cpp",
+    ],
+}
+
 cc_test {
     name: "android.hardware.automotive.vehicle@2.0-manager-unit-tests",
     vendor: true,
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
index 83546e2..b8a606a 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
@@ -79,8 +79,6 @@
 constexpr int WHEEL_FRONT_RIGHT = (int)VehicleAreaWheel::RIGHT_FRONT;
 constexpr int WHEEL_REAR_LEFT = (int)VehicleAreaWheel::LEFT_REAR;
 constexpr int WHEEL_REAR_RIGHT = (int)VehicleAreaWheel::RIGHT_REAR;
-constexpr int INITIAL_USER_INFO = (int)VehicleProperty::INITIAL_USER_INFO;
-constexpr int SWITCH_USER = (int)VehicleProperty::SWITCH_USER;
 
 /**
  * This property is used for test purpose to generate fake events. Here is the test package that
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedUserHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedUserHal.cpp
new file mode 100644
index 0000000..c49fadc
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedUserHal.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "EmulatedUserHal"
+
+#include <cutils/log.h>
+#include <utils/SystemClock.h>
+
+#include "EmulatedUserHal.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+constexpr int INITIAL_USER_INFO = static_cast<int>(VehicleProperty::INITIAL_USER_INFO);
+constexpr int SWITCH_USER = static_cast<int>(VehicleProperty::SWITCH_USER);
+
+bool EmulatedUserHal::isSupported(int32_t prop) {
+    switch (prop) {
+        case INITIAL_USER_INFO:
+        case SWITCH_USER:
+            return true;
+        default:
+            return false;
+    }
+}
+
+android::base::Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onSetProperty(
+        const VehiclePropValue& value) {
+    ALOGV("onSetProperty(): %s", toString(value).c_str());
+
+    switch (value.prop) {
+        case INITIAL_USER_INFO:
+            return onSetInitialUserInfoResponse(value);
+        case SWITCH_USER:
+            return onSetSwitchUserResponse(value);
+        default:
+            return android::base::Error(static_cast<int>(StatusCode::INVALID_ARG))
+                   << "Unsupported property: " << toString(value);
+    }
+}
+
+android::base::Result<std::unique_ptr<VehiclePropValue>>
+EmulatedUserHal::onSetInitialUserInfoResponse(const VehiclePropValue& value) {
+    if (value.value.int32Values.size() == 0) {
+        ALOGE("set(INITIAL_USER_INFO): no int32values, ignoring it: %s", toString(value).c_str());
+        return android::base::Error(static_cast<int>(StatusCode::INVALID_ARG))
+               << "no int32values on " << toString(value);
+    }
+
+    if (value.areaId != 0) {
+        ALOGD("set(INITIAL_USER_INFO) called from lshal; storing it: %s", toString(value).c_str());
+        mInitialUserResponseFromCmd.reset(new VehiclePropValue(value));
+        return {};
+    }
+
+    ALOGD("set(INITIAL_USER_INFO) called from Android: %s", toString(value).c_str());
+
+    int32_t requestId = value.value.int32Values[0];
+    if (mInitialUserResponseFromCmd != nullptr) {
+        ALOGI("replying INITIAL_USER_INFO with lshal value:  %s",
+              toString(*mInitialUserResponseFromCmd).c_str());
+        return sendUserHalResponse(std::move(mInitialUserResponseFromCmd), requestId);
+    }
+
+    // Returns default response
+    auto updatedValue = std::unique_ptr<VehiclePropValue>(new VehiclePropValue);
+    updatedValue->prop = INITIAL_USER_INFO;
+    updatedValue->timestamp = elapsedRealtimeNano();
+    updatedValue->value.int32Values.resize(2);
+    updatedValue->value.int32Values[0] = requestId;
+    updatedValue->value.int32Values[1] = (int32_t)InitialUserInfoResponseAction::DEFAULT;
+
+    ALOGI("no lshal response; replying with InitialUserInfoResponseAction::DEFAULT: %s",
+          toString(*updatedValue).c_str());
+
+    return updatedValue;
+}
+
+android::base::Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onSetSwitchUserResponse(
+        const VehiclePropValue& value) {
+    if (value.value.int32Values.size() == 0) {
+        ALOGE("set(SWITCH_USER): no int32values, ignoring it: %s", toString(value).c_str());
+        return android::base::Error(static_cast<int>(StatusCode::INVALID_ARG))
+               << "no int32values on " << toString(value);
+    }
+
+    if (value.areaId != 0) {
+        ALOGD("set(SWITCH_USER) called from lshal; storing it: %s", toString(value).c_str());
+        mSwitchUserResponseFromCmd.reset(new VehiclePropValue(value));
+        return {};
+    }
+    ALOGD("set(SWITCH_USER) called from Android: %s", toString(value).c_str());
+
+    int32_t requestId = value.value.int32Values[0];
+    if (mSwitchUserResponseFromCmd != nullptr) {
+        ALOGI("replying SWITCH_USER with lshal value:  %s",
+              toString(*mSwitchUserResponseFromCmd).c_str());
+        return sendUserHalResponse(std::move(mSwitchUserResponseFromCmd), requestId);
+    }
+
+    // Returns default response
+    auto updatedValue = std::unique_ptr<VehiclePropValue>(new VehiclePropValue);
+    updatedValue->prop = SWITCH_USER;
+    updatedValue->timestamp = elapsedRealtimeNano();
+    updatedValue->value.int32Values.resize(3);
+    updatedValue->value.int32Values[0] = requestId;
+    updatedValue->value.int32Values[1] = (int32_t)SwitchUserMessageType::VEHICLE_RESPONSE;
+    updatedValue->value.int32Values[2] = (int32_t)SwitchUserStatus::SUCCESS;
+
+    ALOGI("no lshal response; replying with VEHICLE_RESPONSE / SUCCESS: %s",
+          toString(*updatedValue).c_str());
+
+    return updatedValue;
+}
+
+android::base::Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::sendUserHalResponse(
+        std::unique_ptr<VehiclePropValue> response, int32_t requestId) {
+    switch (response->areaId) {
+        case 1:
+            ALOGD("returning response with right request id");
+            response->value.int32Values[0] = requestId;
+            break;
+        case 2:
+            ALOGD("returning response with wrong request id");
+            response->value.int32Values[0] = -requestId;
+            break;
+        case 3:
+            ALOGD("not generating a property change event because of lshal prop: %s",
+                  toString(*response).c_str());
+            return android::base::Error(static_cast<int>(StatusCode::NOT_AVAILABLE))
+                   << "not generating a property change event because of lshal prop: "
+                   << toString(*response);
+        default:
+            ALOGE("invalid action on lshal response: %s", toString(*response).c_str());
+            return android::base::Error(static_cast<int>(StatusCode::INTERNAL_ERROR))
+                   << "invalid action on lshal response: " << toString(*response);
+    }
+
+    ALOGD("updating property to: %s", toString(*response).c_str());
+
+    return response;
+}
+
+void EmulatedUserHal::showDumpHelp(int fd) {
+    dprintf(fd, "%s: dumps state used for user management\n", kUserHalDumpOption);
+}
+
+void EmulatedUserHal::dump(int fd, std::string indent) {
+    if (mInitialUserResponseFromCmd != nullptr) {
+        dprintf(fd, "%sInitialUserInfo response: %s\n", indent.c_str(),
+                toString(*mInitialUserResponseFromCmd).c_str());
+    } else {
+        dprintf(fd, "%sNo InitialUserInfo response\n", indent.c_str());
+    }
+    if (mSwitchUserResponseFromCmd != nullptr) {
+        dprintf(fd, "%sSwitchUser response: %s\n", indent.c_str(),
+                toString(*mSwitchUserResponseFromCmd).c_str());
+    } else {
+        dprintf(fd, "%sNo SwitchUser response\n", indent.c_str());
+    }
+}
+
+}  // namespace impl
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedUserHal.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedUserHal.h
new file mode 100644
index 0000000..b25efcb
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedUserHal.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef android_hardware_automotive_vehicle_V2_0_impl_EmulatedUserHal_H_
+#define android_hardware_automotive_vehicle_V2_0_impl_EmulatedUserHal_H_
+
+#include <android-base/result.h>
+
+#include <android/hardware/automotive/vehicle/2.0/types.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+constexpr char kUserHalDumpOption[] = "--user-hal";
+
+/**
+ * Class used to emulate User HAL behavior through lshal debug requests.
+ */
+class EmulatedUserHal {
+  public:
+    EmulatedUserHal() {}
+
+    ~EmulatedUserHal() = default;
+
+    /**
+     * Checks if the emulator can handle the property.
+     */
+    bool isSupported(int32_t prop);
+
+    /**
+     * Lets the emulator handle the property.
+     *
+     * @return updated property and StatusCode
+     */
+    android::base::Result<std::unique_ptr<VehiclePropValue>> onSetProperty(
+            const VehiclePropValue& value);
+
+    /**
+     * Shows the User HAL emulation help.
+     */
+    void showDumpHelp(int fd);
+
+    /**
+     * Dump its contents.
+     */
+    void dump(int fd, std::string indent);
+
+  private:
+    /**
+     * INITIAL_USER_INFO is called by Android when it starts, and it's expecting a property change
+     * indicating what the initial user should be.
+     *
+     * During normal circumstances, the emulator will reply right away, passing a response if
+     * InitialUserInfoResponseAction::DEFAULT (so Android could use its own logic to decide which
+     * user to boot).
+     *
+     * But during development / testing, the behavior can be changed using lshal dump, which must
+     * use the areaId to indicate what should happen next.
+     *
+     * So, the behavior of set(INITIAL_USER_INFO) is:
+     *
+     * - if it has an areaId, store the property into mInitialUserResponseFromCmd (as it was called
+     * by lshal).
+     * - else if mInitialUserResponseFromCmd is not set, return a response with the same request id
+     * and InitialUserInfoResponseAction::DEFAULT
+     * - else the behavior is defined by the areaId on mInitialUserResponseFromCmd:
+     * - if it's 1, reply with mInitialUserResponseFromCmd and the right request id
+     * - if it's 2, reply with mInitialUserResponseFromCmd but a wrong request id (so Android can
+     * test this error scenario)
+     * - if it's 3, then don't send a property change (so Android can emulate a timeout)
+     *
+     */
+    android::base::Result<std::unique_ptr<VehiclePropValue>> onSetInitialUserInfoResponse(
+            const VehiclePropValue& value);
+
+    /**
+     * Used to emulate SWITCH_USER - see onSetInitialUserInfoResponse() for usage.
+     */
+    android::base::Result<std::unique_ptr<VehiclePropValue>> onSetSwitchUserResponse(
+            const VehiclePropValue& value);
+
+    android::base::Result<std::unique_ptr<VehiclePropValue>> sendUserHalResponse(
+            std::unique_ptr<VehiclePropValue> response, int32_t requestId);
+
+    std::unique_ptr<VehiclePropValue> mInitialUserResponseFromCmd;
+    std::unique_ptr<VehiclePropValue> mSwitchUserResponseFromCmd;
+};
+
+}  // namespace impl
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_automotive_vehicle_V2_0_impl_EmulatedUserHal_H_
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp
index ce7dc65..7f9362f 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp
@@ -38,9 +38,6 @@
 class EmulatedPassthroughConnector : public PassthroughConnector {
   public:
     bool onDump(const hidl_handle& fd, const hidl_vec<hidl_string>& options) override;
-
-  private:
-    void dumpUserHal(int fd, std::string indent);
 };
 
 bool EmulatedPassthroughConnector::onDump(const hidl_handle& handle,
@@ -50,12 +47,12 @@
     if (options.size() > 0) {
         if (options[0] == "--help") {
             dprintf(fd, "Emulator-specific usage:\n");
-            dprintf(fd, "--user-hal: dumps state used for user management \n");
+            mEmulatedUserHal.showDumpHelp(fd);
             dprintf(fd, "\n");
             // Include caller's help options
             return true;
-        } else if (options[0] == "--user-hal") {
-            dumpUserHal(fd, "");
+        } else if (options[0] == kUserHalDumpOption) {
+            mEmulatedUserHal.dump(fd, "");
             return false;
 
         } else {
@@ -65,27 +62,12 @@
     }
 
     dprintf(fd, "Emulator-specific state:\n");
-    dumpUserHal(fd, "  ");
+    mEmulatedUserHal.dump(fd, "  ");
     dprintf(fd, "\n");
 
     return true;
 }
 
-void EmulatedPassthroughConnector::dumpUserHal(int fd, std::string indent) {
-    if (mInitialUserResponseFromCmd != nullptr) {
-        dprintf(fd, "%sInitialUserInfo response: %s\n", indent.c_str(),
-                toString(*mInitialUserResponseFromCmd).c_str());
-    } else {
-        dprintf(fd, "%sNo InitialUserInfo response\n", indent.c_str());
-    }
-    if (mSwitchUserResponseFromCmd != nullptr) {
-        dprintf(fd, "%sSwitchUser response: %s\n", indent.c_str(),
-                toString(*mSwitchUserResponseFromCmd).c_str());
-    } else {
-        dprintf(fd, "%sNo SwitchUser response\n", indent.c_str());
-    }
-}
-
 PassthroughConnectorPtr makeEmulatedPassthroughConnector() {
     return std::make_unique<EmulatedPassthroughConnector>();
 }
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleHalServer.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleHalServer.cpp
index 70e39eb..ad5096e 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleHalServer.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleHalServer.cpp
@@ -181,6 +181,23 @@
 }
 
 StatusCode VehicleHalServer::onSetProperty(const VehiclePropValue& value, bool updateStatus) {
+    if (mEmulatedUserHal.isSupported(value.prop)) {
+        LOG(INFO) << "onSetProperty(): property " << value.prop << " will be handled by UserHal";
+
+        const auto& ret = mEmulatedUserHal.onSetProperty(value);
+        if (!ret.ok()) {
+            LOG(ERROR) << "onSetProperty(): HAL returned error: " << ret.error().message();
+            return StatusCode(ret.error().code());
+        }
+        auto updatedValue = ret.value().get();
+        if (updatedValue != nullptr) {
+            LOG(INFO) << "onSetProperty(): updating property returned by HAL: "
+                      << toString(*updatedValue);
+            onPropertyValueFromCar(*updatedValue, updateStatus);
+        }
+        return StatusCode::OK;
+    }
+
     // Some properties need to be treated non-trivially
     switch (value.prop) {
         case kGenerateFakeDataControllingProperty:
@@ -245,10 +262,6 @@
                     break;
             }
             break;
-        case INITIAL_USER_INFO:
-            return onSetInitialUserInfoResponse(value, updateStatus);
-        case SWITCH_USER:
-            return onSetSwitchUserResponse(value, updateStatus);
         default:
             break;
     }
@@ -262,165 +275,4 @@
     return StatusCode::OK;
 }
 
-/**
- * INITIAL_USER_INFO is called by Android when it starts, and it's expecting a property change
- * indicating what the initial user should be.
- *
- * During normal circumstances, the emulator will reply right away, passing a response if
- * InitialUserInfoResponseAction::DEFAULT (so Android could use its own logic to decide which user
- * to boot).
- *
- * But during development / testing, the behavior can be changed using lshal dump, which must use
- * the areaId to indicate what should happen next.
- *
- * So, the behavior of set(INITIAL_USER_INFO) is:
- *
- * - if it has an areaId, store the property into mInitialUserResponseFromCmd (as it was called by
- *   lshal).
- * - else if mInitialUserResponseFromCmd is not set, return a response with the same request id and
- *   InitialUserInfoResponseAction::DEFAULT
- * - else the behavior is defined by the areaId on mInitialUserResponseFromCmd:
- * - if it's 1, reply with mInitialUserResponseFromCmd and the right request id
- * - if it's 2, reply with mInitialUserResponseFromCmd but a wrong request id (so Android can test
- *   this error scenario)
- * - if it's 3, then don't send a property change (so Android can emulate a timeout)
- *
- */
-StatusCode VehicleHalServer::onSetInitialUserInfoResponse(const VehiclePropValue& value,
-                                                          bool updateStatus) {
-    // TODO: LOG calls below might be more suited to be DEBUG, but those are not being logged
-    // (even when explicitly calling setprop log.tag. As this class should be using ALOG instead of
-    // LOG, it's not worth investigating why...
-
-    if (value.value.int32Values.size() == 0) {
-        LOG(ERROR) << "set(INITIAL_USER_INFO): no int32values, ignoring it: " << toString(value);
-        return StatusCode::INVALID_ARG;
-    }
-
-    if (value.areaId != 0) {
-        LOG(INFO) << "set(INITIAL_USER_INFO) called from lshal; storing it: " << toString(value);
-        mInitialUserResponseFromCmd.reset(new VehiclePropValue(value));
-        return StatusCode::OK;
-    }
-    LOG(INFO) << "set(INITIAL_USER_INFO) called from Android: " << toString(value);
-
-    int32_t requestId = value.value.int32Values[0];
-
-    // Create the update property and set common values
-    auto updatedValue = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
-    updatedValue->prop = INITIAL_USER_INFO;
-    updatedValue->timestamp = elapsedRealtimeNano();
-
-    if (mInitialUserResponseFromCmd == nullptr) {
-        updatedValue->value.int32Values.resize(2);
-        updatedValue->value.int32Values[0] = requestId;
-        updatedValue->value.int32Values[1] = (int32_t)InitialUserInfoResponseAction::DEFAULT;
-        LOG(INFO) << "no lshal response; returning InitialUserInfoResponseAction::DEFAULT: "
-                  << toString(*updatedValue);
-        onPropertyValueFromCar(*updatedValue, updateStatus);
-        return StatusCode::OK;
-    }
-
-    // mInitialUserResponseFromCmd is used for just one request
-    std::unique_ptr<VehiclePropValue> response = std::move(mInitialUserResponseFromCmd);
-
-    // TODO(b/150409377): rather than populate the raw values directly, it should use the
-    // libraries that convert a InitialUserInfoResponse into a VehiclePropValue)
-
-    switch (response->areaId) {
-        case 1:
-            LOG(INFO) << "returning response with right request id";
-            *updatedValue = *response;
-            updatedValue->areaId = 0;
-            updatedValue->value.int32Values[0] = requestId;
-            break;
-        case 2:
-            LOG(INFO) << "returning response with wrong request id";
-            *updatedValue = *response;
-            updatedValue->areaId = 0;
-            updatedValue->value.int32Values[0] = -requestId;
-            break;
-        case 3:
-            LOG(INFO) << "not generating a property change event because of lshal prop: "
-                      << toString(*response);
-            return StatusCode::OK;
-        default:
-            LOG(ERROR) << "invalid action on lshal response: " << toString(*response);
-            return StatusCode::INTERNAL_ERROR;
-    }
-
-    LOG(INFO) << "updating property to: " << toString(*updatedValue);
-    onPropertyValueFromCar(*updatedValue, updateStatus);
-    return StatusCode::OK;
-}
-
-/**
- * Used to emulate SWITCH_USER - see onSetInitialUserInfoResponse() for usage.
- */
-StatusCode VehicleHalServer::onSetSwitchUserResponse(const VehiclePropValue& value,
-                                                     bool updateStatus) {
-    if (value.value.int32Values.size() == 0) {
-        LOG(ERROR) << "set(SWITCH_USER): no int32values, ignoring it: " << toString(value);
-        return StatusCode::INVALID_ARG;
-    }
-
-    if (value.areaId != 0) {
-        LOG(INFO) << "set(SWITCH_USER) called from lshal; storing it: " << toString(value);
-        mSwitchUserResponseFromCmd.reset(new VehiclePropValue(value));
-        return StatusCode::OK;
-    }
-    LOG(INFO) << "set(SWITCH_USER) called from Android: " << toString(value);
-
-    int32_t requestId = value.value.int32Values[0];
-
-    // Create the update property and set common values
-    auto updatedValue = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
-    updatedValue->prop = SWITCH_USER;
-    updatedValue->timestamp = elapsedRealtimeNano();
-
-    if (mSwitchUserResponseFromCmd == nullptr) {
-        updatedValue->value.int32Values.resize(3);
-        updatedValue->value.int32Values[0] = requestId;
-        updatedValue->value.int32Values[1] = (int32_t)SwitchUserMessageType::VEHICLE_RESPONSE;
-        updatedValue->value.int32Values[2] = (int32_t)SwitchUserStatus::SUCCESS;
-        LOG(INFO) << "no lshal response; returning VEHICLE_RESPONSE / SUCCESS: "
-                  << toString(*updatedValue);
-        onPropertyValueFromCar(*updatedValue, updateStatus);
-        return StatusCode::OK;
-    }
-
-    // mSwitchUserResponseFromCmd is used for just one request
-    std::unique_ptr<VehiclePropValue> response = std::move(mSwitchUserResponseFromCmd);
-
-    // TODO(b/150409377): move code below to a local function like sendUserHalResponse(),
-    // as it's the same for all (like onSetInitialUserInfoResponse)
-
-    switch (response->areaId) {
-        case 1:
-            LOG(INFO) << "returning response with right request id";
-            *updatedValue = *response;
-            updatedValue->areaId = 0;
-            updatedValue->value.int32Values[0] = requestId;
-            break;
-        case 2:
-            LOG(INFO) << "returning response with wrong request id";
-            *updatedValue = *response;
-            updatedValue->areaId = 0;
-            updatedValue->value.int32Values[0] = -requestId;
-            break;
-        case 3:
-            LOG(INFO) << "not generating a property change event because of lshal prop: "
-                      << toString(*response);
-            return StatusCode::OK;
-        default:
-            LOG(ERROR) << "invalid action on lshal response: " << toString(*response);
-            return StatusCode::INTERNAL_ERROR;
-    }
-
-    LOG(INFO) << "updating property to: " << toString(*updatedValue);
-    onPropertyValueFromCar(*updatedValue, updateStatus);
-
-    return StatusCode::OK;
-}
-
 }  // namespace android::hardware::automotive::vehicle::V2_0::impl
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleHalServer.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleHalServer.h
index 20e094a..2841fbe 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleHalServer.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleHalServer.h
@@ -19,6 +19,7 @@
 #include <vhal_v2_0/VehicleObjectPool.h>
 #include <vhal_v2_0/VehicleServer.h>
 
+#include "EmulatedUserHal.h"
 #include "GeneratorHub.h"
 
 namespace android::hardware::automotive::vehicle::V2_0::impl {
@@ -53,15 +54,10 @@
     VehiclePropValuePtr createHwInputKeyProp(VehicleHwKeyInputAction action, int32_t keyCode,
                                              int32_t targetDisplay);
 
-    StatusCode onSetInitialUserInfoResponse(const VehiclePropValue& value, bool updateStatus);
-    StatusCode onSetSwitchUserResponse(const VehiclePropValue& value, bool updateStatus);
-
     // data members
 
   protected:
-    // TODO(b/146207078): it might be clearer to move members below to an EmulatedUserHal class
-    std::unique_ptr<VehiclePropValue> mInitialUserResponseFromCmd;
-    std::unique_ptr<VehiclePropValue> mSwitchUserResponseFromCmd;
+    EmulatedUserHal mEmulatedUserHal;
 
   private:
     GeneratorHub mGeneratorHub{
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index 05b8b47..bf5fbfe 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -18,12 +18,13 @@
 
 #include <algorithm>
 #include <chrono>
+#include <condition_variable>
+#include <list>
 #include <mutex>
 #include <regex>
 #include <string>
 #include <unordered_map>
 #include <unordered_set>
-#include <condition_variable>
 
 #include <inttypes.h>
 
@@ -165,6 +166,26 @@
     YUV_REPROCESS,
 };
 
+enum SystemCameraKind {
+    /**
+     * These camera devices are visible to all apps and system components alike
+     */
+    PUBLIC = 0,
+
+    /**
+     * These camera devices are visible only to processes having the
+     * android.permission.SYSTEM_CAMERA permission. They are not exposed to 3P
+     * apps.
+     */
+    SYSTEM_ONLY_CAMERA,
+
+    /**
+     * These camera devices are visible only to HAL clients (that try to connect
+     * on a hwbinder thread).
+     */
+    HIDDEN_SECURE_CAMERA
+};
+
 namespace {
     // "device@<version>/legacy/<id>"
     const char *kDeviceNameRE = "device@([0-9]+\\.[0-9]+)/%s/(.+)";
@@ -851,6 +872,8 @@
     static Status isAutoFocusModeAvailable(
             CameraParameters &cameraParams, const char *mode) ;
     static Status isMonochromeCamera(const camera_metadata_t *staticMeta);
+    static Status getSystemCameraKind(const camera_metadata_t* staticMeta,
+                                      SystemCameraKind* systemCameraKind);
 
     // Used by switchToOffline where a new result queue is created for offline reqs
     void updateInflightResultQueue(std::shared_ptr<ResultMetadataQueue> resultQueue);
@@ -2555,6 +2578,90 @@
     }
 }
 
+TEST_P(CameraHidlTest, systemCameraTest) {
+    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
+    std::map<std::string, std::list<SystemCameraKind>> hiddenPhysicalIdToLogicalMap;
+    for (const auto& name : cameraDeviceNames) {
+        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
+        switch (deviceVersion) {
+            case CAMERA_DEVICE_API_VERSION_3_6:
+            case CAMERA_DEVICE_API_VERSION_3_5:
+            case CAMERA_DEVICE_API_VERSION_3_4:
+            case CAMERA_DEVICE_API_VERSION_3_3:
+            case CAMERA_DEVICE_API_VERSION_3_2: {
+                ::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_x;
+                ALOGI("getCameraCharacteristics: Testing camera device %s", name.c_str());
+                Return<void> ret;
+                ret = mProvider->getCameraDeviceInterface_V3_x(
+                        name, [&](auto status, const auto& device) {
+                            ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
+                            ASSERT_EQ(Status::OK, status);
+                            ASSERT_NE(device, nullptr);
+                            device3_x = device;
+                        });
+                ASSERT_TRUE(ret.isOk());
+
+                ret = device3_x->getCameraCharacteristics([&](auto status, const auto& chars) {
+                    ASSERT_EQ(status, Status::OK);
+                    const camera_metadata_t* staticMeta =
+                            reinterpret_cast<const camera_metadata_t*>(chars.data());
+                    ASSERT_NE(staticMeta, nullptr);
+                    Status rc = isLogicalMultiCamera(staticMeta);
+                    ASSERT_TRUE(Status::OK == rc || Status::METHOD_NOT_SUPPORTED == rc);
+                    if (Status::METHOD_NOT_SUPPORTED == rc) {
+                        return;
+                    }
+                    std::unordered_set<std::string> physicalIds;
+                    ASSERT_EQ(Status::OK, getPhysicalCameraIds(staticMeta, &physicalIds));
+                    SystemCameraKind systemCameraKind = SystemCameraKind::PUBLIC;
+                    rc = getSystemCameraKind(staticMeta, &systemCameraKind);
+                    ASSERT_EQ(rc, Status::OK);
+                    for (auto physicalId : physicalIds) {
+                        bool isPublicId = false;
+                        for (auto& deviceName : cameraDeviceNames) {
+                            std::string publicVersion, publicId;
+                            ASSERT_TRUE(::matchDeviceName(deviceName, mProviderType, &publicVersion,
+                                                          &publicId));
+                            if (physicalId == publicId) {
+                                isPublicId = true;
+                                break;
+                            }
+                        }
+                        // For hidden physical cameras, collect their associated logical cameras
+                        // and store the system camera kind.
+                        if (!isPublicId) {
+                            auto it = hiddenPhysicalIdToLogicalMap.find(physicalId);
+                            if (it == hiddenPhysicalIdToLogicalMap.end()) {
+                                hiddenPhysicalIdToLogicalMap.insert(std::make_pair(
+                                        physicalId, std::list<SystemCameraKind>(systemCameraKind)));
+                            } else {
+                                it->second.push_back(systemCameraKind);
+                            }
+                        }
+                    }
+                });
+                ASSERT_TRUE(ret.isOk());
+            } break;
+            case CAMERA_DEVICE_API_VERSION_1_0: {
+                // Not applicable
+            } break;
+            default: {
+                ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
+                ADD_FAILURE();
+            } break;
+        }
+    }
+
+    // Check that the system camera kind of the logical cameras associated with
+    // each hidden physical camera is the same.
+    for (const auto& it : hiddenPhysicalIdToLogicalMap) {
+        SystemCameraKind neededSystemCameraKind = it.second.front();
+        for (auto foundSystemCamera : it.second) {
+            ASSERT_EQ(neededSystemCameraKind, foundSystemCamera);
+        }
+    }
+}
+
 // Verify that the static camera characteristics can be retrieved
 // successfully.
 TEST_P(CameraHidlTest, getCameraCharacteristics) {
@@ -5671,6 +5778,39 @@
     return ret;
 }
 
+Status CameraHidlTest::getSystemCameraKind(const camera_metadata_t* staticMeta,
+                                           SystemCameraKind* systemCameraKind) {
+    Status ret = Status::OK;
+    if (nullptr == staticMeta || nullptr == systemCameraKind) {
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    camera_metadata_ro_entry entry;
+    int rc = find_camera_metadata_ro_entry(staticMeta, ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
+                                           &entry);
+    if (0 != rc) {
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    if (entry.count == 1 &&
+        entry.data.u8[0] == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA) {
+        *systemCameraKind = SystemCameraKind::HIDDEN_SECURE_CAMERA;
+        return ret;
+    }
+
+    // Go through the capabilities and check if it has
+    // ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA
+    for (size_t i = 0; i < entry.count; ++i) {
+        uint8_t capability = entry.data.u8[i];
+        if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA) {
+            *systemCameraKind = SystemCameraKind::SYSTEM_ONLY_CAMERA;
+            return ret;
+        }
+    }
+    *systemCameraKind = SystemCameraKind::PUBLIC;
+    return ret;
+}
+
 // Check whether this is a monochrome camera using the static camera characteristics.
 Status CameraHidlTest::isMonochromeCamera(const camera_metadata_t *staticMeta) {
     Status ret = Status::METHOD_NOT_SUPPORTED;
@@ -6391,8 +6531,10 @@
         const hidl_vec<hidl_string>& deviceNames) {
     const camera_metadata_t* metadata = (camera_metadata_t*)chars.data();
     ASSERT_NE(nullptr, metadata);
-
-    Status rc = isLogicalMultiCamera(metadata);
+    SystemCameraKind systemCameraKind = SystemCameraKind::PUBLIC;
+    Status rc = getSystemCameraKind(metadata, &systemCameraKind);
+    ASSERT_EQ(rc, Status::OK);
+    rc = isLogicalMultiCamera(metadata);
     ASSERT_TRUE(Status::OK == rc || Status::METHOD_NOT_SUPPORTED == rc);
     if (Status::METHOD_NOT_SUPPORTED == rc) {
         return;
@@ -6411,6 +6553,7 @@
         ASSERT_NE(physicalId, cameraId);
         bool isPublicId = false;
         std::string fullPublicId;
+        SystemCameraKind physSystemCameraKind = SystemCameraKind::PUBLIC;
         for (auto& deviceName : deviceNames) {
             std::string publicVersion, publicId;
             ASSERT_TRUE(::matchDeviceName(deviceName, mProviderType, &publicVersion, &publicId));
@@ -6434,9 +6577,16 @@
             ret = subDevice->getCameraCharacteristics(
                     [&](auto status, const auto& chars) {
                 ASSERT_EQ(Status::OK, status);
-                retcode = find_camera_metadata_ro_entry(
-                        (const camera_metadata_t *)chars.data(),
-                        ANDROID_CONTROL_ZOOM_RATIO_RANGE, &entry);
+                const camera_metadata_t* staticMeta =
+                        reinterpret_cast<const camera_metadata_t*>(chars.data());
+                rc = getSystemCameraKind(staticMeta, &physSystemCameraKind);
+                ASSERT_EQ(rc, Status::OK);
+                // Make sure that the system camera kind of a non-hidden
+                // physical cameras is the same as the logical camera associated
+                // with it.
+                ASSERT_EQ(physSystemCameraKind, systemCameraKind);
+                retcode = find_camera_metadata_ro_entry(staticMeta,
+                                                        ANDROID_CONTROL_ZOOM_RATIO_RANGE, &entry);
                 bool subCameraHasZoomRatioRange = (0 == retcode && entry.count == 2);
                 ASSERT_EQ(hasZoomRatioRange, subCameraHasZoomRatioRange);
             });
@@ -6452,17 +6602,16 @@
         ASSERT_NE(device3_5, nullptr);
 
         // Check camera characteristics for hidden camera id
-        Return<void> ret = device3_5->getPhysicalCameraCharacteristics(physicalId,
-                [&](auto status, const auto& chars) {
-            verifyCameraCharacteristics(status, chars);
-            verifyMonochromeCharacteristics(chars, deviceVersion);
-
-            retcode = find_camera_metadata_ro_entry(
-                    (const camera_metadata_t *)chars.data(),
-                    ANDROID_CONTROL_ZOOM_RATIO_RANGE, &entry);
-            bool subCameraHasZoomRatioRange = (0 == retcode && entry.count == 2);
-            ASSERT_EQ(hasZoomRatioRange, subCameraHasZoomRatioRange);
-        });
+        Return<void> ret = device3_5->getPhysicalCameraCharacteristics(
+                physicalId, [&](auto status, const auto& chars) {
+                    verifyCameraCharacteristics(status, chars);
+                    verifyMonochromeCharacteristics(chars, deviceVersion);
+                    retcode =
+                            find_camera_metadata_ro_entry((const camera_metadata_t*)chars.data(),
+                                                          ANDROID_CONTROL_ZOOM_RATIO_RANGE, &entry);
+                    bool subCameraHasZoomRatioRange = (0 == retcode && entry.count == 2);
+                    ASSERT_EQ(hasZoomRatioRange, subCameraHasZoomRatioRange);
+                });
         ASSERT_TRUE(ret.isOk());
 
         // Check calling getCameraDeviceInterface_V3_x() on hidden camera id returns
diff --git a/compatibility_matrices/compatibility_matrix.5.xml b/compatibility_matrices/compatibility_matrix.5.xml
index 1b051f5..e772b6f 100644
--- a/compatibility_matrices/compatibility_matrix.5.xml
+++ b/compatibility_matrices/compatibility_matrix.5.xml
@@ -371,6 +371,7 @@
     </hal>
     <hal format="hidl" optional="true">
         <name>android.hardware.radio</name>
+        <version>1.4</version>
         <version>1.5</version>
         <interface>
             <name>IRadio</name>
diff --git a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
index 9673821..07486e6 100644
--- a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
+++ b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
@@ -140,7 +140,8 @@
      * with STATUS_INVALID_DATA.
      *
      * @param id a numeric identifier that must be unique within the context of a Credential and may
-     *     be used to reference the profile. If this is not satisfied the call fails with
+     *     be used to reference the profile. This id must be non-negative and less than 32 (allowing
+     *     for a total of 32 profiles). If this is not satisfied the call fails with
      *     STATUS_INVALID_DATA.
      *
      * @param readerCertificate if non-empty, specifies a single X.509 certificate (not a chain of
diff --git a/identity/aidl/default/WritableIdentityCredential.cpp b/identity/aidl/default/WritableIdentityCredential.cpp
index 553a3d8..52cd496 100644
--- a/identity/aidl/default/WritableIdentityCredential.cpp
+++ b/identity/aidl/default/WritableIdentityCredential.cpp
@@ -143,6 +143,12 @@
     }
     accessControlProfileIds_.insert(id);
 
+    if (id < 0 || id >= 32) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA,
+                "Access Control Profile id must be non-negative and less than 32"));
+    }
+
     // Spec requires if |userAuthenticationRequired| is false, then |timeoutMillis| must also
     // be zero.
     if (!userAuthenticationRequired && timeoutMillis != 0) {
diff --git a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
index 56b30af..b68fbb5 100644
--- a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
+++ b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
@@ -641,6 +641,40 @@
     EXPECT_EQ(IIdentityCredentialStore::STATUS_INVALID_DATA, result.serviceSpecificErrorCode());
 }
 
+TEST_P(IdentityCredentialTests, verifyAccessControlProfileIdOutOfRange) {
+    sp<IWritableIdentityCredential> writableCredential;
+    ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_));
+
+    const vector<int32_t> entryCounts = {1};
+    Status result = writableCredential->startPersonalization(1, entryCounts);
+    ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
+                               << endl;
+
+    SecureAccessControlProfile profile;
+
+    // This should fail because the id is >= 32
+    result = writableCredential->addAccessControlProfile(32,     // id
+                                                         {},     // readerCertificate
+                                                         false,  // userAuthenticationRequired
+                                                         0,      // timeoutMillis
+                                                         42,     // secureUserId
+                                                         &profile);
+    ASSERT_FALSE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage();
+    ASSERT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode());
+    ASSERT_EQ(IIdentityCredentialStore::STATUS_INVALID_DATA, result.serviceSpecificErrorCode());
+
+    // This should fail because the id is < 0
+    result = writableCredential->addAccessControlProfile(-1,     // id
+                                                         {},     // readerCertificate
+                                                         false,  // userAuthenticationRequired
+                                                         0,      // timeoutMillis
+                                                         42,     // secureUserId
+                                                         &profile);
+    ASSERT_FALSE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage();
+    ASSERT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode());
+    ASSERT_EQ(IIdentityCredentialStore::STATUS_INVALID_DATA, result.serviceSpecificErrorCode());
+}
+
 INSTANTIATE_TEST_SUITE_P(
         Identity, IdentityCredentialTests,
         testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
diff --git a/media/omx/1.0/vts/functional/master/Android.bp b/media/omx/1.0/vts/functional/master/Android.bp
index 8e58821..5953eb5 100644
--- a/media/omx/1.0/vts/functional/master/Android.bp
+++ b/media/omx/1.0/vts/functional/master/Android.bp
@@ -19,6 +19,7 @@
     defaults: ["VtsHalMediaOmxV1_0Defaults"],
     srcs: ["VtsHalMediaOmxV1_0TargetMasterTest.cpp"],
     test_suites: [
+        "general-tests",
         "vts",
     ],
 }
diff --git a/media/omx/1.0/vts/functional/master/VtsHalMediaOmxV1_0TargetMasterTest.cpp b/media/omx/1.0/vts/functional/master/VtsHalMediaOmxV1_0TargetMasterTest.cpp
index c14f1da..9b4722e 100644
--- a/media/omx/1.0/vts/functional/master/VtsHalMediaOmxV1_0TargetMasterTest.cpp
+++ b/media/omx/1.0/vts/functional/master/VtsHalMediaOmxV1_0TargetMasterTest.cpp
@@ -20,6 +20,7 @@
 #endif
 
 #include <android-base/logging.h>
+#include <android-base/strings.h>
 
 #include <android/hardware/media/omx/1.0/IOmx.h>
 #include <android/hardware/media/omx/1.0/IOmxNode.h>
@@ -33,21 +34,22 @@
 #include <hidl/GtestPrinter.h>
 #include <hidl/ServiceManagement.h>
 
-using ::android::hardware::media::omx::V1_0::IOmx;
-using ::android::hardware::media::omx::V1_0::IOmxObserver;
-using ::android::hardware::media::omx::V1_0::IOmxNode;
-using ::android::hardware::media::omx::V1_0::IOmxStore;
-using ::android::hardware::media::omx::V1_0::Message;
-using ::android::hardware::media::omx::V1_0::CodecBuffer;
-using ::android::hardware::media::omx::V1_0::PortMode;
-using ::android::hidl::allocator::V1_0::IAllocator;
-using ::android::hidl::memory::V1_0::IMemory;
-using ::android::hidl::memory::V1_0::IMapper;
+using ::android::sp;
+using ::android::base::Join;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::hidl_string;
-using ::android::sp;
+using ::android::hardware::media::omx::V1_0::CodecBuffer;
+using ::android::hardware::media::omx::V1_0::IOmx;
+using ::android::hardware::media::omx::V1_0::IOmxNode;
+using ::android::hardware::media::omx::V1_0::IOmxObserver;
+using ::android::hardware::media::omx::V1_0::IOmxStore;
+using ::android::hardware::media::omx::V1_0::Message;
+using ::android::hardware::media::omx::V1_0::PortMode;
+using ::android::hidl::allocator::V1_0::IAllocator;
+using ::android::hidl::memory::V1_0::IMapper;
+using ::android::hidl::memory::V1_0::IMemory;
 
 #include <getopt.h>
 #include <media_hidl_test_common.h>
@@ -70,6 +72,11 @@
     }
 };
 
+struct AttributePattern {
+    const testing::internal::RE key;
+    const testing::internal::RE value;
+};
+
 void displayComponentInfo(hidl_vec<IOmx::ComponentInfo>& nodeList) {
     for (size_t i = 0; i < nodeList.size(); i++) {
         printf("%s | ", nodeList[i].mName.c_str());
@@ -80,6 +87,109 @@
     }
 }
 
+/*
+ * Returns the role based on is_encoder and mime.
+ *
+ * The mapping from a pair (is_encoder, mime) to a role string is
+ * defined in frameworks/av/media/libmedia/MediaDefs.cpp and
+ * frameworks/av/media/libstagefright/omx/OMXUtils.cpp. This function
+ * does essentially the same work as GetComponentRole() in
+ * OMXUtils.cpp.
+ *
+ * Args:
+ *   is_encoder: A boolean indicating whether the role is for an
+ *       encoder or a decoder.
+ *   mime: A string of the desired mime type.
+ *
+ * Returns:
+ *   A const string for the requested role name, empty if mime is not
+ *   recognized.
+ */
+const std::string getComponentRole(bool isEncoder, const std::string mime) {
+    // Mapping from mime types to roles.
+    // These values come from MediaDefs.cpp and OMXUtils.cpp
+    const std::map<const std::string, const std::string> audioMimeToRole = {
+            {"3gpp", "amrnb"},         {"ac3", "ac3"},     {"amr-wb", "amrwb"},
+            {"eac3", "eac3"},          {"flac", "flac"},   {"g711-alaw", "g711alaw"},
+            {"g711-mlaw", "g711mlaw"}, {"gsm", "gsm"},     {"mp4a-latm", "aac"},
+            {"mpeg", "mp3"},           {"mpeg-L1", "mp1"}, {"mpeg-L2", "mp2"},
+            {"opus", "opus"},          {"raw", "raw"},     {"vorbis", "vorbis"},
+    };
+    const std::map<const std::string, const std::string> videoMimeToRole = {
+            {"3gpp", "h263"},         {"avc", "avc"},           {"dolby-vision", "dolby-vision"},
+            {"hevc", "hevc"},         {"mp4v-es", "mpeg4"},     {"mpeg2", "mpeg2"},
+            {"x-vnd.on2.vp8", "vp8"}, {"x-vnd.on2.vp9", "vp9"},
+    };
+    const std::map<const std::string, const std::string> imageMimeToRole = {
+            {"vnd.android.heic", "heic"},
+    };
+
+    // Suffix begins after the mime prefix.
+    const size_t prefixEnd = mime.find("/");
+    if (prefixEnd == std::string::npos || prefixEnd == mime.size()) return "";
+    const std::string mime_suffix = mime.substr(prefixEnd + 1, mime.size() - 1);
+    const std::string middle = isEncoder ? "encoder." : "decoder.";
+    std::string prefix;
+    std::string suffix;
+    if (mime.rfind("audio/", 0) != std::string::npos) {
+        const auto it = audioMimeToRole.find(mime_suffix);
+        if (it == audioMimeToRole.end()) return "";
+        prefix = "audio_";
+        suffix = it->second;
+    } else if (mime.rfind("video/", 0) != std::string::npos) {
+        const auto it = videoMimeToRole.find(mime_suffix);
+        if (it == videoMimeToRole.end()) return "";
+        prefix = "video_";
+        suffix = it->second;
+    } else if (mime.rfind("image/", 0) != std::string::npos) {
+        const auto it = imageMimeToRole.find(mime_suffix);
+        if (it == imageMimeToRole.end()) return "";
+        prefix = "image_";
+        suffix = it->second;
+    } else {
+        return "";
+    }
+    return prefix + middle + suffix;
+}
+
+void validateAttributes(
+        const std::map<const std::string, const testing::internal::RE>& knownPatterns,
+        const std::vector<const struct AttributePattern>& unknownPatterns,
+        hidl_vec<IOmxStore::Attribute> attributes) {
+    std::set<const std::string> attributeKeys;
+    for (const auto& attr : attributes) {
+        // Make sure there are no duplicates
+        const auto [nodeIter, inserted] = attributeKeys.insert(attr.key);
+        EXPECT_EQ(inserted, true) << "Attribute \"" << attr.key << "\" has duplicates.";
+
+        // Check the value against the corresponding regular
+        // expression.
+        const auto knownPattern = knownPatterns.find(attr.key);
+        if (knownPattern != knownPatterns.end()) {
+            EXPECT_EQ(testing::internal::RE::FullMatch(attr.value, knownPattern->second), true)
+                    << "Attribute \"" << attr.key << "\" has invalid value \"" << attr.value << ".";
+            ;
+        } else {
+            // Failed to find exact attribute, check against
+            // possible patterns.
+            bool keyFound = false;
+            for (const auto& unknownPattern : unknownPatterns) {
+                if (testing::internal::RE::PartialMatch(attr.key, unknownPattern.key)) {
+                    keyFound = true;
+                    EXPECT_EQ(testing::internal::RE::FullMatch(attr.value, unknownPattern.value),
+                              true)
+                            << "Attribute \"" << attr.key << "\" has invalid value \"" << attr.value
+                            << ".";
+                }
+            }
+            if (!keyFound) {
+                std::cout << "Warning, Unrecognized attribute \"" << attr.key << "\" with value \""
+                          << attr.value << "\"." << std::endl;
+            }
+        }
+    }
+}
+
 // Make sure IOmx and IOmxStore have the same set of instances.
 TEST(MasterHidlTest, instanceMatchValidation) {
     auto omxInstances = android::hardware::getAllHalInstanceNames(IOmx::descriptor);
@@ -91,7 +201,7 @@
     }
 }
 
-// list service attributes
+// list service attributes and verify expected formats
 TEST_P(MasterHidlTest, ListServiceAttr) {
     description("list service attributes");
     android::hardware::media::omx::V1_0::Status status;
@@ -105,7 +215,36 @@
                     })
                     .isOk());
     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
-    if (attributes.size() == 0) ALOGV("Warning, Attribute list empty");
+    if (attributes.size() == 0) {
+        std::cout << "Warning, Attribute list empty" << std::endl;
+    } else {
+        /*
+         * knownPatterns is a map whose keys are the known "key" for a service
+         * attribute pair (see IOmxStore::Attribute), and whose values are the
+         * corresponding regular expressions that will have to match with the
+         * "value" of the attribute pair. If listServiceAttributes() returns an
+         * attribute that has a matching key but an unmatched value, the test
+         * will fail.
+         */
+        const std::map<const std::string, const testing::internal::RE> knownPatterns = {
+                {"max-video-encoder-input-buffers", "0|[1-9][0-9]*"},
+                {"supports-multiple-secure-codecs", "0|1"},
+                {"supports-secure-with-non-secure-codec", "0|1"},
+        };
+        /*
+         * unknownPatterns is a vector of pairs of regular expressions.
+         * For each attribute whose key is not known (i.e., does not match any
+         * of the keys in the "knownPatterns" variable defined above), that key will be
+         * tried for a match with the first element of each pair of the variable
+         * "unknownPatterns". If a match occurs, the value of that same attribute will be
+         * tried for a match with the second element of the pair. If this second
+         * match fails, the test will fail.
+         */
+        const std::vector<const struct AttributePattern> unknownPatterns = {
+                {"supports-[a-z0-9-]*", "0|1"}};
+
+        validateAttributes(knownPatterns, unknownPatterns, attributes);
+    }
 }
 
 // get node prefix
@@ -114,17 +253,183 @@
     hidl_string prefix;
     omxStore->getNodePrefix(
         [&prefix](hidl_string const& _nl) { prefix = _nl; });
-    if (prefix.empty()) ALOGV("Warning, Node Prefix empty");
+    if (prefix.empty()) std::cout << "Warning, Node Prefix empty" << std::endl;
 }
 
-// list roles
+// list roles and validate all RoleInfo objects
 TEST_P(MasterHidlTest, ListRoles) {
     description("list roles");
     hidl_vec<IOmxStore::RoleInfo> roleList;
     omxStore->listRoles([&roleList](hidl_vec<IOmxStore::RoleInfo> const& _nl) {
         roleList = _nl;
     });
-    if (roleList.size() == 0) ALOGV("Warning, RoleInfo list empty");
+    if (roleList.size() == 0) {
+        GTEST_SKIP() << "Warning, RoleInfo list empty";
+        return;
+    }
+
+    // Basic patterns for matching
+    const std::string toggle = "(0|1)";
+    const std::string string = "(.*)";
+    const std::string num = "(0|([1-9][0-9]*))";
+    const std::string size = "(" + num + "x" + num + ")";
+    const std::string ratio = "(" + num + ":" + num + ")";
+    const std::string range_num = "((" + num + "-" + num + ")|" + num + ")";
+    const std::string range_size = "((" + size + "-" + size + ")|" + size + ")";
+    const std::string range_ratio = "((" + ratio + "-" + ratio + ")|" + ratio + ")";
+    const std::string list_range_num = "(" + range_num + "(," + range_num + ")*)";
+
+    // Matching rules for node attributes with fixed keys
+    const std::map<const std::string, const testing::internal::RE> knownPatterns = {
+            {"alignment", size},
+            {"bitrate-range", range_num},
+            {"block-aspect-ratio-range", range_ratio},
+            {"block-count-range", range_num},
+            {"block-size", size},
+            {"blocks-per-second-range", range_num},
+            {"complexity-default", num},
+            {"complexity-range", range_num},
+            {"feature-adaptive-playback", toggle},
+            {"feature-bitrate-control", "(VBR|CBR|CQ)[,(VBR|CBR|CQ)]*"},
+            {"feature-can-swap-width-height", toggle},
+            {"feature-intra-refresh", toggle},
+            {"feature-partial-frame", toggle},
+            {"feature-secure-playback", toggle},
+            {"feature-tunneled-playback", toggle},
+            {"frame-rate-range", range_num},
+            {"max-channel-count", num},
+            {"max-concurrent-instances", num},
+            {"max-supported-instances", num},
+            {"pixel-aspect-ratio-range", range_ratio},
+            {"quality-default", num},
+            {"quality-range", range_num},
+            {"quality-scale", string},
+            {"sample-rate-ranges", list_range_num},
+            {"size-range", range_size},
+    };
+
+    // Strings for matching rules for node attributes with key patterns
+    const std::vector<const struct AttributePattern> unknownPatterns = {
+            {"measured-frame-rate-" + size + "-range", range_num},
+            {"feature-[a-zA-Z0-9_-]+", string},
+    };
+
+    // Matching rules for node names and owners
+    const testing::internal::RE nodeNamePattern = "[a-zA-Z0-9.-]+";
+    const testing::internal::RE nodeOwnerPattern = "[a-zA-Z0-9._-]+";
+
+    std::set<const std::string> roleKeys;
+    std::map<const std::string, std::set<const std::string>> nodeToRoles;
+    std::map<const std::string, std::set<const std::string>> ownerToNodes;
+    for (const IOmxStore::RoleInfo& role : roleList) {
+        // Make sure there are no duplicates
+        const auto [roleIter, inserted] = roleKeys.insert(role.role);
+        EXPECT_EQ(inserted, true) << "Role \"" << role.role << "\" has duplicates.";
+
+        // Make sure role name follows expected format based on type and
+        // isEncoder
+        const std::string role_name = getComponentRole(role.isEncoder, role.type);
+        EXPECT_EQ(role_name, role.role) << "Role \"" << role.role << "\" does not match "
+                                        << (role.isEncoder ? "an encoder " : "a decoder ")
+                                        << "for mime type \"" << role.type << ".";
+
+        // Check the nodes for this role
+        std::set<const std::string> nodeKeys;
+        for (const IOmxStore::NodeInfo& node : role.nodes) {
+            // Make sure there are no duplicates
+            const auto [nodeIter, inserted] = nodeKeys.insert(node.name);
+            EXPECT_EQ(inserted, true) << "Node \"" << node.name << "\" has duplicates.";
+
+            // Check the format of node name
+            EXPECT_EQ(testing::internal::RE::FullMatch(node.name, nodeNamePattern), true)
+                    << "Node name \"" << node.name << " is invalid.";
+            // Check the format of node owner
+            EXPECT_EQ(testing::internal::RE::FullMatch(node.owner, nodeOwnerPattern), true)
+                    << "Node owner \"" << node.owner << " is invalid.";
+
+            validateAttributes(knownPatterns, unknownPatterns, node.attributes);
+
+            ownerToNodes[node.owner].insert(node.name);
+            nodeToRoles[node.name].insert(role.role);
+        }
+    }
+
+    // Verify the information with IOmx::listNodes().
+    // IOmxStore::listRoles() and IOmx::listNodes() should give consistent
+    // information about nodes and roles.
+    for (const auto& [owner, nodes] : ownerToNodes) {
+        // Obtain the IOmx instance for each "owner"
+        const sp<IOmx> omx = omxStore->getOmx(owner);
+        EXPECT_NE(nullptr, omx);
+
+        // Invoke IOmx::listNodes()
+        android::hardware::media::omx::V1_0::Status status;
+        hidl_vec<IOmx::ComponentInfo> nodeList;
+        EXPECT_TRUE(
+                omx->listNodes([&status, &nodeList](android::hardware::media::omx::V1_0::Status _s,
+                                                    hidl_vec<IOmx::ComponentInfo> const& _nl) {
+                       status = _s;
+                       nodeList = _nl;
+                   }).isOk());
+        ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
+
+        // Verify that roles for each node match with the information from
+        // IOmxStore::listRoles().
+        std::set<const std::string> nodeKeys;
+        for (IOmx::ComponentInfo node : nodeList) {
+            // Make sure there are no duplicates
+            const auto [nodeIter, inserted] = nodeKeys.insert(node.mName);
+            EXPECT_EQ(inserted, true)
+                    << "IOmx::listNodes() lists duplicate nodes \"" << node.mName << "\".";
+
+            // Skip "hidden" nodes, i.e. those that are not advertised by
+            // IOmxStore::listRoles().
+            if (nodes.find(node.mName) == nodes.end()) {
+                std::cout << "Warning, IOmx::listNodes() lists unknown node \"" << node.mName
+                          << "\" for IOmx instance \"" << owner << "\"." << std::endl;
+                continue;
+            }
+
+            // All the roles advertised by IOmxStore::listRoles() for this
+            // node must be included in roleKeys.
+            std::set<const std::string> difference;
+            std::set_difference(nodeToRoles[node.mName].begin(), nodeToRoles[node.mName].end(),
+                                roleKeys.begin(), roleKeys.end(),
+                                std::inserter(difference, difference.begin()));
+            EXPECT_EQ(difference.empty(), true) << "IOmx::listNodes() for IOmx "
+                                                   "instance \""
+                                                << owner
+                                                << "\" does not report some "
+                                                   "expected nodes: "
+                                                << android::base::Join(difference, ", ") << ".";
+        }
+        // Check that all nodes obtained from IOmxStore::listRoles() are
+        // supported by the their corresponding IOmx instances.
+        std::set<const std::string> difference;
+        std::set_difference(nodes.begin(), nodes.end(), nodeKeys.begin(), nodeKeys.end(),
+                            std::inserter(difference, difference.begin()));
+        EXPECT_EQ(difference.empty(), true) << "IOmx::listNodes() for IOmx "
+                                               "instance \""
+                                            << owner
+                                            << "\" does not report some "
+                                               "expected nodes: "
+                                            << android::base::Join(difference, ", ") << ".";
+    }
+
+    if (!nodeToRoles.empty()) {
+        // Check that the prefix is a sensible string.
+        hidl_string prefix;
+        omxStore->getNodePrefix([&prefix](hidl_string const& _nl) { prefix = _nl; });
+        EXPECT_EQ(testing::internal::RE::PartialMatch(prefix, nodeNamePattern), true)
+                << "\"" << prefix << "\" is not a valid prefix for node names.";
+
+        // Check that all node names have the said prefix.
+        for (const auto& node : nodeToRoles) {
+            EXPECT_NE(node.first.rfind(prefix, 0), std::string::npos)
+                    << "Node \"" << node.first << "\" does not start with prefix \"" << prefix
+                    << "\".";
+        }
+    }
 }
 
 // list components and roles.
@@ -143,7 +448,7 @@
             .isOk());
     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
     if (nodeList.size() == 0)
-        ALOGV("Warning, ComponentInfo list empty");
+        std::cout << "Warning, ComponentInfo list empty" << std::endl;
     else {
         // displayComponentInfo(nodeList);
         for (size_t i = 0; i < nodeList.size(); i++) {
diff --git a/sensors/common/default/2.X/multihal/tests/Android.bp b/sensors/common/default/2.X/multihal/tests/Android.bp
index 7692b51..e0b3b8d 100644
--- a/sensors/common/default/2.X/multihal/tests/Android.bp
+++ b/sensors/common/default/2.X/multihal/tests/Android.bp
@@ -86,7 +86,6 @@
     shared_libs: [
         "android.hardware.sensors@1.0",
         "android.hardware.sensors@2.0",
-        "android.hardware.sensors@2.0-ScopedWakelock",
         "android.hardware.sensors@2.1",
         "libbase",
         "libcutils",