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 STATSD_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