Removes vehicle.default target

Bug: 72179784
Test: None

Change-Id: Ia96ef01c98565deb8920d1df2eee31149a2a9338
This commit is contained in:
Hongwei Wang 2018-01-16 17:29:38 -08:00
parent bc04a28238
commit 82e4dc70f4
9 changed files with 0 additions and 3691 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,34 +0,0 @@
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
cc_library_shared {
name: "vehicle.default",
relative_install_path: "hw",
vendor: true,
srcs: [
"vehicle.c",
"timeUtil.cpp",
],
cflags: [
"-Wall",
"-Werror",
],
header_libs: ["libhardware_headers"],
shared_libs: [
"liblog",
"libcutils",
"libutils",
],
}

View file

@ -1,24 +0,0 @@
/*
* Copyright (C) 2015 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 <stdint.h>
#include <utils/SystemClock.h>
extern "C" {
int64_t elapsedRealtimeNano() {
return android::elapsedRealtimeNano();
}
}

View file

@ -1,579 +0,0 @@
/*
* Copyright (C) 2015 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 "vehicle_hw_default"
#define LOG_NDEBUG 1
#define RADIO_PRESET_NUM 6
#define UNUSED __attribute__((__unused__))
#include <errno.h>
#include <inttypes.h>
#include <malloc.h>
#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/time.h>
#include <time.h>
#include <log/log.h>
#include <system/radio.h>
#include <hardware/hardware.h>
#include <hardware/vehicle.h>
extern int64_t elapsedRealtimeNano();
static char VEHICLE_MAKE[] = "android_car";
typedef struct vehicle_device_impl {
vehicle_hw_device_t vehicle_device;
uint32_t initialized_;
vehicle_event_callback_fn event_fn_;
vehicle_error_callback_fn error_fn_;
} vehicle_device_impl_t ;
static pthread_mutex_t lock_;
typedef struct subscription {
// Each subscription has it's own thread.
pthread_t thread_id;
int32_t prop;
float sample_rate;
pthread_mutex_t lock;
// This field should be protected by the above mutex.
// TODO change this to something better as flag alone takes long time to finish.
uint32_t stop_thread;
vehicle_device_impl_t* impl;
pthread_t thread;
pthread_cond_t cond;
char name[100];
} subscription_t;
static vehicle_prop_config_t CONFIGS[] = {
{
.prop = VEHICLE_PROPERTY_INFO_MAKE,
.access = VEHICLE_PROP_ACCESS_READ,
.change_mode = VEHICLE_PROP_CHANGE_MODE_STATIC,
.value_type = VEHICLE_VALUE_TYPE_STRING,
.min_sample_rate = 0,
.max_sample_rate = 0,
.hal_data = NULL,
},
{
.prop = VEHICLE_PROPERTY_GEAR_SELECTION,
.access = VEHICLE_PROP_ACCESS_READ,
.change_mode = VEHICLE_PROP_CHANGE_MODE_ON_CHANGE,
.value_type = VEHICLE_VALUE_TYPE_INT32,
.min_sample_rate = 0,
.max_sample_rate = 0,
.hal_data = NULL,
},
{
.prop = VEHICLE_PROPERTY_DRIVING_STATUS,
.access = VEHICLE_PROP_ACCESS_READ,
.change_mode = VEHICLE_PROP_CHANGE_MODE_ON_CHANGE,
.value_type = VEHICLE_VALUE_TYPE_INT32,
.min_sample_rate = 0,
.max_sample_rate = 0,
.hal_data = NULL,
},
{
.prop = VEHICLE_PROPERTY_PARKING_BRAKE_ON,
.access = VEHICLE_PROP_ACCESS_READ,
.change_mode = VEHICLE_PROP_CHANGE_MODE_ON_CHANGE,
.value_type = VEHICLE_VALUE_TYPE_BOOLEAN,
.min_sample_rate = 0,
.max_sample_rate = 0,
.hal_data = NULL,
},
{
.prop = VEHICLE_PROPERTY_PERF_VEHICLE_SPEED,
.access = VEHICLE_PROP_ACCESS_READ,
.change_mode = VEHICLE_PROP_CHANGE_MODE_CONTINUOUS,
.value_type = VEHICLE_VALUE_TYPE_FLOAT,
.min_sample_rate = 0.1,
.max_sample_rate = 10.0,
.hal_data = NULL,
},
{
.prop = VEHICLE_PROPERTY_RADIO_PRESET,
.access = VEHICLE_PROP_ACCESS_READ_WRITE,
.change_mode = VEHICLE_PROP_CHANGE_MODE_ON_CHANGE,
.value_type = VEHICLE_VALUE_TYPE_INT32_VEC4,
.vehicle_radio_num_presets = RADIO_PRESET_NUM,
.min_sample_rate = 0,
.max_sample_rate = 0,
.hal_data = NULL,
},
};
vehicle_prop_config_t* find_config(int prop) {
unsigned int i;
for (i = 0; i < sizeof(CONFIGS) / sizeof(vehicle_prop_config_t); i++) {
if (CONFIGS[i].prop == prop) {
return &CONFIGS[i];
}
}
return NULL;
}
static int alloc_vehicle_str_from_cstr(const char* string, vehicle_str_t* vehicle_str) {
int len = strlen(string);
vehicle_str->data = (uint8_t*) malloc(len);
if (vehicle_str->data == NULL) {
return -ENOMEM;
}
memcpy(vehicle_str->data, string, len);
vehicle_str->len = len;
return 0;
}
static vehicle_prop_config_t const * vdev_list_properties(vehicle_hw_device_t* device UNUSED,
int* num_properties) {
ALOGD("vdev_list_properties.");
*num_properties = sizeof(CONFIGS) / sizeof(vehicle_prop_config_t);
return CONFIGS;
}
static int vdev_init(vehicle_hw_device_t* device,
vehicle_event_callback_fn event_callback_fn,
vehicle_error_callback_fn error_callback_fn) {
ALOGD("vdev_init.");
vehicle_device_impl_t* impl = (vehicle_device_impl_t*)device;
pthread_mutex_lock(&lock_);
if (impl->initialized_) {
ALOGE("vdev_init: Callback and Error functions are already existing.");
pthread_mutex_unlock(&lock_);
return -EEXIST;
}
impl->initialized_ = 1;
impl->event_fn_ = event_callback_fn;
impl->error_fn_ = error_callback_fn;
pthread_mutex_unlock(&lock_);
return 0;
}
static int vdev_release(vehicle_hw_device_t* device) {
vehicle_device_impl_t* impl = (vehicle_device_impl_t*)device;
pthread_mutex_lock(&lock_);
if (!impl->initialized_) {
ALOGD("vdev_release: Already released before, returning early.");
} else {
// unsubscribe_all()
impl->initialized_ = 0;
}
pthread_mutex_unlock(&lock_);
return 0;
}
static int vdev_get(vehicle_hw_device_t* device UNUSED, vehicle_prop_value_t* data) {
ALOGD("vdev_get.");
//TODO all data supporting read should support get
if (!data) {
ALOGE("vdev_get: Data cannot be null.");
return -EINVAL;
}
vehicle_prop_config_t* config = find_config(data->prop);
if (config == NULL) {
ALOGE("vdev_get: cannot find config 0x%x", data->prop);
return -EINVAL;
}
data->value_type = config->value_type;
// for STATIC type, time can be just 0 instead
data->timestamp = elapsedRealtimeNano();
int r;
switch (data->prop) {
case VEHICLE_PROPERTY_INFO_MAKE:
r = alloc_vehicle_str_from_cstr(VEHICLE_MAKE, &(data->value.str_value));
if (r != 0) {
ALOGE("vdev_get: alloc failed");
return r;
}
break;
case VEHICLE_PROPERTY_RADIO_PRESET: {
int radio_preset = data->value.int32_array[0];
if (radio_preset < VEHICLE_RADIO_PRESET_MIN_VALUE ||
radio_preset >= RADIO_PRESET_NUM) {
ALOGE("%s Invalid radio preset: %d\n", __func__, radio_preset);
return -1;
}
ALOGD("%s Radio Preset number: %d", __func__, radio_preset);
int32_t selector = radio_preset % 2 == 0;
// Populate the channel and subchannel to be some variation of the
// preset number for mocking.
// Restore the preset number.
data->value.int32_array[0] = radio_preset;
// Channel type values taken from
// system/core/include/system/radio.h
data->value.int32_array[1] = selector ? RADIO_BAND_FM : RADIO_BAND_AM;
// For FM set a value in Mhz and for AM set a value in Khz range
// (channel).
data->value.int32_array[2] = selector ? 99000000 : 100000;
// For FM we have a sub-channel and we care about it, for AM pass
// a dummy value.
data->value.int32_array[3] = selector ? radio_preset : -1;
break;
}
default:
// actual implementation will be much complex than this. It should track proper last
// state. Here just fill with zero.
memset(&(data->value), 0, sizeof(data->value));
break;
}
ALOGI("vdev_get, type 0x%x, time %" PRId64 ", value_type %d", data->prop, data->timestamp,
data->value_type);
return 0;
}
static void vdev_release_memory_from_get(struct vehicle_hw_device* device UNUSED,
vehicle_prop_value_t *data) {
switch (data->value_type) {
case VEHICLE_VALUE_TYPE_STRING:
case VEHICLE_VALUE_TYPE_BYTES:
free(data->value.str_value.data);
data->value.str_value.data = NULL;
break;
default:
ALOGW("release_memory_from_get for property 0x%x which is not string or bytes type 0x%x"
, data->prop, data->value_type);
break;
}
}
static int vdev_set(vehicle_hw_device_t* device UNUSED, const vehicle_prop_value_t* data) {
ALOGD("vdev_set.");
// Just print what data will be setting here.
ALOGD("Setting property %d with value type %d\n", data->prop, data->value_type);
vehicle_prop_config_t* config = find_config(data->prop);
if (config == NULL) {
ALOGE("vdev_set: cannot find config 0x%x", data->prop);
return -EINVAL;
}
if (config->value_type != data->value_type) {
ALOGE("vdev_set: type mismatch, passed 0x%x expecting 0x%x", data->value_type,
config->value_type);
return -EINVAL;
}
switch (data->value_type) {
case VEHICLE_VALUE_TYPE_FLOAT:
ALOGD("Value type: FLOAT\nValue: %f\n", data->value.float_value);
break;
case VEHICLE_VALUE_TYPE_INT32:
ALOGD("Value type: INT32\nValue: %" PRId32 "\n", data->value.int32_value);
break;
case VEHICLE_VALUE_TYPE_INT64:
ALOGD("Value type: INT64\nValue: %" PRId64 "\n", data->value.int64_value);
break;
case VEHICLE_VALUE_TYPE_BOOLEAN:
ALOGD("Value type: BOOLEAN\nValue: %d\n", data->value.boolean_value);
break;
case VEHICLE_VALUE_TYPE_STRING:
ALOGD("Value type: STRING\n Size: %d\n", data->value.str_value.len);
// NOTE: We only handle ASCII strings here.
// Print the UTF-8 string.
char *ascii_out = (char *) malloc ((data->value.str_value.len + 1) * sizeof (char));
memcpy(ascii_out, data->value.str_value.data, data->value.str_value.len);
ascii_out[data->value.str_value.len] = '\0';
ALOGD("Value: %s\n", ascii_out);
break;
case VEHICLE_VALUE_TYPE_INT32_VEC4:
ALOGD("Value type: INT32_VEC4\nValue[0]: %d Value[1] %d Value[2] %d Value[3] %d",
data->value.int32_array[0], data->value.int32_array[1],
data->value.int32_array[2], data->value.int32_array[3]);
break;
default:
ALOGD("Value type not yet handled: %d.\n", data->value_type);
}
return 0;
}
void print_subscribe_info(vehicle_device_impl_t* impl UNUSED) {
unsigned int i;
for (i = 0; i < sizeof(CONFIGS) / sizeof(vehicle_prop_config_t); i++) {
subscription_t* sub = (subscription_t*)CONFIGS[i].hal_data;
if (sub != NULL) {
ALOGD("prop: %d rate: %f", sub->prop, sub->sample_rate);
}
}
}
// This should be run in a separate thread always.
void fake_event_thread(struct subscription *sub) {
if (!sub) {
ALOGE("oops! subscription object cannot be NULL.");
exit(-1);
}
prctl(PR_SET_NAME, (unsigned long)sub->name, 0, 0, 0);
// Emit values in a loop, every 2 seconds.
while (1) {
// Create a random value depending on the property type.
vehicle_prop_value_t event;
event.prop = sub->prop;
event.timestamp = elapsedRealtimeNano();
switch (sub->prop) {
case VEHICLE_PROPERTY_GEAR_SELECTION:
event.value_type = VEHICLE_VALUE_TYPE_INT32;
switch ((event.timestamp & 0x30000000)>>28) {
case 0:
event.value.gear_selection = VEHICLE_GEAR_PARK;
break;
case 1:
event.value.gear_selection = VEHICLE_GEAR_NEUTRAL;
break;
case 2:
event.value.gear_selection = VEHICLE_GEAR_DRIVE;
break;
case 3:
event.value.gear_selection = VEHICLE_GEAR_REVERSE;
break;
}
break;
case VEHICLE_PROPERTY_PARKING_BRAKE_ON:
event.value_type = VEHICLE_VALUE_TYPE_BOOLEAN;
if (event.timestamp & 0x20000000) {
event.value.parking_brake = VEHICLE_FALSE;
} else {
event.value.parking_brake = VEHICLE_TRUE;
}
break;
case VEHICLE_PROPERTY_PERF_VEHICLE_SPEED:
event.value_type = VEHICLE_VALUE_TYPE_FLOAT;
event.value.vehicle_speed = (float) ((event.timestamp & 0xff000000)>>24);
break;
case VEHICLE_PROPERTY_RADIO_PRESET:
event.value_type = VEHICLE_VALUE_TYPE_INT32_VEC4;
int presetInfo1[4] = {1 /* preset number */, 0 /* AM Band */, 1000, 0};
int presetInfo2[4] = {2 /* preset number */, 1 /* FM Band */, 1000, 0};
if (event.timestamp & 0x20000000) {
memcpy(event.value.int32_array, presetInfo1, sizeof(presetInfo1));
} else {
memcpy(event.value.int32_array, presetInfo2, sizeof(presetInfo2));
}
break;
default: // unsupported
if (sub->impl == NULL) {
ALOGE("subscription impl NULL");
return;
}
if (sub->impl->error_fn_ != NULL) {
sub->impl->error_fn_(-EINVAL, VEHICLE_PROPERTY_INVALID,
VEHICLE_OPERATION_GENERIC);
} else {
ALOGE("Error function is null");
}
ALOGE("Unsupported prop 0x%x, quit", sub->prop);
return;
}
if (sub->impl->event_fn_ != NULL) {
sub->impl->event_fn_(&event);
} else {
ALOGE("Event function is null");
return;
}
pthread_mutex_lock(&sub->lock);
if (sub->stop_thread) {
ALOGD("exiting subscription request here.");
// Do any cleanup here.
pthread_mutex_unlock(&sub->lock);
return;
}
struct timespec now;
clock_gettime(CLOCK_REALTIME, &now);
now.tv_sec += 1; // sleep for one sec
pthread_cond_timedwait(&sub->cond, &sub->lock, &now);
pthread_mutex_unlock(&sub->lock);
}
}
static int vdev_subscribe(vehicle_hw_device_t* device, int32_t prop, float sample_rate,
int32_t zones UNUSED) {
ALOGD("vdev_subscribe 0x%x, %f", prop, sample_rate);
vehicle_device_impl_t* impl = (vehicle_device_impl_t*)device;
// Check that the device is initialized.
pthread_mutex_lock(&lock_);
if (!impl->initialized_) {
pthread_mutex_unlock(&lock_);
ALOGE("vdev_subscribe: have you called init()?");
return -EINVAL;
}
vehicle_prop_config_t* config = find_config(prop);
if (config == NULL) {
pthread_mutex_unlock(&lock_);
ALOGE("vdev_subscribe not supported property 0x%x", prop);
return -EINVAL;
}
if ((config->access != VEHICLE_PROP_ACCESS_READ) &&
(config->access != VEHICLE_PROP_ACCESS_READ_WRITE)) {
pthread_mutex_unlock(&lock_);
ALOGE("vdev_subscribe read not supported on the property 0x%x", prop);
return -EINVAL;
}
if (config->change_mode == VEHICLE_PROP_CHANGE_MODE_STATIC) {
pthread_mutex_unlock(&lock_);
ALOGE("vdev_subscribe cannot subscribe static property 0x%x", prop);
return -EINVAL;
}
if ((config->change_mode == VEHICLE_PROP_CHANGE_MODE_ON_CHANGE) && (sample_rate != 0)) {
pthread_mutex_unlock(&lock_);
ALOGE("vdev_subscribe on change type should have 0 sample rate, property 0x%x, sample rate %f",
prop, sample_rate);
return -EINVAL;
}
if ((config->max_sample_rate < sample_rate) || (config->min_sample_rate > sample_rate)) {
ALOGE("vdev_subscribe property 0x%x, invalid sample rate %f, min:%f, max:%f",
prop, sample_rate, config->min_sample_rate, config->max_sample_rate);
pthread_mutex_unlock(&lock_);
return -EINVAL;
}
subscription_t* sub = (subscription_t*)config->hal_data;
if (sub == NULL) {
sub = calloc(1, sizeof(subscription_t));
sub->prop = prop;
sub->sample_rate = sample_rate;
sub->stop_thread = 0;
sub->impl = impl;
pthread_mutex_init(&sub->lock, NULL);
pthread_cond_init(&sub->cond, NULL);
config->hal_data = sub;
sprintf(sub->name, "vhal0x%x", prop);
} else if (sub->sample_rate != sample_rate){ // sample rate changed
//TODO notify this to fake sensor thread
sub->sample_rate = sample_rate;
pthread_mutex_unlock(&lock_);
return 0;
}
int ret_code = pthread_create(
&sub->thread, NULL, (void *(*)(void*))fake_event_thread, sub);
if (ret_code != 0) {
return -ret_code;
}
print_subscribe_info(impl);
pthread_mutex_unlock(&lock_);
return 0;
}
static int vdev_unsubscribe(vehicle_hw_device_t* device, int32_t prop) {
ALOGD("vdev_unsubscribe 0x%x", prop);
vehicle_device_impl_t* impl = (vehicle_device_impl_t*)device;
pthread_mutex_lock(&lock_);
vehicle_prop_config_t* config = find_config(prop);
if (config == NULL) {
pthread_mutex_unlock(&lock_);
return -EINVAL;
}
subscription_t* sub = (subscription_t*)config->hal_data;
if (sub == NULL) {
pthread_mutex_unlock(&lock_);
return -EINVAL;
}
config->hal_data = NULL;
pthread_mutex_unlock(&lock_);
pthread_mutex_lock(&sub->lock);
sub->stop_thread = 1;
pthread_cond_signal(&sub->cond);
pthread_mutex_unlock(&sub->lock);
pthread_join(sub->thread, NULL);
pthread_cond_destroy(&sub->cond);
pthread_mutex_destroy(&sub->lock);
free(sub);
pthread_mutex_lock(&lock_);
print_subscribe_info(impl);
pthread_mutex_unlock(&lock_);
return 0;
}
static int vdev_close(hw_device_t* device) {
vehicle_device_impl_t* impl = (vehicle_device_impl_t*)device;
if (impl) {
free(impl);
return 0;
} else {
return -1;
}
}
static int vdev_dump(struct vehicle_hw_device* device UNUSED, int fd UNUSED) {
//TODO
return 0;
}
/*
* The open function is provided as an interface in harwdare.h which fills in
* all the information about specific implementations and version specific
* informations in hw_device_t structure. After calling open() the client should
* use the hw_device_t to execute any Vehicle HAL device specific functions.
*/
static int vdev_open(const hw_module_t* module, const char* name UNUSED,
hw_device_t** device) {
ALOGD("vdev_open");
// Oops, out of memory!
vehicle_device_impl_t* vdev = calloc(1, sizeof(vehicle_device_impl_t));
if (vdev == NULL) {
return -ENOMEM;
}
// Common functions provided by harware.h to access module and device(s).
vdev->vehicle_device.common.tag = HARDWARE_DEVICE_TAG;
vdev->vehicle_device.common.version = VEHICLE_DEVICE_API_VERSION_1_0;
vdev->vehicle_device.common.module = (hw_module_t *) module;
vdev->vehicle_device.common.close = vdev_close;
// Define the Vehicle HAL device specific functions.
vdev->vehicle_device.list_properties = vdev_list_properties;
vdev->vehicle_device.init = vdev_init;
vdev->vehicle_device.release = vdev_release;
vdev->vehicle_device.get = vdev_get;
vdev->vehicle_device.release_memory_from_get = vdev_release_memory_from_get;
vdev->vehicle_device.set = vdev_set;
vdev->vehicle_device.subscribe = vdev_subscribe;
vdev->vehicle_device.unsubscribe = vdev_unsubscribe;
vdev->vehicle_device.dump = vdev_dump;
*device = (hw_device_t *) vdev;
return 0;
}
static struct hw_module_methods_t hal_module_methods = {
.open = vdev_open,
};
/*
* This structure is mandatory to be implemented by each HAL implementation. It
* exposes the open method (see hw_module_methods_t above) which opens a device.
* The vehicle HAL is supposed to be used as a single device HAL hence all the
* functions should be implemented inside of the vehicle_hw_device_t struct (see
* the vehicle.h in include/ folder.
*/
vehicle_module_t HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = VEHICLE_MODULE_API_VERSION_1_0,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = VEHICLE_HARDWARE_MODULE_ID,
.name = "Default vehicle HW HAL",
.author = "",
.methods = &hal_module_methods,
},
};

