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 #pragma once
18 
19 #include <unordered_map>
20 
21 #include "anomaly/AlarmMonitor.h"
22 #include "anomaly/AlarmTracker.h"
23 #include "anomaly/AnomalyTracker.h"
24 #include "condition/ConditionTracker.h"
25 #include "config/ConfigKey.h"
26 #include "config/ConfigMetadataProvider.h"
27 #include "external/StatsPullerManager.h"
28 #include "guardrail/StatsdStats.h"
29 #include "logd/LogEvent.h"
30 #include "matchers/AtomMatchingTracker.h"
31 #include "metrics/MetricProducer.h"
32 #include "packages/UidMap.h"
33 #include "src/statsd_config.pb.h"
34 #include "src/statsd_metadata.pb.h"
35 
36 namespace android {
37 namespace os {
38 namespace statsd {
39 
40 // A MetricsManager is responsible for managing metrics from one single config source.
41 class MetricsManager : public virtual RefBase,
42                        public virtual PullUidProvider,
43                        public virtual ConfigMetadataProvider {
44 public:
45     MetricsManager(const ConfigKey& configKey, const StatsdConfig& config, int64_t timeBaseNs,
46                    const int64_t currentTimeNs, const sp<UidMap>& uidMap,
47                    const sp<StatsPullerManager>& pullerManager,
48                    const sp<AlarmMonitor>& anomalyAlarmMonitor,
49                    const sp<AlarmMonitor>& periodicAlarmMonitor);
50 
51     virtual ~MetricsManager();
52 
53     bool updateConfig(const StatsdConfig& config, int64_t timeBaseNs, const int64_t currentTimeNs,
54                       const sp<AlarmMonitor>& anomalyAlarmMonitor,
55                       const sp<AlarmMonitor>& periodicAlarmMonitor);
56 
57     // Return whether the configuration is valid.
58     bool isConfigValid() const;
59 
60     virtual void onLogEvent(const LogEvent& event);
61 
62     void onAnomalyAlarmFired(
63             int64_t timestampNs,
64             unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet);
65 
66     void onPeriodicAlarmFired(
67             int64_t timestampNs,
68             unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet);
69 
70     void notifyAppUpgrade(int64_t eventTimeNs, const string& apk, int uid, int64_t version);
71 
72     void notifyAppRemoved(int64_t eventTimeNs, const string& apk, int uid);
73 
74     void onUidMapReceived(int64_t eventTimeNs);
75 
76     void onStatsdInitCompleted(int64_t elapsedTimeNs);
77 
78     void init();
79 
80     vector<int32_t> getPullAtomUids(int32_t atomId) override;
81 
82     bool useV2SoftMemoryCalculation() override;
83 
shouldWriteToDisk()84     bool shouldWriteToDisk() const {
85         return mNoReportMetricIds.size() != mAllMetricProducers.size();
86     }
87 
shouldPersistLocalHistory()88     bool shouldPersistLocalHistory() const {
89         return mShouldPersistHistory;
90     }
91 
92     void dumpStates(int out, bool verbose);
93 
isInTtl(const int64_t timestampNs)94     inline bool isInTtl(const int64_t timestampNs) const {
95         return mTtlNs <= 0 || timestampNs < mTtlEndNs;
96     };
97 
hashStringInReport()98     inline bool hashStringInReport() const {
99         return mHashStringsInReport;
100     };
101 
versionStringsInReport()102     inline bool versionStringsInReport() const {
103         return mVersionStringsInReport;
104     };
105 
installerInReport()106     inline bool installerInReport() const {
107         return mInstallerInReport;
108     };
109 
packageCertificateHashSizeBytes()110     inline uint8_t packageCertificateHashSizeBytes() const {
111         return mPackageCertificateHashSizeBytes;
112     }
113 
refreshTtl(const int64_t currentTimestampNs)114     void refreshTtl(const int64_t currentTimestampNs) {
115         if (mTtlNs > 0) {
116             mTtlEndNs = currentTimestampNs + mTtlNs;
117         }
118     };
119 
120     // Returns the elapsed realtime when this metric manager last reported metrics. If this config
121     // has not yet dumped any reports, this is the time the metricsmanager was initialized.
getLastReportTimeNs()122     inline int64_t getLastReportTimeNs() const {
123         return mLastReportTimeNs;
124     };
125 
getLastReportWallClockNs()126     inline int64_t getLastReportWallClockNs() const {
127         return mLastReportWallClockNs;
128     };
129 
getNumMetrics()130     inline size_t getNumMetrics() const {
131         return mAllMetricProducers.size();
132     }
133 
134     virtual void dropData(const int64_t dropTimeNs);
135 
136     virtual void onDumpReport(const int64_t dumpTimeNs, int64_t wallClockNs,
137                               const bool include_current_partial_bucket, const bool erase_data,
138                               const DumpLatency dumpLatency, std::set<string>* str_set,
139                               android::util::ProtoOutputStream* protoOutput);
140 
141     // Computes the total byte size of all metrics managed by a single config source.
142     // Does not change the state.
143     virtual size_t byteSize();
144 
145     // Returns whether or not this config is active.
146     // The config is active if any metric in the config is active.
isActive()147     inline bool isActive() const {
148         return mIsActive;
149     }
150 
151     void loadActiveConfig(const ActiveConfig& config, int64_t currentTimeNs);
152 
153     void writeActiveConfigToProtoOutputStream(int64_t currentTimeNs, const DumpReportReason reason,
154                                               ProtoOutputStream* proto);
155 
156     // Returns true if at least one piece of metadata is written.
157     bool writeMetadataToProto(int64_t currentWallClockTimeNs, int64_t systemElapsedTimeNs,
158                               metadata::StatsMetadata* statsMetadata);
159 
160     void loadMetadata(const metadata::StatsMetadata& metadata, int64_t currentWallClockTimeNs,
161                       int64_t systemElapsedTimeNs);
162 
hasRestrictedMetricsDelegate()163     inline bool hasRestrictedMetricsDelegate() const {
164         return mRestrictedMetricsDelegatePackageName.has_value();
165     }
166 
getRestrictedMetricsDelegate()167     inline string getRestrictedMetricsDelegate() const {
168         return hasRestrictedMetricsDelegate() ? mRestrictedMetricsDelegatePackageName.value() : "";
169     }
170 
getConfigKey()171     inline ConfigKey getConfigKey() const {
172         return mConfigKey;
173     }
174 
175     void enforceRestrictedDataTtls(const int64_t wallClockNs);
176 
177     bool validateRestrictedMetricsDelegate(int32_t callingUid);
178 
179     virtual void flushRestrictedData();
180 
181     // Slow, should not be called in a hotpath.
182     vector<int64_t> getAllMetricIds() const;
183 
184     // Adds all atom ids referenced by matchers in the MetricsManager's config
185     void addAllAtomIds(LogEventFilter::AtomIdSet& allIds) const;
186 
187     // Gets the memory limit for the MetricsManager's config
getMaxMetricsBytes()188     inline size_t getMaxMetricsBytes() const {
189         return mMaxMetricsBytes;
190     }
191 
getTriggerGetDataBytes()192     inline size_t getTriggerGetDataBytes() const {
193         return mTriggerGetDataBytes;
194     }
195 
omitSystemUidsInUidMap()196     inline bool omitSystemUidsInUidMap() const {
197         return mOmitSystemUidsInUidMap;
198     }
199 
200 private:
201     // For test only.
getTtlEndNs()202     inline int64_t getTtlEndNs() const {
203         return mTtlEndNs;
204     }
205 
206     const ConfigKey mConfigKey;
207 
208     sp<UidMap> mUidMap;
209 
210     bool mHashStringsInReport = false;
211     bool mVersionStringsInReport = false;
212     bool mInstallerInReport = false;
213     uint8_t mPackageCertificateHashSizeBytes;
214 
215     int64_t mTtlNs;
216     int64_t mTtlEndNs;
217 
218     int64_t mLastReportTimeNs;
219     int64_t mLastReportWallClockNs;
220 
221     optional<InvalidConfigReason> mInvalidConfigReason;
222 
223     sp<StatsPullerManager> mPullerManager;
224 
225     // The uid log sources from StatsdConfig.
226     std::vector<int32_t> mAllowedUid;
227 
228     // The pkg log sources from StatsdConfig.
229     std::vector<std::string> mAllowedPkg;
230 
231     // The combined uid sources (after translating pkg name to uid).
232     // Logs from uids that are not in the list will be ignored to avoid spamming.
233     std::set<int32_t> mAllowedLogSources;
234 
235     // To guard access to mAllowedLogSources
236     mutable std::mutex mAllowedLogSourcesMutex;
237 
238     std::set<int32_t> mWhitelistedAtomIds;
239 
240     // We can pull any atom from these uids.
241     std::set<int32_t> mDefaultPullUids;
242 
243     // Uids that specific atoms can pull from.
244     // This is a map<atom id, set<uids>>
245     std::map<int32_t, std::set<int32_t>> mPullAtomUids;
246 
247     // Packages that specific atoms can be pulled from.
248     std::map<int32_t, std::set<std::string>> mPullAtomPackages;
249 
250     // All uids to pull for this atom. NOTE: Does not include the default uids for memory.
251     std::map<int32_t, std::set<int32_t>> mCombinedPullAtomUids;
252 
253     // Contains the annotations passed in with StatsdConfig.
254     std::list<std::pair<const int64_t, const int32_t>> mAnnotations;
255 
256     bool mShouldPersistHistory;
257     bool mUseV2SoftMemoryCalculation;
258 
259     bool mOmitSystemUidsInUidMap;
260 
261     // All event tags that are interesting to config metrics matchers.
262     std::unordered_map<int, std::vector<int>> mTagIdsToMatchersMap;
263 
264     // We only store the sp of AtomMatchingTracker, MetricProducer, and ConditionTracker in
265     // MetricsManager. There are relationships between them, and the relationships are denoted by
266     // index instead of pointers. The reasons for this are: (1) the relationship between them are
267     // complicated, so storing index instead of pointers reduces the risk that A holds B's sp, and B
268     // holds A's sp. (2) When we evaluate matcher results, or condition results, we can quickly get
269     // the related results from a cache using the index.
270 
271     // Hold all the atom matchers from the config.
272     std::vector<sp<AtomMatchingTracker>> mAllAtomMatchingTrackers;
273 
274     // Hold all the conditions from the config.
275     std::vector<sp<ConditionTracker>> mAllConditionTrackers;
276 
277     // Hold all metrics from the config.
278     std::vector<sp<MetricProducer>> mAllMetricProducers;
279 
280     // Hold all alert trackers.
281     std::vector<sp<AnomalyTracker>> mAllAnomalyTrackers;
282 
283     // Hold all periodic alarm trackers.
284     std::vector<sp<AlarmTracker>> mAllPeriodicAlarmTrackers;
285 
286     // To make updating configs faster, we map the id of a AtomMatchingTracker, MetricProducer, and
287     // ConditionTracker to its index in the corresponding vector.
288 
289     // Maps the id of an atom matching tracker to its index in mAllAtomMatchingTrackers.
290     std::unordered_map<int64_t, int> mAtomMatchingTrackerMap;
291 
292     // Maps the id of a condition tracker to its index in mAllConditionTrackers.
293     std::unordered_map<int64_t, int> mConditionTrackerMap;
294 
295     // Maps the id of a metric producer to its index in mAllMetricProducers.
296     std::unordered_map<int64_t, int> mMetricProducerMap;
297 
298     // To make the log processing more efficient, we want to do as much filtering as possible
299     // before we go into individual trackers and conditions to match.
300 
301     // 1st filter: check if the event tag id is in mTagIdsToMatchersMap.
302     // 2nd filter: if it is, we parse the event because there is at least one member is interested.
303     //             then pass to all AtomMatchingTrackers (itself also filter events by ids).
304     // 3nd filter: for AtomMatchingTrackers that matched this event, we pass this event to the
305     //             ConditionTrackers and MetricProducers that use this matcher.
306     // 4th filter: for ConditionTrackers that changed value due to this event, we pass
307     //             new conditions to  metrics that use this condition.
308 
309     // The following map is initialized from the statsd_config.
310 
311     // Maps from the index of the AtomMatchingTracker to index of MetricProducer.
312     std::unordered_map<int, std::vector<int>> mTrackerToMetricMap;
313 
314     // Maps from AtomMatchingTracker to ConditionTracker
315     std::unordered_map<int, std::vector<int>> mTrackerToConditionMap;
316 
317     // Maps from ConditionTracker to MetricProducer
318     std::unordered_map<int, std::vector<int>> mConditionToMetricMap;
319 
320     // Maps from life span triggering event to MetricProducers.
321     std::unordered_map<int, std::vector<int>> mActivationAtomTrackerToMetricMap;
322 
323     // Maps deactivation triggering event to MetricProducers.
324     std::unordered_map<int, std::vector<int>> mDeactivationAtomTrackerToMetricMap;
325 
326     // Maps AlertIds to the index of the corresponding AnomalyTracker stored in mAllAnomalyTrackers.
327     // The map is used in LoadMetadata to more efficiently lookup AnomalyTrackers from an AlertId.
328     std::unordered_map<int64_t, int> mAlertTrackerMap;
329 
330     std::vector<int> mMetricIndexesWithActivation;
331 
checkLogCredentials(const LogEvent & event)332     inline bool checkLogCredentials(const LogEvent& event) const {
333         return checkLogCredentials(event.GetUid(), event.GetTagId());
334     }
335 
336     bool checkLogCredentials(int32_t uid, int32_t atomId) const;
337 
338     void initAllowedLogSources();
339 
340     void initPullAtomSources();
341 
342     // Only called on config creation/update to initialize log sources from the config.
343     // Calls initAllowedLogSources and initPullAtomSources. Sets up mInvalidConfigReason on
344     // error.
345     void createAllLogSourcesFromConfig(const StatsdConfig& config);
346 
347     // Verifies the config meets guardrails and updates statsdStats.
348     // Sets up mInvalidConfigReason on error. Should be called on config creation/update
349     void verifyGuardrailsAndUpdateStatsdStats();
350 
351     // Initializes mIsAlwaysActive and mIsActive.
352     // Should be called on config creation/update.
353     void initializeConfigActiveStatus();
354 
355     // The metrics that don't need to be uploaded or even reported.
356     std::set<int64_t> mNoReportMetricIds;
357 
358     // The config is active if any metric in the config is active.
359     bool mIsActive;
360 
361     // The config is always active if any metric in the config does not have an activation signal.
362     bool mIsAlwaysActive;
363 
364     // Hashes of the States used in this config, keyed by the state id, used in config updates.
365     std::map<int64_t, uint64_t> mStateProtoHashes;
366 
367     // Optional package name of the delegate that processes restricted metrics
368     // If set, restricted metrics are only uploaded to the delegate.
369     optional<string> mRestrictedMetricsDelegatePackageName = nullopt;
370 
371     // Only called on config creation/update. Sets the memory limit in bytes for storing metrics.
372     void setMaxMetricsBytesFromConfig(const StatsdConfig& config);
373 
374     // Only called on config creation/update. Sets the soft memory limit in bytes for storing
375     // metrics.
376     void setTriggerGetDataBytesFromConfig(const StatsdConfig& config);
377 
378     // Parse SocketLossInfo and propagate info to the metrics
379     void onLogEventLost(const SocketLossInfo& socketLossInfo);
380 
381     /**
382      * @brief Update metrics depending on #lostAtomId that it was lost due to #reason
383      * @return number of notified metrics
384      */
385     int notifyMetricsAboutLostAtom(int32_t lostAtomId, DataCorruptedReason reason);
386 
387     /**
388      * @brief Updates MetricProducers with DataCorruptionReason due to queue overflow atom loss
389      *        Notifies metrics only when new queue overflow happens since previous dumpReport
390      *        Perform QueueOverflowAtomsStats tracking via managing stats local copy
391      *        The assumption is that QueueOverflowAtomsStats collected over time, and that none of
392      *        atom id counters have disappeared (which is StatsdStats logic until it explicitly
393      *        reset, which should not be happen during statsd service lifetime)
394      * @param overflowStats
395      */
396     void processQueueOverflowStats(const StatsdStats::QueueOverflowAtomsStats& overflowStats);
397 
398     // The memory limit in bytes for storing metrics
399     size_t mMaxMetricsBytes;
400 
401     // The memory limit in bytes for triggering get data.
402     size_t mTriggerGetDataBytes;
403 
404     // Dropped atoms stats due to queue overflow observed up to latest dumpReport request
405     // this map is not cleared during onDumpReport to preserve tracking information and avoid
406     // repeated metric notification about past queue overflow lost event
407     // This map represent local copy of StatsdStats::mPushedAtomDropsStats with relevant atoms ids
408     typedef std::unordered_map<int32_t, int32_t> QueueOverflowAtomsStatsMap;
409     QueueOverflowAtomsStatsMap mQueueOverflowAtomsStats;
410 
411     FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
412     FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid);
413     FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain);
414 
415     FRIEND_TEST(GaugeMetricE2ePushedTest, TestDimensionalSampling);
416     FRIEND_TEST(GaugeMetricE2ePulledTest, TestFirstNSamplesPulledNoTrigger);
417     FRIEND_TEST(GaugeMetricE2ePulledTest, TestFirstNSamplesPulledNoTriggerWithActivation);
418     FRIEND_TEST(GaugeMetricE2ePushedTest, TestMultipleFieldsForPushedEvent);
419     FRIEND_TEST(GaugeMetricE2ePulledTest, TestRandomSamplePulledEvents);
420     FRIEND_TEST(GaugeMetricE2ePulledTest, TestRandomSamplePulledEvent_LateAlarm);
421     FRIEND_TEST(GaugeMetricE2ePulledTest, TestRandomSamplePulledEventsWithActivation);
422     FRIEND_TEST(GaugeMetricE2ePulledTest, TestRandomSamplePulledEventsNoCondition);
423     FRIEND_TEST(GaugeMetricE2ePulledTest, TestConditionChangeToTrueSamplePulledEvents);
424 
425     FRIEND_TEST(AnomalyCountDetectionE2eTest, TestSlicedCountMetric_single_bucket);
426     FRIEND_TEST(AnomalyCountDetectionE2eTest, TestSlicedCountMetric_multiple_buckets);
427     FRIEND_TEST(AnomalyCountDetectionE2eTest,
428                 TestCountMetric_save_refractory_to_disk_no_data_written);
429     FRIEND_TEST(AnomalyCountDetectionE2eTest, TestCountMetric_save_refractory_to_disk);
430     FRIEND_TEST(AnomalyCountDetectionE2eTest, TestCountMetric_load_refractory_from_disk);
431     FRIEND_TEST(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
432     FRIEND_TEST(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_partial_bucket);
433     FRIEND_TEST(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets);
434     FRIEND_TEST(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period);
435 
436     FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms);
437     FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric);
438     FRIEND_TEST(ConfigUpdateE2eAbTest, TestConfigTtl);
439     FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
440     FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation);
441     FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations);
442     FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation);
443     FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
444 
445     FRIEND_TEST(MetricsManagerTest, TestLogSources);
446     FRIEND_TEST(MetricsManagerTest, TestCheckLogCredentialsWhitelistedAtom);
447     FRIEND_TEST(MetricsManagerTest, TestLogSourcesOnConfigUpdate);
448     FRIEND_TEST(MetricsManagerTest_SPlus, TestRestrictedMetricsConfig);
449     FRIEND_TEST(MetricsManagerTest_SPlus, TestRestrictedMetricsConfigUpdate);
450     FRIEND_TEST(MetricsManagerUtilTest, TestSampledMetrics);
451 
452     FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
453     FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot);
454     FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
455     FRIEND_TEST(StatsLogProcessorTest,
456                 TestActivationOnBootMultipleActivationsDifferentActivationTypes);
457     FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
458 
459     FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges);
460     FRIEND_TEST(CountMetricE2eTest, TestSlicedState);
461     FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap);
462     FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates);
463     FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields);
464 
465     FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
466     FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
467     FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
468     FRIEND_TEST(DurationMetricE2eTest, TestWithCondition);
469     FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition);
470     FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition);
471     FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedState);
472     FRIEND_TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState);
473     FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStateMapped);
474     FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset);
475     FRIEND_TEST(DurationMetricE2eTest, TestUploadThreshold);
476 
477     FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges);
478     FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
479     FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
480     FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation);
481     FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
482     FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
483     FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions);
484     FRIEND_TEST(ValueMetricE2eTest, TestInitWithMultipleAggTypes);
485     FRIEND_TEST(ValueMetricE2eTest, TestInitWithDefaultAggType);
486 
487     FRIEND_TEST(SocketLossInfoTest, PropagationTest);
488 
489     FRIEND_TEST(DataCorruptionQueueOverflowTest, TestNotifyOnlyInterestedMetrics);
490     FRIEND_TEST(DataCorruptionQueueOverflowTest, TestNotifyInterestedMetricsWithNewLoss);
491     FRIEND_TEST(DataCorruptionQueueOverflowTest, TestDoNotNotifyInterestedMetricsIfNoUpdate);
492 };
493 
494 }  // namespace statsd
495 }  // namespace os
496 }  // namespace android
497