1// Copyright (C) 2017 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15#include <gtest/gtest.h> 16 17#include "src/StatsLogProcessor.h" 18#include "src/stats_log_util.h" 19#include "tests/statsd_test_util.h" 20 21#include <iostream> 22#include <vector> 23 24namespace android { 25namespace os { 26namespace statsd { 27 28#ifdef __ANDROID__ 29 30namespace { 31 32StatsdConfig CreateStatsdConfig(const Position position) { 33 StatsdConfig config; 34 config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. 35 auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); 36 auto attributionNodeMatcher = 37 wakelockAcquireMatcher.mutable_simple_atom_matcher()->add_field_value_matcher(); 38 attributionNodeMatcher->set_field(1); 39 attributionNodeMatcher->set_position(Position::ANY); 40 auto uidMatcher = attributionNodeMatcher->mutable_matches_tuple()->add_field_value_matcher(); 41 uidMatcher->set_field(1); // uid field. 42 uidMatcher->set_eq_string("com.android.gmscore"); 43 44 *config.add_atom_matcher() = wakelockAcquireMatcher; 45 46 auto countMetric = config.add_count_metric(); 47 countMetric->set_id(123456); 48 countMetric->set_what(wakelockAcquireMatcher.id()); 49 *countMetric->mutable_dimensions_in_what() = 50 CreateAttributionUidAndTagDimensions( 51 android::util::WAKELOCK_STATE_CHANGED, {position}); 52 countMetric->set_bucket(FIVE_MINUTES); 53 return config; 54} 55 56} // namespace 57 58TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid) { 59 auto config = CreateStatsdConfig(Position::FIRST); 60 int64_t bucketStartTimeNs = 10000000000; 61 int64_t bucketSizeNs = 62 TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; 63 64 ConfigKey cfgKey; 65 auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); 66 EXPECT_EQ(processor->mMetricsManagers.size(), 1u); 67 EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); 68 69 // Here it assumes that GMS core has two uids. 70 processor->getUidMap()->updateMap( 71 1, {222, 444, 111, 333}, {1, 1, 2, 2}, 72 {String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"), 73 String16("APP3")}); 74 75 // GMS core node is in the middle. 76 std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"), 77 CreateAttribution(222, "GMSCoreModule1"), 78 CreateAttribution(333, "App3")}; 79 80 // GMS core node is the last one. 81 std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App1"), 82 CreateAttribution(333, "App3"), 83 CreateAttribution(222, "GMSCoreModule1")}; 84 85 // GMS core node is the first one. 86 std::vector<AttributionNodeInternal> attributions3 = {CreateAttribution(222, "GMSCoreModule1"), 87 CreateAttribution(333, "App3")}; 88 89 // Single GMS core node. 90 std::vector<AttributionNodeInternal> attributions4 = {CreateAttribution(222, "GMSCoreModule1")}; 91 92 // GMS core has another uid. 93 std::vector<AttributionNodeInternal> attributions5 = {CreateAttribution(111, "App1"), 94 CreateAttribution(444, "GMSCoreModule2"), 95 CreateAttribution(333, "App3")}; 96 97 // Multiple GMS core nodes. 98 std::vector<AttributionNodeInternal> attributions6 = {CreateAttribution(444, "GMSCoreModule2"), 99 CreateAttribution(222, "GMSCoreModule1")}; 100 101 // No GMS core nodes. 102 std::vector<AttributionNodeInternal> attributions7 = {CreateAttribution(111, "App1"), 103 CreateAttribution(333, "App3")}; 104 std::vector<AttributionNodeInternal> attributions8 = {CreateAttribution(111, "App1")}; 105 106 // GMS core node with isolated uid. 107 const int isolatedUid = 666; 108 std::vector<AttributionNodeInternal> attributions9 = { 109 CreateAttribution(isolatedUid, "GMSCoreModule3")}; 110 111 std::vector<std::unique_ptr<LogEvent>> events; 112 // Events 1~4 are in the 1st bucket. 113 events.push_back(CreateAcquireWakelockEvent( 114 attributions1, "wl1", bucketStartTimeNs + 2)); 115 events.push_back(CreateAcquireWakelockEvent( 116 attributions2, "wl1", bucketStartTimeNs + 200)); 117 events.push_back(CreateAcquireWakelockEvent( 118 attributions3, "wl1", bucketStartTimeNs + bucketSizeNs - 1)); 119 events.push_back(CreateAcquireWakelockEvent( 120 attributions4, "wl1", bucketStartTimeNs + bucketSizeNs)); 121 122 // Events 5~8 are in the 3rd bucket. 123 events.push_back(CreateAcquireWakelockEvent( 124 attributions5, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 1)); 125 events.push_back(CreateAcquireWakelockEvent( 126 attributions6, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 100)); 127 events.push_back(CreateAcquireWakelockEvent( 128 attributions7, "wl2", bucketStartTimeNs + 3 * bucketSizeNs - 2)); 129 events.push_back(CreateAcquireWakelockEvent( 130 attributions8, "wl2", bucketStartTimeNs + 3 * bucketSizeNs)); 131 events.push_back(CreateAcquireWakelockEvent( 132 attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 1)); 133 events.push_back(CreateAcquireWakelockEvent( 134 attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 100)); 135 events.push_back(CreateIsolatedUidChangedEvent( 136 isolatedUid, 222, true/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs - 1)); 137 events.push_back(CreateIsolatedUidChangedEvent( 138 isolatedUid, 222, false/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs + 10)); 139 140 sortLogEventsByTimestamp(&events); 141 142 for (const auto& event : events) { 143 processor->OnLogEvent(event.get()); 144 } 145 ConfigMetricsReportList reports; 146 vector<uint8_t> buffer; 147 processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, ADB_DUMP, 148 &buffer); 149 EXPECT_TRUE(buffer.size() > 0); 150 EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); 151 backfillDimensionPath(&reports); 152 backfillStringInReport(&reports); 153 backfillStartEndTimestamp(&reports); 154 EXPECT_EQ(reports.reports_size(), 1); 155 EXPECT_EQ(reports.reports(0).metrics_size(), 1); 156 157 StatsLogReport::CountMetricDataWrapper countMetrics; 158 sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); 159 EXPECT_EQ(countMetrics.data_size(), 4); 160 161 auto data = countMetrics.data(0); 162 ValidateAttributionUidAndTagDimension( 163 data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 111, 164 "App1"); 165 EXPECT_EQ(data.bucket_info_size(), 2); 166 EXPECT_EQ(data.bucket_info(0).count(), 2); 167 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); 168 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); 169 EXPECT_EQ(data.bucket_info(1).count(), 1); 170 EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); 171 EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); 172 173 data = countMetrics.data(1); 174 ValidateAttributionUidAndTagDimension( 175 data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 222, 176 "GMSCoreModule1"); 177 EXPECT_EQ(data.bucket_info_size(), 2); 178 EXPECT_EQ(data.bucket_info(0).count(), 1); 179 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); 180 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); 181 EXPECT_EQ(data.bucket_info(1).count(), 1); 182 EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); 183 EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); 184 185 data = countMetrics.data(2); 186 ValidateAttributionUidAndTagDimension( 187 data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 222, 188 "GMSCoreModule3"); 189 EXPECT_EQ(data.bucket_info_size(), 1); 190 EXPECT_EQ(data.bucket_info(0).count(), 1); 191 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); 192 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 4 * bucketSizeNs); 193 194 data = countMetrics.data(3); 195 ValidateAttributionUidAndTagDimension( 196 data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 444, 197 "GMSCoreModule2"); 198 EXPECT_EQ(data.bucket_info_size(), 1); 199 EXPECT_EQ(data.bucket_info(0).count(), 1); 200 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); 201 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); 202} 203 204TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain) { 205 auto config = CreateStatsdConfig(Position::ALL); 206 int64_t bucketStartTimeNs = 10000000000; 207 int64_t bucketSizeNs = 208 TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; 209 210 ConfigKey cfgKey; 211 auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); 212 EXPECT_EQ(processor->mMetricsManagers.size(), 1u); 213 EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); 214 215 // Here it assumes that GMS core has two uids. 216 processor->getUidMap()->updateMap( 217 1, {222, 444, 111, 333}, {1, 1, 2, 2}, 218 {String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"), 219 String16("APP3")}); 220 221 // GMS core node is in the middle. 222 std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"), 223 CreateAttribution(222, "GMSCoreModule1"), 224 CreateAttribution(333, "App3")}; 225 226 // GMS core node is the last one. 227 std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App1"), 228 CreateAttribution(333, "App3"), 229 CreateAttribution(222, "GMSCoreModule1")}; 230 231 // GMS core node is the first one. 232 std::vector<AttributionNodeInternal> attributions3 = {CreateAttribution(222, "GMSCoreModule1"), 233 CreateAttribution(333, "App3")}; 234 235 // Single GMS core node. 236 std::vector<AttributionNodeInternal> attributions4 = {CreateAttribution(222, "GMSCoreModule1")}; 237 238 // GMS core has another uid. 239 std::vector<AttributionNodeInternal> attributions5 = {CreateAttribution(111, "App1"), 240 CreateAttribution(444, "GMSCoreModule2"), 241 CreateAttribution(333, "App3")}; 242 243 // Multiple GMS core nodes. 244 std::vector<AttributionNodeInternal> attributions6 = {CreateAttribution(444, "GMSCoreModule2"), 245 CreateAttribution(222, "GMSCoreModule1")}; 246 247 // No GMS core nodes. 248 std::vector<AttributionNodeInternal> attributions7 = {CreateAttribution(111, "App1"), 249 CreateAttribution(333, "App3")}; 250 std::vector<AttributionNodeInternal> attributions8 = {CreateAttribution(111, "App1")}; 251 252 // GMS core node with isolated uid. 253 const int isolatedUid = 666; 254 std::vector<AttributionNodeInternal> attributions9 = { 255 CreateAttribution(isolatedUid, "GMSCoreModule1")}; 256 257 std::vector<std::unique_ptr<LogEvent>> events; 258 // Events 1~4 are in the 1st bucket. 259 events.push_back(CreateAcquireWakelockEvent( 260 attributions1, "wl1", bucketStartTimeNs + 2)); 261 events.push_back(CreateAcquireWakelockEvent( 262 attributions2, "wl1", bucketStartTimeNs + 200)); 263 events.push_back(CreateAcquireWakelockEvent( 264 attributions3, "wl1", bucketStartTimeNs + bucketSizeNs - 1)); 265 events.push_back(CreateAcquireWakelockEvent( 266 attributions4, "wl1", bucketStartTimeNs + bucketSizeNs)); 267 268 // Events 5~8 are in the 3rd bucket. 269 events.push_back(CreateAcquireWakelockEvent( 270 attributions5, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 1)); 271 events.push_back(CreateAcquireWakelockEvent( 272 attributions6, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 100)); 273 events.push_back(CreateAcquireWakelockEvent( 274 attributions7, "wl2", bucketStartTimeNs + 3 * bucketSizeNs - 2)); 275 events.push_back(CreateAcquireWakelockEvent( 276 attributions8, "wl2", bucketStartTimeNs + 3 * bucketSizeNs)); 277 events.push_back(CreateAcquireWakelockEvent( 278 attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 1)); 279 events.push_back(CreateAcquireWakelockEvent( 280 attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 100)); 281 events.push_back(CreateIsolatedUidChangedEvent( 282 isolatedUid, 222, true/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs - 1)); 283 events.push_back(CreateIsolatedUidChangedEvent( 284 isolatedUid, 222, false/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs + 10)); 285 286 sortLogEventsByTimestamp(&events); 287 288 for (const auto& event : events) { 289 processor->OnLogEvent(event.get()); 290 } 291 ConfigMetricsReportList reports; 292 vector<uint8_t> buffer; 293 processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, ADB_DUMP, 294 &buffer); 295 EXPECT_TRUE(buffer.size() > 0); 296 EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); 297 backfillDimensionPath(&reports); 298 backfillStringInReport(&reports); 299 backfillStartEndTimestamp(&reports); 300 EXPECT_EQ(reports.reports_size(), 1); 301 EXPECT_EQ(reports.reports(0).metrics_size(), 1); 302 303 StatsLogReport::CountMetricDataWrapper countMetrics; 304 sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); 305 EXPECT_EQ(countMetrics.data_size(), 6); 306 307 auto data = countMetrics.data(0); 308 ValidateAttributionUidAndTagDimension( 309 data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1"); 310 EXPECT_EQ(2, data.bucket_info_size()); 311 EXPECT_EQ(1, data.bucket_info(0).count()); 312 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, 313 data.bucket_info(0).start_bucket_elapsed_nanos()); 314 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, 315 data.bucket_info(0).end_bucket_elapsed_nanos()); 316 EXPECT_EQ(1, data.bucket_info(1).count()); 317 EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, 318 data.bucket_info(1).start_bucket_elapsed_nanos()); 319 EXPECT_EQ(bucketStartTimeNs + 4 * bucketSizeNs, 320 data.bucket_info(1).end_bucket_elapsed_nanos()); 321 322 data = countMetrics.data(1); 323 ValidateUidDimension( 324 data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 222); 325 ValidateAttributionUidAndTagDimension( 326 data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1"); 327 ValidateUidDimension( 328 data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333); 329 ValidateAttributionUidAndTagDimension( 330 data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333, "App3"); 331 EXPECT_EQ(data.bucket_info_size(), 1); 332 EXPECT_EQ(data.bucket_info(0).count(), 1); 333 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); 334 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); 335 336 data = countMetrics.data(2); 337 ValidateUidDimension( 338 data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 444); 339 ValidateAttributionUidAndTagDimension( 340 data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 444, "GMSCoreModule2"); 341 ValidateUidDimension( 342 data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222); 343 ValidateAttributionUidAndTagDimension( 344 data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1"); 345 EXPECT_EQ(data.bucket_info_size(), 1); 346 EXPECT_EQ(data.bucket_info(0).count(), 1); 347 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, 348 data.bucket_info(0).start_bucket_elapsed_nanos()); 349 EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, 350 data.bucket_info(0).end_bucket_elapsed_nanos()); 351 352 data = countMetrics.data(3); 353 ValidateUidDimension( 354 data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111); 355 ValidateAttributionUidAndTagDimension( 356 data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111, "App1"); 357 ValidateUidDimension( 358 data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222); 359 ValidateAttributionUidAndTagDimension( 360 data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1"); 361 ValidateUidDimension( 362 data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333); 363 ValidateAttributionUidAndTagDimension( 364 data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333, "App3"); 365 EXPECT_EQ(data.bucket_info_size(), 1); 366 EXPECT_EQ(data.bucket_info(0).count(), 1); 367 EXPECT_EQ(bucketStartTimeNs, 368 data.bucket_info(0).start_bucket_elapsed_nanos()); 369 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, 370 data.bucket_info(0).end_bucket_elapsed_nanos()); 371 372 data = countMetrics.data(4); 373 ValidateUidDimension( 374 data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111); 375 ValidateAttributionUidAndTagDimension( 376 data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111, "App1"); 377 ValidateUidDimension( 378 data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333); 379 ValidateAttributionUidAndTagDimension( 380 data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333, "App3"); 381 ValidateUidDimension( 382 data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 222); 383 ValidateAttributionUidAndTagDimension( 384 data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1"); 385 EXPECT_EQ(data.bucket_info_size(), 1); 386 EXPECT_EQ(data.bucket_info(0).count(), 1); 387 EXPECT_EQ(bucketStartTimeNs, 388 data.bucket_info(0).start_bucket_elapsed_nanos()); 389 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, 390 data.bucket_info(0).end_bucket_elapsed_nanos()); 391 392 data = countMetrics.data(5); 393 ValidateUidDimension( 394 data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111); 395 ValidateAttributionUidAndTagDimension( 396 data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111, "App1"); 397 ValidateUidDimension( 398 data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 444); 399 ValidateAttributionUidAndTagDimension( 400 data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 444, "GMSCoreModule2"); 401 ValidateUidDimension( 402 data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333); 403 ValidateAttributionUidAndTagDimension( 404 data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333, "App3"); 405 EXPECT_EQ(data.bucket_info_size(), 1); 406 EXPECT_EQ(data.bucket_info(0).count(), 1); 407 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, 408 data.bucket_info(0).start_bucket_elapsed_nanos()); 409 EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, 410 data.bucket_info(0).end_bucket_elapsed_nanos()); 411} 412 413#else 414GTEST_LOG_(INFO) << "This test does nothing.\n"; 415#endif 416 417} // namespace statsd 418} // namespace os 419} // namespace android 420