1 /*
2 * Copyright (C) 2017 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 "metrics_manager_util.h"
21
22 #include <inttypes.h>
23
24 #include "FieldValue.h"
25 #include "MetricProducer.h"
26 #include "condition/CombinationConditionTracker.h"
27 #include "condition/SimpleConditionTracker.h"
28 #include "external/StatsPullerManager.h"
29 #include "matchers/CombinationLogMatchingTracker.h"
30 #include "matchers/EventMatcherWizard.h"
31 #include "matchers/SimpleLogMatchingTracker.h"
32 #include "metrics/CountMetricProducer.h"
33 #include "metrics/DurationMetricProducer.h"
34 #include "metrics/EventMetricProducer.h"
35 #include "metrics/GaugeMetricProducer.h"
36 #include "metrics/ValueMetricProducer.h"
37 #include "state/StateManager.h"
38 #include "stats_util.h"
39
40 using std::set;
41 using std::unordered_map;
42 using std::vector;
43
44 namespace android {
45 namespace os {
46 namespace statsd {
47
48 namespace {
49
hasLeafNode(const FieldMatcher & matcher)50 bool hasLeafNode(const FieldMatcher& matcher) {
51 if (!matcher.has_field()) {
52 return false;
53 }
54 for (int i = 0; i < matcher.child_size(); ++i) {
55 if (hasLeafNode(matcher.child(i))) {
56 return true;
57 }
58 }
59 return true;
60 }
61
62 } // namespace
63
handleMetricWithLogTrackers(const int64_t what,const int metricIndex,const bool usedForDimension,const vector<sp<LogMatchingTracker>> & allAtomMatchers,const unordered_map<int64_t,int> & logTrackerMap,unordered_map<int,std::vector<int>> & trackerToMetricMap,int & logTrackerIndex)64 bool handleMetricWithLogTrackers(const int64_t what, const int metricIndex,
65 const bool usedForDimension,
66 const vector<sp<LogMatchingTracker>>& allAtomMatchers,
67 const unordered_map<int64_t, int>& logTrackerMap,
68 unordered_map<int, std::vector<int>>& trackerToMetricMap,
69 int& logTrackerIndex) {
70 auto logTrackerIt = logTrackerMap.find(what);
71 if (logTrackerIt == logTrackerMap.end()) {
72 ALOGW("cannot find the AtomMatcher \"%lld\" in config", (long long)what);
73 return false;
74 }
75 if (usedForDimension && allAtomMatchers[logTrackerIt->second]->getAtomIds().size() > 1) {
76 ALOGE("AtomMatcher \"%lld\" has more than one tag ids. When a metric has dimension, "
77 "the \"what\" can only about one atom type.",
78 (long long)what);
79 return false;
80 }
81 logTrackerIndex = logTrackerIt->second;
82 auto& metric_list = trackerToMetricMap[logTrackerIndex];
83 metric_list.push_back(metricIndex);
84 return true;
85 }
86
handlePullMetricTriggerWithLogTrackers(const int64_t trigger,const int metricIndex,const vector<sp<LogMatchingTracker>> & allAtomMatchers,const unordered_map<int64_t,int> & logTrackerMap,unordered_map<int,std::vector<int>> & trackerToMetricMap,int & logTrackerIndex)87 bool handlePullMetricTriggerWithLogTrackers(
88 const int64_t trigger, const int metricIndex,
89 const vector<sp<LogMatchingTracker>>& allAtomMatchers,
90 const unordered_map<int64_t, int>& logTrackerMap,
91 unordered_map<int, std::vector<int>>& trackerToMetricMap, int& logTrackerIndex) {
92 auto logTrackerIt = logTrackerMap.find(trigger);
93 if (logTrackerIt == logTrackerMap.end()) {
94 ALOGW("cannot find the AtomMatcher \"%lld\" in config", (long long)trigger);
95 return false;
96 }
97 if (allAtomMatchers[logTrackerIt->second]->getAtomIds().size() > 1) {
98 ALOGE("AtomMatcher \"%lld\" has more than one tag ids."
99 "Trigger can only be one atom type.",
100 (long long)trigger);
101 return false;
102 }
103 logTrackerIndex = logTrackerIt->second;
104 auto& metric_list = trackerToMetricMap[logTrackerIndex];
105 metric_list.push_back(metricIndex);
106 return true;
107 }
108
handleMetricWithConditions(const int64_t condition,const int metricIndex,const unordered_map<int64_t,int> & conditionTrackerMap,const::google::protobuf::RepeatedPtrField<::android::os::statsd::MetricConditionLink> & links,vector<sp<ConditionTracker>> & allConditionTrackers,int & conditionIndex,unordered_map<int,std::vector<int>> & conditionToMetricMap)109 bool handleMetricWithConditions(
110 const int64_t condition, const int metricIndex,
111 const unordered_map<int64_t, int>& conditionTrackerMap,
112 const ::google::protobuf::RepeatedPtrField<::android::os::statsd::MetricConditionLink>&
113 links,
114 vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex,
115 unordered_map<int, std::vector<int>>& conditionToMetricMap) {
116 auto condition_it = conditionTrackerMap.find(condition);
117 if (condition_it == conditionTrackerMap.end()) {
118 ALOGW("cannot find Predicate \"%lld\" in the config", (long long)condition);
119 return false;
120 }
121
122 for (const auto& link : links) {
123 auto it = conditionTrackerMap.find(link.condition());
124 if (it == conditionTrackerMap.end()) {
125 ALOGW("cannot find Predicate \"%lld\" in the config", (long long)link.condition());
126 return false;
127 }
128 allConditionTrackers[condition_it->second]->setSliced(true);
129 allConditionTrackers[it->second]->setSliced(true);
130 }
131 conditionIndex = condition_it->second;
132
133 // will create new vector if not exist before.
134 auto& metricList = conditionToMetricMap[condition_it->second];
135 metricList.push_back(metricIndex);
136 return true;
137 }
138
139 // Initializes state data structures for a metric.
140 // input:
141 // [config]: the input config
142 // [stateIds]: the slice_by_state ids for this metric
143 // [stateAtomIdMap]: this map contains the mapping from all state ids to atom ids
144 // [allStateGroupMaps]: this map contains the mapping from state ids and state
145 // values to state group ids for all states
146 // output:
147 // [slicedStateAtoms]: a vector of atom ids of all the slice_by_states
148 // [stateGroupMap]: this map should contain the mapping from states ids and state
149 // values to state group ids for all states that this metric
150 // is interested in
handleMetricWithStates(const StatsdConfig & config,const::google::protobuf::RepeatedField<int64_t> & stateIds,const unordered_map<int64_t,int> & stateAtomIdMap,const unordered_map<int64_t,unordered_map<int,int64_t>> & allStateGroupMaps,vector<int> & slicedStateAtoms,unordered_map<int,unordered_map<int,int64_t>> & stateGroupMap)151 bool handleMetricWithStates(
152 const StatsdConfig& config, const ::google::protobuf::RepeatedField<int64_t>& stateIds,
153 const unordered_map<int64_t, int>& stateAtomIdMap,
154 const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
155 vector<int>& slicedStateAtoms,
156 unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) {
157 for (const auto& stateId : stateIds) {
158 auto it = stateAtomIdMap.find(stateId);
159 if (it == stateAtomIdMap.end()) {
160 ALOGW("cannot find State %" PRId64 " in the config", stateId);
161 return false;
162 }
163 int atomId = it->second;
164 slicedStateAtoms.push_back(atomId);
165
166 auto stateIt = allStateGroupMaps.find(stateId);
167 if (stateIt != allStateGroupMaps.end()) {
168 stateGroupMap[atomId] = stateIt->second;
169 }
170 }
171 return true;
172 }
173
handleMetricWithStateLink(const FieldMatcher & stateMatcher,const vector<Matcher> & dimensionsInWhat)174 bool handleMetricWithStateLink(const FieldMatcher& stateMatcher,
175 const vector<Matcher>& dimensionsInWhat) {
176 vector<Matcher> stateMatchers;
177 translateFieldMatcher(stateMatcher, &stateMatchers);
178
179 return subsetDimensions(stateMatchers, dimensionsInWhat);
180 }
181
182 // Validates a metricActivation and populates state.
183 // EventActivationMap and EventDeactivationMap are supplied to a MetricProducer
184 // to provide the producer with state about its activators and deactivators.
185 // Returns false if there are errors.
handleMetricActivation(const StatsdConfig & config,const int64_t metricId,const int metricIndex,const unordered_map<int64_t,int> & metricToActivationMap,const unordered_map<int64_t,int> & logTrackerMap,unordered_map<int,vector<int>> & activationAtomTrackerToMetricMap,unordered_map<int,vector<int>> & deactivationAtomTrackerToMetricMap,vector<int> & metricsWithActivation,unordered_map<int,shared_ptr<Activation>> & eventActivationMap,unordered_map<int,vector<shared_ptr<Activation>>> & eventDeactivationMap)186 bool handleMetricActivation(
187 const StatsdConfig& config,
188 const int64_t metricId,
189 const int metricIndex,
190 const unordered_map<int64_t, int>& metricToActivationMap,
191 const unordered_map<int64_t, int>& logTrackerMap,
192 unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
193 unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
194 vector<int>& metricsWithActivation,
195 unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
196 unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) {
197 // Check if metric has an associated activation
198 auto itr = metricToActivationMap.find(metricId);
199 if (itr == metricToActivationMap.end()) return true;
200
201 int activationIndex = itr->second;
202 const MetricActivation& metricActivation = config.metric_activation(activationIndex);
203
204 for (int i = 0; i < metricActivation.event_activation_size(); i++) {
205 const EventActivation& activation = metricActivation.event_activation(i);
206
207 auto itr = logTrackerMap.find(activation.atom_matcher_id());
208 if (itr == logTrackerMap.end()) {
209 ALOGE("Atom matcher not found for event activation.");
210 return false;
211 }
212
213 ActivationType activationType = (activation.has_activation_type()) ?
214 activation.activation_type() : metricActivation.activation_type();
215 std::shared_ptr<Activation> activationWrapper = std::make_shared<Activation>(
216 activationType, activation.ttl_seconds() * NS_PER_SEC);
217
218 int atomMatcherIndex = itr->second;
219 activationAtomTrackerToMetricMap[atomMatcherIndex].push_back(metricIndex);
220 eventActivationMap.emplace(atomMatcherIndex, activationWrapper);
221
222 if (activation.has_deactivation_atom_matcher_id()) {
223 itr = logTrackerMap.find(activation.deactivation_atom_matcher_id());
224 if (itr == logTrackerMap.end()) {
225 ALOGE("Atom matcher not found for event deactivation.");
226 return false;
227 }
228 int deactivationAtomMatcherIndex = itr->second;
229 deactivationAtomTrackerToMetricMap[deactivationAtomMatcherIndex].push_back(metricIndex);
230 eventDeactivationMap[deactivationAtomMatcherIndex].push_back(activationWrapper);
231 }
232 }
233
234 metricsWithActivation.push_back(metricIndex);
235 return true;
236 }
237
initLogTrackers(const StatsdConfig & config,const UidMap & uidMap,unordered_map<int64_t,int> & logTrackerMap,vector<sp<LogMatchingTracker>> & allAtomMatchers,set<int> & allTagIds)238 bool initLogTrackers(const StatsdConfig& config, const UidMap& uidMap,
239 unordered_map<int64_t, int>& logTrackerMap,
240 vector<sp<LogMatchingTracker>>& allAtomMatchers, set<int>& allTagIds) {
241 vector<AtomMatcher> matcherConfigs;
242 const int atomMatcherCount = config.atom_matcher_size();
243 matcherConfigs.reserve(atomMatcherCount);
244 allAtomMatchers.reserve(atomMatcherCount);
245
246 for (int i = 0; i < atomMatcherCount; i++) {
247 const AtomMatcher& logMatcher = config.atom_matcher(i);
248
249 int index = allAtomMatchers.size();
250 switch (logMatcher.contents_case()) {
251 case AtomMatcher::ContentsCase::kSimpleAtomMatcher:
252 allAtomMatchers.push_back(new SimpleLogMatchingTracker(
253 logMatcher.id(), index, logMatcher.simple_atom_matcher(), uidMap));
254 break;
255 case AtomMatcher::ContentsCase::kCombination:
256 allAtomMatchers.push_back(
257 new CombinationLogMatchingTracker(logMatcher.id(), index));
258 break;
259 default:
260 ALOGE("Matcher \"%lld\" malformed", (long long)logMatcher.id());
261 return false;
262 // continue;
263 }
264 if (logTrackerMap.find(logMatcher.id()) != logTrackerMap.end()) {
265 ALOGE("Duplicate AtomMatcher found!");
266 return false;
267 }
268 logTrackerMap[logMatcher.id()] = index;
269 matcherConfigs.push_back(logMatcher);
270 }
271
272 vector<bool> stackTracker2(allAtomMatchers.size(), false);
273 for (auto& matcher : allAtomMatchers) {
274 if (!matcher->init(matcherConfigs, allAtomMatchers, logTrackerMap, stackTracker2)) {
275 return false;
276 }
277 // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only.
278 const set<int>& tagIds = matcher->getAtomIds();
279 allTagIds.insert(tagIds.begin(), tagIds.end());
280 }
281 return true;
282 }
283
initConditions(const ConfigKey & key,const StatsdConfig & config,const unordered_map<int64_t,int> & logTrackerMap,unordered_map<int64_t,int> & conditionTrackerMap,vector<sp<ConditionTracker>> & allConditionTrackers,unordered_map<int,std::vector<int>> & trackerToConditionMap,vector<ConditionState> & initialConditionCache)284 bool initConditions(const ConfigKey& key, const StatsdConfig& config,
285 const unordered_map<int64_t, int>& logTrackerMap,
286 unordered_map<int64_t, int>& conditionTrackerMap,
287 vector<sp<ConditionTracker>>& allConditionTrackers,
288 unordered_map<int, std::vector<int>>& trackerToConditionMap,
289 vector<ConditionState>& initialConditionCache) {
290 vector<Predicate> conditionConfigs;
291 const int conditionTrackerCount = config.predicate_size();
292 conditionConfigs.reserve(conditionTrackerCount);
293 allConditionTrackers.reserve(conditionTrackerCount);
294 initialConditionCache.reserve(conditionTrackerCount);
295 std::fill(initialConditionCache.begin(), initialConditionCache.end(), ConditionState::kUnknown);
296
297 for (int i = 0; i < conditionTrackerCount; i++) {
298 const Predicate& condition = config.predicate(i);
299 int index = allConditionTrackers.size();
300 switch (condition.contents_case()) {
301 case Predicate::ContentsCase::kSimplePredicate: {
302 allConditionTrackers.push_back(new SimpleConditionTracker(
303 key, condition.id(), index, condition.simple_predicate(), logTrackerMap));
304 break;
305 }
306 case Predicate::ContentsCase::kCombination: {
307 allConditionTrackers.push_back(
308 new CombinationConditionTracker(condition.id(), index));
309 break;
310 }
311 default:
312 ALOGE("Predicate \"%lld\" malformed", (long long)condition.id());
313 return false;
314 }
315 if (conditionTrackerMap.find(condition.id()) != conditionTrackerMap.end()) {
316 ALOGE("Duplicate Predicate found!");
317 return false;
318 }
319 conditionTrackerMap[condition.id()] = index;
320 conditionConfigs.push_back(condition);
321 }
322
323 vector<bool> stackTracker(allConditionTrackers.size(), false);
324 for (size_t i = 0; i < allConditionTrackers.size(); i++) {
325 auto& conditionTracker = allConditionTrackers[i];
326 if (!conditionTracker->init(conditionConfigs, allConditionTrackers, conditionTrackerMap,
327 stackTracker, initialConditionCache)) {
328 return false;
329 }
330 for (const int trackerIndex : conditionTracker->getLogTrackerIndex()) {
331 auto& conditionList = trackerToConditionMap[trackerIndex];
332 conditionList.push_back(i);
333 }
334 }
335 return true;
336 }
337
initStates(const StatsdConfig & config,unordered_map<int64_t,int> & stateAtomIdMap,unordered_map<int64_t,unordered_map<int,int64_t>> & allStateGroupMaps)338 bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
339 unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps) {
340 for (int i = 0; i < config.state_size(); i++) {
341 const State& state = config.state(i);
342 const int64_t stateId = state.id();
343 stateAtomIdMap[stateId] = state.atom_id();
344
345 const StateMap& stateMap = state.map();
346 for (auto group : stateMap.group()) {
347 for (auto value : group.value()) {
348 allStateGroupMaps[stateId][value] = group.group_id();
349 }
350 }
351 }
352
353 return true;
354 }
355
initMetrics(const ConfigKey & key,const StatsdConfig & config,const int64_t timeBaseTimeNs,const int64_t currentTimeNs,const sp<StatsPullerManager> & pullerManager,const unordered_map<int64_t,int> & logTrackerMap,const unordered_map<int64_t,int> & conditionTrackerMap,const vector<sp<LogMatchingTracker>> & allAtomMatchers,const unordered_map<int64_t,int> & stateAtomIdMap,const unordered_map<int64_t,unordered_map<int,int64_t>> & allStateGroupMaps,vector<sp<ConditionTracker>> & allConditionTrackers,const vector<ConditionState> & initialConditionCache,vector<sp<MetricProducer>> & allMetricProducers,unordered_map<int,vector<int>> & conditionToMetricMap,unordered_map<int,vector<int>> & trackerToMetricMap,unordered_map<int64_t,int> & metricMap,std::set<int64_t> & noReportMetricIds,unordered_map<int,vector<int>> & activationAtomTrackerToMetricMap,unordered_map<int,vector<int>> & deactivationAtomTrackerToMetricMap,vector<int> & metricsWithActivation)356 bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
357 const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
358 const unordered_map<int64_t, int>& logTrackerMap,
359 const unordered_map<int64_t, int>& conditionTrackerMap,
360 const vector<sp<LogMatchingTracker>>& allAtomMatchers,
361 const unordered_map<int64_t, int>& stateAtomIdMap,
362 const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
363 vector<sp<ConditionTracker>>& allConditionTrackers,
364 const vector<ConditionState>& initialConditionCache,
365 vector<sp<MetricProducer>>& allMetricProducers,
366 unordered_map<int, vector<int>>& conditionToMetricMap,
367 unordered_map<int, vector<int>>& trackerToMetricMap,
368 unordered_map<int64_t, int>& metricMap, std::set<int64_t>& noReportMetricIds,
369 unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
370 unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
371 vector<int>& metricsWithActivation) {
372 sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
373 sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchers);
374 const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
375 config.event_metric_size() + config.gauge_metric_size() +
376 config.value_metric_size();
377 allMetricProducers.reserve(allMetricsCount);
378
379 // Construct map from metric id to metric activation index. The map will be used to determine
380 // the metric activation corresponding to a metric.
381 unordered_map<int64_t, int> metricToActivationMap;
382 for (int i = 0; i < config.metric_activation_size(); i++) {
383 const MetricActivation& metricActivation = config.metric_activation(i);
384 int64_t metricId = metricActivation.metric_id();
385 if (metricToActivationMap.find(metricId) != metricToActivationMap.end()) {
386 ALOGE("Metric %lld has multiple MetricActivations", (long long) metricId);
387 return false;
388 }
389 metricToActivationMap.insert({metricId, i});
390 }
391
392 // Build MetricProducers for each metric defined in config.
393 // build CountMetricProducer
394 for (int i = 0; i < config.count_metric_size(); i++) {
395 const CountMetric& metric = config.count_metric(i);
396 if (!metric.has_what()) {
397 ALOGW("cannot find \"what\" in CountMetric \"%lld\"", (long long)metric.id());
398 return false;
399 }
400
401 int metricIndex = allMetricProducers.size();
402 metricMap.insert({metric.id(), metricIndex});
403 int trackerIndex;
404 if (!handleMetricWithLogTrackers(metric.what(), metricIndex,
405 metric.has_dimensions_in_what(),
406 allAtomMatchers, logTrackerMap, trackerToMetricMap,
407 trackerIndex)) {
408 return false;
409 }
410
411 int conditionIndex = -1;
412 if (metric.has_condition()) {
413 if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
414 metric.links(), allConditionTrackers, conditionIndex,
415 conditionToMetricMap)) {
416 return false;
417 }
418 } else {
419 if (metric.links_size() > 0) {
420 ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
421 return false;
422 }
423 }
424
425 std::vector<int> slicedStateAtoms;
426 unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
427 if (metric.slice_by_state_size() > 0) {
428 if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
429 allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
430 return false;
431 }
432 } else {
433 if (metric.state_link_size() > 0) {
434 ALOGW("CountMetric has a MetricStateLink but doesn't have a slice_by_state");
435 return false;
436 }
437 }
438
439 unordered_map<int, shared_ptr<Activation>> eventActivationMap;
440 unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
441 bool success = handleMetricActivation(config, metric.id(), metricIndex,
442 metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
443 deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
444 eventDeactivationMap);
445 if (!success) return false;
446
447 sp<MetricProducer> countProducer =
448 new CountMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
449 timeBaseTimeNs, currentTimeNs, eventActivationMap,
450 eventDeactivationMap, slicedStateAtoms, stateGroupMap);
451 allMetricProducers.push_back(countProducer);
452 }
453
454 // build DurationMetricProducer
455 for (int i = 0; i < config.duration_metric_size(); i++) {
456 int metricIndex = allMetricProducers.size();
457 const DurationMetric& metric = config.duration_metric(i);
458 metricMap.insert({metric.id(), metricIndex});
459
460 auto what_it = conditionTrackerMap.find(metric.what());
461 if (what_it == conditionTrackerMap.end()) {
462 ALOGE("DurationMetric's \"what\" is invalid");
463 return false;
464 }
465
466 const Predicate& durationWhat = config.predicate(what_it->second);
467
468 if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) {
469 ALOGE("DurationMetric's \"what\" must be a simple condition");
470 return false;
471 }
472
473 const auto& simplePredicate = durationWhat.simple_predicate();
474
475 bool nesting = simplePredicate.count_nesting();
476
477 int trackerIndices[3] = {-1, -1, -1};
478 if (!simplePredicate.has_start() ||
479 !handleMetricWithLogTrackers(simplePredicate.start(), metricIndex,
480 metric.has_dimensions_in_what(), allAtomMatchers,
481 logTrackerMap, trackerToMetricMap, trackerIndices[0])) {
482 ALOGE("Duration metrics must specify a valid the start event matcher");
483 return false;
484 }
485
486 if (simplePredicate.has_stop() &&
487 !handleMetricWithLogTrackers(simplePredicate.stop(), metricIndex,
488 metric.has_dimensions_in_what(), allAtomMatchers,
489 logTrackerMap, trackerToMetricMap, trackerIndices[1])) {
490 return false;
491 }
492
493 if (simplePredicate.has_stop_all() &&
494 !handleMetricWithLogTrackers(simplePredicate.stop_all(), metricIndex,
495 metric.has_dimensions_in_what(), allAtomMatchers,
496 logTrackerMap, trackerToMetricMap, trackerIndices[2])) {
497 return false;
498 }
499
500 FieldMatcher internalDimensions = simplePredicate.dimensions();
501
502 int conditionIndex = -1;
503
504 if (metric.has_condition()) {
505 bool good = handleMetricWithConditions(
506 metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
507 allConditionTrackers, conditionIndex, conditionToMetricMap);
508 if (!good) {
509 return false;
510 }
511 } else {
512 if (metric.links_size() > 0) {
513 ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
514 return false;
515 }
516 }
517
518 std::vector<int> slicedStateAtoms;
519 unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
520 if (metric.slice_by_state_size() > 0) {
521 if (metric.aggregation_type() == DurationMetric::MAX_SPARSE) {
522 ALOGE("DurationMetric with aggregation type MAX_SPARSE cannot be sliced by state");
523 return false;
524 }
525 if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
526 allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
527 return false;
528 }
529 } else {
530 if (metric.state_link_size() > 0) {
531 ALOGW("DurationMetric has a MetricStateLink but doesn't have a sliced state");
532 return false;
533 }
534 }
535
536 // Check that all metric state links are a subset of dimensions_in_what fields.
537 std::vector<Matcher> dimensionsInWhat;
538 translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
539 for (const auto& stateLink : metric.state_link()) {
540 if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
541 return false;
542 }
543 }
544
545 unordered_map<int, shared_ptr<Activation>> eventActivationMap;
546 unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
547 bool success = handleMetricActivation(config, metric.id(), metricIndex,
548 metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
549 deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
550 eventDeactivationMap);
551 if (!success) return false;
552
553 sp<MetricProducer> durationMetric = new DurationMetricProducer(
554 key, metric, conditionIndex, initialConditionCache, trackerIndices[0],
555 trackerIndices[1], trackerIndices[2], nesting, wizard, internalDimensions,
556 timeBaseTimeNs, currentTimeNs, eventActivationMap, eventDeactivationMap,
557 slicedStateAtoms, stateGroupMap);
558
559 allMetricProducers.push_back(durationMetric);
560 }
561
562 // build EventMetricProducer
563 for (int i = 0; i < config.event_metric_size(); i++) {
564 int metricIndex = allMetricProducers.size();
565 const EventMetric& metric = config.event_metric(i);
566 metricMap.insert({metric.id(), metricIndex});
567 if (!metric.has_id() || !metric.has_what()) {
568 ALOGW("cannot find the metric name or what in config");
569 return false;
570 }
571 int trackerIndex;
572 if (!handleMetricWithLogTrackers(metric.what(), metricIndex, false, allAtomMatchers,
573 logTrackerMap, trackerToMetricMap, trackerIndex)) {
574 return false;
575 }
576
577 int conditionIndex = -1;
578 if (metric.has_condition()) {
579 bool good = handleMetricWithConditions(
580 metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
581 allConditionTrackers, conditionIndex, conditionToMetricMap);
582 if (!good) {
583 return false;
584 }
585 } else {
586 if (metric.links_size() > 0) {
587 ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
588 return false;
589 }
590 }
591
592 unordered_map<int, shared_ptr<Activation>> eventActivationMap;
593 unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
594 bool success = handleMetricActivation(config, metric.id(), metricIndex,
595 metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
596 deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
597 eventDeactivationMap);
598 if (!success) return false;
599
600 sp<MetricProducer> eventMetric =
601 new EventMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
602 timeBaseTimeNs, eventActivationMap, eventDeactivationMap);
603
604 allMetricProducers.push_back(eventMetric);
605 }
606
607 // build ValueMetricProducer
608 for (int i = 0; i < config.value_metric_size(); i++) {
609 const ValueMetric& metric = config.value_metric(i);
610 if (!metric.has_what()) {
611 ALOGW("cannot find \"what\" in ValueMetric \"%lld\"", (long long)metric.id());
612 return false;
613 }
614 if (!metric.has_value_field()) {
615 ALOGW("cannot find \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
616 return false;
617 }
618 std::vector<Matcher> fieldMatchers;
619 translateFieldMatcher(metric.value_field(), &fieldMatchers);
620 if (fieldMatchers.size() < 1) {
621 ALOGW("incorrect \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
622 return false;
623 }
624
625 int metricIndex = allMetricProducers.size();
626 metricMap.insert({metric.id(), metricIndex});
627 int trackerIndex;
628 if (!handleMetricWithLogTrackers(metric.what(), metricIndex,
629 metric.has_dimensions_in_what(),
630 allAtomMatchers, logTrackerMap, trackerToMetricMap,
631 trackerIndex)) {
632 return false;
633 }
634
635 sp<LogMatchingTracker> atomMatcher = allAtomMatchers.at(trackerIndex);
636 // If it is pulled atom, it should be simple matcher with one tagId.
637 if (atomMatcher->getAtomIds().size() != 1) {
638 return false;
639 }
640 int atomTagId = *(atomMatcher->getAtomIds().begin());
641 int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
642
643 int conditionIndex = -1;
644 if (metric.has_condition()) {
645 bool good = handleMetricWithConditions(
646 metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
647 allConditionTrackers, conditionIndex, conditionToMetricMap);
648 if (!good) {
649 return false;
650 }
651 } else {
652 if (metric.links_size() > 0) {
653 ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
654 return false;
655 }
656 }
657
658 std::vector<int> slicedStateAtoms;
659 unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
660 if (metric.slice_by_state_size() > 0) {
661 if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
662 allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
663 return false;
664 }
665 } else {
666 if (metric.state_link_size() > 0) {
667 ALOGW("ValueMetric has a MetricStateLink but doesn't have a sliced state");
668 return false;
669 }
670 }
671
672 // Check that all metric state links are a subset of dimensions_in_what fields.
673 std::vector<Matcher> dimensionsInWhat;
674 translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
675 for (const auto& stateLink : metric.state_link()) {
676 if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
677 return false;
678 }
679 }
680
681 unordered_map<int, shared_ptr<Activation>> eventActivationMap;
682 unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
683 bool success = handleMetricActivation(
684 config, metric.id(), metricIndex, metricToActivationMap, logTrackerMap,
685 activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
686 metricsWithActivation, eventActivationMap, eventDeactivationMap);
687 if (!success) return false;
688
689 sp<MetricProducer> valueProducer = new ValueMetricProducer(
690 key, metric, conditionIndex, initialConditionCache, wizard, trackerIndex,
691 matcherWizard, pullTagId, timeBaseTimeNs, currentTimeNs, pullerManager,
692 eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap);
693 allMetricProducers.push_back(valueProducer);
694 }
695
696 // Gauge metrics.
697 for (int i = 0; i < config.gauge_metric_size(); i++) {
698 const GaugeMetric& metric = config.gauge_metric(i);
699 if (!metric.has_what()) {
700 ALOGW("cannot find \"what\" in GaugeMetric \"%lld\"", (long long)metric.id());
701 return false;
702 }
703
704 if ((!metric.gauge_fields_filter().has_include_all() ||
705 (metric.gauge_fields_filter().include_all() == false)) &&
706 !hasLeafNode(metric.gauge_fields_filter().fields())) {
707 ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
708 return false;
709 }
710 if ((metric.gauge_fields_filter().has_include_all() &&
711 metric.gauge_fields_filter().include_all() == true) &&
712 hasLeafNode(metric.gauge_fields_filter().fields())) {
713 ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
714 return false;
715 }
716
717 int metricIndex = allMetricProducers.size();
718 metricMap.insert({metric.id(), metricIndex});
719 int trackerIndex;
720 if (!handleMetricWithLogTrackers(metric.what(), metricIndex,
721 metric.has_dimensions_in_what(),
722 allAtomMatchers, logTrackerMap, trackerToMetricMap,
723 trackerIndex)) {
724 return false;
725 }
726
727 sp<LogMatchingTracker> atomMatcher = allAtomMatchers.at(trackerIndex);
728 // For GaugeMetric atom, it should be simple matcher with one tagId.
729 if (atomMatcher->getAtomIds().size() != 1) {
730 return false;
731 }
732 int atomTagId = *(atomMatcher->getAtomIds().begin());
733 int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
734
735 int triggerTrackerIndex;
736 int triggerAtomId = -1;
737 if (metric.has_trigger_event()) {
738 if (pullTagId == -1) {
739 ALOGW("Pull atom not specified for trigger");
740 return false;
741 }
742 // event_trigger should be used with FIRST_N_SAMPLES
743 if (metric.sampling_type() != GaugeMetric::FIRST_N_SAMPLES) {
744 return false;
745 }
746 if (!handlePullMetricTriggerWithLogTrackers(metric.trigger_event(), metricIndex,
747 allAtomMatchers, logTrackerMap,
748 trackerToMetricMap, triggerTrackerIndex)) {
749 return false;
750 }
751 sp<LogMatchingTracker> triggerAtomMatcher = allAtomMatchers.at(triggerTrackerIndex);
752 triggerAtomId = *(triggerAtomMatcher->getAtomIds().begin());
753 }
754
755 if (!metric.has_trigger_event() && pullTagId != -1 &&
756 metric.sampling_type() == GaugeMetric::FIRST_N_SAMPLES) {
757 ALOGW("FIRST_N_SAMPLES is only for pushed event or pull_on_trigger");
758 return false;
759 }
760
761 int conditionIndex = -1;
762 if (metric.has_condition()) {
763 bool good = handleMetricWithConditions(
764 metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
765 allConditionTrackers, conditionIndex, conditionToMetricMap);
766 if (!good) {
767 return false;
768 }
769 } else {
770 if (metric.links_size() > 0) {
771 ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
772 return false;
773 }
774 }
775
776 unordered_map<int, shared_ptr<Activation>> eventActivationMap;
777 unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
778 bool success = handleMetricActivation(config, metric.id(), metricIndex,
779 metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
780 deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
781 eventDeactivationMap);
782 if (!success) return false;
783
784 sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(
785 key, metric, conditionIndex, initialConditionCache, wizard, trackerIndex,
786 matcherWizard, pullTagId, triggerAtomId, atomTagId, timeBaseTimeNs, currentTimeNs,
787 pullerManager, eventActivationMap, eventDeactivationMap);
788 allMetricProducers.push_back(gaugeProducer);
789 }
790 for (int i = 0; i < config.no_report_metric_size(); ++i) {
791 const auto no_report_metric = config.no_report_metric(i);
792 if (metricMap.find(no_report_metric) == metricMap.end()) {
793 ALOGW("no_report_metric %" PRId64 " not exist", no_report_metric);
794 return false;
795 }
796 noReportMetricIds.insert(no_report_metric);
797 }
798
799 const set<int> whitelistedAtomIds(config.whitelisted_atom_ids().begin(),
800 config.whitelisted_atom_ids().end());
801 for (const auto& it : allMetricProducers) {
802 // Register metrics to StateTrackers
803 for (int atomId : it->getSlicedStateAtoms()) {
804 // Register listener for non-whitelisted atoms only. Using whitelisted atom as a sliced
805 // state atom is not allowed.
806 if (whitelistedAtomIds.find(atomId) == whitelistedAtomIds.end()) {
807 StateManager::getInstance().registerListener(atomId, it);
808 } else {
809 return false;
810 }
811 }
812 }
813 return true;
814 }
815
initAlerts(const StatsdConfig & config,const unordered_map<int64_t,int> & metricProducerMap,unordered_map<int64_t,int> & alertTrackerMap,const sp<AlarmMonitor> & anomalyAlarmMonitor,vector<sp<MetricProducer>> & allMetricProducers,vector<sp<AnomalyTracker>> & allAnomalyTrackers)816 bool initAlerts(const StatsdConfig& config,
817 const unordered_map<int64_t, int>& metricProducerMap,
818 unordered_map<int64_t, int>& alertTrackerMap,
819 const sp<AlarmMonitor>& anomalyAlarmMonitor,
820 vector<sp<MetricProducer>>& allMetricProducers,
821 vector<sp<AnomalyTracker>>& allAnomalyTrackers) {
822 for (int i = 0; i < config.alert_size(); i++) {
823 const Alert& alert = config.alert(i);
824 const auto& itr = metricProducerMap.find(alert.metric_id());
825 if (itr == metricProducerMap.end()) {
826 ALOGW("alert \"%lld\" has unknown metric id: \"%lld\"", (long long)alert.id(),
827 (long long)alert.metric_id());
828 return false;
829 }
830 if (!alert.has_trigger_if_sum_gt()) {
831 ALOGW("invalid alert: missing threshold");
832 return false;
833 }
834 if (alert.trigger_if_sum_gt() < 0 || alert.num_buckets() <= 0) {
835 ALOGW("invalid alert: threshold=%f num_buckets= %d",
836 alert.trigger_if_sum_gt(), alert.num_buckets());
837 return false;
838 }
839 const int metricIndex = itr->second;
840 sp<MetricProducer> metric = allMetricProducers[metricIndex];
841 sp<AnomalyTracker> anomalyTracker = metric->addAnomalyTracker(alert, anomalyAlarmMonitor);
842 if (anomalyTracker == nullptr) {
843 // The ALOGW for this invalid alert was already displayed in addAnomalyTracker().
844 return false;
845 }
846 alertTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size()));
847 allAnomalyTrackers.push_back(anomalyTracker);
848 }
849 for (int i = 0; i < config.subscription_size(); ++i) {
850 const Subscription& subscription = config.subscription(i);
851 if (subscription.rule_type() != Subscription::ALERT) {
852 continue;
853 }
854 if (subscription.subscriber_information_case() ==
855 Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) {
856 ALOGW("subscription \"%lld\" has no subscriber info.\"",
857 (long long)subscription.id());
858 return false;
859 }
860 const auto& itr = alertTrackerMap.find(subscription.rule_id());
861 if (itr == alertTrackerMap.end()) {
862 ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"",
863 (long long)subscription.id(), (long long)subscription.rule_id());
864 return false;
865 }
866 const int anomalyTrackerIndex = itr->second;
867 allAnomalyTrackers[anomalyTrackerIndex]->addSubscription(subscription);
868 }
869 return true;
870 }
871
initAlarms(const StatsdConfig & config,const ConfigKey & key,const sp<AlarmMonitor> & periodicAlarmMonitor,const int64_t timeBaseNs,const int64_t currentTimeNs,vector<sp<AlarmTracker>> & allAlarmTrackers)872 bool initAlarms(const StatsdConfig& config, const ConfigKey& key,
873 const sp<AlarmMonitor>& periodicAlarmMonitor,
874 const int64_t timeBaseNs, const int64_t currentTimeNs,
875 vector<sp<AlarmTracker>>& allAlarmTrackers) {
876 unordered_map<int64_t, int> alarmTrackerMap;
877 int64_t startMillis = timeBaseNs / 1000 / 1000;
878 int64_t currentTimeMillis = currentTimeNs / 1000 /1000;
879 for (int i = 0; i < config.alarm_size(); i++) {
880 const Alarm& alarm = config.alarm(i);
881 if (alarm.offset_millis() <= 0) {
882 ALOGW("Alarm offset_millis should be larger than 0.");
883 return false;
884 }
885 if (alarm.period_millis() <= 0) {
886 ALOGW("Alarm period_millis should be larger than 0.");
887 return false;
888 }
889 alarmTrackerMap.insert(std::make_pair(alarm.id(), allAlarmTrackers.size()));
890 allAlarmTrackers.push_back(
891 new AlarmTracker(startMillis, currentTimeMillis,
892 alarm, key, periodicAlarmMonitor));
893 }
894 for (int i = 0; i < config.subscription_size(); ++i) {
895 const Subscription& subscription = config.subscription(i);
896 if (subscription.rule_type() != Subscription::ALARM) {
897 continue;
898 }
899 if (subscription.subscriber_information_case() ==
900 Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) {
901 ALOGW("subscription \"%lld\" has no subscriber info.\"",
902 (long long)subscription.id());
903 return false;
904 }
905 const auto& itr = alarmTrackerMap.find(subscription.rule_id());
906 if (itr == alarmTrackerMap.end()) {
907 ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"",
908 (long long)subscription.id(), (long long)subscription.rule_id());
909 return false;
910 }
911 const int trackerIndex = itr->second;
912 allAlarmTrackers[trackerIndex]->addSubscription(subscription);
913 }
914 return true;
915 }
916
initStatsdConfig(const ConfigKey & key,const StatsdConfig & config,UidMap & uidMap,const sp<StatsPullerManager> & pullerManager,const sp<AlarmMonitor> & anomalyAlarmMonitor,const sp<AlarmMonitor> & periodicAlarmMonitor,const int64_t timeBaseNs,const int64_t currentTimeNs,set<int> & allTagIds,vector<sp<LogMatchingTracker>> & allAtomMatchers,vector<sp<ConditionTracker>> & allConditionTrackers,vector<sp<MetricProducer>> & allMetricProducers,vector<sp<AnomalyTracker>> & allAnomalyTrackers,vector<sp<AlarmTracker>> & allPeriodicAlarmTrackers,unordered_map<int,std::vector<int>> & conditionToMetricMap,unordered_map<int,std::vector<int>> & trackerToMetricMap,unordered_map<int,std::vector<int>> & trackerToConditionMap,unordered_map<int,std::vector<int>> & activationAtomTrackerToMetricMap,unordered_map<int,std::vector<int>> & deactivationAtomTrackerToMetricMap,unordered_map<int64_t,int> & alertTrackerMap,vector<int> & metricsWithActivation,std::set<int64_t> & noReportMetricIds)917 bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap,
918 const sp<StatsPullerManager>& pullerManager,
919 const sp<AlarmMonitor>& anomalyAlarmMonitor,
920 const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
921 const int64_t currentTimeNs, set<int>& allTagIds,
922 vector<sp<LogMatchingTracker>>& allAtomMatchers,
923 vector<sp<ConditionTracker>>& allConditionTrackers,
924 vector<sp<MetricProducer>>& allMetricProducers,
925 vector<sp<AnomalyTracker>>& allAnomalyTrackers,
926 vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers,
927 unordered_map<int, std::vector<int>>& conditionToMetricMap,
928 unordered_map<int, std::vector<int>>& trackerToMetricMap,
929 unordered_map<int, std::vector<int>>& trackerToConditionMap,
930 unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
931 unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
932 unordered_map<int64_t, int>& alertTrackerMap,
933 vector<int>& metricsWithActivation,
934 std::set<int64_t>& noReportMetricIds) {
935 unordered_map<int64_t, int> logTrackerMap;
936 unordered_map<int64_t, int> conditionTrackerMap;
937 vector<ConditionState> initialConditionCache;
938 unordered_map<int64_t, int> metricProducerMap;
939 unordered_map<int64_t, int> stateAtomIdMap;
940 unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
941
942 if (!initLogTrackers(config, uidMap, logTrackerMap, allAtomMatchers, allTagIds)) {
943 ALOGE("initLogMatchingTrackers failed");
944 return false;
945 }
946 VLOG("initLogMatchingTrackers succeed...");
947
948 if (!initConditions(key, config, logTrackerMap, conditionTrackerMap, allConditionTrackers,
949 trackerToConditionMap, initialConditionCache)) {
950 ALOGE("initConditionTrackers failed");
951 return false;
952 }
953
954 if (!initStates(config, stateAtomIdMap, allStateGroupMaps)) {
955 ALOGE("initStates failed");
956 return false;
957 }
958 if (!initMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager, logTrackerMap,
959 conditionTrackerMap, allAtomMatchers, stateAtomIdMap, allStateGroupMaps,
960 allConditionTrackers, initialConditionCache, allMetricProducers,
961 conditionToMetricMap, trackerToMetricMap, metricProducerMap, noReportMetricIds,
962 activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
963 metricsWithActivation)) {
964 ALOGE("initMetricProducers failed");
965 return false;
966 }
967 if (!initAlerts(config, metricProducerMap, alertTrackerMap, anomalyAlarmMonitor,
968 allMetricProducers, allAnomalyTrackers)) {
969 ALOGE("initAlerts failed");
970 return false;
971 }
972 if (!initAlarms(config, key, periodicAlarmMonitor,
973 timeBaseNs, currentTimeNs, allPeriodicAlarmTrackers)) {
974 ALOGE("initAlarms failed");
975 return false;
976 }
977
978 return true;
979 }
980
981 } // namespace statsd
982 } // namespace os
983 } // namespace android
984