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 // A device-activity aggregation entry expires after two days (172800 seconds)
27 static const int kDurationToKeepDeviceActivityEntrySecs = 172800;
28 // A transient device-activity aggregation entry is defined as an entry with very few Byte count
29 // (200 Bytes, this is about the size of 5 advertising packets) over a period of time (15 minutes)
30 static const int kByteCountTransientDeviceActivityEntry = 200;
31 static const int kDurationTransientDeviceActivityEntrySecs = 900;
32 static const int kMapSizeTrimDownAggregationEntry = 200;
33
OnBtaaPackets(std::vector<BtaaHciPacket> btaa_packets)34 void AttributionProcessor::OnBtaaPackets(std::vector<BtaaHciPacket> btaa_packets) {
35 AddressActivityKey key;
36
37 for (auto& btaa_packet : btaa_packets) {
38 key.address = btaa_packet.address;
39 key.activity = btaa_packet.activity;
40
41 if (wakelock_duration_aggregator_.find(key) == wakelock_duration_aggregator_.end()) {
42 wakelock_duration_aggregator_[key] = {};
43 }
44 wakelock_duration_aggregator_[key].byte_count += btaa_packet.byte_count;
45
46 if (wakeup_) {
47 wakelock_duration_aggregator_[key].wakeup_count += 1;
48 wakeup_aggregator_.Push(std::move(WakeupDescriptor(btaa_packet.activity, btaa_packet.address)));
49 }
50 }
51 wakeup_ = false;
52 }
53
OnWakelockReleased(uint32_t duration_ms)54 void AttributionProcessor::OnWakelockReleased(uint32_t duration_ms) {
55 uint32_t total_byte_count = 0;
56 uint32_t ms_per_byte = 0;
57
58 for (auto& it : wakelock_duration_aggregator_) {
59 total_byte_count += it.second.byte_count;
60 }
61
62 if (total_byte_count == 0) {
63 return;
64 }
65
66 ms_per_byte = duration_ms / total_byte_count;
67 auto cur_time = std::chrono::system_clock::now();
68 for (auto& it : wakelock_duration_aggregator_) {
69 it.second.wakelock_duration_ms = ms_per_byte * it.second.byte_count;
70 if (btaa_aggregator_.find(it.first) == btaa_aggregator_.end()) {
71 btaa_aggregator_[it.first] = {};
72 btaa_aggregator_[it.first].creation_time = cur_time;
73 }
74
75 auto elapsed_time_sec =
76 std::chrono::duration_cast<std::chrono::seconds>(cur_time - btaa_aggregator_[it.first].creation_time).count();
77 if (elapsed_time_sec > kDurationToKeepDeviceActivityEntrySecs) {
78 btaa_aggregator_[it.first].wakeup_count = 0;
79 btaa_aggregator_[it.first].byte_count = 0;
80 btaa_aggregator_[it.first].wakelock_duration_ms = 0;
81 btaa_aggregator_[it.first].creation_time = cur_time;
82 }
83
84 btaa_aggregator_[it.first].wakeup_count += it.second.wakeup_count;
85 btaa_aggregator_[it.first].byte_count += it.second.byte_count;
86 btaa_aggregator_[it.first].wakelock_duration_ms += it.second.wakelock_duration_ms;
87 }
88 wakelock_duration_aggregator_.clear();
89
90 if (btaa_aggregator_.size() < kMapSizeTrimDownAggregationEntry) {
91 return;
92 }
93 // Trim down the transient entries in the aggregator to avoid that it overgrows
94 for (auto& it : btaa_aggregator_) {
95 auto elapsed_time_sec =
96 std::chrono::duration_cast<std::chrono::seconds>(cur_time - it.second.creation_time).count();
97 if (elapsed_time_sec > kDurationTransientDeviceActivityEntrySecs &&
98 it.second.byte_count < kByteCountTransientDeviceActivityEntry) {
99 btaa_aggregator_.erase(it.first);
100 }
101 }
102 }
103
OnWakeup()104 void AttributionProcessor::OnWakeup() {
105 if (wakeup_) {
106 LOG_INFO("Previous wakeup notification is not consumed.");
107 }
108 wakeup_ = true;
109 }
110
Dump(std::promise<flatbuffers::Offset<ActivityAttributionData>> promise,flatbuffers::FlatBufferBuilder * fb_builder)111 void AttributionProcessor::Dump(
112 std::promise<flatbuffers::Offset<ActivityAttributionData>> promise, flatbuffers::FlatBufferBuilder* fb_builder) {
113 // Dump wakeup attribution data
114 auto title_wakeup = fb_builder->CreateString("----- Wakeup Attribution Dumpsys -----");
115 std::vector<common::TimestampedEntry<WakeupDescriptor>> wakeup_aggregator = wakeup_aggregator_.Pull();
116 std::vector<flatbuffers::Offset<WakeupEntry>> wakeup_entry_offsets;
117 for (auto& it : wakeup_aggregator) {
118 WakeupEntryBuilder wakeup_entry_builder(*fb_builder);
119 std::chrono::milliseconds duration(it.timestamp);
120 std::chrono::time_point<std::chrono::system_clock> wakeup_time(duration);
121 wakeup_entry_builder.add_wakeup_time(fb_builder->CreateString(
122 bluetooth::common::StringFormatTimeWithMilliseconds(kActivityAttributionTimeFormat, wakeup_time).c_str()));
123 wakeup_entry_builder.add_activity(fb_builder->CreateString((ActivityToString(it.entry.activity_))));
124 wakeup_entry_builder.add_address(fb_builder->CreateString(it.entry.address_.ToString()));
125 wakeup_entry_offsets.push_back(wakeup_entry_builder.Finish());
126 }
127 auto wakeup_entries = fb_builder->CreateVector(wakeup_entry_offsets);
128
129 // Dump device-based activity aggregation data
130 auto title_device_activity = fb_builder->CreateString("----- Device-based Activity Attribution Dumpsys -----");
131 std::vector<flatbuffers::Offset<DeviceActivityAggregationEntry>> aggregation_entry_offsets;
132 for (auto& it : btaa_aggregator_) {
133 DeviceActivityAggregationEntryBuilder device_entry_builder(*fb_builder);
134 device_entry_builder.add_address(fb_builder->CreateString(it.first.address.ToString()));
135 device_entry_builder.add_activity(fb_builder->CreateString((ActivityToString(it.first.activity))));
136 device_entry_builder.add_wakeup_count(it.second.wakeup_count);
137 device_entry_builder.add_byte_count(it.second.byte_count);
138 device_entry_builder.add_wakelock_duration_ms(it.second.wakelock_duration_ms);
139 device_entry_builder.add_creation_time(fb_builder->CreateString(
140 bluetooth::common::StringFormatTimeWithMilliseconds(kActivityAttributionTimeFormat, it.second.creation_time)
141 .c_str()));
142 aggregation_entry_offsets.push_back(device_entry_builder.Finish());
143 }
144 auto aggregation_entries = fb_builder->CreateVector(aggregation_entry_offsets);
145
146 ActivityAttributionDataBuilder builder(*fb_builder);
147 builder.add_title_wakeup(title_wakeup);
148 builder.add_num_wakeup(wakeup_aggregator.size());
149 builder.add_wakeup_attribution(wakeup_entries);
150 builder.add_title_activity(title_device_activity);
151 builder.add_num_device_activity(btaa_aggregator_.size());
152 builder.add_device_activity_aggregation(aggregation_entries);
153 btaa_aggregator_.clear();
154
155 flatbuffers::Offset<ActivityAttributionData> dumpsys_data = builder.Finish();
156 promise.set_value(dumpsys_data);
157 }
158
159 #ifndef CASE_RETURN_TEXT
160 #define CASE_RETURN_TEXT(code) \
161 case code: \
162 return #code
163 #endif
164
ActivityToString(Activity activity)165 const char* AttributionProcessor::ActivityToString(Activity activity) {
166 switch (activity) {
167 CASE_RETURN_TEXT(Activity::ACL);
168 CASE_RETURN_TEXT(Activity::ADVERTISE);
169 CASE_RETURN_TEXT(Activity::CONNECT);
170 CASE_RETURN_TEXT(Activity::CONTROL);
171 CASE_RETURN_TEXT(Activity::HFP);
172 CASE_RETURN_TEXT(Activity::ISO);
173 CASE_RETURN_TEXT(Activity::SCAN);
174 CASE_RETURN_TEXT(Activity::VENDOR);
175 default:
176 return "UNKNOWN";
177 }
178 }
179
180 } // namespace activity_attribution
181 } // namespace bluetooth
182