1 /* Copyright (c) 2015-2017, 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 IF_LOC_LOGD {
351 string dv;
352 each->stringify(dv);
353 LOC_LOGD("notify: DataItem In Value:%s", dv.c_str());
354 }
355
356 IDataItemCore* di = DataItemsFactoryProxy::createNewDataItem(each->getId());
357 if (nullptr == di) {
358 LOC_LOGw("Unable to create dataitem:%d", each->getId());
359 continue;
360 }
361
362 // Copy contents into the newly created data item
363 di->copy(each);
364
365 // add this dataitem if updated from last one
366 dataItemVec.push_back(di);
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