Implemented INITIAL_USER_INFO.

This property 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,
so the following scenarios can be tested:
- property timeout
- HAL response using a different request id
- user switch
- user creation

Bug: 146207078
Test: manual tests with lshal and a modified CarServiceHelper
Test: atest android.hardware.automotive.vehicle@2.0-manager-unit-tests \
      android.hardware.automotive.vehicle@2.0-default-impl-unit-tests

Change-Id: Ia5be62c8b19a168c0c6da5307169fc14bf3069c9
This commit is contained in:
Felipe Leme
2020-02-05 15:56:13 -08:00
parent 89763d7185
commit 003fe685c2
4 changed files with 132 additions and 7 deletions

View File

@@ -28,6 +28,8 @@
#include <hwbinder/IPCThreadState.h>
#include <utils/SystemClock.h>
#include "VehicleUtils.h"
// TODO: figure out how to include private/android_filesystem_config.h instead...
@@ -247,10 +249,11 @@ void VehicleHalManager::cmdHelp(int fd) const {
dprintf(fd, "--get <PROP1> [PROP2] [PROPN]: dumps the value of specific properties \n");
// TODO: support other formats (int64, float, bytes)
dprintf(fd,
"--set <PROP> <i|s> <VALUE_1> [<i|s> <VALUE_N>]: sets the value of property PROP, using"
" arbitrary number of key/value parameters (i for int32, s for string). Notice that "
"the string value can be set just once, while the other can have multiple values "
"(so they're used in the respective array)\n");
"--set <PROP> <i|s> <VALUE_1> [<i|s> <VALUE_N>] [a AREA_ID] : sets the value of "
"property PROP, using arbitrary number of key/value parameters (i for int32, "
"s for string) and an optional area.\n"
"Notice that the string value can be set just once, while the other can have multiple "
"values (so they're used in the respective array)\n");
}
void VehicleHalManager::cmdListAllProperties(int fd) const {
@@ -352,13 +355,13 @@ void VehicleHalManager::cmdSetOneProperty(int fd, const hidl_vec<hidl_string>& o
VehiclePropValue prop;
if (!safelyParseInt(fd, 1, options[1], &prop.prop)) return;
prop.timestamp = 0;
prop.areaId = 0; // TODO: add option to pass areaId as parameter
prop.timestamp = elapsedRealtimeNano();
prop.status = VehiclePropertyStatus::AVAILABLE;
// First pass calculate sizes
// First pass: calculate sizes
int sizeInt32 = 0;
int stringIndex = 0;
int areaIndex = 0;
for (int i = 2, kv = 1; kv <= numberValues; kv++) {
// iterate through the kv=1..n key/value pairs, accessing indexes i / i+1 at each step
std::string type = options[i];
@@ -374,6 +377,15 @@ void VehicleHalManager::cmdSetOneProperty(int fd, const hidl_vec<hidl_string>& o
return;
}
stringIndex = i;
} else if (EqualsIgnoreCase(type, "a")) {
if (areaIndex != 0) {
dprintf(fd,
"defining area value (%s) again at index %d (already defined at %d=%s"
")\n",
value.c_str(), i, areaIndex, options[areaIndex + 1].c_str());
return;
}
areaIndex = i;
} else {
dprintf(fd, "invalid (%s) type at index %d\n", type.c_str(), i);
return;
@@ -395,6 +407,8 @@ void VehicleHalManager::cmdSetOneProperty(int fd, const hidl_vec<hidl_string>& o
prop.value.int32Values[indexInt32++] = safeInt;
} else if (EqualsIgnoreCase(type, "s")) {
prop.value.stringValue = value;
} else if (EqualsIgnoreCase(type, "a")) {
if (!safelyParseInt(fd, valueIndex, value, &prop.areaId)) return;
}
i += 2;
}

View File

@@ -76,6 +76,7 @@ constexpr int WHEEL_FRONT_LEFT = (int)VehicleAreaWheel::LEFT_FRONT;
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;
/**
* This property is used for test purpose to generate fake events. Here is the test package that
@@ -990,6 +991,16 @@ const ConfigDeclaration kVehicleProperties[]{
(int)VehicleVendorPermission::PERMISSION_DEFAULT},
},
.initialValue = {.int32Values = {1}}},
{
.config =
{
.prop = toInt(VehicleProperty::INITIAL_USER_INFO),
.access = VehiclePropertyAccess::READ_WRITE,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
},
},
};
} // impl

View File

@@ -13,6 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "automotive.vehicle@2.0-connector"
#include <android-base/logging.h>
#include <utils/SystemClock.h>
@@ -261,6 +264,8 @@ StatusCode EmulatedVehicleServer::onSetProperty(const VehiclePropValue& value, b
break;
}
break;
case INITIAL_USER_INFO:
return onSetInitialUserInfo(value, updateStatus);
default:
break;
}
@@ -274,6 +279,97 @@ StatusCode EmulatedVehicleServer::onSetProperty(const VehiclePropValue& value, b
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 EmulatedVehicleServer::onSetInitialUserInfo(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.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);
if (value.value.int32Values.size() == 0) {
LOG(ERROR) << "invalid request (no requestId): " << toString(value);
return StatusCode::INVALID_ARG;
}
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/138709788): 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;
}
EmulatedPassthroughConnectorPtr makeEmulatedPassthroughConnector() {
return std::make_unique<EmulatedPassthroughConnector>();
}

View File

@@ -77,6 +77,10 @@ class EmulatedVehicleServer : public IVehicleServer {
std::bind(&EmulatedVehicleServer::onFakeValueGenerated, this, std::placeholders::_1)};
VehiclePropValuePool* mValuePool{nullptr};
// TODO(b/146207078): it might be clearer to move members below to an EmulatedUserHal class
std::unique_ptr<VehiclePropValue> mInitialUserResponseFromCmd;
StatusCode onSetInitialUserInfo(const VehiclePropValue& value, bool updateStatus);
};
// Helper functions