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