View file

@ -1,48 +0,0 @@
//
// Copyright (C) 2015 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.
// Build native tests.
cc_test {
name: "vehicle_tests",
srcs: ["vehicle_tests.cpp"],
shared_libs: [
"liblog",
"libhardware",
],
cflags: [
"-Wall",
"-Wextra",
"-Werror",
],
}
// Build HAL command line utility.
cc_binary {
name: "vehicle-hal-tool",
srcs: ["vehicle-hal-tool.c"],
cflags: [
"-Wall",
"-Wno-unused-parameter",
"-Werror",
],
shared_libs: [
"libcutils",
"libhardware",
"liblog",
],
}

View file

@ -1,73 +0,0 @@
What does this document tell?
This document details how to use the vehicle service if you are implementhing
HAL. It lists the various places to look for code and how to build and test the
code on your own dev device.
This code also provides a simple command line utility for the target to test the
vehicle HAL.
What is the code?
The code is split into folowing logical components:
a) hardware/libhardware/include/hardware/vehicle.h - this is the main HAL
interface that will be required to be implemented by the OEMs. It includes all
documentation necessary to understand what vehicle subsystems are exposed,
various units, capabilities and any other relevant details about the HAL design
itself.
b) hardware/libhardware/modules/vehicle/vehicle.c
This is a reference implementation for the OEMs to have a peek into getting
started with a barebones structure. There are implementation for each of the
critical HAL functions such as, get(), set() and subscribe().
c) hardware/libhardware/tests/vehicle/vehicle_test.cpp & vehicle_test_fixtures.h
These are native tests that can be run on the target to validate basic
features of HAL implementation. Things such as loading of HAL and
basic functions are implemented (by check if the returned functions are not NULL
pointers) can be asserted. It also checks if the subscribe function is doing its
job by spitting out data at continuous intervals and printed on the stdout.
d) hardware/libhardware/tests/vehicle/vehicle-hal-tool.c
This tool will provide you with a simple utility which can set commands to the
HAL such as:
i) Getting a property (and printing its value).
ii) Setting a property (and the HAL will take some setting action).
iii) Subscribe to a property (and the HAL should send you values at some certain
intevals).
See the usage() function in vehicle-hal-tool.c for details on how to use the
binary.
How to build and run?
You can build everything by issuing the following from the top of directory. It
is assumed that you have done a first run of make from the top level so that
intermediates are generated.
$ croot
$ mmm hardware/libhardware
This will generate the following binaries that we care about:
i) out/target/product/XXX/vendor/lib/hw/vehicle.default.so
ii) out/target/product/XXX/data/nativetest/vehicle_tests
iii) out/target/product/XXX/system/bin/vehicle-hal-tool
The location for the first shared library would be:
$ adb push out/target/product/XXX/vendor/lib/hw/vehicle.default.so
/vendor/lib/hw
You can also use 'adb sync' if you like, although this is the easiest least
hassle way of putting it in place.
The second binary is a native test - which is nothing but an executable for the
target device. You can load it anywhere in your /data directory and run it as:
$ adb push out/target/product/XXX/data/nativetest/vehicle_tests
/data/tmp/vehicle_tests
$ adb shell
$ ./data/tmp/vehicle_tests
<...output should be spitted with passing tests for atleast the reference
implementation ...>
The last binary is the command line tool, to push the binary on your target do:
$ adb push out/target/product/XXX/system/bin/vehicle-hal-tool
/data/tmp/vehicle-hal-tool

