| // Copyright (C) 2023 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <android-modules-utils/sdk_level.h> |
| #include <gtest/gtest.h> |
| |
| #include <vector> |
| |
| #include "android-base/stringprintf.h" |
| #include "flags/FlagProvider.h" |
| #include "src/StatsLogProcessor.h" |
| #include "src/state/StateTracker.h" |
| #include "src/stats_log_util.h" |
| #include "src/storage/StorageManager.h" |
| #include "src/utils/RestrictedPolicyManager.h" |
| #include "stats_annotations.h" |
| #include "tests/statsd_test_util.h" |
| |
| namespace android { |
| namespace os { |
| namespace statsd { |
| |
| using android::modules::sdklevel::IsAtLeastU; |
| using base::StringPrintf; |
| |
| #ifdef __ANDROID__ |
| |
| namespace { |
| const int64_t oneMonthLater = getWallClockNs() + 31 * 24 * 3600 * NS_PER_SEC; |
| const int64_t configId = 12345; |
| const string delegate_package_name = "com.test.restricted.metrics.package"; |
| const int32_t delegate_uid = 1005; |
| const string config_package_name = "com.test.config.package"; |
| const int32_t config_app_uid = 123; |
| const ConfigKey configKey(config_app_uid, configId); |
| const int64_t eightDaysAgo = getWallClockNs() - 8 * 24 * 3600 * NS_PER_SEC; |
| const int64_t oneDayAgo = getWallClockNs() - 1 * 24 * 3600 * NS_PER_SEC; |
| } // anonymous namespace |
| |
| // Setup for test fixture. |
| class RestrictedEventMetricE2eTest : public ::testing::Test { |
| protected: |
| shared_ptr<MockStatsQueryCallback> mockStatsQueryCallback; |
| vector<string> queryDataResult; |
| vector<string> columnNamesResult; |
| vector<int32_t> columnTypesResult; |
| int32_t rowCountResult = 0; |
| string error; |
| sp<UidMap> uidMap; |
| sp<StatsLogProcessor> processor; |
| int32_t atomTag; |
| int64_t restrictedMetricId; |
| int64_t configAddedTimeNs; |
| StatsdConfig config; |
| |
| private: |
| void SetUp() override { |
| if (!IsAtLeastU()) { |
| GTEST_SKIP(); |
| } |
| |
| mockStatsQueryCallback = SharedRefBase::make<StrictMock<MockStatsQueryCallback>>(); |
| EXPECT_CALL(*mockStatsQueryCallback, sendResults(_, _, _, _)) |
| .Times(AnyNumber()) |
| .WillRepeatedly(Invoke( |
| [this](const vector<string>& queryData, const vector<string>& columnNames, |
| const vector<int32_t>& columnTypes, int32_t rowCount) { |
| queryDataResult = queryData; |
| columnNamesResult = columnNames; |
| columnTypesResult = columnTypes; |
| rowCountResult = rowCount; |
| error = ""; |
| return Status::ok(); |
| })); |
| EXPECT_CALL(*mockStatsQueryCallback, sendFailure(_)) |
| .Times(AnyNumber()) |
| .WillRepeatedly(Invoke([this](const string& err) { |
| error = err; |
| queryDataResult.clear(); |
| columnNamesResult.clear(); |
| columnTypesResult.clear(); |
| rowCountResult = 0; |
| return Status::ok(); |
| })); |
| |
| config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. |
| |
| atomTag = 999; |
| AtomMatcher restrictedAtomMatcher = CreateSimpleAtomMatcher("restricted_matcher", atomTag); |
| *config.add_atom_matcher() = restrictedAtomMatcher; |
| |
| EventMetric restrictedEventMetric = |
| createEventMetric("RestrictedMetricLogged", restrictedAtomMatcher.id(), nullopt); |
| *config.add_event_metric() = restrictedEventMetric; |
| restrictedMetricId = restrictedEventMetric.id(); |
| |
| config.set_restricted_metrics_delegate_package_name(delegate_package_name.c_str()); |
| |
| const int64_t baseTimeNs = 0; // 0:00 |
| configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01 |
| |
| uidMap = new UidMap(); |
| uidMap->updateApp(configAddedTimeNs, String16(delegate_package_name.c_str()), |
| /*uid=*/delegate_uid, /*versionCode=*/1, |
| /*versionString=*/String16("v2"), |
| /*installer=*/String16(""), /*certificateHash=*/{}); |
| uidMap->updateApp(configAddedTimeNs + 1, String16(config_package_name.c_str()), |
| /*uid=*/config_app_uid, /*versionCode=*/1, |
| /*versionString=*/String16("v2"), |
| /*installer=*/String16(""), /*certificateHash=*/{}); |
| |
| processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, configKey, |
| /*puller=*/nullptr, /*atomTag=*/0, uidMap); |
| } |
| |
| void TearDown() override { |
| Mock::VerifyAndClear(mockStatsQueryCallback.get()); |
| queryDataResult.clear(); |
| columnNamesResult.clear(); |
| columnTypesResult.clear(); |
| rowCountResult = 0; |
| error = ""; |
| dbutils::deleteDb(configKey); |
| dbutils::deleteDb(ConfigKey(config_app_uid + 1, configId)); |
| FlagProvider::getInstance().resetOverrides(); |
| } |
| }; |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestQueryThreeEvents) { |
| std::vector<std::unique_ptr<LogEvent>> events; |
| |
| events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100)); |
| events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 200)); |
| events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 300)); |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| } |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| processor->querySql(query.str(), /*minSqlClientVersion=*/0, |
| /*policyConfig=*/{}, mockStatsQueryCallback, |
| /*configKey=*/configId, /*configPackage=*/config_package_name, |
| /*callingUid=*/delegate_uid); |
| |
| EXPECT_EQ(rowCountResult, 3); |
| EXPECT_THAT(queryDataResult, ElementsAre(to_string(atomTag), to_string(configAddedTimeNs + 100), |
| _, // wallClockNs |
| _, // field_1 |
| to_string(atomTag), to_string(configAddedTimeNs + 200), |
| _, // wallClockNs |
| _, // field_1 |
| to_string(atomTag), to_string(configAddedTimeNs + 300), |
| _, // wallClockNs |
| _ // field_1 |
| )); |
| |
| EXPECT_THAT(columnNamesResult, |
| ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1")); |
| |
| EXPECT_THAT(columnTypesResult, |
| ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER)); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestInvalidSchemaIncreasingFieldCount) { |
| std::vector<std::unique_ptr<LogEvent>> events; |
| |
| AStatsEvent* statsEvent = AStatsEvent_obtain(); |
| AStatsEvent_setAtomId(statsEvent, atomTag); |
| AStatsEvent_addInt32Annotation(statsEvent, ASTATSLOG_ANNOTATION_ID_RESTRICTION_CATEGORY, |
| ASTATSLOG_RESTRICTION_CATEGORY_DIAGNOSTIC); |
| AStatsEvent_overwriteTimestamp(statsEvent, configAddedTimeNs + 200); |
| // This event has two extra fields |
| AStatsEvent_writeString(statsEvent, "111"); |
| AStatsEvent_writeInt32(statsEvent, 11); |
| AStatsEvent_writeFloat(statsEvent, 11.0); |
| std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); |
| parseStatsEventToLogEvent(statsEvent, logEvent.get()); |
| |
| events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100)); |
| events.push_back(std::move(logEvent)); |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| processor->WriteDataToDisk(DEVICE_SHUTDOWN, FAST, |
| event->GetElapsedTimestampNs() + 20 * NS_PER_SEC, |
| getWallClockNs()); |
| } |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| processor->querySql(query.str(), /*minSqlClientVersion=*/0, |
| /*policyConfig=*/{}, mockStatsQueryCallback, |
| /*configKey=*/configId, /*configPackage=*/config_package_name, |
| /*callingUid=*/delegate_uid); |
| |
| EXPECT_EQ(rowCountResult, 1); |
| // Event 2 rejected. |
| EXPECT_THAT(queryDataResult, ElementsAre(to_string(atomTag), to_string(configAddedTimeNs + 100), |
| _, // wallClockNs |
| _ // field_1 |
| )); |
| |
| EXPECT_THAT(columnNamesResult, |
| ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1")); |
| |
| EXPECT_THAT(columnTypesResult, |
| ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER)); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestInvalidSchemaDecreasingFieldCount) { |
| std::vector<std::unique_ptr<LogEvent>> events; |
| |
| AStatsEvent* statsEvent = AStatsEvent_obtain(); |
| AStatsEvent_setAtomId(statsEvent, atomTag); |
| AStatsEvent_addInt32Annotation(statsEvent, ASTATSLOG_ANNOTATION_ID_RESTRICTION_CATEGORY, |
| ASTATSLOG_RESTRICTION_CATEGORY_DIAGNOSTIC); |
| AStatsEvent_overwriteTimestamp(statsEvent, configAddedTimeNs + 100); |
| // This event has one extra field. |
| AStatsEvent_writeString(statsEvent, "111"); |
| AStatsEvent_writeInt32(statsEvent, 11); |
| std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); |
| parseStatsEventToLogEvent(statsEvent, logEvent.get()); |
| |
| events.push_back(std::move(logEvent)); |
| events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 200)); |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| processor->WriteDataToDisk(DEVICE_SHUTDOWN, FAST, |
| event->GetElapsedTimestampNs() + 20 * NS_PER_SEC, |
| getWallClockNs()); |
| } |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| processor->querySql(query.str(), /*minSqlClientVersion=*/0, |
| /*policyConfig=*/{}, mockStatsQueryCallback, |
| /*configKey=*/configId, /*configPackage=*/config_package_name, |
| /*callingUid=*/delegate_uid); |
| |
| EXPECT_EQ(rowCountResult, 1); |
| // Event 2 Rejected |
| EXPECT_THAT(queryDataResult, ElementsAre(to_string(atomTag), to_string(configAddedTimeNs + 100), |
| _, // wallClockNs |
| "111", // field_1 |
| to_string(11) // field_2 |
| )); |
| |
| EXPECT_THAT(columnNamesResult, ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", |
| "field_1", "field_2")); |
| |
| EXPECT_THAT(columnTypesResult, ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, |
| SQLITE_TEXT, SQLITE_INTEGER)); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestInvalidSchemaDifferentFieldType) { |
| std::vector<std::unique_ptr<LogEvent>> events; |
| |
| AStatsEvent* statsEvent = AStatsEvent_obtain(); |
| AStatsEvent_setAtomId(statsEvent, atomTag); |
| AStatsEvent_addInt32Annotation(statsEvent, ASTATSLOG_ANNOTATION_ID_RESTRICTION_CATEGORY, |
| ASTATSLOG_RESTRICTION_CATEGORY_DIAGNOSTIC); |
| AStatsEvent_overwriteTimestamp(statsEvent, configAddedTimeNs + 200); |
| // This event has a string instead of an int field |
| AStatsEvent_writeString(statsEvent, "test_string"); |
| std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); |
| parseStatsEventToLogEvent(statsEvent, logEvent.get()); |
| |
| events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100)); |
| events.push_back(std::move(logEvent)); |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| processor->WriteDataToDisk(DEVICE_SHUTDOWN, FAST, |
| event->GetElapsedTimestampNs() + 20 * NS_PER_SEC, |
| getWallClockNs()); |
| } |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| processor->querySql(query.str(), /*minSqlClientVersion=*/0, |
| /*policyConfig=*/{}, mockStatsQueryCallback, |
| /*configKey=*/configId, /*configPackage=*/config_package_name, |
| /*callingUid=*/delegate_uid); |
| |
| EXPECT_EQ(rowCountResult, 1); |
| // Event 2 rejected. |
| EXPECT_THAT(queryDataResult, ElementsAre(to_string(atomTag), to_string(configAddedTimeNs + 100), |
| _, // wallClockNs |
| _ // field_1 |
| )); |
| EXPECT_THAT(columnNamesResult, |
| ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1")); |
| EXPECT_THAT(columnTypesResult, |
| ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER)); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestNewMetricSchemaAcrossReboot) { |
| int64_t currentWallTimeNs = getWallClockNs(); |
| int64_t originalEventElapsedTime = configAddedTimeNs + 100; |
| std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(atomTag, originalEventElapsedTime); |
| processor->OnLogEvent(event1.get()); |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| processor->querySql(query.str(), /*minSqlClientVersion=*/0, |
| /*policyConfig=*/{}, mockStatsQueryCallback, |
| /*configKey=*/configId, /*configPackage=*/config_package_name, |
| /*callingUid=*/delegate_uid); |
| EXPECT_EQ(rowCountResult, 1); |
| EXPECT_THAT(queryDataResult, |
| ElementsAre(to_string(atomTag), to_string(originalEventElapsedTime), |
| _, // wallTimestampNs |
| _ // field_1 |
| )); |
| EXPECT_THAT(columnNamesResult, |
| ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1")); |
| EXPECT_THAT(columnTypesResult, |
| ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER)); |
| |
| // Create a new processor to simulate a reboot |
| auto processor2 = |
| CreateStatsLogProcessor(/*baseTimeNs=*/0, configAddedTimeNs, config, configKey, |
| /*puller=*/nullptr, /*atomTag=*/0, uidMap); |
| |
| // Create a restricted event with one extra field. |
| AStatsEvent* statsEvent = AStatsEvent_obtain(); |
| AStatsEvent_setAtomId(statsEvent, atomTag); |
| AStatsEvent_addInt32Annotation(statsEvent, ASTATSLOG_ANNOTATION_ID_RESTRICTION_CATEGORY, |
| ASTATSLOG_RESTRICTION_CATEGORY_DIAGNOSTIC); |
| AStatsEvent_overwriteTimestamp(statsEvent, originalEventElapsedTime + 100); |
| // This event has one extra field. |
| AStatsEvent_writeString(statsEvent, "111"); |
| AStatsEvent_writeInt32(statsEvent, 11); |
| std::unique_ptr<LogEvent> event2 = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); |
| parseStatsEventToLogEvent(statsEvent, event2.get()); |
| processor2->OnLogEvent(event2.get()); |
| |
| processor2->querySql(query.str(), /*minSqlClientVersion=*/0, |
| /*policyConfig=*/{}, mockStatsQueryCallback, |
| /*configKey=*/configId, /*configPackage=*/config_package_name, |
| /*callingUid=*/delegate_uid); |
| EXPECT_EQ(rowCountResult, 1); |
| EXPECT_THAT(queryDataResult, |
| ElementsAre(to_string(atomTag), to_string(originalEventElapsedTime + 100), |
| _, // wallTimestampNs |
| to_string(111), // field_1 |
| to_string(11) // field_2 |
| )); |
| EXPECT_THAT(columnNamesResult, ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", |
| "field_1", "field_2")); |
| EXPECT_THAT(columnTypesResult, ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, |
| SQLITE_TEXT, SQLITE_INTEGER)); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestOneEventMultipleUids) { |
| uidMap->updateApp(configAddedTimeNs, String16(delegate_package_name.c_str()), |
| /*uid=*/delegate_uid + 1, /*versionCode=*/1, |
| /*versionString=*/String16("v2"), |
| /*installer=*/String16(""), /*certificateHash=*/{}); |
| uidMap->updateApp(configAddedTimeNs + 1, String16(config_package_name.c_str()), |
| /*uid=*/config_app_uid + 1, /*versionCode=*/1, |
| /*versionString=*/String16("v2"), |
| /*installer=*/String16(""), /*certificateHash=*/{}); |
| |
| std::vector<std::unique_ptr<LogEvent>> events; |
| |
| events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100)); |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| } |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| processor->querySql(query.str(), /*minSqlClientVersion=*/0, |
| /*policyConfig=*/{}, mockStatsQueryCallback, |
| /*configKey=*/configId, /*configPackage=*/config_package_name, |
| /*callingUid=*/delegate_uid); |
| |
| EXPECT_EQ(rowCountResult, 1); |
| EXPECT_THAT(queryDataResult, ElementsAre(to_string(atomTag), to_string(configAddedTimeNs + 100), |
| _, // wallClockNs |
| _ // field_1 |
| )); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestOneEventStaticUid) { |
| ConfigKey key2(2000, configId); // shell uid |
| processor->OnConfigUpdated(configAddedTimeNs + 1 * NS_PER_SEC, key2, config); |
| |
| std::vector<std::unique_ptr<LogEvent>> events; |
| |
| events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100)); |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| } |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| processor->querySql(query.str(), /*minSqlClientVersion=*/0, |
| /*policyConfig=*/{}, mockStatsQueryCallback, |
| /*configKey=*/configId, /*configPackage=*/"AID_SHELL", |
| /*callingUid=*/delegate_uid); |
| |
| EXPECT_EQ(rowCountResult, 1); |
| EXPECT_THAT(queryDataResult, ElementsAre(to_string(atomTag), to_string(configAddedTimeNs + 100), |
| _, // wallClockNs |
| _ // field_1 |
| )); |
| dbutils::deleteDb(key2); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestTooManyConfigsAmbiguousQuery) { |
| ConfigKey key2(config_app_uid + 1, configId); |
| processor->OnConfigUpdated(configAddedTimeNs + 1 * NS_PER_SEC, key2, config); |
| |
| uidMap->updateApp(configAddedTimeNs, String16(delegate_package_name.c_str()), |
| /*uid=*/delegate_uid + 1, /*versionCode=*/1, |
| /*versionString=*/String16("v2"), |
| /*installer=*/String16(""), /*certificateHash=*/{}); |
| uidMap->updateApp(configAddedTimeNs + 1, String16(config_package_name.c_str()), |
| /*uid=*/config_app_uid + 1, /*versionCode=*/1, |
| /*versionString=*/String16("v2"), |
| /*installer=*/String16(""), /*certificateHash=*/{}); |
| |
| std::vector<std::unique_ptr<LogEvent>> events; |
| |
| events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100)); |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| } |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| processor->querySql(query.str(), /*minSqlClientVersion=*/0, |
| /*policyConfig=*/{}, mockStatsQueryCallback, |
| /*configKey=*/configId, /*configPackage=*/config_package_name, |
| /*callingUid=*/delegate_uid); |
| |
| EXPECT_EQ(error, "Ambiguous ConfigKey"); |
| dbutils::deleteDb(key2); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestUnknownConfigPackage) { |
| std::vector<std::unique_ptr<LogEvent>> events; |
| |
| events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100)); |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| } |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| processor->querySql(query.str(), /*minSqlClientVersion=*/0, |
| /*policyConfig=*/{}, mockStatsQueryCallback, |
| /*configKey=*/configId, /*configPackage=*/"unknown.config.package", |
| /*callingUid=*/delegate_uid); |
| |
| EXPECT_EQ(error, "No configs found matching the config key"); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestUnknownDelegatePackage) { |
| std::vector<std::unique_ptr<LogEvent>> events; |
| |
| events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100)); |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| } |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| processor->querySql(query.str(), /*minSqlClientVersion=*/0, |
| /*policyConfig=*/{}, mockStatsQueryCallback, |
| /*configKey=*/configId, /*configPackage=*/config_package_name, |
| /*callingUid=*/delegate_uid + 1); |
| |
| EXPECT_EQ(error, "No matching configs for restricted metrics delegate"); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestUnsupportedDatabaseVersion) { |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| processor->querySql(query.str(), /*minSqlClientVersion=*/INT_MAX, |
| /*policyConfig=*/{}, mockStatsQueryCallback, |
| /*configKey=*/configId, /*configPackage=*/config_package_name, |
| /*callingUid=*/delegate_uid); |
| |
| EXPECT_THAT(error, StartsWith("Unsupported sqlite version")); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestInvalidQuery) { |
| std::vector<std::unique_ptr<LogEvent>> events; |
| |
| events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100)); |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| } |
| |
| std::stringstream query; |
| query << "SELECT * FROM invalid_metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| processor->querySql(query.str(), /*minSqlClientVersion=*/0, |
| /*policyConfig=*/{}, mockStatsQueryCallback, |
| /*configKey=*/configId, /*configPackage=*/config_package_name, |
| /*callingUid=*/delegate_uid); |
| |
| EXPECT_THAT(error, StartsWith("failed to query db")); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestEnforceTtlRemovesOldEvents) { |
| int64_t currentWallTimeNs = getWallClockNs(); |
| // 8 days are used here because the TTL threshold is 7 days. |
| int64_t eightDaysAgo = currentWallTimeNs - 8 * 24 * 3600 * NS_PER_SEC; |
| int64_t originalEventElapsedTime = configAddedTimeNs + 100; |
| std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(atomTag, originalEventElapsedTime); |
| event1->setLogdWallClockTimestampNs(eightDaysAgo); |
| |
| // Send log events to StatsLogProcessor. |
| processor->OnLogEvent(event1.get(), originalEventElapsedTime); |
| processor->WriteDataToDisk(DEVICE_SHUTDOWN, FAST, originalEventElapsedTime + 20 * NS_PER_SEC, |
| getWallClockNs()); |
| processor->EnforceDataTtls(currentWallTimeNs, originalEventElapsedTime + 100); |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| string err; |
| std::vector<int32_t> columnTypes; |
| std::vector<string> columnNames; |
| std::vector<std::vector<std::string>> rows; |
| EXPECT_TRUE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err)); |
| ASSERT_EQ(rows.size(), 0); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestConfigRemovalDeletesData) { |
| std::vector<std::unique_ptr<LogEvent>> events; |
| |
| events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100)); |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| } |
| // Query to make sure data is flushed |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| processor->querySql(query.str(), /*minSqlClientVersion=*/0, |
| /*policyConfig=*/{}, mockStatsQueryCallback, |
| /*configKey=*/configId, /*configPackage=*/config_package_name, |
| /*callingUid=*/delegate_uid); |
| |
| processor->OnConfigRemoved(configKey); |
| |
| string err; |
| std::vector<int32_t> columnTypes; |
| std::vector<string> columnNames; |
| std::vector<std::vector<std::string>> rows; |
| EXPECT_FALSE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err)); |
| |
| EXPECT_THAT(err, StartsWith("unable to open database file")); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestConfigRemovalDeletesDataWithoutFlush) { |
| std::vector<std::unique_ptr<LogEvent>> events; |
| |
| events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100)); |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| } |
| processor->OnConfigRemoved(configKey); |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| string err; |
| std::vector<int32_t> columnTypes; |
| std::vector<string> columnNames; |
| std::vector<std::vector<std::string>> rows; |
| EXPECT_FALSE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err)); |
| |
| EXPECT_THAT(err, StartsWith("unable to open database file")); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestConfigUpdateRestrictedDelegateCleared) { |
| std::vector<std::unique_ptr<LogEvent>> events; |
| events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100)); |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| } |
| |
| // Update the existing config with no delegate |
| config.clear_restricted_metrics_delegate_package_name(); |
| processor->OnConfigUpdated(configAddedTimeNs + 1 * NS_PER_SEC, configKey, config); |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| string err; |
| std::vector<int32_t> columnTypes; |
| std::vector<string> columnNames; |
| std::vector<std::vector<std::string>> rows; |
| EXPECT_FALSE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err)); |
| EXPECT_EQ(rows.size(), 0); |
| EXPECT_THAT(err, StartsWith("unable to open database file")); |
| dbutils::deleteDb(configKey); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestNonModularConfigUpdateRestrictedDelegate) { |
| std::vector<std::unique_ptr<LogEvent>> events; |
| events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100)); |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| } |
| |
| // Update the existing config without modular update |
| processor->OnConfigUpdated(configAddedTimeNs + 1 * NS_PER_SEC, configKey, config, false); |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| string err; |
| std::vector<int32_t> columnTypes; |
| std::vector<string> columnNames; |
| std::vector<std::vector<std::string>> rows; |
| EXPECT_FALSE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err)); |
| EXPECT_EQ(rows.size(), 0); |
| EXPECT_THAT(err, StartsWith("no such table")); |
| dbutils::deleteDb(configKey); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestModularConfigUpdateNewRestrictedDelegate) { |
| config.clear_restricted_metrics_delegate_package_name(); |
| // Update the existing config without a restricted delegate |
| processor->OnConfigUpdated(configAddedTimeNs + 10, configKey, config); |
| |
| // Update the existing config with a new restricted delegate |
| config.set_restricted_metrics_delegate_package_name("new.delegate.package"); |
| processor->OnConfigUpdated(configAddedTimeNs + 1 * NS_PER_SEC, configKey, config); |
| |
| std::vector<std::unique_ptr<LogEvent>> events; |
| events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 2 * NS_PER_SEC)); |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| } |
| |
| uint64_t dumpTimeNs = configAddedTimeNs + 100 * NS_PER_SEC; |
| ConfigMetricsReportList reports; |
| vector<uint8_t> buffer; |
| processor->onDumpReport(configKey, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer); |
| EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); |
| ASSERT_EQ(reports.reports_size(), 0); |
| |
| // Assert the config update was not modular and a RestrictedEventMetricProducer was created. |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| string err; |
| std::vector<int32_t> columnTypes; |
| std::vector<string> columnNames; |
| std::vector<std::vector<std::string>> rows; |
| EXPECT_TRUE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err)); |
| EXPECT_EQ(rows.size(), 1); |
| EXPECT_THAT(rows[0], |
| ElementsAre(to_string(atomTag), to_string(configAddedTimeNs + 2 * NS_PER_SEC), |
| _, // wallClockNs |
| _ // field_1 |
| )); |
| EXPECT_THAT(columnNames, |
| ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1")); |
| EXPECT_THAT(columnTypes, |
| ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER)); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestModularConfigUpdateChangeRestrictedDelegate) { |
| std::vector<std::unique_ptr<LogEvent>> events; |
| events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100)); |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| } |
| |
| // Update the existing config with a new restricted delegate |
| int32_t newDelegateUid = delegate_uid + 1; |
| config.set_restricted_metrics_delegate_package_name("new.delegate.package"); |
| uidMap->updateApp(configAddedTimeNs, String16("new.delegate.package"), |
| /*uid=*/newDelegateUid, /*versionCode=*/1, |
| /*versionString=*/String16("v2"), |
| /*installer=*/String16(""), /*certificateHash=*/{}); |
| processor->OnConfigUpdated(configAddedTimeNs + 1 * NS_PER_SEC, configKey, config); |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| processor->querySql(query.str(), /*minSqlClientVersion=*/0, |
| /*policyConfig=*/{}, mockStatsQueryCallback, |
| /*configKey=*/configId, /*configPackage=*/config_package_name, |
| /*callingUid=*/newDelegateUid); |
| |
| EXPECT_EQ(rowCountResult, 1); |
| EXPECT_THAT(queryDataResult, ElementsAre(to_string(atomTag), to_string(configAddedTimeNs + 100), |
| _, // wallClockNs |
| _ // field_1 |
| )); |
| EXPECT_THAT(columnNamesResult, |
| ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1")); |
| EXPECT_THAT(columnTypesResult, |
| ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER)); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestInvalidConfigUpdateRestrictedDelegate) { |
| std::vector<std::unique_ptr<LogEvent>> events; |
| events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100)); |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| } |
| |
| EventMetric metricWithoutMatcher = createEventMetric("metricWithoutMatcher", 999999, nullopt); |
| *config.add_event_metric() = metricWithoutMatcher; |
| // Update the existing config with an invalid config update |
| processor->OnConfigUpdated(configAddedTimeNs + 1 * NS_PER_SEC, configKey, config); |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| string err; |
| std::vector<int32_t> columnTypes; |
| std::vector<string> columnNames; |
| std::vector<std::vector<std::string>> rows; |
| EXPECT_FALSE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err)); |
| EXPECT_EQ(rows.size(), 0); |
| EXPECT_THAT(err, StartsWith("unable to open database file")); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestRestrictedConfigUpdateDoesNotUpdateUidMap) { |
| auto& configKeyMap = processor->getUidMap()->mLastUpdatePerConfigKey; |
| EXPECT_EQ(configKeyMap.find(configKey), configKeyMap.end()); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestRestrictedConfigUpdateAddsDelegateRemovesUidMapEntry) { |
| auto& configKeyMap = processor->getUidMap()->mLastUpdatePerConfigKey; |
| config.clear_restricted_metrics_delegate_package_name(); |
| // Update the existing config without a restricted delegate |
| processor->OnConfigUpdated(configAddedTimeNs + 1 * NS_PER_SEC, configKey, config); |
| EXPECT_NE(configKeyMap.find(configKey), configKeyMap.end()); |
| // Update the existing config with a new restricted delegate |
| config.set_restricted_metrics_delegate_package_name(delegate_package_name.c_str()); |
| processor->OnConfigUpdated(configAddedTimeNs + 1 * NS_PER_SEC, configKey, config); |
| EXPECT_EQ(configKeyMap.find(configKey), configKeyMap.end()); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestLogEventsEnforceTtls) { |
| int64_t currentWallTimeNs = getWallClockNs(); |
| int64_t originalEventElapsedTime = configAddedTimeNs + 100; |
| // 2 hours used here because the TTL check period is 1 hour. |
| int64_t newEventElapsedTime = configAddedTimeNs + 2 * 3600 * NS_PER_SEC + 1; // 2 hrs later |
| std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(atomTag, originalEventElapsedTime); |
| event1->setLogdWallClockTimestampNs(eightDaysAgo); |
| std::unique_ptr<LogEvent> event2 = |
| CreateRestrictedLogEvent(atomTag, originalEventElapsedTime + 100); |
| event2->setLogdWallClockTimestampNs(oneDayAgo); |
| std::unique_ptr<LogEvent> event3 = CreateRestrictedLogEvent(atomTag, newEventElapsedTime); |
| event3->setLogdWallClockTimestampNs(currentWallTimeNs); |
| |
| processor->mLastTtlTime = originalEventElapsedTime; |
| // Send log events to StatsLogProcessor. |
| processor->OnLogEvent(event1.get(), originalEventElapsedTime); |
| processor->OnLogEvent(event2.get(), newEventElapsedTime); |
| processor->OnLogEvent(event3.get(), newEventElapsedTime + 100); |
| processor->flushRestrictedDataLocked(newEventElapsedTime); |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| string err; |
| std::vector<int32_t> columnTypes; |
| std::vector<string> columnNames; |
| std::vector<std::vector<std::string>> rows; |
| EXPECT_TRUE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err)); |
| ASSERT_EQ(rows.size(), 2); |
| EXPECT_THAT(columnNames, |
| ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1")); |
| EXPECT_THAT(columnTypes, |
| ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER)); |
| EXPECT_THAT(rows[0], ElementsAre(to_string(atomTag), to_string(originalEventElapsedTime + 100), |
| to_string(oneDayAgo), _)); |
| EXPECT_THAT(rows[1], ElementsAre(to_string(atomTag), to_string(newEventElapsedTime), |
| to_string(currentWallTimeNs), _)); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestLogEventsDoesNotEnforceTtls) { |
| int64_t currentWallTimeNs = getWallClockNs(); |
| int64_t originalEventElapsedTime = configAddedTimeNs + 100; |
| // 30 min used here because the TTL check period is 1 hour. |
| int64_t newEventElapsedTime = configAddedTimeNs + (3600 * NS_PER_SEC) / 2; // 30 min later |
| std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(atomTag, originalEventElapsedTime); |
| event1->setLogdWallClockTimestampNs(eightDaysAgo); |
| std::unique_ptr<LogEvent> event2 = CreateRestrictedLogEvent(atomTag, newEventElapsedTime); |
| event2->setLogdWallClockTimestampNs(currentWallTimeNs); |
| |
| processor->mLastTtlTime = originalEventElapsedTime; |
| // Send log events to StatsLogProcessor. |
| processor->OnLogEvent(event1.get(), originalEventElapsedTime); |
| processor->OnLogEvent(event2.get(), newEventElapsedTime); |
| processor->flushRestrictedDataLocked(newEventElapsedTime); |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| string err; |
| std::vector<int32_t> columnTypes; |
| std::vector<string> columnNames; |
| std::vector<std::vector<std::string>> rows; |
| EXPECT_TRUE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err)); |
| ASSERT_EQ(rows.size(), 2); |
| EXPECT_THAT(columnNames, |
| ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1")); |
| EXPECT_THAT(columnTypes, |
| ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER)); |
| EXPECT_THAT(rows[0], ElementsAre(to_string(atomTag), to_string(originalEventElapsedTime), |
| to_string(eightDaysAgo), _)); |
| EXPECT_THAT(rows[1], ElementsAre(to_string(atomTag), to_string(newEventElapsedTime), |
| to_string(currentWallTimeNs), _)); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestQueryEnforceTtls) { |
| int64_t currentWallTimeNs = getWallClockNs(); |
| int64_t originalEventElapsedTime = configAddedTimeNs + 100; |
| // 30 min used here because the TTL check period is 1 hour. |
| int64_t newEventElapsedTime = configAddedTimeNs + (3600 * NS_PER_SEC) / 2; // 30 min later |
| std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(atomTag, originalEventElapsedTime); |
| event1->setLogdWallClockTimestampNs(eightDaysAgo); |
| std::unique_ptr<LogEvent> event2 = CreateRestrictedLogEvent(atomTag, newEventElapsedTime); |
| event2->setLogdWallClockTimestampNs(currentWallTimeNs); |
| |
| processor->mLastTtlTime = originalEventElapsedTime; |
| // Send log events to StatsLogProcessor. |
| processor->OnLogEvent(event1.get(), originalEventElapsedTime); |
| processor->OnLogEvent(event2.get(), newEventElapsedTime); |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| processor->querySql(query.str(), /*minSqlClientVersion=*/0, |
| /*policyConfig=*/{}, mockStatsQueryCallback, |
| /*configKey=*/configId, /*configPackage=*/config_package_name, |
| /*callingUid=*/delegate_uid); |
| |
| EXPECT_EQ(rowCountResult, 1); |
| EXPECT_THAT(queryDataResult, ElementsAre(to_string(atomTag), to_string(newEventElapsedTime), |
| to_string(currentWallTimeNs), |
| _ // field_1 |
| )); |
| EXPECT_THAT(columnNamesResult, |
| ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1")); |
| EXPECT_THAT(columnTypesResult, |
| ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER)); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestNotFlushed) { |
| std::vector<std::unique_ptr<LogEvent>> events; |
| events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100)); |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get(), event->GetElapsedTimestampNs()); |
| } |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| string err; |
| std::vector<int32_t> columnTypes; |
| std::vector<string> columnNames; |
| std::vector<std::vector<std::string>> rows; |
| EXPECT_FALSE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err)); |
| EXPECT_EQ(rows.size(), 0); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestEnforceDbGuardrails) { |
| int64_t currentWallTimeNs = getWallClockNs(); |
| int64_t originalEventElapsedTime = |
| configAddedTimeNs + (3600 * NS_PER_SEC) * 2; // 2 hours after boot |
| // 2 hours used here because the TTL check period is 1 hour. |
| int64_t dbEnforcementTimeNs = |
| configAddedTimeNs + (3600 * NS_PER_SEC) * 4; // 4 hours after boot |
| std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(atomTag, originalEventElapsedTime); |
| event1->setLogdWallClockTimestampNs(currentWallTimeNs); |
| // Send log events to StatsLogProcessor. |
| processor->OnLogEvent(event1.get(), originalEventElapsedTime); |
| |
| EXPECT_TRUE(StorageManager::hasFile( |
| base::StringPrintf("%s/%s", STATS_RESTRICTED_DATA_DIR, "123_12345.db").c_str())); |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| processor->querySql(query.str(), /*minSqlClientVersion=*/0, |
| /*policyConfig=*/{}, mockStatsQueryCallback, |
| /*configKey=*/configId, /*configPackage=*/config_package_name, |
| /*callingUid=*/delegate_uid); |
| EXPECT_EQ(rowCountResult, 1); |
| EXPECT_THAT(queryDataResult, |
| ElementsAre(to_string(atomTag), to_string(originalEventElapsedTime), |
| to_string(currentWallTimeNs), |
| _ // field_1 |
| )); |
| EXPECT_THAT(columnNamesResult, |
| ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1")); |
| EXPECT_THAT(columnTypesResult, |
| ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER)); |
| |
| processor->enforceDbGuardrailsIfNecessaryLocked(oneMonthLater, dbEnforcementTimeNs); |
| |
| EXPECT_FALSE(StorageManager::hasFile( |
| base::StringPrintf("%s/%s", STATS_RESTRICTED_DATA_DIR, "123_12345.db").c_str())); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestEnforceDbGuardrailsDoesNotDeleteBeforeGuardrail) { |
| int64_t currentWallTimeNs = getWallClockNs(); |
| int64_t originalEventElapsedTime = |
| configAddedTimeNs + (3600 * NS_PER_SEC) * 2; // 2 hours after boot |
| // 2 hours used here because the TTL check period is 1 hour. |
| std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(atomTag, originalEventElapsedTime); |
| event1->setLogdWallClockTimestampNs(currentWallTimeNs); |
| // Send log events to StatsLogProcessor. |
| processor->OnLogEvent(event1.get(), originalEventElapsedTime); |
| |
| EXPECT_TRUE(StorageManager::hasFile( |
| base::StringPrintf("%s/%s", STATS_RESTRICTED_DATA_DIR, "123_12345.db").c_str())); |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| processor->querySql(query.str(), /*minSqlClientVersion=*/0, |
| /*policyConfig=*/{}, mockStatsQueryCallback, |
| /*configKey=*/configId, /*configPackage=*/config_package_name, |
| /*callingUid=*/delegate_uid); |
| EXPECT_EQ(rowCountResult, 1); |
| EXPECT_THAT(queryDataResult, |
| ElementsAre(to_string(atomTag), to_string(originalEventElapsedTime), |
| to_string(currentWallTimeNs), |
| _ // field_1 |
| )); |
| EXPECT_THAT(columnNamesResult, |
| ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1")); |
| EXPECT_THAT(columnTypesResult, |
| ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER)); |
| |
| processor->enforceDbGuardrailsIfNecessaryLocked(oneMonthLater, originalEventElapsedTime); |
| |
| EXPECT_TRUE(StorageManager::hasFile( |
| base::StringPrintf("%s/%s", STATS_RESTRICTED_DATA_DIR, "123_12345.db").c_str())); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestFlushInWriteDataToDisk) { |
| std::vector<std::unique_ptr<LogEvent>> events; |
| events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100)); |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get(), event->GetElapsedTimestampNs()); |
| } |
| |
| // Call WriteDataToDisk after 20 second because cooldown period is 15 second. |
| processor->WriteDataToDisk(DEVICE_SHUTDOWN, FAST, 20 * NS_PER_SEC, getWallClockNs()); |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| string err; |
| std::vector<int32_t> columnTypes; |
| std::vector<string> columnNames; |
| std::vector<std::vector<std::string>> rows; |
| EXPECT_TRUE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err)); |
| EXPECT_EQ(rows.size(), 1); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestFlushPeriodically) { |
| std::vector<std::unique_ptr<LogEvent>> events; |
| |
| events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100)); |
| events.push_back(CreateRestrictedLogEvent( |
| atomTag, configAddedTimeNs + StatsdStats::kMinFlushRestrictedPeriodNs + 1)); |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get(), event->GetElapsedTimestampNs()); |
| } |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| string err; |
| std::vector<int32_t> columnTypes; |
| std::vector<string> columnNames; |
| std::vector<std::vector<std::string>> rows; |
| EXPECT_TRUE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err)); |
| // Only first event is flushed when second event is logged. |
| EXPECT_EQ(rows.size(), 1); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestOnLogEventMalformedDbNameDeleted) { |
| vector<string> emptyData; |
| string fileName = StringPrintf("%s/malformedname.db", STATS_RESTRICTED_DATA_DIR); |
| StorageManager::writeFile(fileName.c_str(), emptyData.data(), emptyData.size()); |
| EXPECT_TRUE(StorageManager::hasFile(fileName.c_str())); |
| int64_t originalEventElapsedTime = configAddedTimeNs + 100; |
| // 2 hours used here because the TTL check period is 1 hour. |
| int64_t newEventElapsedTime = configAddedTimeNs + 2 * 3600 * NS_PER_SEC + 1; // 2 hrs later |
| std::unique_ptr<LogEvent> event2 = CreateRestrictedLogEvent(atomTag, newEventElapsedTime); |
| event2->setLogdWallClockTimestampNs(getWallClockNs()); |
| |
| processor->mLastTtlTime = originalEventElapsedTime; |
| // Send log events to StatsLogProcessor. |
| processor->OnLogEvent(event2.get(), newEventElapsedTime); |
| |
| EXPECT_FALSE(StorageManager::hasFile(fileName.c_str())); |
| StorageManager::deleteFile(fileName.c_str()); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestRestrictedMetricSavesTtlToDisk) { |
| metadata::StatsMetadataList result; |
| processor->WriteMetadataToProto(getWallClockNs(), configAddedTimeNs, &result); |
| |
| ASSERT_EQ(result.stats_metadata_size(), 1); |
| metadata::StatsMetadata statsMetadata = result.stats_metadata(0); |
| EXPECT_EQ(statsMetadata.config_key().config_id(), configId); |
| EXPECT_EQ(statsMetadata.config_key().uid(), config_app_uid); |
| |
| ASSERT_EQ(statsMetadata.metric_metadata_size(), 1); |
| metadata::MetricMetadata metricMetadata = statsMetadata.metric_metadata(0); |
| EXPECT_EQ(metricMetadata.metric_id(), restrictedMetricId); |
| EXPECT_EQ(metricMetadata.restricted_category(), CATEGORY_UNKNOWN); |
| result.Clear(); |
| |
| std::unique_ptr<LogEvent> event = CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100); |
| processor->OnLogEvent(event.get()); |
| processor->WriteMetadataToProto(getWallClockNs(), configAddedTimeNs, &result); |
| |
| ASSERT_EQ(result.stats_metadata_size(), 1); |
| statsMetadata = result.stats_metadata(0); |
| EXPECT_EQ(statsMetadata.config_key().config_id(), configId); |
| EXPECT_EQ(statsMetadata.config_key().uid(), config_app_uid); |
| |
| ASSERT_EQ(statsMetadata.metric_metadata_size(), 1); |
| metricMetadata = statsMetadata.metric_metadata(0); |
| EXPECT_EQ(metricMetadata.metric_id(), restrictedMetricId); |
| EXPECT_EQ(metricMetadata.restricted_category(), CATEGORY_DIAGNOSTIC); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestRestrictedMetricLoadsTtlFromDisk) { |
| int64_t currentWallTimeNs = getWallClockNs(); |
| int64_t originalEventElapsedTime = configAddedTimeNs + 100; |
| std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(atomTag, originalEventElapsedTime); |
| event1->setLogdWallClockTimestampNs(eightDaysAgo); |
| processor->OnLogEvent(event1.get(), originalEventElapsedTime); |
| processor->flushRestrictedDataLocked(originalEventElapsedTime); |
| int64_t wallClockNs = 1584991200 * NS_PER_SEC; // random time |
| int64_t metadataWriteTime = originalEventElapsedTime + 5000 * NS_PER_SEC; |
| processor->SaveMetadataToDisk(wallClockNs, metadataWriteTime); |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| string err; |
| std::vector<int32_t> columnTypes; |
| std::vector<string> columnNames; |
| std::vector<std::vector<std::string>> rows; |
| EXPECT_TRUE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err)); |
| ASSERT_EQ(rows.size(), 1); |
| EXPECT_THAT(columnNames, |
| ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1")); |
| EXPECT_THAT(columnTypes, |
| ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER)); |
| EXPECT_THAT(rows[0], ElementsAre(to_string(atomTag), to_string(originalEventElapsedTime), |
| to_string(eightDaysAgo), _)); |
| |
| auto processor2 = |
| CreateStatsLogProcessor(/*baseTimeNs=*/0, configAddedTimeNs, config, configKey, |
| /*puller=*/nullptr, /*atomTag=*/0, uidMap); |
| // 2 hours used here because the TTL check period is 1 hour. |
| int64_t newEventElapsedTime = configAddedTimeNs + 2 * 3600 * NS_PER_SEC + 1; // 2 hrs later |
| processor2->LoadMetadataFromDisk(wallClockNs, newEventElapsedTime); |
| |
| // Log another event and check that the original TTL is maintained across reboot |
| std::unique_ptr<LogEvent> event2 = CreateRestrictedLogEvent(atomTag, newEventElapsedTime); |
| event2->setLogdWallClockTimestampNs(currentWallTimeNs); |
| processor2->OnLogEvent(event2.get(), newEventElapsedTime); |
| processor2->flushRestrictedDataLocked(newEventElapsedTime); |
| |
| columnTypes.clear(); |
| columnNames.clear(); |
| rows.clear(); |
| EXPECT_TRUE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err)); |
| ASSERT_EQ(rows.size(), 1); |
| EXPECT_THAT(columnNames, |
| ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1")); |
| EXPECT_THAT(columnTypes, |
| ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER)); |
| EXPECT_THAT(rows[0], ElementsAre(to_string(atomTag), to_string(newEventElapsedTime), |
| to_string(currentWallTimeNs), _)); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestNewRestrictionCategoryEventDeletesTable) { |
| int64_t currentWallTimeNs = getWallClockNs(); |
| int64_t originalEventElapsedTime = configAddedTimeNs + 100; |
| std::unique_ptr<LogEvent> event1 = |
| CreateNonRestrictedLogEvent(atomTag, originalEventElapsedTime); |
| processor->OnLogEvent(event1.get()); |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId); |
| processor->querySql(query.str(), /*minSqlClientVersion=*/0, |
| /*policyConfig=*/{}, mockStatsQueryCallback, |
| /*configKey=*/configId, /*configPackage=*/config_package_name, |
| /*callingUid=*/delegate_uid); |
| EXPECT_EQ(rowCountResult, 1); |
| EXPECT_THAT(queryDataResult, |
| ElementsAre(to_string(atomTag), to_string(originalEventElapsedTime), |
| _, // wallTimestampNs |
| _ // field_1 |
| )); |
| EXPECT_THAT(columnNamesResult, |
| ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1")); |
| EXPECT_THAT(columnTypesResult, |
| ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER)); |
| |
| // Log a second event that will go into the cache |
| std::unique_ptr<LogEvent> event2 = |
| CreateNonRestrictedLogEvent(atomTag, originalEventElapsedTime + 100); |
| processor->OnLogEvent(event2.get()); |
| |
| // Log a third event with a different category |
| std::unique_ptr<LogEvent> event3 = |
| CreateRestrictedLogEvent(atomTag, originalEventElapsedTime + 200); |
| processor->OnLogEvent(event3.get()); |
| |
| processor->querySql(query.str(), /*minSqlClientVersion=*/0, |
| /*policyConfig=*/{}, mockStatsQueryCallback, |
| /*configKey=*/configId, /*configPackage=*/config_package_name, |
| /*callingUid=*/delegate_uid); |
| EXPECT_EQ(rowCountResult, 1); |
| EXPECT_THAT(queryDataResult, |
| ElementsAre(to_string(atomTag), to_string(originalEventElapsedTime + 200), |
| _, // wallTimestampNs |
| _ // field_1 |
| )); |
| EXPECT_THAT(columnNamesResult, |
| ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1")); |
| EXPECT_THAT(columnTypesResult, |
| ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER)); |
| } |
| |
| TEST_F(RestrictedEventMetricE2eTest, TestDeviceInfoTableCreated) { |
| std::string query = "SELECT * FROM device_info"; |
| processor->querySql(query.c_str(), /*minSqlClientVersion=*/0, |
| /*policyConfig=*/{}, mockStatsQueryCallback, |
| /*configKey=*/configId, /*configPackage=*/config_package_name, |
| /*callingUid=*/delegate_uid); |
| EXPECT_EQ(rowCountResult, 1); |
| EXPECT_THAT(queryDataResult, ElementsAre(_, _, _, _, _, _, _, _, _, _)); |
| EXPECT_THAT(columnNamesResult, |
| ElementsAre("sdkVersion", "model", "product", "hardware", "device", "osBuild", |
| "fingerprint", "brand", "manufacturer", "board")); |
| EXPECT_THAT(columnTypesResult, |
| ElementsAre(SQLITE_INTEGER, SQLITE_TEXT, SQLITE_TEXT, SQLITE_TEXT, SQLITE_TEXT, |
| SQLITE_TEXT, SQLITE_TEXT, SQLITE_TEXT, SQLITE_TEXT, SQLITE_TEXT)); |
| } |
| #else |
| GTEST_LOG_(INFO) << "This test does nothing.\n"; |
| #endif |
| |
| } // namespace statsd |
| } // namespace os |
| } // namespace android |