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