blob: 098f284fdc64ee3678dd6c449b7fa4a606a63556 [file] [log] [blame]
// Copyright (C) 2020 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-base/properties.h>
#include <android-base/stringprintf.h>
#include <gtest/gtest.h>
#include "flags/flags.h"
#include "src/StatsLogProcessor.h"
#include "src/storage/StorageManager.h"
#include "tests/statsd_test_util.h"
namespace android {
namespace os {
namespace statsd {
#ifdef __ANDROID__
#define STATS_DATA_DIR "/data/misc/stats-data"
using android::base::SetProperty;
using android::base::StringPrintf;
using namespace std;
namespace {
StatsdConfig CreateSimpleConfig() {
StatsdConfig config;
config.add_allowed_log_source("AID_STATSD");
config.set_hash_strings_in_metric_report(false);
*config.add_atom_matcher() = CreateBatteryStateUsbMatcher();
// Simple count metric so the config isn't empty.
CountMetric* countMetric1 = config.add_count_metric();
countMetric1->set_id(StringToId("Count1"));
countMetric1->set_what(config.atom_matcher(0).id());
countMetric1->set_bucket(FIVE_MINUTES);
return config;
}
} // namespace
// Setup for parameterized tests.
class ConfigUpdateE2eAbTest : public TestWithParam<bool> {
private:
string originalFlagValue;
public:
void SetUp() override {
originalFlagValue = getFlagBool(PARTIAL_CONFIG_UPDATE_FLAG, "");
string rawFlagName =
StringPrintf("persist.device_config.%s.%s", STATSD_NATIVE_NAMESPACE.c_str(),
PARTIAL_CONFIG_UPDATE_FLAG.c_str());
SetProperty(rawFlagName, GetParam() ? "true" : "false");
}
void TearDown() override {
string rawFlagName =
StringPrintf("persist.device_config.%s.%s", STATSD_NATIVE_NAMESPACE.c_str(),
PARTIAL_CONFIG_UPDATE_FLAG.c_str());
SetProperty(rawFlagName, originalFlagValue);
}
};
INSTANTIATE_TEST_SUITE_P(ConfigUpdateE2eAbTest, ConfigUpdateE2eAbTest, testing::Bool());
TEST_P(ConfigUpdateE2eAbTest, TestUidMapVersionStringInstaller) {
sp<UidMap> uidMap = new UidMap();
vector<int32_t> uids({1000});
vector<int64_t> versions({1});
vector<String16> apps({String16("app1")});
vector<String16> versionStrings({String16("v1")});
vector<String16> installers({String16("installer1")});
uidMap->updateMap(1, uids, versions, versionStrings, apps, installers);
StatsdConfig config = CreateSimpleConfig();
config.set_version_strings_in_metric_report(true);
config.set_installer_in_metric_report(false);
int64_t baseTimeNs = getElapsedRealtimeNs();
ConfigKey cfgKey(0, 12345);
sp<StatsLogProcessor> processor =
CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey, nullptr, 0, uidMap);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
EXPECT_TRUE(metricsManager->isConfigValid());
// Now update.
config.set_version_strings_in_metric_report(false);
config.set_installer_in_metric_report(true);
processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_EQ(metricsManager == processor->mMetricsManagers.begin()->second, GetParam());
EXPECT_TRUE(metricsManager->isConfigValid());
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
// First report is written to disk when the update happens.
ASSERT_EQ(reports.reports_size(), 2);
UidMapping uidMapping = reports.reports(1).uid_map();
ASSERT_EQ(uidMapping.snapshots_size(), 1);
ASSERT_EQ(uidMapping.snapshots(0).package_info_size(), 1);
EXPECT_FALSE(uidMapping.snapshots(0).package_info(0).has_version_string());
EXPECT_EQ(uidMapping.snapshots(0).package_info(0).installer(), "installer1");
}
TEST_P(ConfigUpdateE2eAbTest, TestHashStrings) {
sp<UidMap> uidMap = new UidMap();
vector<int32_t> uids({1000});
vector<int64_t> versions({1});
vector<String16> apps({String16("app1")});
vector<String16> versionStrings({String16("v1")});
vector<String16> installers({String16("installer1")});
uidMap->updateMap(1, uids, versions, versionStrings, apps, installers);
StatsdConfig config = CreateSimpleConfig();
config.set_version_strings_in_metric_report(true);
config.set_hash_strings_in_metric_report(true);
int64_t baseTimeNs = getElapsedRealtimeNs();
ConfigKey cfgKey(0, 12345);
sp<StatsLogProcessor> processor =
CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey, nullptr, 0, uidMap);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
EXPECT_TRUE(metricsManager->isConfigValid());
// Now update.
config.set_hash_strings_in_metric_report(false);
processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_EQ(metricsManager == processor->mMetricsManagers.begin()->second, GetParam());
EXPECT_TRUE(metricsManager->isConfigValid());
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
// First report is written to disk when the update happens.
ASSERT_EQ(reports.reports_size(), 2);
UidMapping uidMapping = reports.reports(1).uid_map();
ASSERT_EQ(uidMapping.snapshots_size(), 1);
ASSERT_EQ(uidMapping.snapshots(0).package_info_size(), 1);
EXPECT_TRUE(uidMapping.snapshots(0).package_info(0).has_version_string());
EXPECT_FALSE(uidMapping.snapshots(0).package_info(0).has_version_string_hash());
}
TEST_P(ConfigUpdateE2eAbTest, TestAnnotations) {
StatsdConfig config = CreateSimpleConfig();
StatsdConfig_Annotation* annotation = config.add_annotation();
annotation->set_field_int64(11);
annotation->set_field_int32(1);
int64_t baseTimeNs = getElapsedRealtimeNs();
ConfigKey cfgKey(0, 12345);
sp<StatsLogProcessor> processor =
CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey);
// Now update
config.clear_annotation();
annotation = config.add_annotation();
annotation->set_field_int64(22);
annotation->set_field_int32(2);
processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config);
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
// First report is written to disk when the update happens.
ASSERT_EQ(reports.reports_size(), 2);
ConfigMetricsReport report = reports.reports(1);
EXPECT_EQ(report.annotation_size(), 1);
EXPECT_EQ(report.annotation(0).field_int64(), 22);
EXPECT_EQ(report.annotation(0).field_int32(), 2);
}
TEST_P(ConfigUpdateE2eAbTest, TestPersistLocally) {
StatsdConfig config = CreateSimpleConfig();
config.set_persist_locally(false);
int64_t baseTimeNs = getElapsedRealtimeNs();
ConfigKey cfgKey(0, 12345);
sp<StatsLogProcessor> processor =
CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey);
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
ASSERT_EQ(reports.reports_size(), 1);
// Number of reports should still be 1 since persist_locally is false.
reports.Clear();
buffer.clear();
processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
ASSERT_EQ(reports.reports_size(), 1);
// Now update.
config.set_persist_locally(true);
processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config);
// Should get 2: 1 in memory + 1 on disk. Both should be saved on disk.
reports.Clear();
buffer.clear();
processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
ASSERT_EQ(reports.reports_size(), 2);
// Should get 3, 2 on disk + 1 in memory.
reports.Clear();
buffer.clear();
processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
ASSERT_EQ(reports.reports_size(), 3);
string suffix = StringPrintf("%d_%lld", cfgKey.GetUid(), (long long)cfgKey.GetId());
StorageManager::deleteSuffixedFiles(STATS_DATA_DIR, suffix.c_str());
string historySuffix =
StringPrintf("%d_%lld_history", cfgKey.GetUid(), (long long)cfgKey.GetId());
StorageManager::deleteSuffixedFiles(STATS_DATA_DIR, historySuffix.c_str());
}
TEST_P(ConfigUpdateE2eAbTest, TestNoReportMetrics) {
StatsdConfig config = CreateSimpleConfig();
// Second simple count metric.
CountMetric* countMetric = config.add_count_metric();
countMetric->set_id(StringToId("Count2"));
countMetric->set_what(config.atom_matcher(0).id());
countMetric->set_bucket(FIVE_MINUTES);
config.add_no_report_metric(config.count_metric(0).id());
int64_t baseTimeNs = getElapsedRealtimeNs();
ConfigKey cfgKey(0, 12345);
sp<StatsLogProcessor> processor =
CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey);
// Now update.
config.clear_no_report_metric();
config.add_no_report_metric(config.count_metric(1).id());
processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config);
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
// First report is written to disk when the update happens.
ASSERT_EQ(reports.reports_size(), 2);
// First report (before update) has the first count metric.
ASSERT_EQ(reports.reports(0).metrics_size(), 1);
EXPECT_EQ(reports.reports(0).metrics(0).metric_id(), config.count_metric(1).id());
// Second report (after update) has the first count metric.
ASSERT_EQ(reports.reports(1).metrics_size(), 1);
EXPECT_EQ(reports.reports(1).metrics(0).metric_id(), config.count_metric(0).id());
}
TEST_P(ConfigUpdateE2eAbTest, TestAtomsAllowedFromAnyUid) {
StatsdConfig config = CreateSimpleConfig();
int64_t baseTimeNs = getElapsedRealtimeNs();
ConfigKey cfgKey(0, 12345);
sp<StatsLogProcessor> processor =
CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey);
// Uses AID_ROOT, which isn't in allowed log sources.
unique_ptr<LogEvent> event = CreateBatteryStateChangedEvent(
baseTimeNs + 2, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB);
processor->OnLogEvent(event.get());
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, baseTimeNs + 1001, true, true, ADB_DUMP, FAST, &buffer);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
ASSERT_EQ(reports.reports_size(), 1);
// Check the metric and make sure it has 0 count.
ASSERT_EQ(reports.reports(0).metrics_size(), 1);
EXPECT_FALSE(reports.reports(0).metrics(0).has_count_metrics());
// Now update. Allow plugged state to be logged from any uid, so the atom will be counted.
config.add_whitelisted_atom_ids(util::PLUGGED_STATE_CHANGED);
processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config);
unique_ptr<LogEvent> event2 = CreateBatteryStateChangedEvent(
baseTimeNs + 2000, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB);
processor->OnLogEvent(event.get());
reports.Clear();
buffer.clear();
processor->onDumpReport(cfgKey, baseTimeNs + 3000, true, true, ADB_DUMP, FAST, &buffer);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
ASSERT_EQ(reports.reports_size(), 2);
// Check the metric and make sure it has 0 count.
ASSERT_EQ(reports.reports(1).metrics_size(), 1);
EXPECT_TRUE(reports.reports(1).metrics(0).has_count_metrics());
ASSERT_EQ(reports.reports(1).metrics(0).count_metrics().data_size(), 1);
ASSERT_EQ(reports.reports(1).metrics(0).count_metrics().data(0).bucket_info_size(), 1);
EXPECT_EQ(reports.reports(1).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1);
}
TEST_P(ConfigUpdateE2eAbTest, TestConfigTtl) {
StatsdConfig config = CreateSimpleConfig();
config.set_ttl_in_seconds(1);
int64_t baseTimeNs = getElapsedRealtimeNs();
ConfigKey cfgKey(0, 12345);
sp<StatsLogProcessor> processor =
CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
EXPECT_EQ(metricsManager->getTtlEndNs(), baseTimeNs + NS_PER_SEC);
config.set_ttl_in_seconds(5);
processor->OnConfigUpdated(baseTimeNs + 2 * NS_PER_SEC, cfgKey, config);
metricsManager = processor->mMetricsManagers.begin()->second;
EXPECT_EQ(metricsManager->getTtlEndNs(), baseTimeNs + 7 * NS_PER_SEC);
// Clear the data stored on disk as a result of the update.
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, baseTimeNs + 3 * NS_PER_SEC, false, true, ADB_DUMP, FAST,
&buffer);
}
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
} // namespace statsd
} // namespace os
} // namespace android