1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define DEBUG false // STOPSHIP if true
18 #include "Log.h"
19
20 #include "SubscriberReporter.h"
21
22 using std::lock_guard;
23
24 namespace android {
25 namespace os {
26 namespace statsd {
27
28 using std::vector;
29
broadcastSubscriberDied(void * rawPir)30 void SubscriberReporter::broadcastSubscriberDied(void* rawPir) {
31 SubscriberReporter& thiz = getInstance();
32
33 // Erase the mapping from a (config_key, subscriberId) to a pir if the
34 // mapping exists. This requires iterating over the map, but this operation
35 // should be rare and the map is expected to be small.
36 lock_guard<mutex> lock(thiz.mLock);
37 for (auto subscriberMapIt = thiz.mIntentMap.begin(); subscriberMapIt != thiz.mIntentMap.end();
38 subscriberMapIt++) {
39 unordered_map<int64_t, shared_ptr<IPendingIntentRef>>& subscriberMap =
40 subscriberMapIt->second;
41 for (auto pirIt = subscriberMap.begin(); pirIt != subscriberMap.end(); pirIt++) {
42 if (pirIt->second.get() == rawPir) {
43 subscriberMap.erase(pirIt);
44 if (subscriberMap.empty()) {
45 thiz.mIntentMap.erase(subscriberMapIt);
46 }
47 // pirIt and subscriberMapIt are now invalid.
48 return;
49 }
50 }
51 }
52 }
53
SubscriberReporter()54 SubscriberReporter::SubscriberReporter() :
55 mBroadcastSubscriberDeathRecipient(AIBinder_DeathRecipient_new(broadcastSubscriberDied)) {
56 }
57
setBroadcastSubscriber(const ConfigKey & configKey,int64_t subscriberId,const shared_ptr<IPendingIntentRef> & pir)58 void SubscriberReporter::setBroadcastSubscriber(const ConfigKey& configKey,
59 int64_t subscriberId,
60 const shared_ptr<IPendingIntentRef>& pir) {
61 VLOG("SubscriberReporter::setBroadcastSubscriber called with configKey %s and subscriberId "
62 "%lld.",
63 configKey.ToString().c_str(), (long long)subscriberId);
64 {
65 lock_guard<mutex> lock(mLock);
66 mIntentMap[configKey][subscriberId] = pir;
67 }
68 // Pass the raw binder pointer address to be the cookie of the death recipient. While the death
69 // notification is fired, the cookie is used for identifying which binder was died. Because
70 // the NDK binder doesn't pass dead binder pointer to binder death handler, the binder death
71 // handler can't know who died.
72 // If a dedicated cookie is used to store metadata (config key, subscriber id) for direct
73 // lookup, a data structure is needed manage the cookies.
74 AIBinder_linkToDeath(pir->asBinder().get(), mBroadcastSubscriberDeathRecipient.get(),
75 pir.get());
76 }
77
unsetBroadcastSubscriber(const ConfigKey & configKey,int64_t subscriberId)78 void SubscriberReporter::unsetBroadcastSubscriber(const ConfigKey& configKey,
79 int64_t subscriberId) {
80 VLOG("SubscriberReporter::unsetBroadcastSubscriber called.");
81 lock_guard<mutex> lock(mLock);
82 auto subscriberMapIt = mIntentMap.find(configKey);
83 if (subscriberMapIt != mIntentMap.end()) {
84 subscriberMapIt->second.erase(subscriberId);
85 if (subscriberMapIt->second.empty()) {
86 mIntentMap.erase(configKey);
87 }
88 }
89 }
90
alertBroadcastSubscriber(const ConfigKey & configKey,const Subscription & subscription,const MetricDimensionKey & dimKey) const91 void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey,
92 const Subscription& subscription,
93 const MetricDimensionKey& dimKey) const {
94 // Reminder about ids:
95 // subscription id - name of the Subscription (that ties the Alert to the broadcast)
96 // subscription rule_id - the name of the Alert (that triggers the broadcast)
97 // subscriber_id - name of the PendingIntent to use to send the broadcast
98 // config uid - the uid that uploaded the config (and therefore gave the PendingIntent,
99 // although the intent may be to broadcast to a different uid)
100 // config id - the name of this config (for this particular uid)
101
102 VLOG("SubscriberReporter::alertBroadcastSubscriber called.");
103 lock_guard<mutex> lock(mLock);
104
105 if (!subscription.has_broadcast_subscriber_details()
106 || !subscription.broadcast_subscriber_details().has_subscriber_id()) {
107 ALOGE("Broadcast subscriber does not have an id.");
108 return;
109 }
110 int64_t subscriberId = subscription.broadcast_subscriber_details().subscriber_id();
111
112 vector<string> cookies;
113 cookies.reserve(subscription.broadcast_subscriber_details().cookie_size());
114 for (auto& cookie : subscription.broadcast_subscriber_details().cookie()) {
115 cookies.push_back(cookie);
116 }
117
118 auto it1 = mIntentMap.find(configKey);
119 if (it1 == mIntentMap.end()) {
120 ALOGW("Cannot inform subscriber for missing config key %s ", configKey.ToString().c_str());
121 return;
122 }
123 auto it2 = it1->second.find(subscriberId);
124 if (it2 == it1->second.end()) {
125 ALOGW("Cannot inform subscriber of config %s for missing subscriberId %lld ",
126 configKey.ToString().c_str(), (long long)subscriberId);
127 return;
128 }
129 sendBroadcastLocked(it2->second, configKey, subscription, cookies, dimKey);
130 }
131
sendBroadcastLocked(const shared_ptr<IPendingIntentRef> & pir,const ConfigKey & configKey,const Subscription & subscription,const vector<string> & cookies,const MetricDimensionKey & dimKey) const132 void SubscriberReporter::sendBroadcastLocked(const shared_ptr<IPendingIntentRef>& pir,
133 const ConfigKey& configKey,
134 const Subscription& subscription,
135 const vector<string>& cookies,
136 const MetricDimensionKey& dimKey) const {
137 VLOG("SubscriberReporter::sendBroadcastLocked called.");
138 pir->sendSubscriberBroadcast(
139 configKey.GetUid(),
140 configKey.GetId(),
141 subscription.id(),
142 subscription.rule_id(),
143 cookies,
144 dimKey.getDimensionKeyInWhat().toStatsDimensionsValueParcel());
145 }
146
147 } // namespace statsd
148 } // namespace os
149 } // namespace android
150