1 /* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
2  *
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are
5  * met:
6  *     * Redistributions of source code must retain the above copyright
7  *       notice, this list of conditions and the following disclaimer.
8  *     * Redistributions in binary form must reproduce the above
9  *       copyright notice, this list of conditions and the following
10  *       disclaimer in the documentation and/or other materials provided
11  *       with the distribution.
12  *     * Neither the name of The Linux Foundation, nor the names of its
13  *       contributors may be used to endorse or promote products derived
14  *       from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  */
29 #define LOG_TAG "LocSvc_SystemStatusOsObserver"
30 
31 #include <algorithm>
32 #include <SystemStatus.h>
33 #include <SystemStatusOsObserver.h>
34 #include <IDataItemCore.h>
35 #include <DataItemsFactoryProxy.h>
36 
37 namespace loc_core
38 {
39 template <typename CINT, typename COUT>
containerTransfer(CINT & inContainer)40 COUT SystemStatusOsObserver::containerTransfer(CINT& inContainer) {
41     COUT outContainer(0);
42     for (auto item : inContainer) {
43         outContainer.insert(outContainer.begin(), item);
44     }
45     return outContainer;
46 }
47 
~SystemStatusOsObserver()48 SystemStatusOsObserver::~SystemStatusOsObserver() {
49     // Close data-item library handle
50     DataItemsFactoryProxy::closeDataItemLibraryHandle();
51 
52     // Destroy cache
53     for (auto each : mDataItemCache) {
54         if (nullptr != each.second) {
55             delete each.second;
56         }
57     }
58 
59     mDataItemCache.clear();
60 }
61 
setSubscriptionObj(IDataItemSubscription * subscriptionObj)62 void SystemStatusOsObserver::setSubscriptionObj(IDataItemSubscription* subscriptionObj)
63 {
64     struct SetSubsObj : public LocMsg {
65         ObserverContext& mContext;
66         IDataItemSubscription* mSubsObj;
67         inline SetSubsObj(ObserverContext& context, IDataItemSubscription* subscriptionObj) :
68                 mContext(context), mSubsObj(subscriptionObj) {}
69         void proc() const {
70             mContext.mSubscriptionObj = mSubsObj;
71 
72             if (!mContext.mSSObserver->mDataItemToClients.empty()) {
73                 list<DataItemId> dis(
74                         containerTransfer<unordered_set<DataItemId>, list<DataItemId>>(
75                                 mContext.mSSObserver->mDataItemToClients.getKeys()));
76                 mContext.mSubscriptionObj->subscribe(dis, mContext.mSSObserver);
77                 mContext.mSubscriptionObj->requestData(dis, mContext.mSSObserver);
78             }
79         }
80     };
81 
82     if (nullptr == subscriptionObj) {
83         LOC_LOGw("subscriptionObj is NULL");
84     } else {
85         mContext.mMsgTask->sendMsg(new SetSubsObj(mContext, subscriptionObj));
86     }
87 }
88 
89 /******************************************************************************
90  IDataItemSubscription Overrides
91 ******************************************************************************/
subscribe(const list<DataItemId> & l,IDataItemObserver * client,bool toRequestData)92 void SystemStatusOsObserver::subscribe(const list<DataItemId>& l, IDataItemObserver* client,
93                                        bool toRequestData)
94 {
95     struct HandleSubscribeReq : public LocMsg {
96         inline HandleSubscribeReq(SystemStatusOsObserver* parent,
97                 list<DataItemId>& l, IDataItemObserver* client, bool requestData) :
98                 mParent(parent), mClient(client),
99                 mDataItemSet(containerTransfer<list<DataItemId>, unordered_set<DataItemId>>(l)),
100                 diItemlist(l),
101                 mToRequestData(requestData) {}
102 
103         void proc() const {
104             unordered_set<DataItemId> dataItemsToSubscribe(0);
105             mParent->mDataItemToClients.add(mDataItemSet, {mClient}, &dataItemsToSubscribe);
106             mParent->mClientToDataItems.add(mClient, mDataItemSet);
107 
108             mParent->sendCachedDataItems(mDataItemSet, mClient);
109 
110             // Send subscription set to framework
111             if (nullptr != mParent->mContext.mSubscriptionObj) {
112                 if (mToRequestData) {
113                     LOC_LOGD("Request Data sent to framework for the following");
114                     mParent->mContext.mSubscriptionObj->requestData(diItemlist, mParent);
115                 } else if (!dataItemsToSubscribe.empty()) {
116                     LOC_LOGD("Subscribe Request sent to framework for the following");
117                     mParent->logMe(dataItemsToSubscribe);
118                     mParent->mContext.mSubscriptionObj->subscribe(
119                             containerTransfer<unordered_set<DataItemId>, list<DataItemId>>(
120                                     std::move(dataItemsToSubscribe)),
121                             mParent);
122                 }
123             }
124         }
125         mutable SystemStatusOsObserver* mParent;
126         IDataItemObserver* mClient;
127         const unordered_set<DataItemId> mDataItemSet;
128         const list<DataItemId> diItemlist;
129         bool mToRequestData;
130     };
131 
132     if (l.empty() || nullptr == client) {
133         LOC_LOGw("Data item set is empty or client is nullptr");
134     } else {
135         mContext.mMsgTask->sendMsg(
136                 new HandleSubscribeReq(this, (list<DataItemId>&)l, client, toRequestData));
137     }
138 }
139 
updateSubscription(const list<DataItemId> & l,IDataItemObserver * client)140 void SystemStatusOsObserver::updateSubscription(
141         const list<DataItemId>& l, IDataItemObserver* client)
142 {
143     struct HandleUpdateSubscriptionReq : public LocMsg {
144         HandleUpdateSubscriptionReq(SystemStatusOsObserver* parent,
145                                     list<DataItemId>& l, IDataItemObserver* client) :
146                 mParent(parent), mClient(client),
147                 mDataItemSet(containerTransfer<list<DataItemId>, unordered_set<DataItemId>>(l)) {}
148 
149         void proc() const {
150             unordered_set<DataItemId> dataItemsToSubscribe(0);
151             unordered_set<DataItemId> dataItemsToUnsubscribe(0);
152             unordered_set<IDataItemObserver*> clients({mClient});
153             // below removes clients from all entries keyed with the return of the
154             // mClientToDataItems.update() call. If leaving an empty set of clients as the
155             // result, the entire entry will be removed. dataItemsToUnsubscribe will be
156             // populated to keep the keys of the removed entries.
157             mParent->mDataItemToClients.trimOrRemove(
158                     // this call updates <IDataItemObserver*, DataItemId> map; removes
159                     // the DataItemId's that are not new to the clietn from mDataItemSet;
160                     // and returns a set of mDataItemSet's that are no longer used by client.
161                     // This unused set of mDataItemSet's is passed to trimOrRemove method of
162                     // <DataItemId, IDataItemObserver*> map to remove the client from the
163                     // corresponding entries, and gets a set of the entries that are
164                     // removed from the <DataItemId, IDataItemObserver*> map as a result.
165                     mParent->mClientToDataItems.update(mClient,
166                                                        (unordered_set<DataItemId>&)mDataItemSet),
167                     clients, &dataItemsToUnsubscribe, nullptr);
168             // below adds mClient to <DataItemId, IDataItemObserver*> map, and populates
169             // new keys added to that map, which are DataItemIds to be subscribed.
170             mParent->mDataItemToClients.add(mDataItemSet, clients, &dataItemsToSubscribe);
171 
172             // Send First Response
173             mParent->sendCachedDataItems(mDataItemSet, mClient);
174 
175             if (nullptr != mParent->mContext.mSubscriptionObj) {
176                 // Send subscription set to framework
177                 if (!dataItemsToSubscribe.empty()) {
178                     LOC_LOGD("Subscribe Request sent to framework for the following");
179                     mParent->logMe(dataItemsToSubscribe);
180 
181                     mParent->mContext.mSubscriptionObj->subscribe(
182                             containerTransfer<unordered_set<DataItemId>, list<DataItemId>>(
183                                     std::move(dataItemsToSubscribe)),
184                             mParent);
185                 }
186 
187                 // Send unsubscribe to framework
188                 if (!dataItemsToUnsubscribe.empty()) {
189                     LOC_LOGD("Unsubscribe Request sent to framework for the following");
190                     mParent->logMe(dataItemsToUnsubscribe);
191 
192                     mParent->mContext.mSubscriptionObj->unsubscribe(
193                             containerTransfer<unordered_set<DataItemId>, list<DataItemId>>(
194                                     std::move(dataItemsToUnsubscribe)),
195                             mParent);
196                 }
197             }
198         }
199         SystemStatusOsObserver* mParent;
200         IDataItemObserver* mClient;
201         unordered_set<DataItemId> mDataItemSet;
202     };
203 
204     if (l.empty() || nullptr == client) {
205         LOC_LOGw("Data item set is empty or client is nullptr");
206     } else {
207         mContext.mMsgTask->sendMsg(
208                 new HandleUpdateSubscriptionReq(this, (list<DataItemId>&)l, client));
209     }
210 }
211 
unsubscribe(const list<DataItemId> & l,IDataItemObserver * client)212 void SystemStatusOsObserver::unsubscribe(
213         const list<DataItemId>& l, IDataItemObserver* client)
214 {
215     struct HandleUnsubscribeReq : public LocMsg {
216         HandleUnsubscribeReq(SystemStatusOsObserver* parent,
217                 list<DataItemId>& l, IDataItemObserver* client) :
218                 mParent(parent), mClient(client),
219                 mDataItemSet(containerTransfer<list<DataItemId>, unordered_set<DataItemId>>(l)) {}
220 
221         void proc() const {
222             unordered_set<DataItemId> dataItemsUnusedByClient(0);
223             unordered_set<IDataItemObserver*> clientToRemove(0);
224             mParent->mClientToDataItems.trimOrRemove({mClient}, mDataItemSet,  &clientToRemove,
225                                                      &dataItemsUnusedByClient);
226             unordered_set<DataItemId> dataItemsToUnsubscribe(0);
227             mParent->mDataItemToClients.trimOrRemove(dataItemsUnusedByClient, {mClient},
228                                                      &dataItemsToUnsubscribe, nullptr);
229 
230             if (nullptr != mParent->mContext.mSubscriptionObj && !dataItemsToUnsubscribe.empty()) {
231                 LOC_LOGD("Unsubscribe Request sent to framework for the following data items");
232                 mParent->logMe(dataItemsToUnsubscribe);
233 
234                 // Send unsubscribe to framework
235                 mParent->mContext.mSubscriptionObj->unsubscribe(
236                         containerTransfer<unordered_set<DataItemId>, list<DataItemId>>(
237                                   std::move(dataItemsToUnsubscribe)),
238                         mParent);
239             }
240         }
241         SystemStatusOsObserver* mParent;
242         IDataItemObserver* mClient;
243         unordered_set<DataItemId> mDataItemSet;
244     };
245 
246     if (l.empty() || nullptr == client) {
247         LOC_LOGw("Data item set is empty or client is nullptr");
248     } else {
249         mContext.mMsgTask->sendMsg(new HandleUnsubscribeReq(this, (list<DataItemId>&)l, client));
250     }
251 }
252 
unsubscribeAll(IDataItemObserver * client)253 void SystemStatusOsObserver::unsubscribeAll(IDataItemObserver* client)
254 {
255     struct HandleUnsubscribeAllReq : public LocMsg {
256         HandleUnsubscribeAllReq(SystemStatusOsObserver* parent,
257                 IDataItemObserver* client) :
258                 mParent(parent), mClient(client) {}
259 
260         void proc() const {
261             unordered_set<DataItemId> diByClient = mParent->mClientToDataItems.getValSet(mClient);
262             if (!diByClient.empty()) {
263                 unordered_set<DataItemId> dataItemsToUnsubscribe;
264                 mParent->mClientToDataItems.remove(mClient);
265                 mParent->mDataItemToClients.trimOrRemove(diByClient, {mClient},
266                                                          &dataItemsToUnsubscribe, nullptr);
267 
268                 if (!dataItemsToUnsubscribe.empty() &&
269                     nullptr != mParent->mContext.mSubscriptionObj) {
270 
271                     LOC_LOGD("Unsubscribe Request sent to framework for the following data items");
272                     mParent->logMe(dataItemsToUnsubscribe);
273 
274                     // Send unsubscribe to framework
275                     mParent->mContext.mSubscriptionObj->unsubscribe(
276                             containerTransfer<unordered_set<DataItemId>, list<DataItemId>>(
277                                     std::move(dataItemsToUnsubscribe)),
278                             mParent);
279                 }
280             }
281         }
282         SystemStatusOsObserver* mParent;
283         IDataItemObserver* mClient;
284     };
285 
286     if (nullptr == client) {
287         LOC_LOGw("Data item set is empty or client is nullptr");
288     } else {
289         mContext.mMsgTask->sendMsg(new HandleUnsubscribeAllReq(this, client));
290     }
291 }
292 
293 /******************************************************************************
294  IDataItemObserver Overrides
295 ******************************************************************************/
notify(const list<IDataItemCore * > & dlist)296 void SystemStatusOsObserver::notify(const list<IDataItemCore*>& dlist)
297 {
298     struct HandleNotify : public LocMsg {
299         HandleNotify(SystemStatusOsObserver* parent, vector<IDataItemCore*>& v) :
300                 mParent(parent), mDiVec(std::move(v)) {}
301 
302         inline virtual ~HandleNotify() {
303             for (auto item : mDiVec) {
304                 delete item;
305             }
306         }
307 
308         void proc() const {
309             // Update Cache with received data items and prepare
310             // list of data items to be sent.
311             unordered_set<DataItemId> dataItemIdsToBeSent(0);
312             for (auto item : mDiVec) {
313                 if (mParent->updateCache(item)) {
314                     dataItemIdsToBeSent.insert(item->getId());
315                 }
316             }
317 
318             // Send data item to all subscribed clients
319             unordered_set<IDataItemObserver*> clientSet(0);
320             for (auto each : dataItemIdsToBeSent) {
321                 auto clients = mParent->mDataItemToClients.getValSetPtr(each);
322                 if (nullptr != clients) {
323                     clientSet.insert(clients->begin(), clients->end());
324                 }
325             }
326 
327             for (auto client : clientSet) {
328                 unordered_set<DataItemId> dataItemIdsForThisClient(
329                         mParent->mClientToDataItems.getValSet(client));
330                 for (auto itr = dataItemIdsForThisClient.begin();
331                         itr != dataItemIdsForThisClient.end(); ) {
332                     if (dataItemIdsToBeSent.find(*itr) == dataItemIdsToBeSent.end()) {
333                         itr = dataItemIdsForThisClient.erase(itr);
334                     } else {
335                         itr++;
336                     }
337                 }
338 
339                 mParent->sendCachedDataItems(dataItemIdsForThisClient, client);
340             }
341         }
342         SystemStatusOsObserver* mParent;
343         const vector<IDataItemCore*> mDiVec;
344     };
345 
346     if (!dlist.empty()) {
347         vector<IDataItemCore*> dataItemVec(dlist.size());
348 
349         for (auto each : dlist) {
350 
351             IDataItemCore* di = DataItemsFactoryProxy::createNewDataItem(each->getId());
352             if (nullptr == di) {
353                 LOC_LOGw("Unable to create dataitem:%d", each->getId());
354                 continue;
355             }
356 
357             // Copy contents into the newly created data item
358             di->copy(each);
359 
360             // add this dataitem if updated from last one
361             dataItemVec.push_back(di);
362             IF_LOC_LOGD {
363                 string dv;
364                 di->stringify(dv);
365                 LOC_LOGd("notify: DataItem In Value:%s", dv.c_str());
366             }
367         }
368 
369         if (!dataItemVec.empty()) {
370             mContext.mMsgTask->sendMsg(new HandleNotify(this, dataItemVec));
371         }
372     }
373 }
374 
375 /******************************************************************************
376  IFrameworkActionReq Overrides
377 ******************************************************************************/
turnOn(DataItemId dit,int timeOut)378 void SystemStatusOsObserver::turnOn(DataItemId dit, int timeOut)
379 {
380     if (nullptr == mContext.mFrameworkActionReqObj) {
381         LOC_LOGE("%s:%d]: Framework action request object is NULL", __func__, __LINE__);
382         return;
383     }
384 
385     // Check if data item exists in mActiveRequestCount
386     DataItemIdToInt::iterator citer = mActiveRequestCount.find(dit);
387     if (citer == mActiveRequestCount.end()) {
388         // Data item not found in map
389         // Add reference count as 1 and add dataitem to map
390         pair<DataItemId, int> cpair(dit, 1);
391         mActiveRequestCount.insert(cpair);
392         LOC_LOGD("Sending turnOn request");
393 
394         // Send action turn on to framework
395         struct HandleTurnOnMsg : public LocMsg {
396             HandleTurnOnMsg(IFrameworkActionReq* framework,
397                     DataItemId dit, int timeOut) :
398                     mFrameworkActionReqObj(framework), mDataItemId(dit), mTimeOut(timeOut) {}
399             virtual ~HandleTurnOnMsg() {}
400             void proc() const {
401                 mFrameworkActionReqObj->turnOn(mDataItemId, mTimeOut);
402             }
403             IFrameworkActionReq* mFrameworkActionReqObj;
404             DataItemId mDataItemId;
405             int mTimeOut;
406         };
407         mContext.mMsgTask->sendMsg(
408                 new (nothrow) HandleTurnOnMsg(mContext.mFrameworkActionReqObj, dit, timeOut));
409     }
410     else {
411         // Found in map, update reference count
412         citer->second++;
413         LOC_LOGD("turnOn - Data item:%d Num_refs:%d", dit, citer->second);
414     }
415 }
416 
turnOff(DataItemId dit)417 void SystemStatusOsObserver::turnOff(DataItemId dit)
418 {
419     if (nullptr == mContext.mFrameworkActionReqObj) {
420         LOC_LOGE("%s:%d]: Framework action request object is NULL", __func__, __LINE__);
421         return;
422     }
423 
424     // Check if data item exists in mActiveRequestCount
425     DataItemIdToInt::iterator citer = mActiveRequestCount.find(dit);
426     if (citer != mActiveRequestCount.end()) {
427         // found
428         citer->second--;
429         LOC_LOGD("turnOff - Data item:%d Remaining:%d", dit, citer->second);
430         if(citer->second == 0) {
431             // if this was last reference, remove item from map and turn off module
432             mActiveRequestCount.erase(citer);
433 
434             // Send action turn off to framework
435             struct HandleTurnOffMsg : public LocMsg {
436                 HandleTurnOffMsg(IFrameworkActionReq* framework, DataItemId dit) :
437                     mFrameworkActionReqObj(framework), mDataItemId(dit) {}
438                 virtual ~HandleTurnOffMsg() {}
439                 void proc() const {
440                     mFrameworkActionReqObj->turnOff(mDataItemId);
441                 }
442                 IFrameworkActionReq* mFrameworkActionReqObj;
443                 DataItemId mDataItemId;
444             };
445             mContext.mMsgTask->sendMsg(
446                     new (nothrow) HandleTurnOffMsg(mContext.mFrameworkActionReqObj, dit));
447         }
448     }
449 }
450 
451 #ifdef USE_GLIB
connectBackhaul()452 bool SystemStatusOsObserver::connectBackhaul()
453 {
454     bool result = false;
455 
456     if (mContext.mFrameworkActionReqObj != NULL) {
457         struct HandleConnectBackhaul : public LocMsg {
458             HandleConnectBackhaul(IFrameworkActionReq* fwkActReq) :
459                     mFwkActionReqObj(fwkActReq) {}
460             virtual ~HandleConnectBackhaul() {}
461             void proc() const {
462                 LOC_LOGD("HandleConnectBackhaul");
463                 mFwkActionReqObj->connectBackhaul();
464             }
465             IFrameworkActionReq* mFwkActionReqObj;
466         };
467         mContext.mMsgTask->sendMsg(
468                 new (nothrow) HandleConnectBackhaul(mContext.mFrameworkActionReqObj));
469         result = true;
470     }
471     else {
472         ++mBackHaulConnectReqCount;
473         LOC_LOGE("Framework action request object is NULL.Caching connect request: %d",
474                         mBackHaulConnectReqCount);
475         result = false;
476     }
477     return result;
478 
479 }
480 
disconnectBackhaul()481 bool SystemStatusOsObserver::disconnectBackhaul()
482 {
483     bool result = false;
484 
485     if (mContext.mFrameworkActionReqObj != NULL) {
486         struct HandleDisconnectBackhaul : public LocMsg {
487             HandleDisconnectBackhaul(IFrameworkActionReq* fwkActReq) :
488                     mFwkActionReqObj(fwkActReq) {}
489             virtual ~HandleDisconnectBackhaul() {}
490             void proc() const {
491                 LOC_LOGD("HandleDisconnectBackhaul");
492                 mFwkActionReqObj->disconnectBackhaul();
493             }
494             IFrameworkActionReq* mFwkActionReqObj;
495         };
496         mContext.mMsgTask->sendMsg(
497                 new (nothrow) HandleDisconnectBackhaul(mContext.mFrameworkActionReqObj));
498     }
499     else {
500         if (mBackHaulConnectReqCount > 0) {
501             --mBackHaulConnectReqCount;
502         }
503         LOC_LOGE("Framework action request object is NULL.Caching disconnect request: %d",
504                         mBackHaulConnectReqCount);
505         result = false;
506     }
507     return result;
508 }
509 #endif
510 /******************************************************************************
511  Helpers
512 ******************************************************************************/
sendCachedDataItems(const unordered_set<DataItemId> & s,IDataItemObserver * to)513 void SystemStatusOsObserver::sendCachedDataItems(
514         const unordered_set<DataItemId>& s, IDataItemObserver* to)
515 {
516     if (nullptr == to) {
517         LOC_LOGv("client pointer is NULL.");
518     } else {
519         string clientName;
520         to->getName(clientName);
521         list<IDataItemCore*> dataItems(0);
522 
523         for (auto each : s) {
524             auto citer = mDataItemCache.find(each);
525             if (citer != mDataItemCache.end()) {
526                 string dv;
527                 citer->second->stringify(dv);
528                 LOC_LOGI("DataItem: %s >> %s", dv.c_str(), clientName.c_str());
529                 dataItems.push_front(citer->second);
530             }
531         }
532 
533         if (dataItems.empty()) {
534             LOC_LOGv("No items to notify.");
535         } else {
536             to->notify(dataItems);
537         }
538     }
539 }
540 
updateCache(IDataItemCore * d)541 bool SystemStatusOsObserver::updateCache(IDataItemCore* d)
542 {
543     bool dataItemUpdated = false;
544 
545     // Request systemstatus to record this dataitem in its cache
546     // if the return is false, it means that SystemStatus is not
547     // handling it, so SystemStatusOsObserver also doesn't.
548     // So it has to be true to proceed.
549     if (nullptr != d && mSystemStatus->eventDataItemNotify(d)) {
550         auto citer = mDataItemCache.find(d->getId());
551         if (citer == mDataItemCache.end()) {
552             // New data item; not found in cache
553             IDataItemCore* dataitem = DataItemsFactoryProxy::createNewDataItem(d->getId());
554             if (nullptr != dataitem) {
555                 // Copy the contents of the data item
556                 dataitem->copy(d);
557                 // Insert in mDataItemCache
558                 mDataItemCache.insert(std::make_pair(d->getId(), dataitem));
559                 dataItemUpdated = true;
560             }
561         } else {
562             // Found in cache; Update cache if necessary
563             citer->second->copy(d, &dataItemUpdated);
564         }
565 
566         if (dataItemUpdated) {
567             LOC_LOGV("DataItem:%d updated:%d", d->getId(), dataItemUpdated);
568         }
569     }
570 
571     return dataItemUpdated;
572 }
573 
574 } // namespace loc_core
575 
576