1 // Copyright (C) 2017 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "StatsService.h"
16
17 #include <android/binder_interface_utils.h>
18 #include <gmock/gmock.h>
19 #include <gtest/gtest.h>
20 #include <stdio.h>
21
22 #include "config/ConfigKey.h"
23 #include "packages/UidMap.h"
24 #include "src/statsd_config.pb.h"
25 #include "tests/statsd_test_util.h"
26
27 using namespace android;
28 using namespace testing;
29
30 namespace android {
31 namespace os {
32 namespace statsd {
33
34 using android::modules::sdklevel::IsAtLeastU;
35 using android::util::ProtoOutputStream;
36 using ::ndk::SharedRefBase;
37
38 #ifdef __ANDROID__
39
40 namespace {
41
42 const int64_t metricId = 123456;
43 const int32_t ATOM_TAG = util::SUBSYSTEM_SLEEP_STATE;
44
CreateStatsdConfig(const GaugeMetric::SamplingType samplingType)45 StatsdConfig CreateStatsdConfig(const GaugeMetric::SamplingType samplingType) {
46 StatsdConfig config;
47 config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
48 config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
49 auto atomMatcher = CreateSimpleAtomMatcher("TestMatcher", ATOM_TAG);
50 *config.add_atom_matcher() = atomMatcher;
51 *config.add_gauge_metric() =
52 createGaugeMetric("GAUGE1", atomMatcher.id(), samplingType, nullopt, nullopt);
53 config.set_hash_strings_in_metric_report(false);
54 return config;
55 }
56
57 class FakeSubsystemSleepCallbackWithTiming : public FakeSubsystemSleepCallback {
58 public:
onPullAtom(int atomTag,const shared_ptr<IPullAtomResultReceiver> & resultReceiver)59 Status onPullAtom(int atomTag,
60 const shared_ptr<IPullAtomResultReceiver>& resultReceiver) override {
61 mPullTimeNs = getElapsedRealtimeNs();
62 return FakeSubsystemSleepCallback::onPullAtom(atomTag, resultReceiver);
63 }
64 int64_t mPullTimeNs = 0;
65 };
66
67 } // namespace
68
TEST(StatsServiceTest,TestAddConfig_simple)69 TEST(StatsServiceTest, TestAddConfig_simple) {
70 const sp<UidMap> uidMap = new UidMap();
71 shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(
72 uidMap, /* queue */ nullptr, /* LogEventFilter */ nullptr);
73 const int kConfigKey = 12345;
74 const int kCallingUid = 123;
75 StatsdConfig config;
76 config.set_id(kConfigKey);
77 string serialized = config.SerializeAsString();
78
79 EXPECT_TRUE(service->addConfigurationChecked(kCallingUid, kConfigKey,
80 {serialized.begin(), serialized.end()}));
81 service->removeConfiguration(kConfigKey, kCallingUid);
82 ConfigKey configKey(kCallingUid, kConfigKey);
83 service->mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(),
84 false /* include_current_bucket*/, true /* erase_data */,
85 ADB_DUMP, NO_TIME_CONSTRAINTS, nullptr);
86 }
87
TEST(StatsServiceTest,TestAddConfig_empty)88 TEST(StatsServiceTest, TestAddConfig_empty) {
89 const sp<UidMap> uidMap = new UidMap();
90 shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(
91 uidMap, /* queue */ nullptr, /* LogEventFilter */ nullptr);
92 string serialized = "";
93 const int kConfigKey = 12345;
94 const int kCallingUid = 123;
95 EXPECT_TRUE(service->addConfigurationChecked(kCallingUid, kConfigKey,
96 {serialized.begin(), serialized.end()}));
97 service->removeConfiguration(kConfigKey, kCallingUid);
98 ConfigKey configKey(kCallingUid, kConfigKey);
99 service->mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(),
100 false /* include_current_bucket*/, true /* erase_data */,
101 ADB_DUMP, NO_TIME_CONSTRAINTS, nullptr);
102 }
103
TEST(StatsServiceTest,TestAddConfig_invalid)104 TEST(StatsServiceTest, TestAddConfig_invalid) {
105 const sp<UidMap> uidMap = new UidMap();
106 shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(
107 uidMap, /* queue */ nullptr, /* LogEventFilter */ nullptr);
108 string serialized = "Invalid config!";
109
110 EXPECT_FALSE(
111 service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
112 }
113
TEST(StatsServiceTest,TestGetUidFromArgs)114 TEST(StatsServiceTest, TestGetUidFromArgs) {
115 Vector<String8> args;
116 args.push(String8("-1"));
117 args.push(String8("0"));
118 args.push(String8("1"));
119 args.push(String8("a1"));
120 args.push(String8(""));
121
122 int32_t uid;
123
124 const sp<UidMap> uidMap = new UidMap();
125 shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(
126 uidMap, /* queue */ nullptr, /* LogEventFilter */ nullptr);
127 service->mEngBuild = true;
128
129 // "-1"
130 EXPECT_FALSE(service->getUidFromArgs(args, 0, uid));
131
132 // "0"
133 EXPECT_TRUE(service->getUidFromArgs(args, 1, uid));
134 EXPECT_EQ(0, uid);
135
136 // "1"
137 EXPECT_TRUE(service->getUidFromArgs(args, 2, uid));
138 EXPECT_EQ(1, uid);
139
140 // "a1"
141 EXPECT_FALSE(service->getUidFromArgs(args, 3, uid));
142
143 // ""
144 EXPECT_FALSE(service->getUidFromArgs(args, 4, uid));
145
146 // For a non-userdebug, uid "1" cannot be impersonated.
147 service->mEngBuild = false;
148 EXPECT_FALSE(service->getUidFromArgs(args, 2, uid));
149 }
150
151 class StatsServiceStatsdInitTest : public StatsServiceConfigTest,
152 public testing::WithParamInterface<bool> {
153 public:
StatsServiceStatsdInitTest()154 StatsServiceStatsdInitTest() : kInitDelaySec(GetParam() ? 0 : 3) {
155 }
156
ToString(testing::TestParamInfo<bool> info)157 static std::string ToString(testing::TestParamInfo<bool> info) {
158 return info.param ? "NoDelay" : "WithDelay";
159 }
160
161 protected:
162 const int kInitDelaySec = 0;
163
createStatsService()164 shared_ptr<StatsService> createStatsService() override {
165 return SharedRefBase::make<StatsService>(new UidMap(), /*queue=*/nullptr,
166 /*LogEventFilter=*/nullptr,
167 /*initEventDelaySecs=*/kInitDelaySec);
168 }
169 };
170
171 INSTANTIATE_TEST_SUITE_P(StatsServiceStatsdInitTest, StatsServiceStatsdInitTest, testing::Bool(),
172 StatsServiceStatsdInitTest::ToString);
173
TEST_P(StatsServiceStatsdInitTest,StatsServiceStatsdInitTest)174 TEST_P(StatsServiceStatsdInitTest, StatsServiceStatsdInitTest) {
175 // used for error threshold tolerance due to sleep() is involved
176 const int64_t ERROR_THRESHOLD_NS = GetParam() ? 1000000 : 5 * 1000000;
177
178 auto pullAtomCallback = SharedRefBase::make<FakeSubsystemSleepCallbackWithTiming>();
179
180 // TODO: evaluate to use service->registerNativePullAtomCallback() API
181 service->mPullerManager->RegisterPullAtomCallback(/*uid=*/0, ATOM_TAG, NS_PER_SEC,
182 NS_PER_SEC * 10, {}, pullAtomCallback);
183
184 const int64_t createConfigTimeNs = getElapsedRealtimeNs();
185 StatsdConfig config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE);
186 config.set_id(kConfigKey);
187 ASSERT_TRUE(sendConfig(config));
188 ASSERT_EQ(2, pullAtomCallback->pullNum);
189
190 service->mProcessor->mPullerManager->ForceClearPullerCache();
191
192 const int64_t initCompletedTimeNs = getElapsedRealtimeNs();
193 service->onStatsdInitCompleted();
194 ASSERT_EQ(3, pullAtomCallback->pullNum);
195
196 // Checking pull with or without delay according to the flag value
197 const int64_t lastPullNs = pullAtomCallback->mPullTimeNs;
198
199 if (GetParam()) {
200 // when flag is defined - should be small delay between init & pull
201 // expect delay smaller than 1 second
202 EXPECT_GE(lastPullNs, initCompletedTimeNs);
203 EXPECT_LE(lastPullNs, initCompletedTimeNs + ERROR_THRESHOLD_NS);
204 } else {
205 // when flag is not defined - big delay is expected (kInitDelaySec)
206 EXPECT_GE(lastPullNs, initCompletedTimeNs + kInitDelaySec * NS_PER_SEC);
207 EXPECT_LE(lastPullNs,
208 initCompletedTimeNs + kInitDelaySec * NS_PER_SEC + ERROR_THRESHOLD_NS);
209 }
210
211 const int64_t bucketSizeNs =
212 TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
213 const int64_t dumpReportTsNanos = createConfigTimeNs + bucketSizeNs + NS_PER_SEC;
214
215 vector<uint8_t> output;
216 ConfigKey configKey(kCallingUid, kConfigKey);
217 service->mProcessor->onDumpReport(configKey, dumpReportTsNanos,
218 /*include_current_bucket=*/false, /*erase_data=*/true,
219 ADB_DUMP, FAST, &output);
220 ConfigMetricsReportList reports;
221 reports.ParseFromArray(output.data(), output.size());
222 ASSERT_EQ(1, reports.reports_size());
223
224 backfillDimensionPath(&reports);
225 backfillStartEndTimestamp(&reports);
226 backfillAggregatedAtoms(&reports);
227 StatsLogReport::GaugeMetricDataWrapper gaugeMetrics =
228 reports.reports(0).metrics(0).gauge_metrics();
229 ASSERT_EQ(gaugeMetrics.skipped_size(), 0);
230 ASSERT_GT((int)gaugeMetrics.data_size(), 0);
231 const auto data = gaugeMetrics.data(0);
232 ASSERT_EQ(2, data.bucket_info_size());
233
234 const auto bucketInfo0 = data.bucket_info(0);
235 const auto bucketInfo1 = data.bucket_info(1);
236
237 EXPECT_GE(NanoToMillis(bucketInfo0.start_bucket_elapsed_nanos()),
238 NanoToMillis(createConfigTimeNs));
239 EXPECT_LE(NanoToMillis(bucketInfo0.start_bucket_elapsed_nanos()),
240 NanoToMillis(createConfigTimeNs + ERROR_THRESHOLD_NS));
241
242 EXPECT_EQ(NanoToMillis(bucketInfo0.end_bucket_elapsed_nanos()),
243 NanoToMillis(bucketInfo1.start_bucket_elapsed_nanos()));
244
245 ASSERT_EQ(1, bucketInfo1.atom_size());
246 ASSERT_GT(bucketInfo1.atom(0).subsystem_sleep_state().time_millis(), 0);
247
248 EXPECT_GE(NanoToMillis(bucketInfo1.start_bucket_elapsed_nanos()),
249 NanoToMillis(createConfigTimeNs + kInitDelaySec * NS_PER_SEC));
250 EXPECT_LE(NanoToMillis(bucketInfo1.start_bucket_elapsed_nanos()),
251 NanoToMillis(createConfigTimeNs + kInitDelaySec * NS_PER_SEC + ERROR_THRESHOLD_NS));
252
253 EXPECT_GE(NanoToMillis(createConfigTimeNs + bucketSizeNs),
254 NanoToMillis(bucketInfo1.end_bucket_elapsed_nanos()));
255 EXPECT_LE(NanoToMillis(createConfigTimeNs + bucketSizeNs),
256 NanoToMillis(bucketInfo1.end_bucket_elapsed_nanos() + ERROR_THRESHOLD_NS));
257 }
258
259 #else
260 GTEST_LOG_(INFO) << "This test does nothing.\n";
261 #endif
262
263 } // namespace statsd
264 } // namespace os
265 } // namespace android
266