1 // Copyright (C) 2019 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 "src/external/StatsCallbackPuller.h"
16
17 #include <aidl/android/os/BnPullAtomCallback.h>
18 #include <aidl/android/os/IPullAtomResultReceiver.h>
19 #include <aidl/android/util/StatsEventParcel.h>
20 #include <android/binder_interface_utils.h>
21 #include <gmock/gmock.h>
22 #include <gtest/gtest.h>
23 #include <stdio.h>
24
25 #include <chrono>
26 #include <thread>
27 #include <vector>
28
29 #include "../metrics/metrics_test_helper.h"
30 #include "src/stats_log_util.h"
31 #include "stats_event.h"
32 #include "tests/statsd_test_util.h"
33
34 #ifdef __ANDROID__
35
36 namespace android {
37 namespace os {
38 namespace statsd {
39
40 using namespace testing;
41 using Status = ::ndk::ScopedAStatus;
42 using aidl::android::os::BnPullAtomCallback;
43 using aidl::android::os::IPullAtomResultReceiver;
44 using aidl::android::util::StatsEventParcel;
45 using ::ndk::SharedRefBase;
46 using std::make_shared;
47 using std::shared_ptr;
48 using std::vector;
49 using std::this_thread::sleep_for;
50 using testing::Contains;
51
52 namespace {
53 int pullTagId = -12;
54 bool pullSuccess;
55 vector<int64_t> values;
56 int64_t pullDelayNs;
57 int64_t pullTimeoutNs;
58 int64_t pullCoolDownNs;
59 std::thread pullThread;
60
createSimpleEvent(int64_t value)61 AStatsEvent* createSimpleEvent(int64_t value) {
62 AStatsEvent* event = AStatsEvent_obtain();
63 AStatsEvent_setAtomId(event, pullTagId);
64 AStatsEvent_writeInt64(event, value);
65 AStatsEvent_build(event);
66 return event;
67 }
68
executePull(const shared_ptr<IPullAtomResultReceiver> & resultReceiver)69 void executePull(const shared_ptr<IPullAtomResultReceiver>& resultReceiver) {
70 // Convert stats_events into StatsEventParcels.
71 vector<StatsEventParcel> parcels;
72 for (int i = 0; i < values.size(); i++) {
73 AStatsEvent* event = createSimpleEvent(values[i]);
74 size_t size;
75 uint8_t* buffer = AStatsEvent_getBuffer(event, &size);
76
77 StatsEventParcel p;
78 // vector.assign() creates a copy, but this is inevitable unless
79 // stats_event.h/c uses a vector as opposed to a buffer.
80 p.buffer.assign(buffer, buffer + size);
81 parcels.push_back(std::move(p));
82 AStatsEvent_release(event);
83 }
84
85 sleep_for(std::chrono::nanoseconds(pullDelayNs));
86 resultReceiver->pullFinished(pullTagId, pullSuccess, parcels);
87 }
88
89 class FakePullAtomCallback : public BnPullAtomCallback {
90 public:
onPullAtom(int atomTag,const shared_ptr<IPullAtomResultReceiver> & resultReceiver)91 Status onPullAtom(int atomTag,
92 const shared_ptr<IPullAtomResultReceiver>& resultReceiver) override {
93 // Force pull to happen in separate thread to simulate binder.
94 pullThread = std::thread(executePull, resultReceiver);
95 return Status::ok();
96 }
97 };
98
99 class StatsCallbackPullerTest : public ::testing::Test {
100 public:
StatsCallbackPullerTest()101 StatsCallbackPullerTest() {
102 }
103
SetUp()104 void SetUp() override {
105 pullSuccess = false;
106 pullDelayNs = 0;
107 values.clear();
108 pullTimeoutNs = 10000000000LL; // 10 seconds.
109 pullCoolDownNs = 1000000000; // 1 second.
110 }
111
TearDown()112 void TearDown() override {
113 if (pullThread.joinable()) {
114 pullThread.join();
115 }
116 values.clear();
117 }
118 };
119 } // Anonymous namespace.
120
TEST_F(StatsCallbackPullerTest,PullSuccess)121 TEST_F(StatsCallbackPullerTest, PullSuccess) {
122 shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
123 int64_t value = 43;
124 pullSuccess = true;
125 values.push_back(value);
126
127 StatsCallbackPuller puller(pullTagId, cb, pullCoolDownNs, pullTimeoutNs, {});
128
129 vector<std::shared_ptr<LogEvent>> dataHolder;
130 int64_t startTimeNs = getElapsedRealtimeNs();
131 EXPECT_TRUE(puller.PullInternal(&dataHolder));
132 int64_t endTimeNs = getElapsedRealtimeNs();
133
134 ASSERT_EQ(1, dataHolder.size());
135 EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
136 EXPECT_LT(startTimeNs, dataHolder[0]->GetElapsedTimestampNs());
137 EXPECT_GT(endTimeNs, dataHolder[0]->GetElapsedTimestampNs());
138 ASSERT_EQ(1, dataHolder[0]->size());
139 EXPECT_EQ(value, dataHolder[0]->getValues()[0].mValue.int_value);
140 }
141
TEST_F(StatsCallbackPullerTest,PullFail)142 TEST_F(StatsCallbackPullerTest, PullFail) {
143 shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
144 pullSuccess = false;
145 int64_t value = 1234;
146 values.push_back(value);
147
148 StatsCallbackPuller puller(pullTagId, cb, pullCoolDownNs, pullTimeoutNs, {});
149
150 vector<shared_ptr<LogEvent>> dataHolder;
151 EXPECT_FALSE(puller.PullInternal(&dataHolder));
152 ASSERT_EQ(0, dataHolder.size());
153 }
154
TEST_F(StatsCallbackPullerTest,PullTimeout)155 TEST_F(StatsCallbackPullerTest, PullTimeout) {
156 shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
157 pullSuccess = true;
158 pullDelayNs = MillisToNano(5); // 5ms.
159 pullTimeoutNs = 10000; // 10 microseconds.
160 int64_t value = 4321;
161 values.push_back(value);
162
163 StatsCallbackPuller puller(pullTagId, cb, pullCoolDownNs, pullTimeoutNs, {});
164
165 vector<shared_ptr<LogEvent>> dataHolder;
166 int64_t startTimeNs = getElapsedRealtimeNs();
167 // Returns true to let StatsPuller code evaluate the timeout.
168 EXPECT_TRUE(puller.PullInternal(&dataHolder));
169 int64_t endTimeNs = getElapsedRealtimeNs();
170 int64_t actualPullDurationNs = endTimeNs - startTimeNs;
171
172 // Pull should take at least the timeout amount of time, but should stop early because the delay
173 // is bigger.
174 EXPECT_LT(pullTimeoutNs, actualPullDurationNs);
175 EXPECT_GT(pullDelayNs, actualPullDurationNs);
176 ASSERT_EQ(0, dataHolder.size());
177
178 // Let the pull return and make sure that the dataHolder is not modified.
179 pullThread.join();
180 ASSERT_EQ(0, dataHolder.size());
181 }
182
183 // Register a puller and ensure that the timeout logic works.
TEST_F(StatsCallbackPullerTest,RegisterAndTimeout)184 TEST_F(StatsCallbackPullerTest, RegisterAndTimeout) {
185 shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
186 pullSuccess = true;
187 pullDelayNs = MillisToNano(5); // 5 ms.
188 pullTimeoutNs = 10000; // 10 microsseconds.
189 int64_t value = 4321;
190 int32_t uid = 123;
191 values.push_back(value);
192
193 sp<StatsPullerManager> pullerManager = new StatsPullerManager();
194 pullerManager->RegisterPullAtomCallback(uid, pullTagId, pullCoolDownNs, pullTimeoutNs,
195 vector<int32_t>(), cb);
196 vector<shared_ptr<LogEvent>> dataHolder;
197 int64_t startTimeNs = getElapsedRealtimeNs();
198 // Returns false, since StatsPuller code will evaluate the timeout.
199 EXPECT_FALSE(pullerManager->Pull(pullTagId, {uid}, startTimeNs, &dataHolder));
200 int64_t endTimeNs = getElapsedRealtimeNs();
201 int64_t actualPullDurationNs = endTimeNs - startTimeNs;
202
203 // Pull should take at least the timeout amount of time, but should stop early because the delay
204 // is bigger. Make sure that the time is closer to the timeout, than to the intended delay.
205 EXPECT_LT(pullTimeoutNs, actualPullDurationNs);
206 EXPECT_GT(pullDelayNs / 5, actualPullDurationNs);
207 ASSERT_EQ(0, dataHolder.size());
208
209 // Let the pull return and make sure that the dataHolder is not modified.
210 pullThread.join();
211 ASSERT_EQ(0, dataHolder.size());
212 }
213
214 } // namespace statsd
215 } // namespace os
216 } // namespace android
217 #else
218 GTEST_LOG_(INFO) << "This test does nothing.\n";
219 #endif
220