View file

@ -1,537 +0,0 @@
/*
* Copyright (C) 2015 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 "vehicle-hal-tool"
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <log/log.h>
#include <hardware/hardware.h>
#include <hardware/vehicle.h>
void usage() {
printf("Usage: "
"./vehicle-hal-tool [-l] [-m -p -t [-v]]\n"
"-l - List properties\n"
"-m - Mode (cannot be used with -l). Accepted strings: get, set or sub.\n"
"-p - Property (only used with -m)\n"
"-t - Type (only used with -m)\n"
"-w - Wait time in seconds (only used with -m set to sub)\n"
"-v - Value to which vehicle_prop_value is set\n"
"Depending on the type pass the value:\n"
"Int: pass a quoted integer\n"
"Float: pass a quoted float\n"
"Int array: pass a quoted space delimited int array, eg: \"1 2 3 4\" for\n:"
"setting int32_array's all 4 elements (see VEHICLE_VALUE_TYPE_INT32_VEC4\n"
"String: pass a normal string\n\n"
"The configurations to use the tool are as follows:\n"
"List Properties\n"
"---------------\n"
"./vehicle-hal-tool -l \n"
"Lists the various properties defined in HAL implementation. Use this to check if "
"the HAL implementation is correctly set up and exposing the capabilities correctly.\n"
"Get Properties\n"
"---------------\n"
"./vehicle-hal-tool -m get -p <prop> -t <type> [-v <vehicle_prop_value>]\n"
"Example: ./vehicle-hal-tool -m get -p 1028 -t 3 # VEHICLE_PROPERTY_DRIVING_STATUS\n"
"./vehicle-hal-tool -m get -p 257 -t 1 # VEHICLE_PROPERTY_INFO_MAKE\n"
"./vehicle-hal-tool -m get -p 2049 -t 19 -v \"3 0 0 0\"\n"
" # VEHICLE_PROPERTY_RADIO_PRESET\n"
"with preset value set to 3.\n\n"
"Set properties\n"
"--------------\n"
"./vehicle-hal-tool -m set -p 10 -t 1 -v random_property\n"
"Set properties may not be applicable to most properties\n\n"
"Subscribe properties\n"
"--------------------\n"
"Subscribes to be notified about a property change (depending on whether\n"
"it is a on change property or a continuous property) for seconds provided\n"
"as -w paramter.\n"
"./vehicle-hal-tool -m sub -p 1028 -w 10\n"
);
}
void list_all_properties(vehicle_hw_device_t *device) {
int num_configs = -1;
const vehicle_prop_config_t *configs = device->list_properties(device, &num_configs);
if (num_configs < 0) {
printf("List configs error. %d", num_configs);
exit(1);
}
printf("Listing configs\n--------------------\n");
int i = 0;
for (i = 0; i < num_configs; i++) {
const vehicle_prop_config_t *config_temp = configs + i;
printf("Property ID: %d\n"
"Property config_flags: %d\n"
"Property change mode: %d\n"
"Property min sample rate: %f\n"
"Property max sample rate: %f\n",
config_temp->prop, config_temp->config_flags, config_temp->change_mode,
config_temp->min_sample_rate, config_temp->max_sample_rate);
}
}
static void print_property(const vehicle_prop_value_t *data) {
switch (data->value_type) {
case VEHICLE_VALUE_TYPE_STRING:
printf("Value type: STRING\n Size: %d\n", data->value.str_value.len);
// This implementation only supports ASCII.
char *ascii_out = (char *) malloc((data->value.str_value.len + 1) * sizeof(char));
memcpy(ascii_out, data->value.str_value.data, data->value.str_value.len);
ascii_out[data->value.str_value.len] = '\0';
printf("Value Type: STRING %s\n", ascii_out);
free(ascii_out);
break;
case VEHICLE_VALUE_TYPE_BYTES:
printf("Value type: BYTES\n Size: %d", data->value.bytes_value.len);
for (int i = 0; i < data->value.bytes_value.len; i++) {
if ((i % 16) == 0) {
printf("\n %04X: ", i);
}
printf("%02X ", data->value.bytes_value.data[i]);
}
printf("\n");
break;
case VEHICLE_VALUE_TYPE_BOOLEAN:
printf("Value type: BOOLEAN\nValue: %d\n", data->value.boolean_value);
break;
case VEHICLE_VALUE_TYPE_ZONED_BOOLEAN:
printf("Value type: ZONED_BOOLEAN\nZone: %d\n", data->zone);
printf("Value: %d\n", data->value.boolean_value);
break;
case VEHICLE_VALUE_TYPE_INT64:
printf("Value type: INT64\nValue: %" PRId64 "\n", data->value.int64_value);
break;
case VEHICLE_VALUE_TYPE_FLOAT:
printf("Value type: FLOAT\nValue: %f\n", data->value.float_value);
break;
case VEHICLE_VALUE_TYPE_FLOAT_VEC2:
printf("Value type: FLOAT_VEC2\nValue[0]: %f ", data->value.float_array[0]);
printf("Value[1]: %f\n", data->value.float_array[1]);
break;
case VEHICLE_VALUE_TYPE_FLOAT_VEC3:
printf("Value type: FLOAT_VEC3\nValue[0]: %f ", data->value.float_array[0]);
printf("Value[1]: %f ", data->value.float_array[1]);
printf("Value[2]: %f\n", data->value.float_array[2]);
break;
case VEHICLE_VALUE_TYPE_FLOAT_VEC4:
printf("Value type: FLOAT_VEC4\nValue[0]: %f ", data->value.float_array[0]);
printf("Value[1]: %f ", data->value.float_array[1]);
printf("Value[2]: %f ", data->value.float_array[2]);
printf("Value[3]: %f\n", data->value.float_array[3]);
break;
case VEHICLE_VALUE_TYPE_INT32:
printf("Value type: INT32\nValue: %d\n", data->value.int32_value);
break;
case VEHICLE_VALUE_TYPE_INT32_VEC2:
printf("Value type: INT32_VEC2\nValue[0]: %d ", data->value.int32_array[0]);
printf("Value[1]: %d\n", data->value.int32_array[1]);
break;
case VEHICLE_VALUE_TYPE_INT32_VEC3:
printf("Value type: INT32_VEC3\nValue[0]: %d ", data->value.int32_array[0]);
printf("Value[1]: %d ", data->value.int32_array[1]);
printf("Value[2]: %d\n", data->value.int32_array[2]);
break;
case VEHICLE_VALUE_TYPE_INT32_VEC4:
printf("Value type: INT32_VEC4\nValue[0]: %d ", data->value.int32_array[0]);
printf("Value[1]: %d ", data->value.int32_array[1]);
printf("Value[2]: %d ", data->value.int32_array[2]);
printf("Value[3]: %d\n", data->value.int32_array[3]);
break;
case VEHICLE_VALUE_TYPE_ZONED_FLOAT:
printf("Value type: ZONED_FLOAT\nZone: %d ", data->zone);
printf("Value: %f\n", data->value.float_value);
break;
case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC2:
printf("Value type: ZONED_FLOAT_VEC2\nZone: %d ", data->zone);
printf("Value[0]: %f", data->value.float_array[0]);
printf("Value[1]: %f\n", data->value.float_array[1]);
break;
case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC3:
printf("Value type: ZONED_FLOAT_VEC3\nZone: %d ", data->zone);
printf("Value[0]: %f ", data->value.float_array[0]);
printf("Value[1]: %f ", data->value.float_array[1]);
printf("Value[2]: %f\n", data->value.float_array[2]);
break;
case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC4:
printf("Value type: ZONED_FLOAT_VEC4\nZone: %d ", data->zone);
printf("Value[0]: %f ", data->value.float_array[0]);
printf("Value[1]: %f ", data->value.float_array[1]);
printf("Value[2]: %f ", data->value.float_array[2]);
printf("Value[3]: %f\n", data->value.float_array[3]);
break;
case VEHICLE_VALUE_TYPE_ZONED_INT32:
printf("Value type: ZONED_INT32\nZone: %d ", data->zone);
printf("Value: %d\n", data->value.int32_value);
break;
case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC2:
printf("Value type: ZONED_INT32_VEC2\nZone: %d ", data->zone);
printf("Value[0]: %d ", data->value.int32_array[0]);
printf("Value[1]: %d\n", data->value.int32_array[1]);
break;
case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC3:
printf("Value type: ZONED_INT32_VEC3\nZone: %d ", data->zone);
printf("Value[0]: %d ", data->value.int32_array[0]);
printf("Value[1]: %d ", data->value.int32_array[1]);
printf("Value[2]: %d\n", data->value.int32_array[2]);
break;
case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC4:
printf("Value type: ZONED_INT32_VEC4\nZone: %d ", data->zone);
printf("Value[0]: %d ", data->value.int32_array[0]);
printf("Value[1]: %d ", data->value.int32_array[1]);
printf("Value[2]: %d ", data->value.int32_array[2]);
printf("Value[3]: %d\n", data->value.int32_array[3]);
break;
default:
printf("Value type not yet handled: %d.\n", data->value_type);
}
}
void get_property(
vehicle_hw_device_t *device, int32_t property, int32_t type, char *value_string) {
vehicle_prop_value_t *data = (vehicle_prop_value_t *) malloc (sizeof(vehicle_prop_value_t));
// Parse the string according to type.
if (value_string != NULL && strlen(value_string) > 0) {
switch (type) {
case VEHICLE_VALUE_TYPE_INT32:
sscanf(value_string, "%d", &(data->value.int32_value));
break;
case VEHICLE_VALUE_TYPE_INT32_VEC4:
{
int32_t vec[4];
sscanf(value_string, "%d %d %d %d", &vec[0], &vec[1], &vec[2], &vec[3]);
memcpy(data->value.int32_array, vec, sizeof(vec));
break;
}
default:
printf("%s Setting value type not supported: %d\n", __func__, type);
exit(1);
}
}
data->prop = property;
int ret_code = device->get(device, data);
if (ret_code != 0) {
printf("Cannot get property: %d\n", ret_code);
exit(1);
}
// We simply convert the data into the type mentioned by the result of the
// get call.
printf("Get output\n------------\n");
print_property(data);
free(data);
}
void set_property(vehicle_hw_device_t *device,
int32_t property,
int32_t type,
char *data) {
vehicle_prop_value_t vehicle_data;
vehicle_data.prop = property;
vehicle_data.value_type = type;
int32_t zone = 0;
float value = 0.0;
switch (type) {
case VEHICLE_VALUE_TYPE_STRING:
// TODO: Make the code generic to UTF8 characters.
vehicle_data.value.str_value.len = strlen(data);
vehicle_data.value.str_value.data =
(uint8_t *) malloc (strlen(data) * sizeof(uint8_t));
memcpy(vehicle_data.value.str_value.data, data, strlen(data) + 1);
break;
case VEHICLE_VALUE_TYPE_BYTES: {
int len = strlen(data);
int numBytes = (len + 1) / 3;
uint8_t *buf = calloc(numBytes, sizeof(uint8_t));
char *byte = strtok(data, " ");
for (int i = 0; byte != NULL && i < numBytes; i++) {
buf[i] = strtol(data, NULL, 16);
byte = strtok(NULL, " ");
}
vehicle_data.value.bytes_value.len = numBytes;
vehicle_data.value.bytes_value.data = buf;
}
break;
case VEHICLE_VALUE_TYPE_BOOLEAN:
vehicle_data.value.boolean_value = atoi(data);
break;
case VEHICLE_VALUE_TYPE_ZONED_BOOLEAN:
sscanf(data, "%d %d", &vehicle_data.zone,
&vehicle_data.value.boolean_value);
break;
case VEHICLE_VALUE_TYPE_INT64:
vehicle_data.value.int64_value = atoi(data);
break;
case VEHICLE_VALUE_TYPE_FLOAT:
vehicle_data.value.float_value = atof(data);
break;
case VEHICLE_VALUE_TYPE_FLOAT_VEC2:
sscanf(data, "%f %f", &vehicle_data.value.float_array[0],
&vehicle_data.value.float_array[1]);
break;
case VEHICLE_VALUE_TYPE_FLOAT_VEC3:
sscanf(data, "%f %f %f", &vehicle_data.value.float_array[0],
&vehicle_data.value.float_array[1],
&vehicle_data.value.float_array[2]);
break;
case VEHICLE_VALUE_TYPE_FLOAT_VEC4:
sscanf(data, "%f %f %f %f", &vehicle_data.value.float_array[0],
&vehicle_data.value.float_array[1],
&vehicle_data.value.float_array[2],
&vehicle_data.value.float_array[3]);
break;
case VEHICLE_VALUE_TYPE_INT32:
vehicle_data.value.int32_value = atoi(data);
break;
case VEHICLE_VALUE_TYPE_INT32_VEC2:
sscanf(data, "%d %d", &vehicle_data.value.int32_array[0],
&vehicle_data.value.int32_array[1]);
break;
case VEHICLE_VALUE_TYPE_INT32_VEC3:
sscanf(data, "%d %d %d", &vehicle_data.value.int32_array[0],
&vehicle_data.value.int32_array[1],
&vehicle_data.value.int32_array[2]);
break;
case VEHICLE_VALUE_TYPE_INT32_VEC4:
sscanf(data, "%d %d %d %d", &vehicle_data.value.int32_array[0],
&vehicle_data.value.int32_array[1],
&vehicle_data.value.int32_array[2],
&vehicle_data.value.int32_array[3]);
break;
case VEHICLE_VALUE_TYPE_ZONED_FLOAT:
sscanf(data, "%d %f", &zone, &value);
vehicle_data.zone = zone;
vehicle_data.value.float_value = value;
break;
case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC2:
sscanf(data, "%d %f %f", &vehicle_data.zone,
&vehicle_data.value.float_array[0],
&vehicle_data.value.float_array[1]);
break;
case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC3:
sscanf(data, "%d %f %f %f", &vehicle_data.zone,
&vehicle_data.value.float_array[0],
&vehicle_data.value.float_array[1],
&vehicle_data.value.float_array[2]);
break;
case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC4:
sscanf(data, "%d %f %f %f %f", &vehicle_data.zone,
&vehicle_data.value.float_array[0],
&vehicle_data.value.float_array[1],
&vehicle_data.value.float_array[2],
&vehicle_data.value.float_array[3]);
break;
case VEHICLE_VALUE_TYPE_ZONED_INT32:
sscanf(data, "%d %d", &vehicle_data.zone,
&vehicle_data.value.int32_value);
break;
case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC2:
sscanf(data, "%d %d %d", &vehicle_data.zone,
&vehicle_data.value.int32_array[0],
&vehicle_data.value.int32_array[1]);
break;
case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC3:
sscanf(data, "%d %d %d %d", &vehicle_data.zone,
&vehicle_data.value.int32_array[0],
&vehicle_data.value.int32_array[1],
&vehicle_data.value.int32_array[2]);
break;
case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC4:
sscanf(data, "%d %d %d %d %d", &vehicle_data.zone,
&vehicle_data.value.int32_array[0],
&vehicle_data.value.int32_array[1],
&vehicle_data.value.int32_array[2],
&vehicle_data.value.int32_array[3]);
break;
default:
printf("set_property: Value type not yet handled: %d\n", type);
exit(1);
}
printf("Setting Property id: %d\n", vehicle_data.prop);
print_property(&vehicle_data);
int ret_code = device->set(device, &vehicle_data);
if (ret_code != 0) {
printf("Cannot set property: %d\n", ret_code);
exit(1);
}
}
int vehicle_event_callback(const vehicle_prop_value_t *event_data) {
// Print what we got.
printf("Got some value from callback property: %d\n", event_data->prop);
printf("Timestamp: %" PRId64 "\n", event_data->timestamp);
print_property(event_data);
return 0;
}
int vehicle_error_callback(int32_t error_code, int32_t property, int32_t operation) {
// Print what we got.
printf("Error code obtained: %d\n", error_code);
return 0;
}
void subscribe_to_property(
vehicle_hw_device_t *device,
int32_t prop,
float sample_rate,
uint32_t wait_in_seconds) {
// Init the device with a callback.
int ret_code = device->subscribe(device, prop, 0, 0);
if (ret_code != 0) {
printf("Could not subscribe: %d\n", ret_code);
exit(1);
}
// Callbacks will happen on one of the threads created by the HAL hence we
// can simply sleep here and see the output.
sleep(wait_in_seconds);
// Unsubscribe and uninit.
ret_code = device->unsubscribe(device, prop);
if (ret_code != 0) {
printf("Error unsubscribing the HAL, still continuining to uninit HAL ...");
}
}
int main(int argc, char* argv[]) {
// Open the vehicle module and just ask for the list of properties.
const hw_module_t *hw_module = NULL;
int ret_code = hw_get_module(VEHICLE_HARDWARE_MODULE_ID, &hw_module);
if (ret_code != 0) {
printf("Cannot open the hw module. Does the HAL exist? %d\n", ret_code);
return -1;
}
vehicle_module_t *vehicle_module = (vehicle_module_t *)(hw_module);
hw_device_t *device = NULL;
ret_code = vehicle_module->common.methods->open(hw_module, NULL, &device);
if (!device) {
printf("Cannot open the hw device: %d\n", ret_code);
return -1;
}
vehicle_hw_device_t *vehicle_device = (vehicle_hw_device_t *) (device);
printf("HAL Loaded!\n");
vehicle_device->init(vehicle_device, vehicle_event_callback, vehicle_error_callback);
// If this is a list properties command - we check for -l command.
int list_properties = 0;
// Type of the property (see #defines in vehicle.h).
int property = -1;
// Type of the value of the property (see enum vehicle_value_type).
int type = -1;
// Whether the mode is "get" or "set".
char mode[100] = "";
// Actual value as a string representation (supports only PODs for now).
// TODO: Support structures and complex types in the tool.
char value[100] = "";
// Wait time for the subscribe type of calls.
// We keep a default in case the user does not specify one.
int wait_time_in_sec = 10;
// Sample rate for subscribe type of calls.
// Default value is 0 for onchange type of properties.
int sample_rate = 0;
// Int array string which represents the vehicle_value_t in array of
// numbers. See vehicle_prop_value_t.value.int32_array.
char int_array_string[1000]; int_array_string[0] = '\0';
int opt;
while ((opt = getopt(argc, argv, "lm:p:t:v:w:s:")) != -1) {
switch (opt) {
case 'l':
list_properties = 1;
break;
case 'm':
strcpy(mode, optarg);
break;
case 'p':
property = atoi(optarg);
break;
case 't':
type = atoi(optarg);
break;
case 'v':
strcpy(value, optarg);
break;
case 'w':
wait_time_in_sec = atoi(optarg);
break;
case 's':
sample_rate = atoi(optarg);
break;
}
}
// We should have atleast one of list properties or mode (for get or set).
if (!list_properties &&
!(!strcmp(mode, "get") || !strcmp(mode, "set") || !strcmp(mode, "sub"))) {
usage();
exit(1);
}
if (list_properties) {
printf("Listing properties...\n");
list_all_properties(vehicle_device);
} else if (!strcmp(mode, "get")) {
printf("Getting property ...\n");
if (property == -1) {
printf("Use -p to pass a valid Property.\n");
usage();
exit(1);
}
int32_t int_array_list[4];
int count = -1;
if (strlen(int_array_string) > 0) {
count = sscanf(int_array_string, "%d%d%d%d",
&int_array_list[0], &int_array_list[1], &int_array_list[2], &int_array_list[3]);
}
get_property(vehicle_device, property, type, value);
} else if (!strcmp(mode, "set")) {
printf("Setting property ...\n");
if (property == -1 || type == -1) {
printf("Use -p to pass a valid Property and -t to pass a valid Type.\n");
usage();
exit(1);
}
set_property(vehicle_device, property, type, value);
} else if (!strcmp(mode, "sub")) {
printf("Subscribing property ...\n");
if (property == -1 || wait_time_in_sec <= 0) {
printf("Use -p to pass a valid property and -w to pass a valid wait time(s)\n");
usage();
exit(1);
}
subscribe_to_property(vehicle_device, property, sample_rate, wait_time_in_sec);
}
ret_code = vehicle_device->release(vehicle_device);
if (ret_code != 0) {
printf("Error uniniting HAL, exiting anyways.");
}
return 0;
}

View file

@ -1,97 +0,0 @@
/*
* Copyright (C) 2015 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_HAL_VEHICLE_TEST_
#define __ANDROID_HAL_VEHICLE_TEST_
#include <gtest/gtest.h>
#include <hardware/hardware.h>
#include <hardware/vehicle.h>
namespace tests {
static const uint64_t kVersion = HARDWARE_DEVICE_API_VERSION_2(1, 0, 1);
class VehicleModule : public testing::Test {
public:
VehicleModule() :
vehicle_module_(NULL) {}
~VehicleModule() {}
protected:
virtual void SetUp() {
const hw_module_t *hw_module = NULL;
ASSERT_EQ(0, hw_get_module(VEHICLE_HARDWARE_MODULE_ID, &hw_module))
<< "Can't get vehicle module";
ASSERT_TRUE(NULL != hw_module)
<< "hw_get_module didn't return a valid hardware module";
vehicle_module_ = reinterpret_cast<const vehicle_module_t*>(hw_module);
}
const vehicle_module_t* vehicle_module() { return vehicle_module_; }
private:
const vehicle_module_t* vehicle_module_;
};
int VehicleEventCallback(const vehicle_prop_value_t* event_data) {
// Print what we got.
std::cout << "got some value from callback: "
<< event_data->prop
<< " uint32 value: "
<< event_data->value.int32_value << "\n";
return 0;
}
int VehicleErrorCallback(int32_t /*error_code*/, int32_t /*property*/, int32_t /*operation*/) {
// Do nothing.
return 0;
}
class VehicleDevice : public VehicleModule {
public:
VehicleDevice() :
vehicle_device_(NULL) {}
~VehicleDevice() {}
protected:
virtual void SetUp() {
VehicleModule::SetUp();
hw_device_t *device = NULL;
ASSERT_TRUE(NULL != vehicle_module()->common.methods->open)
<< "Vehicle open() is unimplemented";
ASSERT_EQ(0, vehicle_module()->common.methods->open(
(const hw_module_t*)vehicle_module(), NULL, &device))
<< "Can't open vehicle device";
ASSERT_TRUE(NULL != device)
<< "Vehicle open() returned a NULL device";
ASSERT_EQ(kVersion, device->version)
<< "Unsupported version";
vehicle_device_ = reinterpret_cast<vehicle_hw_device_t*>(device);
}
vehicle_hw_device_t* vehicle_device() { return vehicle_device_; }
vehicle_event_callback_fn callback_fn() {
return VehicleEventCallback;
}
vehicle_error_callback_fn error_fn() {
return VehicleErrorCallback;
}
private:
vehicle_hw_device_t* vehicle_device_;
};
} // namespace tests
#endif // __ANDROID_HAL_VEHICLE_TEST_

