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::util::ProtoOutputStream;
35  using ::ndk::SharedRefBase;
36  
37  #ifdef __ANDROID__
38  
39  namespace {
40  
41  const int64_t metricId = 123456;
42  const int32_t ATOM_TAG = util::SUBSYSTEM_SLEEP_STATE;
43  
CreateStatsdConfig(const GaugeMetric::SamplingType samplingType)44  StatsdConfig CreateStatsdConfig(const GaugeMetric::SamplingType samplingType) {
45      StatsdConfig config;
46      config.add_default_pull_packages("AID_ROOT");  // Fake puller is registered with root.
47      auto atomMatcher = CreateSimpleAtomMatcher("TestMatcher", ATOM_TAG);
48      *config.add_atom_matcher() = atomMatcher;
49      *config.add_gauge_metric() =
50              createGaugeMetric("GAUGE1", atomMatcher.id(), samplingType, nullopt, nullopt);
51      config.set_hash_strings_in_metric_report(false);
52      return config;
53  }
54  
55  class FakeSubsystemSleepCallbackWithTiming : public FakeSubsystemSleepCallback {
56  public:
onPullAtom(int atomTag,const shared_ptr<IPullAtomResultReceiver> & resultReceiver)57      Status onPullAtom(int atomTag,
58                        const shared_ptr<IPullAtomResultReceiver>& resultReceiver) override {
59          mPullTimeNs = getElapsedRealtimeNs();
60          return FakeSubsystemSleepCallback::onPullAtom(atomTag, resultReceiver);
61      }
62      int64_t mPullTimeNs = 0;
63  };
64  
65  }  // namespace
66  
TEST(StatsServiceTest,TestAddConfig_simple)67  TEST(StatsServiceTest, TestAddConfig_simple) {
68      const sp<UidMap> uidMap = new UidMap();
69      shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(
70              uidMap, /* queue */ nullptr, std::make_shared<LogEventFilter>());
71      const int kConfigKey = 12345;
72      const int kCallingUid = 123;
73      StatsdConfig config;
74      config.set_id(kConfigKey);
75      string serialized = config.SerializeAsString();
76  
77      EXPECT_TRUE(service->addConfigurationChecked(kCallingUid, kConfigKey,
78                                                   {serialized.begin(), serialized.end()}));
79      service->removeConfiguration(kConfigKey, kCallingUid);
80      ConfigKey configKey(kCallingUid, kConfigKey);
81      service->mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(),
82                                        false /* include_current_bucket*/, true /* erase_data */,
83                                        ADB_DUMP, NO_TIME_CONSTRAINTS, nullptr);
84  }
85  
TEST(StatsServiceTest,TestAddConfig_empty)86  TEST(StatsServiceTest, TestAddConfig_empty) {
87      const sp<UidMap> uidMap = new UidMap();
88      shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(
89              uidMap, /* queue */ nullptr, std::make_shared<LogEventFilter>());
90      string serialized = "";
91      const int kConfigKey = 12345;
92      const int kCallingUid = 123;
93      EXPECT_TRUE(service->addConfigurationChecked(kCallingUid, kConfigKey,
94                                                   {serialized.begin(), serialized.end()}));
95      service->removeConfiguration(kConfigKey, kCallingUid);
96      ConfigKey configKey(kCallingUid, kConfigKey);
97      service->mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(),
98                                        false /* include_current_bucket*/, true /* erase_data */,
99                                        ADB_DUMP, NO_TIME_CONSTRAINTS, nullptr);
100  }
101  
TEST(StatsServiceTest,TestAddConfig_invalid)102  TEST(StatsServiceTest, TestAddConfig_invalid) {
103      const sp<UidMap> uidMap = new UidMap();
104      shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(
105              uidMap, /* queue */ nullptr, std::make_shared<LogEventFilter>());
106      string serialized = "Invalid config!";
107  
108      EXPECT_FALSE(
109              service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
110  }
111  
TEST(StatsServiceTest,TestGetUidFromArgs)112  TEST(StatsServiceTest, TestGetUidFromArgs) {
113      Vector<String8> args;
114      args.push(String8("-1"));
115      args.push(String8("0"));
116      args.push(String8("1"));
117      args.push(String8("a1"));
118      args.push(String8(""));
119  
120      int32_t uid;
121  
122      const sp<UidMap> uidMap = new UidMap();
123      shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(
124              uidMap, /* queue */ nullptr, std::make_shared<LogEventFilter>());
125      service->mEngBuild = true;
126  
127      // "-1"
128      EXPECT_FALSE(service->getUidFromArgs(args, 0, uid));
129  
130      // "0"
131      EXPECT_TRUE(service->getUidFromArgs(args, 1, uid));
132      EXPECT_EQ(0, uid);
133  
134      // "1"
135      EXPECT_TRUE(service->getUidFromArgs(args, 2, uid));
136      EXPECT_EQ(1, uid);
137  
138      // "a1"
139      EXPECT_FALSE(service->getUidFromArgs(args, 3, uid));
140  
141      // ""
142      EXPECT_FALSE(service->getUidFromArgs(args, 4, uid));
143  
144      // For a non-userdebug, uid "1" cannot be impersonated.
145      service->mEngBuild = false;
146      EXPECT_FALSE(service->getUidFromArgs(args, 2, uid));
147  }
148  
TEST_F(StatsServiceConfigTest,StatsServiceStatsdInitTest)149  TEST_F(StatsServiceConfigTest, StatsServiceStatsdInitTest) {
150      // used for error threshold tolerance due to sleep() is involved
151      const int64_t ERROR_THRESHOLD_NS = 25 * 1000000;  // 25 ms
152      const int INIT_DELAY_SEC = 3;
153  
154      auto pullAtomCallback = SharedRefBase::make<FakeSubsystemSleepCallbackWithTiming>();
155  
156      // TODO: evaluate to use service->registerNativePullAtomCallback() API
157      service->mPullerManager->RegisterPullAtomCallback(/*uid=*/0, ATOM_TAG, NS_PER_SEC,
158                                                        NS_PER_SEC * 10, {}, pullAtomCallback);
159  
160      StatsdConfig config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE);
161      config.set_id(kConfigKey);
162      const int64_t createConfigTimeNs = getElapsedRealtimeNs();
163      ASSERT_TRUE(sendConfig(config));
164      ASSERT_EQ(2, pullAtomCallback->pullNum);
165  
166      service->mProcessor->mPullerManager->ForceClearPullerCache();
167  
168      const int64_t initCompletedTimeNs = getElapsedRealtimeNs();
169      service->onStatsdInitCompleted(INIT_DELAY_SEC);
170      ASSERT_EQ(3, pullAtomCallback->pullNum);
171  
172      // Checking pull with or without delay according to the flag value
173      const int64_t lastPullNs = pullAtomCallback->mPullTimeNs;
174  
175      EXPECT_GE(lastPullNs, initCompletedTimeNs + INIT_DELAY_SEC * NS_PER_SEC);
176      EXPECT_LE(lastPullNs, initCompletedTimeNs + INIT_DELAY_SEC * NS_PER_SEC + ERROR_THRESHOLD_NS);
177  
178      const int64_t bucketSizeNs =
179              TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
180      const int64_t dumpReportTsNanos = createConfigTimeNs + bucketSizeNs + NS_PER_SEC;
181  
182      vector<uint8_t> output;
183      ConfigKey configKey(kCallingUid, kConfigKey);
184      service->mProcessor->onDumpReport(configKey, dumpReportTsNanos,
185                                        /*include_current_bucket=*/false, /*erase_data=*/true,
186                                        ADB_DUMP, FAST, &output);
187      ConfigMetricsReportList reports;
188      reports.ParseFromArray(output.data(), output.size());
189      ASSERT_EQ(1, reports.reports_size());
190  
191      backfillDimensionPath(&reports);
192      backfillStartEndTimestamp(&reports);
193      backfillAggregatedAtoms(&reports);
194      StatsLogReport::GaugeMetricDataWrapper gaugeMetrics =
195              reports.reports(0).metrics(0).gauge_metrics();
196      ASSERT_EQ(gaugeMetrics.skipped_size(), 0);
197      ASSERT_GT((int)gaugeMetrics.data_size(), 0);
198      const auto data = gaugeMetrics.data(0);
199      ASSERT_EQ(2, data.bucket_info_size());
200  
201      const auto bucketInfo0 = data.bucket_info(0);
202      const auto bucketInfo1 = data.bucket_info(1);
203  
204      EXPECT_GE(NanoToMillis(bucketInfo0.start_bucket_elapsed_nanos()),
205                NanoToMillis(createConfigTimeNs));
206      EXPECT_LE(NanoToMillis(bucketInfo0.start_bucket_elapsed_nanos()),
207                NanoToMillis(createConfigTimeNs + ERROR_THRESHOLD_NS));
208  
209      EXPECT_EQ(NanoToMillis(bucketInfo0.end_bucket_elapsed_nanos()),
210                NanoToMillis(bucketInfo1.start_bucket_elapsed_nanos()));
211  
212      ASSERT_EQ(1, bucketInfo1.atom_size());
213      ASSERT_GT(bucketInfo1.atom(0).subsystem_sleep_state().time_millis(), 0);
214  
215      EXPECT_GE(NanoToMillis(bucketInfo1.start_bucket_elapsed_nanos()),
216                NanoToMillis(initCompletedTimeNs + INIT_DELAY_SEC * NS_PER_SEC));
217      EXPECT_LE(NanoToMillis(bucketInfo1.start_bucket_elapsed_nanos()),
218                NanoToMillis(initCompletedTimeNs + INIT_DELAY_SEC * NS_PER_SEC + ERROR_THRESHOLD_NS));
219  
220      // this check confirms that bucket end is not affected by the StatsService init delay
221      EXPECT_EQ(NanoToMillis(bucketInfo1.end_bucket_elapsed_nanos()),
222                NanoToMillis(service->mProcessor->mTimeBaseNs + bucketSizeNs));
223  }
224  
225  #else
226  GTEST_LOG_(INFO) << "This test does nothing.\n";
227  #endif
228  
229  }  // namespace statsd
230  }  // namespace os
231  }  // namespace android
232