| /* |
| * Copyright (C) 2016 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 "VehicleNetwork" |
| |
| #include <binder/PermissionCache.h> |
| #include <utils/Errors.h> |
| #include <utils/SystemClock.h> |
| |
| #include <private/android_filesystem_config.h> |
| |
| #include <vehicle-internal.h> |
| |
| #include "VehicleHalPropertyUtil.h" |
| #include "VehicleNetworkService.h" |
| |
| //#define DBG_EVENT |
| //#define DBG_VERBOSE |
| #ifdef DBG_EVENT |
| #define EVENT_LOG(x...) ALOGD(x) |
| #else |
| #define EVENT_LOG(x...) |
| #endif |
| #ifdef DBG_VERBOSE |
| #define LOG_VERBOSE(x...) ALOGD(x) |
| #else |
| #define LOG_VERBOSE(x...) |
| #endif |
| |
| namespace android { |
| |
| VehicleHalMessageHandler::VehicleHalMessageHandler(const sp<Looper>& looper, |
| VehicleNetworkService& service) |
| : mLooper(looper), |
| mService(service), |
| mFreeListIndex(0), |
| mLastDispatchTime(0) { |
| } |
| |
| VehicleHalMessageHandler::~VehicleHalMessageHandler() { |
| Mutex::Autolock autoLock(mLock); |
| for (int i = 0; i < NUM_PROPERTY_EVENT_LISTS; i++) { |
| for (auto& e : mHalPropertyList[i]) { |
| VehiclePropValueUtil::deleteMembers(e); |
| delete e; |
| } |
| } |
| for (VehicleHalError* e : mHalErrors) { |
| delete e; |
| } |
| } |
| |
| static const int MS_TO_NS = 1000000; |
| |
| void VehicleHalMessageHandler::handleHalEvent(vehicle_prop_value_t *eventData) { |
| EVENT_LOG("handleHalEvent 0x%x", eventData->prop); |
| Mutex::Autolock autoLock(mLock); |
| List<vehicle_prop_value_t*>& propList = mHalPropertyList[mFreeListIndex]; |
| propList.push_back(eventData); |
| int64_t deltaFromLast = elapsedRealtime() - mLastDispatchTime; |
| if (deltaFromLast > DISPATCH_INTERVAL_MS) { |
| mLooper->sendMessage(this, Message(HAL_EVENT)); |
| } else { |
| mLooper->sendMessageDelayed((DISPATCH_INTERVAL_MS - deltaFromLast) * MS_TO_NS, |
| this, Message(HAL_EVENT)); |
| } |
| } |
| |
| void VehicleHalMessageHandler::handleHalError(VehicleHalError* error) { |
| Mutex::Autolock autoLock(mLock); |
| mHalErrors.push_back(error); |
| mLooper->sendMessage(this, Message(HAL_ERROR)); |
| } |
| |
| void VehicleHalMessageHandler::handleMockStart() { |
| Mutex::Autolock autoLock(mLock); |
| mHalPropertyList[0].clear(); |
| mHalPropertyList[1].clear(); |
| sp<MessageHandler> self(this); |
| mLooper->removeMessages(self); |
| } |
| |
| void VehicleHalMessageHandler::doHandleHalEvent() { |
| // event dispatching can take time, so do it outside lock and that requires double buffering. |
| // inside lock, free buffer is swapped with non-free buffer. |
| List<vehicle_prop_value_t*>* events = NULL; |
| do { |
| Mutex::Autolock autoLock(mLock); |
| mLastDispatchTime = elapsedRealtime(); |
| int nonFreeListIndex = mFreeListIndex ^ 0x1; |
| List<vehicle_prop_value_t*>* nonFreeList = &(mHalPropertyList[nonFreeListIndex]); |
| List<vehicle_prop_value_t*>* freeList = &(mHalPropertyList[mFreeListIndex]); |
| if (nonFreeList->size() > 0) { |
| for (auto& e : *freeList) { |
| nonFreeList->push_back(e); |
| } |
| freeList->clear(); |
| events = nonFreeList; |
| } else if (freeList->size() > 0) { |
| events = freeList; |
| mFreeListIndex = nonFreeListIndex; |
| } |
| } while (false); |
| if (events != NULL) { |
| EVENT_LOG("doHandleHalEvent, num events:%d", events->size()); |
| mService.dispatchHalEvents(*events); |
| //TODO implement return to memory pool |
| for (auto& e : *events) { |
| VehiclePropValueUtil::deleteMembers(e); |
| delete e; |
| } |
| events->clear(); |
| } |
| } |
| |
| void VehicleHalMessageHandler::doHandleHalError() { |
| VehicleHalError* error = NULL; |
| do { |
| Mutex::Autolock autoLock(mLock); |
| if (mHalErrors.size() > 0) { |
| auto itr = mHalErrors.begin(); |
| error = *itr; |
| mHalErrors.erase(itr); |
| } |
| } while (false); |
| if (error != NULL) { |
| mService.dispatchHalError(error); |
| } |
| } |
| |
| void VehicleHalMessageHandler::handleMessage(const Message& message) { |
| switch (message.what) { |
| case HAL_EVENT: |
| doHandleHalEvent(); |
| break; |
| case HAL_ERROR: |
| doHandleHalError(); |
| break; |
| } |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| void MockDeathHandler::binderDied(const wp<IBinder>& who) { |
| mService.handleHalMockDeath(who); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| PropertyValueCache::PropertyValueCache() { |
| |
| } |
| |
| PropertyValueCache::~PropertyValueCache() { |
| for (size_t i = 0; i < mCache.size(); i++) { |
| vehicle_prop_value_t* v = mCache.editValueAt(i); |
| VehiclePropValueUtil::deleteMembers(v); |
| delete v; |
| } |
| mCache.clear(); |
| } |
| |
| void PropertyValueCache::writeToCache(const vehicle_prop_value_t& value) { |
| vehicle_prop_value_t* v; |
| ssize_t index = mCache.indexOfKey(value.prop); |
| if (index < 0) { |
| v = VehiclePropValueUtil::allocVehicleProp(value); |
| ASSERT_OR_HANDLE_NO_MEMORY(v, return); |
| mCache.add(value.prop, v); |
| } else { |
| v = mCache.editValueAt(index); |
| VehiclePropValueUtil::copyVehicleProp(v, value, true /* deleteOldData */); |
| } |
| } |
| |
| bool PropertyValueCache::readFromCache(vehicle_prop_value_t* value) { |
| ssize_t index = mCache.indexOfKey(value->prop); |
| if (index < 0) { |
| ALOGE("readFromCache 0x%x, not found", value->prop); |
| return false; |
| } |
| const vehicle_prop_value_t* cached = mCache.valueAt(index); |
| //TODO this can be improved by just passing pointer and not deleting members. |
| status_t r = VehiclePropValueUtil::copyVehicleProp(value, *cached); |
| if (r != NO_ERROR) { |
| ALOGD("readFromCache 0x%x, copy failed %d", value->prop, r); |
| return false; |
| } |
| return true; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| |
| VehicleNetworkService* VehicleNetworkService::sInstance = NULL; |
| |
| status_t VehicleNetworkService::dump(int fd, const Vector<String16>& /*args*/) { |
| static const String16 sDump("android.permission.DUMP"); |
| String8 msg; |
| if (!PermissionCache::checkCallingPermission(sDump)) { |
| msg.appendFormat("Permission Denial: " |
| "can't dump VNS from pid=%d, uid=%d\n", |
| IPCThreadState::self()->getCallingPid(), |
| IPCThreadState::self()->getCallingUid()); |
| write(fd, msg.string(), msg.size()); |
| return NO_ERROR; |
| } |
| msg.append("MockingEnabled=%d\n", mMockingEnabled ? 1 : 0); |
| msg.append("*Properties\n"); |
| for (auto& prop : mProperties->getList()) { |
| VechilePropertyUtil::dumpProperty(msg, *prop); |
| } |
| if (mMockingEnabled) { |
| msg.append("*Mocked Properties\n"); |
| for (auto& prop : mPropertiesForMocking->getList()) { |
| //TODO dump more info |
| msg.appendFormat("property 0x%x\n", prop->prop); |
| } |
| } |
| msg.append("*Active clients*\n"); |
| for (size_t i = 0; i < mBinderToClientMap.size(); i++) { |
| msg.appendFormat("pid %d uid %d\n", mBinderToClientMap.valueAt(i)->getPid(), |
| mBinderToClientMap.valueAt(i)->getUid()); |
| } |
| msg.append("*Active clients per property*\n"); |
| for (size_t i = 0; i < mPropertyToClientsMap.size(); i++) { |
| msg.appendFormat("prop 0x%x, pids:", mPropertyToClientsMap.keyAt(i)); |
| sp<HalClientSpVector> clients = mPropertyToClientsMap.valueAt(i); |
| for (size_t j = 0; j < clients->size(); j++) { |
| msg.appendFormat("%d,", clients->itemAt(j)->getPid()); |
| } |
| msg.append("\n"); |
| } |
| msg.append("*Subscription info per property*\n"); |
| for (size_t i = 0; i < mSubscriptionInfos.size(); i++) { |
| const SubscriptionInfo& info = mSubscriptionInfos.valueAt(i); |
| msg.appendFormat("prop 0x%x, sample rate %f Hz, zones 0x%x\n", mSubscriptionInfos.keyAt(i), |
| info.sampleRate, info.zones); |
| } |
| msg.append("*Event counts per property*\n"); |
| for (size_t i = 0; i < mEventsCount.size(); i++) { |
| msg.appendFormat("prop 0x%x: %d\n", mEventsCount.keyAt(i), |
| mEventsCount.valueAt(i)); |
| } |
| msg.append("*Vehicle Network Service Permissions*\n"); |
| mVehiclePropertyAccessControl.dump(msg); |
| |
| write(fd, msg.string(), msg.size()); |
| return NO_ERROR; |
| } |
| |
| bool VehicleNetworkService::isOperationAllowed(int32_t property, bool isWrite) { |
| const uid_t uid = IPCThreadState::self()->getCallingUid(); |
| |
| bool allowed = mVehiclePropertyAccessControl.testAccess(property, uid, isWrite); |
| if (!allowed) { |
| ALOGW("Property 0x%x: access not allowed for uid %d, isWrite %d", property, uid, isWrite); |
| } |
| return allowed; |
| } |
| |
| VehicleNetworkService::VehicleNetworkService() |
| : mModule(NULL), |
| mMockingEnabled(false) { |
| sInstance = this; |
| |
| // Load vehicle network services policy file |
| if(!mVehiclePropertyAccessControl.init()) { |
| LOG_ALWAYS_FATAL("Vehicle property access policy could not be initialized."); |
| } |
| } |
| |
| VehicleNetworkService::~VehicleNetworkService() { |
| sInstance = NULL; |
| for (size_t i = 0; i < mPropertyToClientsMap.size(); i++) { |
| sp<HalClientSpVector> clients = mPropertyToClientsMap.editValueAt(i); |
| clients->clear(); |
| } |
| mBinderToClientMap.clear(); |
| mPropertyToClientsMap.clear(); |
| mSubscriptionInfos.clear(); |
| } |
| |
| void VehicleNetworkService::binderDied(const wp<IBinder>& who) { |
| List<int32_t> propertiesToUnsubscribe; |
| do { |
| Mutex::Autolock autoLock(mLock); |
| sp<IBinder> ibinder = who.promote(); |
| ibinder->unlinkToDeath(this); |
| ssize_t index = mBinderToClientMap.indexOfKey(ibinder); |
| if (index < 0) { |
| // already removed. ignore |
| return; |
| } |
| sp<HalClient> currentClient = mBinderToClientMap.editValueAt(index); |
| ALOGW("client binder death, pid: %d, uid:%d", currentClient->getPid(), |
| currentClient->getUid()); |
| mBinderToClientMap.removeItemsAt(index); |
| |
| for (size_t i = 0; i < mPropertyToClientsMap.size(); i++) { |
| sp<HalClientSpVector>& clients = mPropertyToClientsMap.editValueAt(i); |
| clients->remove(currentClient); |
| // TODO update frame rate |
| if (clients->size() == 0) { |
| int32_t property = mPropertyToClientsMap.keyAt(i); |
| propertiesToUnsubscribe.push_back(property); |
| mSubscriptionInfos.removeItem(property); |
| } |
| } |
| for (int32_t property : propertiesToUnsubscribe) { |
| mPropertyToClientsMap.removeItem(property); |
| } |
| } while (false); |
| for (int32_t property : propertiesToUnsubscribe) { |
| mDevice->unsubscribe(mDevice, property); |
| } |
| } |
| |
| void VehicleNetworkService::handleHalMockDeath(const wp<IBinder>& who) { |
| ALOGE("Hal mock binder died"); |
| sp<IBinder> ibinder = who.promote(); |
| stopMocking(IVehicleNetworkHalMock::asInterface(ibinder)); |
| } |
| |
| int VehicleNetworkService::eventCallback(const vehicle_prop_value_t *eventData) { |
| EVENT_LOG("eventCallback 0x%x"); |
| sInstance->onHalEvent(eventData); |
| return NO_ERROR; |
| } |
| |
| int VehicleNetworkService::errorCallback(int32_t errorCode, int32_t property, int32_t operation) { |
| status_t r = sInstance->onHalError(errorCode, property, operation); |
| if (r != NO_ERROR) { |
| ALOGE("VehicleNetworkService::errorCallback onHalError failed with %d", r); |
| } |
| return NO_ERROR; |
| } |
| |
| extern "C" { |
| vehicle_prop_config_t const * getInternalProperties(); |
| int getNumInternalProperties(); |
| }; |
| |
| void VehicleNetworkService::onFirstRef() { |
| Mutex::Autolock autoLock(mLock); |
| status_t r = loadHal(); |
| if (r!= NO_ERROR) { |
| ALOGE("cannot load HAL, error:%d", r); |
| return; |
| } |
| mHandlerThread = new HandlerThread(); |
| r = mHandlerThread->start("HAL.NATIVE_LOOP"); |
| if (r != NO_ERROR) { |
| ALOGE("cannot start handler thread, error:%d", r); |
| return; |
| } |
| sp<VehicleHalMessageHandler> handler(new VehicleHalMessageHandler(mHandlerThread->getLooper(), |
| *this)); |
| ASSERT_ALWAYS_ON_NO_MEMORY(handler.get()); |
| mHandler = handler; |
| r = mDevice->init(mDevice, eventCallback, errorCallback); |
| if (r != NO_ERROR) { |
| ALOGE("HAL init failed:%d", r); |
| return; |
| } |
| int numConfigs = 0; |
| vehicle_prop_config_t const* configs = mDevice->list_properties(mDevice, &numConfigs); |
| mProperties = new VehiclePropertiesHolder(false /* deleteConfigsInDestructor */); |
| ASSERT_ALWAYS_ON_NO_MEMORY(mProperties); |
| for (int i = 0; i < numConfigs; i++) { |
| mProperties->getList().push_back(&configs[i]); |
| } |
| configs = getInternalProperties(); |
| for (int i = 0; i < getNumInternalProperties(); i++) { |
| mProperties->getList().push_back(&configs[i]); |
| } |
| } |
| |
| void VehicleNetworkService::release() { |
| do { |
| Mutex::Autolock autoLock(mLock); |
| mHandlerThread->quit(); |
| } while (false); |
| mDevice->release(mDevice); |
| } |
| |
| vehicle_prop_config_t const * VehicleNetworkService::findConfigLocked(int32_t property) { |
| for (auto& config : (mMockingEnabled ? |
| mPropertiesForMocking->getList() : mProperties->getList())) { |
| if (config->prop == property) { |
| return config; |
| } |
| } |
| ALOGW("property not found 0x%x", property); |
| return NULL; |
| } |
| |
| bool VehicleNetworkService::isGettableLocked(int32_t property) { |
| vehicle_prop_config_t const * config = findConfigLocked(property); |
| if (config == NULL) { |
| return false; |
| } |
| if ((config->access & VEHICLE_PROP_ACCESS_READ) == 0) { |
| ALOGI("cannot get, property 0x%x is write only", property); |
| return false; |
| } |
| return true; |
| } |
| |
| bool VehicleNetworkService::isSettableLocked(int32_t property, int32_t valueType) { |
| vehicle_prop_config_t const * config = findConfigLocked(property); |
| if (config == NULL) { |
| return false; |
| } |
| if ((config->access & VEHICLE_PROP_ACCESS_WRITE) == 0) { |
| ALOGI("cannot set, property 0x%x is read only", property); |
| return false; |
| } |
| if (config->value_type != valueType) { |
| ALOGW("cannot set, property 0x%x expects type 0x%x while got 0x%x", property, |
| config->value_type, valueType); |
| return false; |
| } |
| return true; |
| } |
| |
| bool VehicleNetworkService::isSubscribableLocked(int32_t property) { |
| vehicle_prop_config_t const * config = findConfigLocked(property); |
| if (config == NULL) { |
| return false; |
| } |
| if ((config->access & VEHICLE_PROP_ACCESS_READ) == 0) { |
| ALOGI("cannot subscribe, property 0x%x is write only", property); |
| return false; |
| } |
| if (config->change_mode == VEHICLE_PROP_CHANGE_MODE_STATIC) { |
| ALOGI("cannot subscribe, property 0x%x is static", property); |
| return false; |
| } |
| return true; |
| } |
| |
| bool VehicleNetworkService::isZonedProperty(vehicle_prop_config_t const * config) { |
| if (config == NULL) { |
| return false; |
| } |
| switch (config->value_type) { |
| case VEHICLE_VALUE_TYPE_ZONED_INT32: |
| case VEHICLE_VALUE_TYPE_ZONED_FLOAT: |
| case VEHICLE_VALUE_TYPE_ZONED_BOOLEAN: |
| case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC2: |
| case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC3: |
| case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC2: |
| case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC3: |
| return true; |
| } |
| return false; |
| } |
| |
| sp<VehiclePropertiesHolder> VehicleNetworkService::listProperties(int32_t property) { |
| Mutex::Autolock autoLock(mLock); |
| if (property == 0) { |
| if (!mMockingEnabled) { |
| return mProperties; |
| } else { |
| return mPropertiesForMocking; |
| } |
| } else { |
| sp<VehiclePropertiesHolder> p; |
| const vehicle_prop_config_t* config = findConfigLocked(property); |
| if (config != NULL) { |
| p = new VehiclePropertiesHolder(false /* deleteConfigsInDestructor */); |
| p->getList().push_back(config); |
| ASSERT_OR_HANDLE_NO_MEMORY(p.get(), return p); |
| } |
| return p; |
| } |
| } |
| |
| status_t VehicleNetworkService::getProperty(vehicle_prop_value_t *data) { |
| bool inMocking = false; |
| do { // for lock scoping |
| Mutex::Autolock autoLock(mLock); |
| if (!isGettableLocked(data->prop)) { |
| ALOGW("getProperty, cannot get 0x%x", data->prop); |
| return BAD_VALUE; |
| } |
| if ((data->prop >= (int32_t)VEHICLE_PROPERTY_INTERNAL_START) && |
| (data->prop <= (int32_t)VEHICLE_PROPERTY_INTERNAL_END)) { |
| if (!mCache.readFromCache(data)) { |
| return BAD_VALUE; |
| } |
| return NO_ERROR; |
| } |
| //TODO caching for static, on-change type? |
| if (mMockingEnabled) { |
| inMocking = true; |
| } |
| } while (false); |
| // set done outside lock to allow concurrent access |
| if (inMocking) { |
| status_t r = mHalMock->onPropertyGet(data); |
| if (r != NO_ERROR) { |
| ALOGW("getProperty 0x%x failed, mock returned %d", data->prop, r); |
| } |
| return r; |
| } |
| /* |
| * get call can return -EAGAIN error when hal has not fetched all data. In that case, |
| * keep retrying for certain time with some sleep. This will happen only at initial stage. |
| */ |
| status_t r = -EAGAIN; |
| int retryCount = 0; |
| while (true) { |
| r = mDevice->get(mDevice, data); |
| if (r == NO_ERROR) { |
| break; |
| } |
| if (r != -EAGAIN) { |
| break; |
| } |
| retryCount++; |
| if (retryCount > MAX_GET_RETRY_FOR_NOT_READY) { |
| ALOGE("Vehicle hal keep retrying not ready after multiple retries"); |
| break; |
| } |
| usleep(GET_WAIT_US); |
| } |
| if (r != NO_ERROR) { |
| ALOGW("getProperty 0x%x failed, HAL returned %d", data->prop, r); |
| } |
| return r; |
| } |
| |
| void VehicleNetworkService::releaseMemoryFromGet(vehicle_prop_value_t* value) { |
| switch (value->prop) { |
| case VEHICLE_VALUE_TYPE_STRING: |
| case VEHICLE_VALUE_TYPE_BYTES: { |
| Mutex::Autolock autoLock(mLock); |
| mDevice->release_memory_from_get(mDevice, value); |
| } break; |
| } |
| } |
| |
| status_t VehicleNetworkService::setProperty(const vehicle_prop_value_t& data) { |
| bool isInternalProperty = false; |
| bool inMocking = false; |
| do { // for lock scoping |
| Mutex::Autolock autoLock(mLock); |
| if (!isSettableLocked(data.prop, data.value_type)) { |
| ALOGW("setProperty, cannot set 0x%x", data.prop); |
| return BAD_VALUE; |
| } |
| if ((data.prop >= (int32_t)VEHICLE_PROPERTY_INTERNAL_START) && |
| (data.prop <= (int32_t)VEHICLE_PROPERTY_INTERNAL_END)) { |
| isInternalProperty = true; |
| mCache.writeToCache(data); |
| } |
| if (mMockingEnabled) { |
| inMocking = true; |
| } |
| } while (false); |
| if (inMocking) { |
| status_t r = mHalMock->onPropertySet(data); |
| if (r != NO_ERROR) { |
| ALOGW("setProperty 0x%x failed, mock returned %d", data.prop, r); |
| return r; |
| } |
| } |
| if (isInternalProperty) { |
| // for internal property, just publish it. |
| onHalEvent(&data, inMocking); |
| return NO_ERROR; |
| } |
| if (inMocking) { |
| return NO_ERROR; |
| } |
| //TODO add value check requires auto generated code to return value range for enum types |
| // set done outside lock to allow concurrent access |
| status_t r = mDevice->set(mDevice, &data); |
| if (r != NO_ERROR) { |
| ALOGW("setProperty 0x%x failed, HAL returned %d", data.prop, r); |
| } |
| return r; |
| } |
| |
| status_t VehicleNetworkService::subscribe(const sp<IVehicleNetworkListener> &listener, int32_t prop, |
| float sampleRate, int32_t zones) { |
| bool shouldSubscribe = false; |
| bool inMock = false; |
| int32_t newZones = zones; |
| do { |
| Mutex::Autolock autoLock(mLock); |
| if (!isSubscribableLocked(prop)) { |
| return BAD_VALUE; |
| } |
| vehicle_prop_config_t const * config = findConfigLocked(prop); |
| if (config->change_mode == VEHICLE_PROP_CHANGE_MODE_ON_CHANGE) { |
| if (sampleRate != 0) { |
| ALOGW("Sample rate set to non-zeo for on change type. Ignore it"); |
| sampleRate = 0; |
| } |
| } else { |
| if (sampleRate > config->max_sample_rate) { |
| ALOGW("sample rate %f higher than max %f. limit to max", sampleRate, |
| config->max_sample_rate); |
| sampleRate = config->max_sample_rate; |
| } |
| if (sampleRate < config->min_sample_rate) { |
| ALOGW("sample rate %f lower than min %f. limit to min", sampleRate, |
| config->min_sample_rate); |
| sampleRate = config->min_sample_rate; |
| } |
| } |
| if (isZonedProperty(config)) { |
| if ((zones != 0) && ((zones & config->vehicle_zone_flags) != zones)) { |
| ALOGE("subscribe requested zones 0x%x out of range, supported:0x%x", zones, |
| config->vehicle_zone_flags); |
| return BAD_VALUE; |
| } |
| } else { // ignore zone |
| zones = 0; |
| } |
| sp<IBinder> ibinder = IInterface::asBinder(listener); |
| LOG_VERBOSE("subscribe, binder 0x%x prop 0x%x", ibinder.get(), prop); |
| sp<HalClient> client = findOrCreateClientLocked(ibinder, listener); |
| if (client.get() == NULL) { |
| ALOGE("subscribe, no memory, cannot create HalClient"); |
| return NO_MEMORY; |
| } |
| sp<HalClientSpVector> clientsForProperty = findOrCreateClientsVectorForPropertyLocked(prop); |
| if (clientsForProperty.get() == NULL) { |
| ALOGE("subscribe, no memory, cannot create HalClientSpVector"); |
| return NO_MEMORY; |
| } |
| clientsForProperty->add(client); |
| ssize_t index = mSubscriptionInfos.indexOfKey(prop); |
| if (index < 0) { |
| // first time subscription for this property |
| shouldSubscribe = true; |
| } else { |
| const SubscriptionInfo& info = mSubscriptionInfos.valueAt(index); |
| if (info.sampleRate < sampleRate) { |
| shouldSubscribe = true; |
| } |
| newZones = (info.zones == 0) ? 0 : ((zones == 0) ? 0 : (info.zones | zones)); |
| if (info.zones != newZones) { |
| shouldSubscribe = true; |
| } |
| } |
| client->setSubscriptionInfo(prop, sampleRate, zones); |
| if (shouldSubscribe) { |
| inMock = mMockingEnabled; |
| SubscriptionInfo info(sampleRate, newZones); |
| mSubscriptionInfos.add(prop, info); |
| if ((prop >= (int32_t)VEHICLE_PROPERTY_INTERNAL_START) && |
| (prop <= (int32_t)VEHICLE_PROPERTY_INTERNAL_END)) { |
| LOG_VERBOSE("subscribe to internal property, prop 0x%x", prop); |
| return NO_ERROR; |
| } |
| } |
| } while (false); |
| if (shouldSubscribe) { |
| status_t r; |
| if (inMock) { |
| r = mHalMock->onPropertySubscribe(prop, sampleRate, newZones); |
| if (r != NO_ERROR) { |
| ALOGW("subscribe 0x%x failed, mock returned %d", prop, r); |
| } |
| } else { |
| LOG_VERBOSE("subscribe to HAL, prop 0x%x sample rate:%f zones:0x%x", prop, sampleRate, |
| newZones); |
| r = mDevice->subscribe(mDevice, prop, sampleRate, newZones); |
| if (r != NO_ERROR) { |
| ALOGW("subscribe 0x%x failed, HAL returned %d", prop, r); |
| } |
| } |
| return r; |
| } |
| return NO_ERROR; |
| } |
| |
| void VehicleNetworkService::unsubscribe(const sp<IVehicleNetworkListener> &listener, int32_t prop) { |
| bool shouldUnsubscribe = false; |
| bool inMocking = false; |
| do { |
| Mutex::Autolock autoLock(mLock); |
| if (!isSubscribableLocked(prop)) { |
| return; |
| } |
| sp<IBinder> ibinder = IInterface::asBinder(listener); |
| LOG_VERBOSE("unsubscribe, binder 0x%x, prop 0x%x", ibinder.get(), prop); |
| sp<HalClient> client = findClientLocked(ibinder); |
| if (client.get() == NULL) { |
| ALOGD("unsubscribe client not found in binder map"); |
| return; |
| } |
| shouldUnsubscribe = removePropertyFromClientLocked(ibinder, client, prop); |
| if ((prop >= (int32_t)VEHICLE_PROPERTY_INTERNAL_START) && |
| (prop <= (int32_t)VEHICLE_PROPERTY_INTERNAL_END)) { |
| LOG_VERBOSE("unsubscribe to internal property, prop 0x%x", prop); |
| return; |
| } |
| if (mMockingEnabled) { |
| inMocking = true; |
| } |
| } while (false); |
| if (shouldUnsubscribe) { |
| if (inMocking) { |
| mHalMock->onPropertyUnsubscribe(prop); |
| } else { |
| mDevice->unsubscribe(mDevice, prop); |
| } |
| } |
| } |
| |
| sp<HalClient> VehicleNetworkService::findClientLocked(sp<IBinder>& ibinder) { |
| sp<HalClient> client; |
| ssize_t index = mBinderToClientMap.indexOfKey(ibinder); |
| if (index < 0) { |
| return client; |
| } |
| return mBinderToClientMap.editValueAt(index); |
| } |
| |
| sp<HalClient> VehicleNetworkService::findOrCreateClientLocked(sp<IBinder>& ibinder, |
| const sp<IVehicleNetworkListener> &listener) { |
| sp<HalClient> client; |
| ssize_t index = mBinderToClientMap.indexOfKey(ibinder); |
| if (index < 0) { |
| IPCThreadState* self = IPCThreadState::self(); |
| pid_t pid = self->getCallingPid(); |
| uid_t uid = self->getCallingUid(); |
| client = new HalClient(listener, pid, uid); |
| ASSERT_OR_HANDLE_NO_MEMORY(client.get(), return client); |
| ibinder->linkToDeath(this); |
| LOG_VERBOSE("add binder 0x%x to map", ibinder.get()); |
| mBinderToClientMap.add(ibinder, client); |
| } else { |
| client = mBinderToClientMap.editValueAt(index); |
| } |
| return client; |
| } |
| |
| sp<HalClientSpVector> VehicleNetworkService::findClientsVectorForPropertyLocked(int32_t property) { |
| sp<HalClientSpVector> clientsForProperty; |
| ssize_t index = mPropertyToClientsMap.indexOfKey(property); |
| if (index >= 0) { |
| clientsForProperty = mPropertyToClientsMap.editValueAt(index); |
| } |
| return clientsForProperty; |
| } |
| |
| sp<HalClientSpVector> VehicleNetworkService::findOrCreateClientsVectorForPropertyLocked( |
| int32_t property) { |
| sp<HalClientSpVector> clientsForProperty; |
| ssize_t index = mPropertyToClientsMap.indexOfKey(property); |
| if (index >= 0) { |
| clientsForProperty = mPropertyToClientsMap.editValueAt(index); |
| } else { |
| clientsForProperty = new HalClientSpVector(); |
| ASSERT_OR_HANDLE_NO_MEMORY(clientsForProperty.get(), return clientsForProperty); |
| mPropertyToClientsMap.add(property, clientsForProperty); |
| } |
| return clientsForProperty; |
| } |
| |
| /** |
| * remove given property from client and remove HalCLient if necessary. |
| * @return true if the property should be unsubscribed from HAL (=no more clients). |
| */ |
| bool VehicleNetworkService::removePropertyFromClientLocked(sp<IBinder>& ibinder, |
| sp<HalClient>& client, int32_t property) { |
| if(!client->removePropertyAndCheckIfActive(property)) { |
| // client is no longer necessary |
| mBinderToClientMap.removeItem(ibinder); |
| ibinder->unlinkToDeath(this); |
| } |
| sp<HalClientSpVector> clientsForProperty = findClientsVectorForPropertyLocked(property); |
| if (clientsForProperty.get() == NULL) { |
| // no subscription |
| return false; |
| } |
| clientsForProperty->remove(client); |
| //TODO reset sample rate. do not care for now. |
| if (clientsForProperty->size() == 0) { |
| mPropertyToClientsMap.removeItem(property); |
| mSubscriptionInfos.removeItem(property); |
| return true; |
| } |
| return false; |
| } |
| |
| status_t VehicleNetworkService::injectEvent(const vehicle_prop_value_t& value) { |
| ALOGI("injectEvent property:0x%x", value.prop); |
| return onHalEvent(&value, true); |
| } |
| |
| status_t VehicleNetworkService::startMocking(const sp<IVehicleNetworkHalMock>& mock) { |
| sp<VehicleHalMessageHandler> handler; |
| List<sp<HalClient> > clientsToDispatch; |
| do { |
| Mutex::Autolock autoLock(mLock); |
| if (mMockingEnabled) { |
| ALOGW("startMocking while already enabled"); |
| // allow it as test can fail without clearing |
| if (mHalMock != NULL) { |
| IInterface::asBinder(mHalMock)->unlinkToDeath(mHalMockDeathHandler.get()); |
| } |
| } |
| ALOGW("starting vehicle HAL mocking"); |
| sp<IBinder> ibinder = IInterface::asBinder(mock); |
| if (mHalMockDeathHandler.get() == NULL) { |
| mHalMockDeathHandler = new MockDeathHandler(*this); |
| } |
| ibinder->linkToDeath(mHalMockDeathHandler); |
| mHalMock = mock; |
| mMockingEnabled = true; |
| // Mock implementation should make sure that its startMocking call is not blocking its |
| // onlistProperties call. Otherwise, this will lead into dead-lock. |
| mPropertiesForMocking = mock->onListProperties(); |
| handleHalRestartAndGetClientsToDispatchLocked(clientsToDispatch); |
| //TODO handle binder death |
| handler = mHandler; |
| } while (false); |
| handler->handleMockStart(); |
| for (auto& client : clientsToDispatch) { |
| client->dispatchHalRestart(true); |
| } |
| clientsToDispatch.clear(); |
| return NO_ERROR; |
| } |
| |
| void VehicleNetworkService::stopMocking(const sp<IVehicleNetworkHalMock>& mock) { |
| List<sp<HalClient> > clientsToDispatch; |
| do { |
| Mutex::Autolock autoLock(mLock); |
| if (mHalMock.get() == NULL) { |
| return; |
| } |
| sp<IBinder> ibinder = IInterface::asBinder(mock); |
| if (ibinder != IInterface::asBinder(mHalMock)) { |
| ALOGE("stopMocking, not the one started"); |
| return; |
| } |
| ALOGW("stopping vehicle HAL mocking"); |
| ibinder->unlinkToDeath(mHalMockDeathHandler.get()); |
| mHalMock = NULL; |
| mMockingEnabled = false; |
| handleHalRestartAndGetClientsToDispatchLocked(clientsToDispatch); |
| } while (false); |
| for (auto& client : clientsToDispatch) { |
| client->dispatchHalRestart(false); |
| } |
| clientsToDispatch.clear(); |
| } |
| |
| void VehicleNetworkService::handleHalRestartAndGetClientsToDispatchLocked( |
| List<sp<HalClient> >& clientsToDispatch) { |
| // all subscriptions are invalid |
| mPropertyToClientsMap.clear(); |
| mSubscriptionInfos.clear(); |
| mEventsCount.clear(); |
| List<sp<HalClient> > clientsToRemove; |
| for (size_t i = 0; i < mBinderToClientMap.size(); i++) { |
| sp<HalClient> client = mBinderToClientMap.valueAt(i); |
| client->removeAllProperties(); |
| if (client->isMonitoringHalRestart()) { |
| clientsToDispatch.push_back(client); |
| } |
| if (!client->isActive()) { |
| clientsToRemove.push_back(client); |
| } |
| } |
| for (auto& client : clientsToRemove) { |
| // client is no longer necessary |
| sp<IBinder> ibinder = IInterface::asBinder(client->getListener()); |
| mBinderToClientMap.removeItem(ibinder); |
| ibinder->unlinkToDeath(this); |
| } |
| clientsToRemove.clear(); |
| } |
| |
| status_t VehicleNetworkService::injectHalError(int32_t errorCode, int32_t property, |
| int32_t operation) { |
| return onHalError(errorCode, property, operation, true /*isInjection*/); |
| } |
| |
| status_t VehicleNetworkService::startErrorListening(const sp<IVehicleNetworkListener> &listener) { |
| sp<IBinder> ibinder = IInterface::asBinder(listener); |
| sp<HalClient> client; |
| do { |
| Mutex::Autolock autoLock(mLock); |
| client = findOrCreateClientLocked(ibinder, listener); |
| } while (false); |
| if (client.get() == NULL) { |
| ALOGW("startErrorListening failed, no memory"); |
| return NO_MEMORY; |
| } |
| client->setHalErrorMonitoringState(true); |
| return NO_ERROR; |
| } |
| |
| void VehicleNetworkService::stopErrorListening(const sp<IVehicleNetworkListener> &listener) { |
| sp<IBinder> ibinder = IInterface::asBinder(listener); |
| sp<HalClient> client; |
| do { |
| Mutex::Autolock autoLock(mLock); |
| client = findClientLocked(ibinder); |
| } while (false); |
| if (client.get() != NULL) { |
| client->setHalErrorMonitoringState(false); |
| } |
| } |
| |
| status_t VehicleNetworkService::startHalRestartMonitoring( |
| const sp<IVehicleNetworkListener> &listener) { |
| sp<IBinder> ibinder = IInterface::asBinder(listener); |
| sp<HalClient> client; |
| do { |
| Mutex::Autolock autoLock(mLock); |
| client = findOrCreateClientLocked(ibinder, listener); |
| } while (false); |
| if (client.get() == NULL) { |
| ALOGW("startHalRestartMonitoring failed, no memory"); |
| return NO_MEMORY; |
| } |
| client->setHalRestartMonitoringState(true); |
| return NO_ERROR; |
| } |
| |
| void VehicleNetworkService::stopHalRestartMonitoring(const sp<IVehicleNetworkListener> &listener) { |
| sp<IBinder> ibinder = IInterface::asBinder(listener); |
| sp<HalClient> client; |
| do { |
| Mutex::Autolock autoLock(mLock); |
| client = findClientLocked(ibinder); |
| } while (false); |
| if (client.get() != NULL) { |
| client->setHalRestartMonitoringState(false); |
| } |
| } |
| |
| status_t VehicleNetworkService::onHalEvent(const vehicle_prop_value_t* eventData, bool isInjection) |
| { |
| sp<VehicleHalMessageHandler> handler; |
| do { |
| Mutex::Autolock autoLock(mLock); |
| if (!isInjection) { |
| if (mMockingEnabled) { |
| // drop real HAL event if mocking is enabled |
| return NO_ERROR; |
| } |
| } |
| ssize_t index = mEventsCount.indexOfKey(eventData->prop); |
| if (index < 0) { |
| mEventsCount.add(eventData->prop, 1); |
| } else { |
| int count = mEventsCount.valueAt(index); |
| count++; |
| mEventsCount.add(eventData->prop, count); |
| } |
| handler = mHandler; |
| } while (false); |
| //TODO add memory pool |
| vehicle_prop_value_t* copy = VehiclePropValueUtil::allocVehicleProp(*eventData); |
| ASSERT_OR_HANDLE_NO_MEMORY(copy, return NO_MEMORY); |
| handler->handleHalEvent(copy); |
| return NO_ERROR; |
| } |
| |
| status_t VehicleNetworkService::onHalError(int32_t errorCode, int32_t property, int32_t operation, |
| bool isInjection) { |
| sp<VehicleHalMessageHandler> handler; |
| VehicleHalError* error = NULL; |
| do { |
| Mutex::Autolock autoLock(mLock); |
| if (!isInjection) { |
| if (mMockingEnabled) { |
| // drop real HAL error if mocking is enabled |
| return NO_ERROR; |
| } |
| } |
| |
| error = new VehicleHalError(errorCode, property, operation); |
| if (error == NULL) { |
| return NO_MEMORY; |
| } |
| handler = mHandler; |
| } while (false); |
| ALOGI("HAL error, error code:%d, property:0x%x, operation:%d, isInjection:%d", |
| errorCode, property, operation, isInjection? 1 : 0); |
| handler->handleHalError(error); |
| return NO_ERROR; |
| } |
| |
| void VehicleNetworkService::dispatchHalEvents(List<vehicle_prop_value_t*>& events) { |
| HalClientSpVector activeClients; |
| do { // for lock scoping |
| Mutex::Autolock autoLock(mLock); |
| for (vehicle_prop_value_t* e : events) { |
| ssize_t index = mPropertyToClientsMap.indexOfKey(e->prop); |
| if (index < 0) { |
| EVENT_LOG("HAL event for not subscribed property 0x%x", e->prop); |
| continue; |
| } |
| sp<HalClientSpVector>& clients = mPropertyToClientsMap.editValueAt(index); |
| EVENT_LOG("dispatchHalEvents, prop 0x%x, active clients %d", e->prop, clients->size()); |
| for (size_t i = 0; i < clients->size(); i++) { |
| sp<HalClient>& client = clients->editItemAt(i); |
| activeClients.add(client); |
| client->addEvent(e); |
| } |
| } |
| } while (false); |
| EVENT_LOG("dispatchHalEvents num events %d, active clients:%d", events.size(), |
| activeClients.size()); |
| for (size_t i = 0; i < activeClients.size(); i++) { |
| sp<HalClient> client = activeClients.editItemAt(i); |
| client->dispatchEvents(); |
| } |
| activeClients.clear(); |
| } |
| |
| void VehicleNetworkService::dispatchHalError(VehicleHalError* error) { |
| List<sp<HalClient> > clientsToDispatch; |
| do { |
| Mutex::Autolock autoLock(mLock); |
| if (error->property != 0) { |
| sp<HalClientSpVector> clientsForProperty = findClientsVectorForPropertyLocked( |
| error->property); |
| if (clientsForProperty.get() != NULL) { |
| for (size_t i = 0; i < clientsForProperty->size(); i++) { |
| sp<HalClient> client = clientsForProperty->itemAt(i); |
| clientsToDispatch.push_back(client); |
| } |
| } |
| } |
| // Send to global error handler if property is 0 or if no client subscribing. |
| if (error->property == 0 || clientsToDispatch.size() == 0) { |
| for (size_t i = 0; i < mBinderToClientMap.size(); i++) { |
| sp<HalClient> client = mBinderToClientMap.valueAt(i); |
| if (client->isMonitoringHalError()) { |
| clientsToDispatch.push_back(client); |
| } |
| } |
| } |
| } while (false); |
| ALOGI("dispatchHalError error:%d, property:0x%x, operation:%d, num clients to dispatch:%d", |
| error->errorCode, error->property, error->operation, clientsToDispatch.size()); |
| for (auto& client : clientsToDispatch) { |
| client->dispatchHalError(error->errorCode, error->property, error->operation); |
| } |
| clientsToDispatch.clear(); |
| } |
| |
| status_t VehicleNetworkService::loadHal() { |
| int r = hw_get_module(VEHICLE_HARDWARE_MODULE_ID, (hw_module_t const**)&mModule); |
| if (r != NO_ERROR) { |
| ALOGE("cannot load HAL module, error:%d", r); |
| return r; |
| } |
| r = mModule->common.methods->open(&mModule->common, VEHICLE_HARDWARE_DEVICE, |
| (hw_device_t**)&mDevice); |
| return r; |
| } |
| |
| void VehicleNetworkService::closeHal() { |
| mDevice->common.close(&mDevice->common); |
| } |
| }; |