blob: a45a88e2a90403bdb108af638c02288a743cff4f [file] [log] [blame]
// 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