Implemented SWITCH_USER in the emulated User HAL.

Examples:

$ adb shell lshal debug android.hardware.automotive.vehicle@2.0::IVehicle/default --user-hal
No InitialUserInfo response
No SwitchUser response

$ adb shell lshal debug android.hardware.automotive.vehicle@2.0::IVehicle/default --set 299896584 a 1 i 666 i 3 i 1 s "D\\'OH!"
Set property {.timestamp = 63698971001637, .areaId = 1, .prop = 299896584, .status = AVAILABLE, .value = {.int32Values = [3]{666, 3, 1}, .floatValues = [0]{}, .int64Values = [0]{}, .bytes = [0]{}, .stringValue = "D'OH!"}}

$ adb shell lshal debug android.hardware.automotive.vehicle@2.0::IVehicle/default --user-hal
No InitialUserInfo response
SwitchUser response: {.timestamp = 63698971001637, .areaId = 1, .prop = 299896584, .status = AVAILABLE, .value = {.int32Values = [3]{666, 3, 1}, .floatValues = [0]{}, .int64Values = [0]{}, .bytes = [0]{}, .stringValue = "D'OH!"}}

Test: see commands above
Bug: 150409110

Change-Id: Id5b92774fccebbc39ed8d3f553df3f5aa30fc656
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 53c9ffb..ea75986 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
@@ -80,6 +80,7 @@
 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
@@ -1019,6 +1020,14 @@
                                 .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
                         },
         },
+        {
+                .config =
+                        {
+                                .prop = toInt(VehicleProperty::SWITCH_USER),
+                                .access = VehiclePropertyAccess::READ_WRITE,
+                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+                        },
+        },
 };
 
 }  // impl
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 9c3c95f..ce7dc65 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
@@ -73,10 +73,16 @@
 
 void EmulatedPassthroughConnector::dumpUserHal(int fd, std::string indent) {
     if (mInitialUserResponseFromCmd != nullptr) {
-        dprintf(fd, "%sInitial User Info: %s\n", indent.c_str(),
+        dprintf(fd, "%sInitialUserInfo response: %s\n", indent.c_str(),
                 toString(*mInitialUserResponseFromCmd).c_str());
     } else {
-        dprintf(fd, "%sNo Initial User Info\n", indent.c_str());
+        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());
     }
 }
 
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 a91ca8e..70e39eb 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
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "VehicleHalServer"
+
 #include "VehicleHalServer.h"
 
 #include <fstream>
@@ -244,7 +246,9 @@
             }
             break;
         case INITIAL_USER_INFO:
-            return onSetInitialUserInfo(value, updateStatus);
+            return onSetInitialUserInfoResponse(value, updateStatus);
+        case SWITCH_USER:
+            return onSetSwitchUserResponse(value, updateStatus);
         default:
             break;
     }
@@ -282,8 +286,8 @@
  * - if it's 3, then don't send a property change (so Android can emulate a timeout)
  *
  */
-StatusCode VehicleHalServer::onSetInitialUserInfo(const VehiclePropValue& value,
-                                                  bool updateStatus) {
+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...
@@ -320,7 +324,7 @@
     // mInitialUserResponseFromCmd is used for just one request
     std::unique_ptr<VehiclePropValue> response = std::move(mInitialUserResponseFromCmd);
 
-    // TODO(b/138709788): rather than populate the raw values directly, it should use the
+    // 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) {
@@ -350,4 +354,73 @@
     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 b1ae106..20e094a 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
@@ -53,13 +53,15 @@
     VehiclePropValuePtr createHwInputKeyProp(VehicleHwKeyInputAction action, int32_t keyCode,
                                              int32_t targetDisplay);
 
-    StatusCode onSetInitialUserInfo(const VehiclePropValue& value, bool updateStatus);
+    StatusCode onSetInitialUserInfoResponse(const VehiclePropValue& value, bool updateStatus);
+    StatusCode onSetSwitchUserResponse(const VehiclePropValue& value, bool updateStatus);
 
-  // data members
+    // 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;
 
   private:
     GeneratorHub mGeneratorHub{