View file

@ -1,129 +0,0 @@
/*
* Copyright (C) 2015 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 <gtest/gtest.h>
#include "vehicle_test_fixtures.h"
#include "hardware/vehicle.h"
namespace tests {
// Check if list_properties command exists.
TEST_F(VehicleDevice, isThereListProperties) {
ASSERT_TRUE(NULL != vehicle_device()->list_properties)
<< "list_properties() function is not implemented";
std::cout << "Test succeeds.\n";
}
// HAL should provide atleast one property. The output of this command should be
// used to verify the vailidity of the function.
TEST_F(VehicleDevice, listPropertiesMoreThanOne) {
vehicle_prop_config_t const* config;
int num_configs = -1;
config = vehicle_device()->list_properties(vehicle_device(), &num_configs);
ASSERT_TRUE(num_configs > -1) << "list_properties() call failed.";
ASSERT_TRUE(num_configs > 0) << "list_properties() returned zero items.";
std::cout << "Number of properties reported: " << num_configs << "\n";
for (int i = 0; i < num_configs; i++) {
// Print each of the properties.
const vehicle_prop_config_t& config_temp = config[i];
std::cout << "Property ID: " << config_temp.prop << "\n";
std::cout << "Property flags: " << config_temp.config_flags << "\n";
std::cout << "Property change mode: " << config_temp.change_mode << "\n";
std::cout << "Property min sample rate: " << config_temp.min_sample_rate << "\n";
std::cout << "Property max sample rate: " << config_temp.max_sample_rate << "\n\n";
}
}
// Test get() command.
// The fields are hardcoded in the dummy implementation and here.
TEST_F(VehicleDevice, getDriveState) {
vehicle_prop_value_t data;
data.prop = VEHICLE_PROPERTY_DRIVING_STATUS;
// Set drive_state field to EINVAL so that we can check that its valid when
// it comes back.
data.value_type = -EINVAL;
data.value.driving_status = -EINVAL;
vehicle_device()->get(vehicle_device(), &data);
// Check that retured values are not invalid.
ASSERT_NE(data.value_type, -EINVAL) << "Drive state value type should be integer.";
ASSERT_NE(data.value.driving_status, -EINVAL) << "Driving status should be positive.";
std::cout << "Driving status value type: " << data.value_type << "\n"
<< "Driving status: " << data.value.driving_status << "\n";
}
// Test the workflows for subscribe and init/release.
// Subscribe will return error before init() is called or after release() is
// called.
TEST_F(VehicleDevice, initTest) {
// Test that init on a new device works. When getting an instance, we are
// already calling 'open' on the device.
int ret_code =
vehicle_device()->init(vehicle_device(), callback_fn(), error_fn());
ASSERT_EQ(ret_code, 0) << "ret code: " << ret_code;
// Trying to init again should return an error.
ret_code = vehicle_device()->init(vehicle_device(), callback_fn(), error_fn());
ASSERT_EQ(ret_code, -EEXIST) << "ret code: " << ret_code;
// Uninit should always return 0.
ret_code = vehicle_device()->release(vehicle_device());
ASSERT_EQ(ret_code, 0) << "ret code: " << ret_code;
// We should be able to init again.
ret_code = vehicle_device()->init(vehicle_device(), callback_fn(), error_fn());
ASSERT_EQ(ret_code, 0) << "ret code: " << ret_code;
// Finally release.
ret_code = vehicle_device()->release(vehicle_device());
ASSERT_EQ(ret_code, 0) << "ret_code: " << ret_code;
}
// Test that subscribe works.
// We wait for 10 seconds while which the vehicle.c can post messages from
// within it's own thread.
TEST_F(VehicleDevice, subscribeTest) {
// If the device is not init subscribe should fail off the bat.
int ret_code = vehicle_device()->subscribe(vehicle_device(), VEHICLE_PROPERTY_DRIVING_STATUS,
0, 0);
ASSERT_EQ(ret_code, -EINVAL) << "Return code is: " << ret_code;
// Let's init the device.
ret_code = vehicle_device()->init(vehicle_device(), callback_fn(), error_fn());
ASSERT_EQ(ret_code, 0) << "Return code is: " << ret_code;
// Subscribe should now go through.
ret_code = vehicle_device()->subscribe(vehicle_device(), VEHICLE_PROPERTY_DRIVING_STATUS, 0, 0);
ASSERT_EQ(ret_code, 0) << "Return code is: " << ret_code;
// We should start getting some messages thrown from the callback. Let's
// wait for 20 seconds before unsubscribing.
std::cout << "Sleeping for 20 seconds.";
sleep(20);
std::cout << "Waking from sleep.";
// This property does not exist, so we should get -EINVAL.
ret_code = vehicle_device()->unsubscribe(vehicle_device(), VEHICLE_PROPERTY_INFO_VIN);
ASSERT_EQ(ret_code, -EINVAL) << "Return code is: " << ret_code;
// This property exists, so we should get a success return code - also this
// will be a blocking call.
ret_code = vehicle_device()->unsubscribe(vehicle_device(), VEHICLE_PROPERTY_DRIVING_STATUS);
ASSERT_EQ(ret_code, 0) << "Return code is: " << ret_code;
}
} // namespace tests