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 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 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: 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: 101 StatsCallbackPullerTest() { 102 } 103 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 112 void TearDown() override { 113 if (pullThread.joinable()) { 114 pullThread.join(); 115 } 116 values.clear(); 117 } 118 }; 119 } // Anonymous namespace. 120 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 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 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. 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