1 // Copyright (C) 2024 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 <gmock/gmock.h>
16 #include <gtest/gtest.h>
17
18 #include <map>
19 #include <set>
20 #include <vector>
21
22 #include "src/metrics/parsing_utils/metrics_manager_util.h"
23 #include "src/stats_log.pb.h"
24 #include "src/statsd_config.pb.h"
25 #include "statsd_test_util.h"
26
27 using namespace testing;
28 using android::sp;
29 using std::map;
30 using std::set;
31 using std::vector;
32
33 #ifdef __ANDROID__
34
35 namespace android {
36 namespace os {
37 namespace statsd {
38
39 namespace {
40 constexpr int kConfigId = 12345;
41 const ConfigKey kConfigKey(0, kConfigId);
42
43 constexpr long kTimeBaseSec = 1000;
44 constexpr int64_t kBucketStartTimeNs = 10000000000;
45 constexpr int64_t kAtomsLogTimeNs = kBucketStartTimeNs + 10;
46 constexpr int64_t kReportRequestTimeNs = kBucketStartTimeNs + 100;
47
48 constexpr int32_t kAppUid = AID_APP_START + 1;
49 constexpr int32_t kInterestAtomId = 3;
50 constexpr int32_t kNotInterestedMetricId = 3;
51 constexpr int32_t kUnusedAtomId = kInterestAtomId + 100;
52
53 const string kInterestAtomMatcherName = "CUSTOM_EVENT" + std::to_string(kInterestAtomId);
54 const string kInterestedMetricName = "EVENT_METRIC_INTERESTED_IN_" + kInterestAtomMatcherName;
55
56 const int64_t kInterestedMetricId = StringToId(kInterestedMetricName);
57
58 const string kAppName = "TestApp";
59 const set<int32_t> kAppUids = {kAppUid, kAppUid + 10000};
60 const map<string, set<int32_t>> kPkgToUids = {{kAppName, kAppUids}};
61
buildGoodEventConfig()62 StatsdConfig buildGoodEventConfig() {
63 StatsdConfig config;
64 config.set_id(kConfigId);
65
66 {
67 auto atomMatcher = CreateSimpleAtomMatcher("SCREEN_IS_ON", SCREEN_STATE_ATOM_ID);
68 *config.add_atom_matcher() = atomMatcher;
69 *config.add_event_metric() =
70 createEventMetric("EVENT_METRIC_SCREEN_IS_ON", atomMatcher.id(), std::nullopt);
71 }
72
73 {
74 auto atomMatcher = CreateSimpleAtomMatcher(kInterestAtomMatcherName, kInterestAtomId);
75 *config.add_atom_matcher() = atomMatcher;
76 auto eventMetric = createEventMetric(kInterestedMetricName, atomMatcher.id(), std::nullopt);
77 *config.add_event_metric() = eventMetric;
78 EXPECT_EQ(eventMetric.id(), kInterestedMetricId);
79 }
80
81 return config;
82 }
83
getMetricsReport(MetricsManager & metricsManager,int64_t reportRequestTs)84 ConfigMetricsReport getMetricsReport(MetricsManager& metricsManager, int64_t reportRequestTs) {
85 ProtoOutputStream output;
86 set<int32_t> usedUids;
87 metricsManager.onDumpReport(reportRequestTs, reportRequestTs,
88 /*include_current_partial_bucket*/ true, /*erase_data*/ true,
89 /*dumpLatency*/ NO_TIME_CONSTRAINTS, nullptr, usedUids, &output);
90
91 ConfigMetricsReport metricsReport;
92 outputStreamToProto(&output, &metricsReport);
93 return metricsReport;
94 }
95
96 } // anonymous namespace
97
98 /**
99 * @brief Variety of test parameters combination represents always allowed atom (true/false)
100 * logged by allowed package uid (true, false) to be tested with SocketLossInfo atom propagation
101 */
102
103 enum class AllowedFromAnyUidFlag { NOT_ALLOWED = 0, ALLOWED };
104
105 enum class AllowedFromSpecificUidFlag { NOT_ALLOWED = 0, ALLOWED };
106
107 template <typename T>
108 struct TypedParam {
109 T value;
110 string label;
111
operator Tandroid::os::statsd::TypedParam112 operator T() const {
113 return value;
114 }
115 };
116
117 using AllowedFromAnyUidFlagParam = TypedParam<AllowedFromAnyUidFlag>;
118 using AllowedFromSpecificUidFlagParam = TypedParam<AllowedFromSpecificUidFlag>;
119
120 class SocketLossInfoTest
121 : public testing::TestWithParam<
122 std::tuple<AllowedFromAnyUidFlagParam, AllowedFromSpecificUidFlagParam>> {
123 protected:
124 std::shared_ptr<MetricsManager> mMetricsManager;
125
SetUp()126 void SetUp() override {
127 StatsdConfig config;
128
129 // creates config with 2 metrics where one dedicated event metric interested in
130 // kInterestAtomId
131 config = buildGoodEventConfig();
132
133 // Test parametrized on allowed_atom (true/false)
134 if (isAtomAllowedFromAnyUid()) {
135 config.add_whitelisted_atom_ids(SCREEN_STATE_ATOM_ID);
136 config.add_whitelisted_atom_ids(kInterestAtomId);
137 }
138
139 // Test parametrized on allowed_package (true/false)
140 if (isAtomFromAllowedUid()) {
141 config.add_allowed_log_source(kAppName);
142 }
143
144 sp<MockUidMap> uidMap = new StrictMock<MockUidMap>();
145 EXPECT_CALL(*uidMap, getAppUid(_)).WillRepeatedly(Invoke([](const string& pkg) {
146 const auto& it = kPkgToUids.find(pkg);
147 if (it != kPkgToUids.end()) {
148 return it->second;
149 }
150 return set<int32_t>();
151 }));
152 sp<StatsPullerManager> pullerManager = new StatsPullerManager();
153 sp<AlarmMonitor> anomalyAlarmMonitor;
154 sp<AlarmMonitor> periodicAlarmMonitor;
155
156 mMetricsManager = std::make_shared<MetricsManager>(
157 kConfigKey, config, kTimeBaseSec, kTimeBaseSec, uidMap, pullerManager,
158 anomalyAlarmMonitor, periodicAlarmMonitor);
159
160 EXPECT_TRUE(mMetricsManager->isConfigValid());
161 }
162
isAtomAllowedFromAnyUid() const163 bool isAtomAllowedFromAnyUid() const {
164 return std::get<0>(GetParam()) == AllowedFromAnyUidFlag::ALLOWED;
165 }
166
isAtomFromAllowedUid() const167 bool isAtomFromAllowedUid() const {
168 return std::get<1>(GetParam()) == AllowedFromSpecificUidFlag::ALLOWED;
169 }
170
isAtomLoggingAllowed() const171 bool isAtomLoggingAllowed() const {
172 return isAtomAllowedFromAnyUid() || isAtomFromAllowedUid();
173 }
174
175 public:
176 void doPropagationTest() __INTRODUCED_IN(__ANDROID_API_T__);
177 void doTestNotifyOnlyInterestedMetrics() __INTRODUCED_IN(__ANDROID_API_T__);
178 void doTestNotifyInterestedMetricsWithNewLoss() __INTRODUCED_IN(__ANDROID_API_T__);
179 void doTestDoNotNotifyInterestedMetricsIfNoUpdate() __INTRODUCED_IN(__ANDROID_API_T__);
180 };
181
182 INSTANTIATE_TEST_SUITE_P(
183 SocketLossInfoTest, SocketLossInfoTest,
184 testing::Combine(testing::ValuesIn<AllowedFromAnyUidFlagParam>({
185 {AllowedFromAnyUidFlag::NOT_ALLOWED, "NotAllowedFromAnyUid"},
186 {AllowedFromAnyUidFlag::ALLOWED, "AllowedFromAnyUid"},
187 }),
188 testing::ValuesIn<AllowedFromSpecificUidFlagParam>({
189 {AllowedFromSpecificUidFlag::NOT_ALLOWED,
190 "NotAllowedFromSpecificUid"},
191 {AllowedFromSpecificUidFlag::ALLOWED, "AllowedFromSpecificUid"},
192 })),
__anone45cf6f80302(const testing::TestParamInfo<SocketLossInfoTest::ParamType>& info) 193 [](const testing::TestParamInfo<SocketLossInfoTest::ParamType>& info) {
194 return std::get<0>(info.param).label + std::get<1>(info.param).label;
195 });
196
TEST_P_GUARDED(SocketLossInfoTest,PropagationTest,__ANDROID_API_T__)197 TEST_P_GUARDED(SocketLossInfoTest, PropagationTest, __ANDROID_API_T__) {
198 LogEvent eventOfInterest(kAppUid /* uid */, 0 /* pid */);
199 CreateNoValuesLogEvent(&eventOfInterest, kInterestAtomId /* atom id */, 0 /* timestamp */);
200 EXPECT_EQ(mMetricsManager->checkLogCredentials(eventOfInterest), isAtomLoggingAllowed());
201 EXPECT_EQ(mMetricsManager->mAllMetricProducers[0]->mDataCorruptedDueToSocketLoss,
202 MetricProducer::DataCorruptionSeverity::kNone);
203
204 const auto eventSocketLossReported = createSocketLossInfoLogEvent(kAppUid, kInterestAtomId);
205
206 // the STATS_SOCKET_LOSS_REPORTED on its own will not be propagated/consumed by any metric
207 EXPECT_EQ(mMetricsManager->checkLogCredentials(*eventSocketLossReported.get()),
208 isAtomFromAllowedUid());
209
210 // the loss info for an atom kInterestAtomId will be evaluated even when
211 // STATS_SOCKET_LOSS_REPORTED atom is not explicitly in allowed list
212 mMetricsManager->onLogEvent(*eventSocketLossReported.get());
213
214 // check that corresponding event metric was properly updated (or not) with loss info
215 for (const auto& metricProducer : mMetricsManager->mAllMetricProducers) {
216 if (metricProducer->getMetricId() == kInterestedMetricId) {
217 EXPECT_EQ(metricProducer->mDataCorruptedDueToSocketLoss !=
218 MetricProducer::DataCorruptionSeverity::kNone,
219 isAtomLoggingAllowed());
220 } else {
221 EXPECT_EQ(metricProducer->mDataCorruptedDueToSocketLoss,
222 MetricProducer::DataCorruptionSeverity::kNone);
223 }
224 }
225 }
226
TEST_P_GUARDED(SocketLossInfoTest,TestNotifyOnlyInterestedMetrics,__ANDROID_API_T__)227 TEST_P_GUARDED(SocketLossInfoTest, TestNotifyOnlyInterestedMetrics, __ANDROID_API_T__) {
228 const auto eventSocketLossReported = createSocketLossInfoLogEvent(kAppUid, kUnusedAtomId);
229
230 mMetricsManager->onLogEvent(*eventSocketLossReported.get());
231 ConfigMetricsReport metricsReport = getMetricsReport(*mMetricsManager, kReportRequestTimeNs);
232 EXPECT_EQ(metricsReport.metrics_size(), 2);
233 EXPECT_THAT(metricsReport.metrics(),
234 Each(Property(&StatsLogReport::data_corrupted_reason_size, 0)));
235
236 const auto usedEventSocketLossReported = createSocketLossInfoLogEvent(kAppUid, kInterestAtomId);
237 mMetricsManager->onLogEvent(*usedEventSocketLossReported.get());
238
239 metricsReport = getMetricsReport(*mMetricsManager, kReportRequestTimeNs + 100);
240 ASSERT_EQ(metricsReport.metrics_size(), 2);
241 for (const auto& statsLogReport : metricsReport.metrics()) {
242 if (statsLogReport.metric_id() == kInterestedMetricId && isAtomLoggingAllowed()) {
243 EXPECT_THAT(statsLogReport.data_corrupted_reason(),
244 ElementsAre(DATA_CORRUPTED_SOCKET_LOSS));
245 } else {
246 EXPECT_EQ(statsLogReport.data_corrupted_reason_size(), 0);
247 }
248 }
249 }
250
TEST_P_GUARDED(SocketLossInfoTest,TestNotifyInterestedMetricsWithNewLoss,__ANDROID_API_T__)251 TEST_P_GUARDED(SocketLossInfoTest, TestNotifyInterestedMetricsWithNewLoss, __ANDROID_API_T__) {
252 auto usedEventSocketLossReported = createSocketLossInfoLogEvent(kAppUid, kInterestAtomId);
253 mMetricsManager->onLogEvent(*usedEventSocketLossReported.get());
254
255 ConfigMetricsReport metricsReport = getMetricsReport(*mMetricsManager, kReportRequestTimeNs);
256 ASSERT_EQ(metricsReport.metrics_size(), 2);
257 for (const auto& statsLogReport : metricsReport.metrics()) {
258 if (statsLogReport.metric_id() == kInterestedMetricId && isAtomLoggingAllowed()) {
259 EXPECT_THAT(statsLogReport.data_corrupted_reason(),
260 ElementsAre(DATA_CORRUPTED_SOCKET_LOSS));
261 } else {
262 EXPECT_EQ(statsLogReport.data_corrupted_reason_size(), 0);
263 }
264 }
265
266 // new socket loss event as result event metric should be notified about loss again
267 usedEventSocketLossReported = createSocketLossInfoLogEvent(kAppUid, kInterestAtomId);
268 mMetricsManager->onLogEvent(*usedEventSocketLossReported.get());
269
270 metricsReport = getMetricsReport(*mMetricsManager, kReportRequestTimeNs + 100);
271 ASSERT_EQ(metricsReport.metrics_size(), 2);
272 for (const auto& statsLogReport : metricsReport.metrics()) {
273 if (statsLogReport.metric_id() == kInterestedMetricId && isAtomLoggingAllowed()) {
274 EXPECT_THAT(statsLogReport.data_corrupted_reason(),
275 ElementsAre(DATA_CORRUPTED_SOCKET_LOSS));
276 } else {
277 EXPECT_EQ(statsLogReport.data_corrupted_reason_size(), 0);
278 }
279 }
280 }
281
TEST_P_GUARDED(SocketLossInfoTest,TestDoNotNotifyInterestedMetricsIfNoUpdate,__ANDROID_API_T__)282 TEST_P_GUARDED(SocketLossInfoTest, TestDoNotNotifyInterestedMetricsIfNoUpdate, __ANDROID_API_T__) {
283 auto usedEventSocketLossReported = createSocketLossInfoLogEvent(kAppUid, kInterestAtomId);
284 mMetricsManager->onLogEvent(*usedEventSocketLossReported.get());
285
286 ConfigMetricsReport metricsReport = getMetricsReport(*mMetricsManager, kReportRequestTimeNs);
287 ASSERT_EQ(metricsReport.metrics_size(), 2);
288 for (const auto& statsLogReport : metricsReport.metrics()) {
289 if (statsLogReport.metric_id() == kInterestedMetricId && isAtomLoggingAllowed()) {
290 EXPECT_THAT(statsLogReport.data_corrupted_reason(),
291 ElementsAre(DATA_CORRUPTED_SOCKET_LOSS));
292 } else {
293 EXPECT_EQ(statsLogReport.data_corrupted_reason_size(), 0);
294 }
295 }
296
297 // no more dropped events as result event metric should not be notified about loss events
298
299 metricsReport = getMetricsReport(*mMetricsManager, kReportRequestTimeNs + 100);
300 EXPECT_EQ(metricsReport.metrics_size(), 2);
301 EXPECT_THAT(metricsReport.metrics(),
302 Each(Property(&StatsLogReport::data_corrupted_reason_size, 0)));
303 }
304
305 class DataCorruptionQueueOverflowTest : public testing::Test {
306 protected:
307 std::shared_ptr<MetricsManager> mMetricsManager;
308
SetUp()309 void SetUp() override {
310 StatsdStats::getInstance().reset();
311
312 sp<UidMap> uidMap;
313 sp<StatsPullerManager> pullerManager = new StatsPullerManager();
314
315 // there will be one event metric interested in kInterestAtomId
316 StatsdConfig config = buildGoodEventConfig();
317
318 mMetricsManager =
319 std::make_shared<MetricsManager>(kConfigKey, config, kTimeBaseSec, kTimeBaseSec,
320 uidMap, pullerManager, nullptr, nullptr);
321
322 EXPECT_TRUE(mMetricsManager->isConfigValid());
323 }
324
TearDown()325 void TearDown() override {
326 StatsdStats::getInstance().reset();
327 }
328 };
329
TEST_F(DataCorruptionQueueOverflowTest,TestNotifyOnlyInterestedMetrics)330 TEST_F(DataCorruptionQueueOverflowTest, TestNotifyOnlyInterestedMetrics) {
331 StatsdStats::getInstance().noteEventQueueOverflow(kAtomsLogTimeNs, kInterestAtomId,
332 /*isSkipped*/ false);
333
334 StatsdStats::getInstance().noteEventQueueOverflow(kAtomsLogTimeNs, kUnusedAtomId,
335 /*isSkipped*/ false);
336
337 EXPECT_TRUE(mMetricsManager->mQueueOverflowAtomsStats.empty());
338 ConfigMetricsReport metricsReport = getMetricsReport(*mMetricsManager, kReportRequestTimeNs);
339 ASSERT_EQ(metricsReport.metrics_size(), 2);
340 EXPECT_THAT(mMetricsManager->mQueueOverflowAtomsStats,
341 UnorderedElementsAre(std::make_pair(kInterestAtomId, 1),
342 std::make_pair(kUnusedAtomId, 1)));
343
344 for (const auto& statsLogReport : metricsReport.metrics()) {
345 if (statsLogReport.metric_id() == kInterestedMetricId) {
346 EXPECT_THAT(statsLogReport.data_corrupted_reason(),
347 ElementsAre(DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW));
348 } else {
349 EXPECT_EQ(statsLogReport.data_corrupted_reason_size(), 0);
350 }
351 }
352 }
353
TEST_F(DataCorruptionQueueOverflowTest,TestNotifyInterestedMetricsWithNewLoss)354 TEST_F(DataCorruptionQueueOverflowTest, TestNotifyInterestedMetricsWithNewLoss) {
355 StatsdStats::getInstance().noteEventQueueOverflow(kAtomsLogTimeNs, kInterestAtomId,
356 /*isSkipped*/ false);
357
358 ConfigMetricsReport metricsReport = getMetricsReport(*mMetricsManager, kReportRequestTimeNs);
359 ASSERT_EQ(metricsReport.metrics_size(), 2);
360 ASSERT_EQ(mMetricsManager->mQueueOverflowAtomsStats.size(), 1);
361 EXPECT_EQ(mMetricsManager->mQueueOverflowAtomsStats[kInterestAtomId], 1);
362
363 for (const auto& statsLogReport : metricsReport.metrics()) {
364 if (statsLogReport.metric_id() == kInterestedMetricId) {
365 EXPECT_THAT(statsLogReport.data_corrupted_reason(),
366 ElementsAre(DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW));
367 } else {
368 EXPECT_EQ(statsLogReport.data_corrupted_reason_size(), 0);
369 }
370 }
371
372 // new dropped event as result event metric should be notified about loss events
373 StatsdStats::getInstance().noteEventQueueOverflow(kReportRequestTimeNs + 100, kInterestAtomId,
374 /*isSkipped*/ false);
375
376 metricsReport = getMetricsReport(*mMetricsManager, kReportRequestTimeNs + 200);
377 ASSERT_EQ(metricsReport.metrics_size(), 2);
378 ASSERT_EQ(mMetricsManager->mQueueOverflowAtomsStats.size(), 1);
379 EXPECT_EQ(mMetricsManager->mQueueOverflowAtomsStats[kInterestAtomId], 2);
380
381 for (const auto& statsLogReport : metricsReport.metrics()) {
382 if (statsLogReport.metric_id() == kInterestedMetricId) {
383 EXPECT_THAT(statsLogReport.data_corrupted_reason(),
384 ElementsAre(DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW));
385 } else {
386 EXPECT_EQ(statsLogReport.data_corrupted_reason_size(), 0);
387 }
388 }
389 }
390
TEST_F(DataCorruptionQueueOverflowTest,TestDoNotNotifyInterestedMetricsIfNoUpdate)391 TEST_F(DataCorruptionQueueOverflowTest, TestDoNotNotifyInterestedMetricsIfNoUpdate) {
392 StatsdStats::getInstance().noteEventQueueOverflow(kAtomsLogTimeNs, kInterestAtomId,
393 /*isSkipped*/ false);
394
395 ConfigMetricsReport metricsReport = getMetricsReport(*mMetricsManager, kReportRequestTimeNs);
396 ASSERT_EQ(metricsReport.metrics_size(), 2);
397 ASSERT_EQ(mMetricsManager->mQueueOverflowAtomsStats.size(), 1);
398 EXPECT_EQ(mMetricsManager->mQueueOverflowAtomsStats[kInterestAtomId], 1);
399
400 for (const auto& statsLogReport : metricsReport.metrics()) {
401 if (statsLogReport.metric_id() == kInterestedMetricId) {
402 EXPECT_THAT(statsLogReport.data_corrupted_reason(),
403 ElementsAre(DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW));
404 } else {
405 EXPECT_EQ(statsLogReport.data_corrupted_reason_size(), 0);
406 }
407 }
408
409 // no more dropped events as result event metric should not be notified about loss events
410
411 metricsReport = getMetricsReport(*mMetricsManager, kReportRequestTimeNs + 100);
412 ASSERT_EQ(mMetricsManager->mQueueOverflowAtomsStats.size(), 1);
413 EXPECT_EQ(mMetricsManager->mQueueOverflowAtomsStats[kInterestAtomId], 1);
414 EXPECT_EQ(metricsReport.metrics_size(), 2);
415 EXPECT_THAT(metricsReport.metrics(),
416 Each(Property(&StatsLogReport::data_corrupted_reason_size, 0)));
417 }
418
TEST_F(DataCorruptionQueueOverflowTest,TestDoNotNotifyNewInterestedMetricsIfNoUpdate)419 TEST_F(DataCorruptionQueueOverflowTest, TestDoNotNotifyNewInterestedMetricsIfNoUpdate) {
420 const int32_t kNewInterestAtomId = kUnusedAtomId + 1;
421
422 StatsdStats::getInstance().noteEventQueueOverflow(kAtomsLogTimeNs, kInterestAtomId,
423 /*isSkipped*/ false);
424 StatsdStats::getInstance().noteEventQueueOverflow(kAtomsLogTimeNs, kNewInterestAtomId,
425 /*isSkipped*/ false);
426
427 ConfigMetricsReport metricsReport = getMetricsReport(*mMetricsManager, kReportRequestTimeNs);
428 ASSERT_EQ(metricsReport.metrics_size(), 2);
429 EXPECT_THAT(mMetricsManager->mQueueOverflowAtomsStats,
430 UnorderedElementsAre(std::make_pair(kInterestAtomId, 1),
431 std::make_pair(kNewInterestAtomId, 1)));
432
433 for (const auto& statsLogReport : metricsReport.metrics()) {
434 if (statsLogReport.metric_id() == kInterestedMetricId) {
435 EXPECT_THAT(statsLogReport.data_corrupted_reason(),
436 ElementsAre(DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW));
437 } else {
438 EXPECT_EQ(statsLogReport.data_corrupted_reason_size(), 0);
439 }
440 }
441
442 // adding 2 more metrics interested in atoms to update existing config
443 // new metrics should not be updated with loss atom info from queue overflow
444 // since atom loss events happen before metrics were added
445 {
446 StatsdConfig config = buildGoodEventConfig();
447 const int64_t matcherId = StringToId(kInterestAtomMatcherName);
448 *config.add_event_metric() =
449 createEventMetric("EVENT_METRIC_FOR_EXISTING_ATOM", matcherId, std::nullopt);
450
451 // adding new metric which is interested on unused atom before
452 // for which lost event was detected
453 auto atomMatcher = CreateSimpleAtomMatcher("NewTestMatcher", kNewInterestAtomId);
454 *config.add_atom_matcher() = atomMatcher;
455 *config.add_event_metric() =
456 createEventMetric("EVENT_METRIC_FOR_NEW_ATOM", atomMatcher.id(), std::nullopt);
457
458 mMetricsManager->updateConfig(config, kReportRequestTimeNs + 100,
459 kReportRequestTimeNs + 100, nullptr, nullptr);
460 }
461
462 // no more dropped events as result event metric should not be notified about loss events
463
464 metricsReport = getMetricsReport(*mMetricsManager, kReportRequestTimeNs + 200);
465 EXPECT_THAT(mMetricsManager->mQueueOverflowAtomsStats,
466 UnorderedElementsAre(std::make_pair(kInterestAtomId, 1),
467 std::make_pair(kNewInterestAtomId, 1)));
468 EXPECT_EQ(metricsReport.metrics_size(), 4);
469 EXPECT_THAT(metricsReport.metrics(),
470 Each(Property(&StatsLogReport::data_corrupted_reason_size, 0)));
471 }
472
TEST_GUARDED(DataCorruptionTest,TestStateLostPropagation,__ANDROID_API_T__)473 TEST_GUARDED(DataCorruptionTest, TestStateLostPropagation, __ANDROID_API_T__) {
474 // Initialize config with state and count metric
475 StatsdConfig config;
476
477 auto syncStartMatcher = CreateSyncStartAtomMatcher();
478 *config.add_atom_matcher() = syncStartMatcher;
479
480 auto state = CreateScreenState();
481 *config.add_state() = state;
482
483 // Create count metric that slices by screen state.
484 auto countMetric = config.add_count_metric();
485 countMetric->set_id(kNotInterestedMetricId);
486 countMetric->set_what(syncStartMatcher.id());
487 countMetric->set_bucket(TimeUnit::FIVE_MINUTES);
488 countMetric->add_slice_by_state(state.id());
489
490 // Initialize StatsLogProcessor.
491 const uint64_t bucketSizeNs =
492 TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
493 int uid = 12345;
494 int64_t cfgId = 98765;
495 ConfigKey cfgKey(uid, cfgId);
496 auto processor =
497 CreateStatsLogProcessor(kBucketStartTimeNs, kBucketStartTimeNs, config, cfgKey);
498
499 // Check that CountMetricProducer was initialized correctly.
500 ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
501 sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
502 EXPECT_TRUE(metricsManager->isConfigValid());
503
504 // Check that StateTrackers were initialized correctly.
505 EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
506 EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
507
508 auto usedEventSocketLossReported =
509 createSocketLossInfoLogEvent(AID_SYSTEM, SCREEN_STATE_ATOM_ID);
510 processor->OnLogEvent(usedEventSocketLossReported.get());
511
512 ConfigMetricsReport metricsReport = getMetricsReport(*metricsManager, kReportRequestTimeNs);
513 ASSERT_EQ(metricsReport.metrics_size(), 1);
514 const auto& statsLogReport = metricsReport.metrics(0);
515 EXPECT_THAT(statsLogReport.data_corrupted_reason(), ElementsAre(DATA_CORRUPTED_SOCKET_LOSS));
516 }
517
TEST(DataCorruptionTest,TestStateLostFromQueueOverflowPropagation)518 TEST(DataCorruptionTest, TestStateLostFromQueueOverflowPropagation) {
519 // Initialize config with state and count metric
520 StatsdConfig config;
521
522 auto syncStartMatcher = CreateSyncStartAtomMatcher();
523 *config.add_atom_matcher() = syncStartMatcher;
524
525 auto state = CreateScreenState();
526 *config.add_state() = state;
527
528 // Create count metric that slices by screen state.
529 auto countMetric = config.add_count_metric();
530 countMetric->set_id(kNotInterestedMetricId);
531 countMetric->set_what(syncStartMatcher.id());
532 countMetric->set_bucket(TimeUnit::FIVE_MINUTES);
533 countMetric->add_slice_by_state(state.id());
534
535 // Initialize StatsLogProcessor.
536 const uint64_t bucketSizeNs =
537 TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
538 int uid = 12345;
539 int64_t cfgId = 98765;
540 ConfigKey cfgKey(uid, cfgId);
541 auto processor =
542 CreateStatsLogProcessor(kBucketStartTimeNs, kBucketStartTimeNs, config, cfgKey);
543
544 // Check that CountMetricProducer was initialized correctly.
545 ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
546 sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
547 EXPECT_TRUE(metricsManager->isConfigValid());
548
549 // Check that StateTrackers were initialized correctly.
550 EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
551 EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
552
553 StatsdStats::getInstance().noteEventQueueOverflow(kAtomsLogTimeNs, SCREEN_STATE_ATOM_ID,
554 /*isSkipped*/ false);
555
556 vector<uint8_t> buffer;
557 ConfigMetricsReportList reports;
558 processor->onDumpReport(cfgKey, kBucketStartTimeNs + bucketSizeNs * 2 + 1, false, true,
559 ADB_DUMP, FAST, &buffer);
560 ASSERT_GT(buffer.size(), 0);
561 EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
562 ASSERT_EQ(1, reports.reports_size());
563 const ConfigMetricsReport metricsReport = reports.reports(0);
564 ASSERT_EQ(metricsReport.metrics_size(), 1);
565 const auto& statsLogReport = metricsReport.metrics(0);
566 EXPECT_THAT(statsLogReport.data_corrupted_reason(),
567 ElementsAre(DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW));
568 }
569
570 } // namespace statsd
571 } // namespace os
572 } // namespace android
573
574 #else
575 GTEST_LOG_(INFO) << "This test does nothing.\n";
576 #endif
577