C++程序  |  1084行  |  37.63 KB

/*
 * 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);
}
};