/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of The Linux Foundation, nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #define LOG_TAG "LocSvc_SystemStatusOsObserver" #include <algorithm> #include <SystemStatus.h> #include <SystemStatusOsObserver.h> #include <IDataItemCore.h> #include <IClientIndex.h> #include <IDataItemIndex.h> #include <IndexFactory.h> #include <DataItemsFactoryProxy.h> namespace loc_core { SystemStatusOsObserver::SystemStatusOsObserver(const MsgTask* msgTask) : mAddress("SystemStatusOsObserver"), mClientIndex(IndexFactory<IDataItemObserver*, DataItemId> :: createClientIndex()), mDataItemIndex(IndexFactory<IDataItemObserver*, DataItemId> :: createDataItemIndex()) { mContext.mMsgTask = msgTask; } SystemStatusOsObserver::~SystemStatusOsObserver() { // Close data-item library handle DataItemsFactoryProxy::closeDataItemLibraryHandle(); // Destroy cache for (auto each : mDataItemCache) { if (nullptr != each.second) { delete each.second; } } mDataItemCache.clear(); delete mClientIndex; delete mDataItemIndex; } void SystemStatusOsObserver::setSubscriptionObj(IDataItemSubscription* subscriptionObj) { mContext.mSubscriptionObj = subscriptionObj; LOC_LOGD("Request cache size - Subscribe:%zu RequestData:%zu", mSubscribeReqCache.size(), mReqDataCache.size()); // we have received the subscription object. process cached requests // process - subscribe request cache for (auto each : mSubscribeReqCache) { subscribe(each.second, each.first); } // process - requestData request cache for (auto each : mReqDataCache) { requestData(each.second, each.first); } } // Helper to cache requests subscribe and requestData till subscription obj is obtained void SystemStatusOsObserver::cacheObserverRequest(ObserverReqCache& reqCache, const list<DataItemId>& l, IDataItemObserver* client) { ObserverReqCache::iterator dicIter = reqCache.find(client); if (dicIter != reqCache.end()) { // found list<DataItemId> difference(0); set_difference(l.begin(), l.end(), dicIter->second.begin(), dicIter->second.end(), inserter(difference, difference.begin())); if (!difference.empty()) { difference.sort(); dicIter->second.merge(difference); dicIter->second.unique(); } } else { // not found reqCache[client] = l; } } /****************************************************************************** IDataItemSubscription Overrides ******************************************************************************/ void SystemStatusOsObserver::subscribe( const list<DataItemId>& l, IDataItemObserver* client) { if (nullptr == mContext.mSubscriptionObj) { LOC_LOGD("%s]: Subscription object is NULL. Caching requests", __func__); cacheObserverRequest(mSubscribeReqCache, l, client); return; } struct HandleSubscribeReq : public LocMsg { HandleSubscribeReq(SystemStatusOsObserver* parent, const list<DataItemId>& l, IDataItemObserver* client) : mParent(parent), mClient(client), mDataItemList(l) {} virtual ~HandleSubscribeReq() {} void proc() const { if (mDataItemList.empty()) { LOC_LOGV("mDataItemList is empty. Nothing to do. Exiting"); return; } // Handle First Response list<DataItemId> pendingFirstResponseList(0); mParent->mClientIndex->add(mClient, mDataItemList, pendingFirstResponseList); // Do not send first response for only pendingFirstResponseList, // instead send for all the data items (present in the cache) that // have been subscribed for each time. mParent->sendFirstResponse(mDataItemList, mClient); list<DataItemId> yetToSubscribeDataItemsList(0); mParent->mDataItemIndex->add(mClient, mDataItemList, yetToSubscribeDataItemsList); // Send subscription list to framework if (!yetToSubscribeDataItemsList.empty()) { mParent->mContext.mSubscriptionObj->subscribe(yetToSubscribeDataItemsList, mParent); LOC_LOGD("Subscribe Request sent to framework for the following"); mParent->logMe(yetToSubscribeDataItemsList); } } SystemStatusOsObserver* mParent; IDataItemObserver* mClient; const list<DataItemId> mDataItemList; }; mContext.mMsgTask->sendMsg(new (nothrow) HandleSubscribeReq(this, l, client)); } void SystemStatusOsObserver::updateSubscription( const list<DataItemId>& l, IDataItemObserver* client) { if (nullptr == mContext.mSubscriptionObj) { LOC_LOGE("%s:%d]: Subscription object is NULL", __func__, __LINE__); return; } struct HandleUpdateSubscriptionReq : public LocMsg { HandleUpdateSubscriptionReq(SystemStatusOsObserver* parent, const list<DataItemId>& l, IDataItemObserver* client) : mParent(parent), mClient(client), mDataItemList(l) {} virtual ~HandleUpdateSubscriptionReq() {} void proc() const { if (mDataItemList.empty()) { LOC_LOGV("mDataItemList is empty. Nothing to do. Exiting"); return; } list<DataItemId> currentlySubscribedList(0); mParent->mClientIndex->getSubscribedList(mClient, currentlySubscribedList); list<DataItemId> removeDataItemList(0); set_difference(currentlySubscribedList.begin(), currentlySubscribedList.end(), mDataItemList.begin(), mDataItemList.end(), inserter(removeDataItemList,removeDataItemList.begin())); // Handle First Response list<DataItemId> pendingFirstResponseList(0); mParent->mClientIndex->add(mClient, mDataItemList, pendingFirstResponseList); // Send First Response mParent->sendFirstResponse(pendingFirstResponseList, mClient); list<DataItemId> yetToSubscribeDataItemsList(0); mParent->mDataItemIndex->add( mClient, mDataItemList, yetToSubscribeDataItemsList); // Send subscription list to framework if (!yetToSubscribeDataItemsList.empty()) { mParent->mContext.mSubscriptionObj->subscribe( yetToSubscribeDataItemsList, mParent); LOC_LOGD("Subscribe Request sent to framework for the following"); mParent->logMe(yetToSubscribeDataItemsList); } list<DataItemId> unsubscribeList(0); list<DataItemId> unused(0); mParent->mClientIndex->remove(mClient, removeDataItemList, unused); if (!mParent->mClientIndex->isSubscribedClient(mClient)) { mParent->mDataItemIndex->remove( list<IDataItemObserver*> (1,mClient), unsubscribeList); } if (!unsubscribeList.empty()) { // Send unsubscribe to framework mParent->mContext.mSubscriptionObj->unsubscribe(unsubscribeList, mParent); LOC_LOGD("Unsubscribe Request sent to framework for the following"); mParent->logMe(unsubscribeList); } } SystemStatusOsObserver* mParent; IDataItemObserver* mClient; const list<DataItemId> mDataItemList; }; mContext.mMsgTask->sendMsg(new (nothrow) HandleUpdateSubscriptionReq(this, l, client)); } void SystemStatusOsObserver::requestData( const list<DataItemId>& l, IDataItemObserver* client) { if (nullptr == mContext.mSubscriptionObj) { LOC_LOGD("%s]: Subscription object is NULL. Caching requests", __func__); cacheObserverRequest(mReqDataCache, l, client); return; } struct HandleRequestData : public LocMsg { HandleRequestData(SystemStatusOsObserver* parent, const list<DataItemId>& l, IDataItemObserver* client) : mParent(parent), mClient(client), mDataItemList(l) {} virtual ~HandleRequestData() {} void proc() const { if (mDataItemList.empty()) { LOC_LOGV("mDataItemList is empty. Nothing to do. Exiting"); return; } list<DataItemId> yetToSubscribeDataItemsList(0); mParent->mClientIndex->add( mClient, mDataItemList, yetToSubscribeDataItemsList); mParent->mDataItemIndex->add( mClient, mDataItemList, yetToSubscribeDataItemsList); // Send subscription list to framework if (!mDataItemList.empty()) { mParent->mContext.mSubscriptionObj->requestData(mDataItemList, mParent); LOC_LOGD("Subscribe Request sent to framework for the following"); mParent->logMe(yetToSubscribeDataItemsList); } } SystemStatusOsObserver* mParent; IDataItemObserver* mClient; const list<DataItemId> mDataItemList; }; mContext.mMsgTask->sendMsg(new (nothrow) HandleRequestData(this, l, client)); } void SystemStatusOsObserver::unsubscribe( const list<DataItemId>& l, IDataItemObserver* client) { if (nullptr == mContext.mSubscriptionObj) { LOC_LOGE("%s:%d]: Subscription object is NULL", __func__, __LINE__); return; } struct HandleUnsubscribeReq : public LocMsg { HandleUnsubscribeReq(SystemStatusOsObserver* parent, const list<DataItemId>& l, IDataItemObserver* client) : mParent(parent), mClient(client), mDataItemList(l) {} virtual ~HandleUnsubscribeReq() {} void proc() const { if (mDataItemList.empty()) { LOC_LOGV("mDataItemList is empty. Nothing to do. Exiting"); return; } list<DataItemId> unsubscribeList(0); list<DataItemId> unused(0); mParent->mClientIndex->remove(mClient, mDataItemList, unused); for (auto each : mDataItemList) { list<IDataItemObserver*> clientListSubs(0); list<IDataItemObserver*> clientListOut(0); mParent->mDataItemIndex->remove( each, list<IDataItemObserver*> (1,mClient), clientListOut); // check if there are any other subscribed client for this data item id mParent->mDataItemIndex->getListOfSubscribedClients(each, clientListSubs); if (clientListSubs.empty()) { LOC_LOGD("Client list subscribed is empty for dataitem - %d", each); unsubscribeList.push_back(each); } } if (!unsubscribeList.empty()) { // Send unsubscribe to framework mParent->mContext.mSubscriptionObj->unsubscribe(unsubscribeList, mParent); LOC_LOGD("Unsubscribe Request sent to framework for the following data items"); mParent->logMe(unsubscribeList); } } SystemStatusOsObserver* mParent; IDataItemObserver* mClient; const list<DataItemId> mDataItemList; }; mContext.mMsgTask->sendMsg(new (nothrow) HandleUnsubscribeReq(this, l, client)); } void SystemStatusOsObserver::unsubscribeAll(IDataItemObserver* client) { if (nullptr == mContext.mSubscriptionObj) { LOC_LOGE("%s:%d]: Subscription object is NULL", __func__, __LINE__); return; } struct HandleUnsubscribeAllReq : public LocMsg { HandleUnsubscribeAllReq(SystemStatusOsObserver* parent, IDataItemObserver* client) : mParent(parent), mClient(client) {} virtual ~HandleUnsubscribeAllReq() {} void proc() const { list<IDataItemObserver*> clients(1, mClient); list<DataItemId> unsubscribeList(0); if(0 == mParent->mClientIndex->remove(mClient)) { return; } mParent->mDataItemIndex->remove(clients, unsubscribeList); if (!unsubscribeList.empty()) { // Send unsubscribe to framework mParent->mContext.mSubscriptionObj->unsubscribe(unsubscribeList, mParent); LOC_LOGD("Unsubscribe Request sent to framework for the following data items"); mParent->logMe(unsubscribeList); } } SystemStatusOsObserver* mParent; IDataItemObserver* mClient; }; mContext.mMsgTask->sendMsg(new (nothrow) HandleUnsubscribeAllReq(this, client)); } /****************************************************************************** IDataItemObserver Overrides ******************************************************************************/ void SystemStatusOsObserver::notify(const list<IDataItemCore*>& dlist) { list<IDataItemCore*> dataItemList(0); for (auto each : dlist) { string dv; each->stringify(dv); LOC_LOGD("notify: DataItem In Value:%s", dv.c_str()); IDataItemCore* di = DataItemsFactoryProxy::createNewDataItem(each->getId()); if (nullptr == di) { LOC_LOGE("Unable to create dataitem:%d", each->getId()); return; } // Copy contents into the newly created data item di->copy(each); dataItemList.push_back(di); // Request systemstatus to record this dataitem in its cache SystemStatus* systemstatus = SystemStatus::getInstance(mContext.mMsgTask); if(nullptr != systemstatus) { systemstatus->eventDataItemNotify(di); } } struct HandleNotify : public LocMsg { HandleNotify(SystemStatusOsObserver* parent, const list<IDataItemCore*>& l) : mParent(parent), mDList(l) {} virtual ~HandleNotify() { for (auto each : mDList) { delete each; } } void proc() const { // Update Cache with received data items and prepare // list of data items to be sent. list<DataItemId> dataItemIdsToBeSent(0); for (auto item : mDList) { bool dataItemUpdated = false; mParent->updateCache(item, dataItemUpdated); if (dataItemUpdated) { dataItemIdsToBeSent.push_back(item->getId()); } } // Send data item to all subscribed clients list<IDataItemObserver*> clientList(0); for (auto each : dataItemIdsToBeSent) { list<IDataItemObserver*> clients(0); mParent->mDataItemIndex->getListOfSubscribedClients(each, clients); for (auto each_cient: clients) { clientList.push_back(each_cient); } } clientList.unique(); for (auto client : clientList) { list<DataItemId> dataItemIdsSubscribedByThisClient(0); list<DataItemId> dataItemIdsToBeSentForThisClient(0); mParent->mClientIndex->getSubscribedList( client, dataItemIdsSubscribedByThisClient); dataItemIdsSubscribedByThisClient.sort(); dataItemIdsToBeSent.sort(); set_intersection(dataItemIdsToBeSent.begin(), dataItemIdsToBeSent.end(), dataItemIdsSubscribedByThisClient.begin(), dataItemIdsSubscribedByThisClient.end(), inserter(dataItemIdsToBeSentForThisClient, dataItemIdsToBeSentForThisClient.begin())); mParent->sendCachedDataItems(dataItemIdsToBeSentForThisClient, client); dataItemIdsSubscribedByThisClient.clear(); dataItemIdsToBeSentForThisClient.clear(); } } SystemStatusOsObserver* mParent; const list<IDataItemCore*> mDList; }; mContext.mMsgTask->sendMsg(new (nothrow) HandleNotify(this, dataItemList)); } /****************************************************************************** IFrameworkActionReq Overrides ******************************************************************************/ void SystemStatusOsObserver::turnOn(DataItemId dit, int timeOut) { if (nullptr == mContext.mFrameworkActionReqObj) { LOC_LOGE("%s:%d]: Framework action request object is NULL", __func__, __LINE__); return; } // Check if data item exists in mActiveRequestCount map<DataItemId, int>::iterator citer = mActiveRequestCount.find(dit); if (citer == mActiveRequestCount.end()) { // Data item not found in map // Add reference count as 1 and add dataitem to map pair<DataItemId, int> cpair(dit, 1); mActiveRequestCount.insert(cpair); LOC_LOGD("Sending turnOn request"); // Send action turn on to framework struct HandleTurnOnMsg : public LocMsg { HandleTurnOnMsg(IFrameworkActionReq* framework, DataItemId dit, int timeOut) : mFrameworkActionReqObj(framework), mDataItemId(dit), mTimeOut(timeOut) {} virtual ~HandleTurnOnMsg() {} void proc() const { mFrameworkActionReqObj->turnOn(mDataItemId, mTimeOut); } IFrameworkActionReq* mFrameworkActionReqObj; DataItemId mDataItemId; int mTimeOut; }; mContext.mMsgTask->sendMsg(new (nothrow) HandleTurnOnMsg(this, dit, timeOut)); } else { // Found in map, update reference count citer->second++; LOC_LOGD("turnOn - Data item:%d Num_refs:%d", dit, citer->second); } } void SystemStatusOsObserver::turnOff(DataItemId dit) { if (nullptr == mContext.mFrameworkActionReqObj) { LOC_LOGE("%s:%d]: Framework action request object is NULL", __func__, __LINE__); return; } // Check if data item exists in mActiveRequestCount map<DataItemId, int>::iterator citer = mActiveRequestCount.find(dit); if (citer != mActiveRequestCount.end()) { // found citer->second--; LOC_LOGD("turnOff - Data item:%d Remaining:%d", dit, citer->second); if(citer->second == 0) { // if this was last reference, remove item from map and turn off module mActiveRequestCount.erase(citer); // Send action turn off to framework struct HandleTurnOffMsg : public LocMsg { HandleTurnOffMsg(IFrameworkActionReq* framework, DataItemId dit) : mFrameworkActionReqObj(framework), mDataItemId(dit) {} virtual ~HandleTurnOffMsg() {} void proc() const { mFrameworkActionReqObj->turnOff(mDataItemId); } IFrameworkActionReq* mFrameworkActionReqObj; DataItemId mDataItemId; }; mContext.mMsgTask->sendMsg( new (nothrow) HandleTurnOffMsg(mContext.mFrameworkActionReqObj, dit)); } } } /****************************************************************************** Helpers ******************************************************************************/ void SystemStatusOsObserver::sendFirstResponse( const list<DataItemId>& l, IDataItemObserver* to) { if (l.empty()) { LOC_LOGV("list is empty. Nothing to do. Exiting"); return; } string clientName; to->getName(clientName); list<IDataItemCore*> dataItems(0); for (auto each : l) { map<DataItemId, IDataItemCore*>::const_iterator citer = mDataItemCache.find(each); if (citer != mDataItemCache.end()) { string dv; citer->second->stringify(dv); LOC_LOGI("DataItem: %s >> %s", dv.c_str(), clientName.c_str()); dataItems.push_back(citer->second); } } if (dataItems.empty()) { LOC_LOGV("No items to notify. Nothing to do. Exiting"); return; } to->notify(dataItems); } void SystemStatusOsObserver::sendCachedDataItems( const list<DataItemId>& l, IDataItemObserver* to) { string clientName; to->getName(clientName); list<IDataItemCore*> dataItems(0); for (auto each : l) { string dv; IDataItemCore* di = mDataItemCache[each]; di->stringify(dv); LOC_LOGI("DataItem: %s >> %s", dv.c_str(), clientName.c_str()); dataItems.push_back(di); } to->notify(dataItems); } void SystemStatusOsObserver::updateCache(IDataItemCore* d, bool& dataItemUpdated) { if (nullptr == d) { return; } // Check if data item exists in cache map<DataItemId, IDataItemCore*>::iterator citer = mDataItemCache.find(d->getId()); if (citer == mDataItemCache.end()) { // New data item; not found in cache IDataItemCore* dataitem = DataItemsFactoryProxy::createNewDataItem(d->getId()); if (nullptr == dataitem) { return; } // Copy the contents of the data item dataitem->copy(d); pair<DataItemId, IDataItemCore*> cpair(d->getId(), dataitem); // Insert in mDataItemCache mDataItemCache.insert(cpair); dataItemUpdated = true; } else { // Found in cache; Update cache if necessary if(0 == citer->second->copy(d, &dataItemUpdated)) { return; } } if (dataItemUpdated) { LOC_LOGV("DataItem:%d updated:%d", d->getId(), dataItemUpdated); } } } // namespace loc_core