• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 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 #include "btaa/attribution_processor.h"
18 #include "common/strings.h"
19 
20 #include "os/log.h"
21 
22 namespace bluetooth {
23 namespace activity_attribution {
24 
25 constexpr char kActivityAttributionTimeFormat[] = "%Y-%m-%d %H:%M:%S";
26 static const std::string kUnknownPackageInfo = "UNKNOWN";
27 // A device-activity aggregation entry expires after two days (172800 seconds)
28 static const int kDurationToKeepDeviceActivityEntrySecs = 172800;
29 // A transient device-activity aggregation entry is defined as an entry with very few Byte count
30 // (200 Bytes, this is about the size of 5 advertising packets) over a period of time (15 minutes)
31 static const int kByteCountTransientDeviceActivityEntry = 200;
32 static const int kDurationTransientDeviceActivityEntrySecs = 900;
33 static const int kMapSizeTrimDownAggregationEntry = 200;
34 
OnBtaaPackets(std::vector<BtaaHciPacket> btaa_packets)35 void AttributionProcessor::OnBtaaPackets(std::vector<BtaaHciPacket> btaa_packets) {
36   AddressActivityKey key;
37 
38   for (auto& btaa_packet : btaa_packets) {
39     key.address = btaa_packet.address;
40     key.activity = btaa_packet.activity;
41 
42     if (wakelock_duration_aggregator_.find(key) == wakelock_duration_aggregator_.end()) {
43       wakelock_duration_aggregator_[key] = {};
44     }
45     wakelock_duration_aggregator_[key].byte_count += btaa_packet.byte_count;
46 
47     if (wakeup_) {
48       wakelock_duration_aggregator_[key].wakeup_count += 1;
49       device_wakeup_aggregator_.Push(std::move(DeviceWakeupDescriptor(btaa_packet.activity, btaa_packet.address)));
50       std::string package_info = kUnknownPackageInfo;
51       std::string address = btaa_packet.address.ToString();
52       if (address_app_map_.find(address) != address_app_map_.end()) {
53         package_info = address_app_map_[address];
54       }
55       app_wakeup_aggregator_.Push(std::move(AppWakeupDescriptor(btaa_packet.activity, package_info)));
56     }
57   }
58   wakeup_ = false;
59 }
60 
OnWakelockReleased(uint32_t duration_ms)61 void AttributionProcessor::OnWakelockReleased(uint32_t duration_ms) {
62   uint32_t total_byte_count = 0;
63 
64   for (auto& it : wakelock_duration_aggregator_) {
65     total_byte_count += it.second.byte_count;
66   }
67 
68   if (total_byte_count == 0) {
69     return;
70   }
71 
72   auto cur_time = now_func_();
73   for (auto& it : wakelock_duration_aggregator_) {
74     it.second.wakelock_duration_ms = (uint64_t)duration_ms * it.second.byte_count / total_byte_count;
75     if (btaa_aggregator_.find(it.first) == btaa_aggregator_.end()) {
76       btaa_aggregator_[it.first] = {};
77       btaa_aggregator_[it.first].creation_time = cur_time;
78     }
79 
80     auto elapsed_time_sec =
81         std::chrono::duration_cast<std::chrono::seconds>(cur_time - btaa_aggregator_[it.first].creation_time).count();
82     if (elapsed_time_sec > kDurationToKeepDeviceActivityEntrySecs) {
83       btaa_aggregator_[it.first].wakeup_count = 0;
84       btaa_aggregator_[it.first].byte_count = 0;
85       btaa_aggregator_[it.first].wakelock_duration_ms = 0;
86       btaa_aggregator_[it.first].creation_time = cur_time;
87     }
88 
89     btaa_aggregator_[it.first].wakeup_count += it.second.wakeup_count;
90     btaa_aggregator_[it.first].byte_count += it.second.byte_count;
91     btaa_aggregator_[it.first].wakelock_duration_ms += it.second.wakelock_duration_ms;
92 
93     std::string address = it.first.address.ToString();
94     std::string package_info = kUnknownPackageInfo;
95     if (address_app_map_.find(address) != address_app_map_.end()) {
96       package_info = address_app_map_[address];
97     }
98     AppActivityKey key;
99     key.app = package_info;
100     key.activity = it.first.activity;
101 
102     if (app_activity_aggregator_.find(key) == app_activity_aggregator_.end()) {
103       app_activity_aggregator_[key] = {};
104       app_activity_aggregator_[key].creation_time = cur_time;
105     }
106 
107     elapsed_time_sec =
108         std::chrono::duration_cast<std::chrono::seconds>(cur_time - app_activity_aggregator_[key].creation_time)
109             .count();
110     if (elapsed_time_sec > kDurationToKeepDeviceActivityEntrySecs) {
111       app_activity_aggregator_[key].wakeup_count = 0;
112       app_activity_aggregator_[key].byte_count = 0;
113       app_activity_aggregator_[key].wakelock_duration_ms = 0;
114       app_activity_aggregator_[key].creation_time = cur_time;
115     }
116 
117     app_activity_aggregator_[key].wakeup_count += it.second.wakeup_count;
118     app_activity_aggregator_[key].byte_count += it.second.byte_count;
119     app_activity_aggregator_[key].wakelock_duration_ms += it.second.wakelock_duration_ms;
120   }
121   wakelock_duration_aggregator_.clear();
122 
123   if (btaa_aggregator_.size() <= kMapSizeTrimDownAggregationEntry &&
124       app_activity_aggregator_.size() <= kMapSizeTrimDownAggregationEntry) {
125     return;
126   }
127   // Trim down the transient entries in the aggregator to avoid that it overgrows
128   if (btaa_aggregator_.size() > kMapSizeTrimDownAggregationEntry) {
129     auto it = btaa_aggregator_.begin();
130     while (it != btaa_aggregator_.end()) {
131       auto elapsed_time_sec =
132           std::chrono::duration_cast<std::chrono::seconds>(cur_time - it->second.creation_time).count();
133       if (elapsed_time_sec > kDurationTransientDeviceActivityEntrySecs &&
134           it->second.byte_count < kByteCountTransientDeviceActivityEntry) {
135         it = btaa_aggregator_.erase(it);
136       } else {
137         it++;
138       }
139     }
140   }
141 
142   if (app_activity_aggregator_.size() > kMapSizeTrimDownAggregationEntry) {
143     auto it = app_activity_aggregator_.begin();
144     while (it != app_activity_aggregator_.end()) {
145       auto elapsed_time_sec =
146           std::chrono::duration_cast<std::chrono::seconds>(cur_time - it->second.creation_time).count();
147       if (elapsed_time_sec > kDurationTransientDeviceActivityEntrySecs &&
148           it->second.byte_count < kByteCountTransientDeviceActivityEntry) {
149         it = app_activity_aggregator_.erase(it);
150       } else {
151         it++;
152       }
153     }
154   }
155 }
156 
OnWakeup()157 void AttributionProcessor::OnWakeup() {
158   if (wakeup_) {
159     LOG_INFO("Previous wakeup notification is not consumed.");
160   }
161   wakeup_ = true;
162 }
163 
NotifyActivityAttributionInfo(int uid,const std::string & package_name,const std::string & device_address)164 void AttributionProcessor::NotifyActivityAttributionInfo(
165     int uid, const std::string& package_name, const std::string& device_address) {
166   if (address_app_map_.size() > kMapSizeTrimDownAggregationEntry) {
167     LOG_INFO("The map from device address and app info overflows.");
168     return;
169   }
170   address_app_map_[device_address] = package_name + "/" + std::to_string(uid);
171 }
172 
Dump(std::promise<flatbuffers::Offset<ActivityAttributionData>> promise,flatbuffers::FlatBufferBuilder * fb_builder)173 void AttributionProcessor::Dump(
174     std::promise<flatbuffers::Offset<ActivityAttributionData>> promise, flatbuffers::FlatBufferBuilder* fb_builder) {
175   // Dump device-based wakeup attribution data
176   auto title_device_wakeup = fb_builder->CreateString("----- Device-based Wakeup Attribution Dumpsys -----");
177   std::vector<common::TimestampedEntry<DeviceWakeupDescriptor>> device_wakeup_aggregator =
178       device_wakeup_aggregator_.Pull();
179   std::vector<flatbuffers::Offset<WakeupEntry>> device_wakeup_entry_offsets;
180   for (auto& it : device_wakeup_aggregator) {
181     WakeupEntryBuilder wakeup_entry_builder(*fb_builder);
182     std::chrono::milliseconds duration(it.timestamp);
183     std::chrono::time_point<std::chrono::system_clock> wakeup_time(duration);
184     wakeup_entry_builder.add_wakeup_time(fb_builder->CreateString(
185         bluetooth::common::StringFormatTimeWithMilliseconds(kActivityAttributionTimeFormat, wakeup_time).c_str()));
186     wakeup_entry_builder.add_activity(fb_builder->CreateString((ActivityToString(it.entry.activity_))));
187     wakeup_entry_builder.add_address(fb_builder->CreateString(it.entry.address_.ToString()));
188     device_wakeup_entry_offsets.push_back(wakeup_entry_builder.Finish());
189   }
190   auto device_wakeup_entries = fb_builder->CreateVector(device_wakeup_entry_offsets);
191 
192   // Dump device-based activity aggregation data
193   auto title_device_activity = fb_builder->CreateString("----- Device-based Activity Attribution Dumpsys -----");
194   std::vector<flatbuffers::Offset<ActivityAggregationEntry>> device_aggregation_entry_offsets;
195   for (auto& it : btaa_aggregator_) {
196     ActivityAggregationEntryBuilder device_entry_builder(*fb_builder);
197     device_entry_builder.add_address(fb_builder->CreateString(it.first.address.ToString()));
198     device_entry_builder.add_activity(fb_builder->CreateString((ActivityToString(it.first.activity))));
199     device_entry_builder.add_wakeup_count(it.second.wakeup_count);
200     device_entry_builder.add_byte_count(it.second.byte_count);
201     device_entry_builder.add_wakelock_duration_ms(it.second.wakelock_duration_ms);
202     device_entry_builder.add_creation_time(fb_builder->CreateString(
203         bluetooth::common::StringFormatTimeWithMilliseconds(kActivityAttributionTimeFormat, it.second.creation_time)
204             .c_str()));
205     device_aggregation_entry_offsets.push_back(device_entry_builder.Finish());
206   }
207   auto device_aggregation_entries = fb_builder->CreateVector(device_aggregation_entry_offsets);
208 
209   // Dump App-based wakeup attribution data
210   auto title_app_wakeup = fb_builder->CreateString("----- App-based Wakeup Attribution Dumpsys -----");
211   std::vector<common::TimestampedEntry<AppWakeupDescriptor>> app_wakeup_aggregator = app_wakeup_aggregator_.Pull();
212   std::vector<flatbuffers::Offset<WakeupEntry>> app_wakeup_entry_offsets;
213   for (auto& it : app_wakeup_aggregator) {
214     WakeupEntryBuilder wakeup_entry_builder(*fb_builder);
215     std::chrono::milliseconds duration(it.timestamp);
216     std::chrono::time_point<std::chrono::system_clock> wakeup_time(duration);
217     wakeup_entry_builder.add_wakeup_time(fb_builder->CreateString(
218         bluetooth::common::StringFormatTimeWithMilliseconds(kActivityAttributionTimeFormat, wakeup_time).c_str()));
219     wakeup_entry_builder.add_activity(fb_builder->CreateString((ActivityToString(it.entry.activity_))));
220     wakeup_entry_builder.add_package_info(fb_builder->CreateString(it.entry.package_info_));
221     app_wakeup_entry_offsets.push_back(wakeup_entry_builder.Finish());
222   }
223   auto app_wakeup_entries = fb_builder->CreateVector(app_wakeup_entry_offsets);
224 
225   // Dump app-based activity aggregation data
226   auto title_app_activity = fb_builder->CreateString("----- App-based Activity Attribution Dumpsys -----");
227   std::vector<flatbuffers::Offset<ActivityAggregationEntry>> app_aggregation_entry_offsets;
228   for (auto& it : app_activity_aggregator_) {
229     ActivityAggregationEntryBuilder app_entry_builder(*fb_builder);
230     app_entry_builder.add_package_info(fb_builder->CreateString(it.first.app));
231     app_entry_builder.add_activity(fb_builder->CreateString((ActivityToString(it.first.activity))));
232     app_entry_builder.add_wakeup_count(it.second.wakeup_count);
233     app_entry_builder.add_byte_count(it.second.byte_count);
234     app_entry_builder.add_wakelock_duration_ms(it.second.wakelock_duration_ms);
235     app_entry_builder.add_creation_time(fb_builder->CreateString(
236         bluetooth::common::StringFormatTimeWithMilliseconds(kActivityAttributionTimeFormat, it.second.creation_time)
237             .c_str()));
238     app_aggregation_entry_offsets.push_back(app_entry_builder.Finish());
239   }
240   auto app_aggregation_entries = fb_builder->CreateVector(app_aggregation_entry_offsets);
241 
242   ActivityAttributionDataBuilder builder(*fb_builder);
243   builder.add_title_device_wakeup(title_device_wakeup);
244   builder.add_num_device_wakeup(device_wakeup_aggregator.size());
245   builder.add_device_wakeup_attribution(device_wakeup_entries);
246   builder.add_title_device_activity(title_device_activity);
247   builder.add_num_device_activity(btaa_aggregator_.size());
248   builder.add_device_activity_aggregation(device_aggregation_entries);
249   btaa_aggregator_.clear();
250 
251   builder.add_title_app_wakeup(title_app_wakeup);
252   builder.add_num_app_wakeup(app_wakeup_aggregator.size());
253   builder.add_app_wakeup_attribution(app_wakeup_entries);
254   builder.add_title_app_activity(title_app_activity);
255   builder.add_num_app_activity(app_activity_aggregator_.size());
256   builder.add_app_activity_aggregation(app_aggregation_entries);
257   app_activity_aggregator_.clear();
258 
259   flatbuffers::Offset<ActivityAttributionData> dumpsys_data = builder.Finish();
260   promise.set_value(dumpsys_data);
261 }
262 
263 #ifndef CASE_RETURN_TEXT
264 #define CASE_RETURN_TEXT(code) \
265   case code:                   \
266     return #code
267 #endif
268 
ActivityToString(Activity activity)269 const char* AttributionProcessor::ActivityToString(Activity activity) {
270   switch (activity) {
271     CASE_RETURN_TEXT(Activity::ACL);
272     CASE_RETURN_TEXT(Activity::ADVERTISE);
273     CASE_RETURN_TEXT(Activity::CONNECT);
274     CASE_RETURN_TEXT(Activity::CONTROL);
275     CASE_RETURN_TEXT(Activity::HFP);
276     CASE_RETURN_TEXT(Activity::ISO);
277     CASE_RETURN_TEXT(Activity::SCAN);
278     CASE_RETURN_TEXT(Activity::VENDOR);
279     default:
280       return "UNKNOWN";
281   }
282 }
283 
284 }  // namespace activity_attribution
285 }  // namespace bluetooth
286