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