1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <android-base/logging.h>
18 #include <android/hardware/health/storage/1.0/IStorage.h>
19 #include <gtest/gtest.h>
20 #include <hidl/GtestPrinter.h>
21 #include <hidl/HidlTransportSupport.h>
22 #include <hidl/ServiceManagement.h>
23 #include <unistd.h>
24 #include <thread>
25
26 namespace android {
27 namespace hardware {
28 namespace health {
29 namespace storage {
30 namespace V1_0 {
31
32 using ::std::literals::chrono_literals::operator""ms;
33
34 #define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk()) << ret.description()
35
36 // Dev GC timeout. This is the timeout used by vold.
37 const uint64_t kDevGcTimeoutSec = 120;
38 const std::chrono::seconds kDevGcTimeout{kDevGcTimeoutSec};
39 // Dev GC timeout tolerance. The HAL may not immediately return after the
40 // timeout, so include an acceptable tolerance.
41 const std::chrono::seconds kDevGcTolerance{3};
42 // Time accounted for RPC calls.
43 const std::chrono::milliseconds kRpcTime{1000};
44
45 template <typename R>
toString(std::chrono::duration<R,std::milli> time)46 std::string toString(std::chrono::duration<R, std::milli> time) {
47 return std::to_string(time.count()) + "ms";
48 }
49
50 /** An atomic boolean flag that indicates whether a task has finished. */
51 class Flag {
52 public:
onFinish()53 void onFinish() {
54 std::unique_lock<std::mutex> lock(mMutex);
55 onFinishLocked(&lock);
56 }
57 template <typename R, typename P>
wait(std::chrono::duration<R,P> duration)58 bool wait(std::chrono::duration<R, P> duration) {
59 std::unique_lock<std::mutex> lock(mMutex);
60 return waitLocked(&lock, duration);
61 }
62
63 protected:
64 /** Will unlock. */
onFinishLocked(std::unique_lock<std::mutex> * lock)65 void onFinishLocked(std::unique_lock<std::mutex>* lock) {
66 mFinished = true;
67 lock->unlock();
68 mCv.notify_all();
69 }
70 template <typename R, typename P>
waitLocked(std::unique_lock<std::mutex> * lock,std::chrono::duration<R,P> duration)71 bool waitLocked(std::unique_lock<std::mutex>* lock, std::chrono::duration<R, P> duration) {
72 mCv.wait_for(*lock, duration, [this] { return mFinished; });
73 return mFinished;
74 }
75
76 bool mFinished{false};
77 std::mutex mMutex;
78 std::condition_variable mCv;
79 };
80
81 class GcCallback : public IGarbageCollectCallback, public Flag {
82 public:
onFinish(Result result)83 Return<void> onFinish(Result result) override {
84 std::unique_lock<std::mutex> lock(mMutex);
85 mResult = result;
86 Flag::onFinishLocked(&lock);
87 return Void();
88 }
89
90 /**
91 * Wait for a specific "timeout". If GC has finished, test that the result
92 * is equal to the "expected" value.
93 */
94 template <typename R, typename P>
waitForResult(std::chrono::duration<R,P> timeout,Result expected)95 void waitForResult(std::chrono::duration<R, P> timeout, Result expected) {
96 std::unique_lock<std::mutex> lock(mMutex);
97 ASSERT_TRUE(waitLocked(&lock, timeout)) << "timeout after " << toString(timeout);
98 EXPECT_EQ(expected, mResult);
99 }
100
101 private:
102 Result mResult{Result::UNKNOWN_ERROR};
103 };
104
105 class HealthStorageHidlTest : public ::testing::TestWithParam<std::string> {
106 public:
SetUp()107 virtual void SetUp() override {
108 fs = IStorage::getService(GetParam());
109
110 ASSERT_NE(fs, nullptr);
111 LOG(INFO) << "Service is remote " << fs->isRemote();
112 }
113
TearDown()114 virtual void TearDown() override {
115 EXPECT_TRUE(ping(kRpcTime))
116 << "Service is not responsive; expect subsequent tests to fail.";
117 }
118
119 /**
120 * Ping the service and expect it to return after "timeout". Return true
121 * iff the service is responsive within "timeout".
122 */
123 template <typename R, typename P>
ping(std::chrono::duration<R,P> timeout)124 bool ping(std::chrono::duration<R, P> timeout) {
125 // Ensure the service is responsive after the test.
126 sp<IStorage> service = fs;
127 auto pingFlag = std::make_shared<Flag>();
128 std::thread([service, pingFlag] {
129 service->ping();
130 pingFlag->onFinish();
131 })
132 .detach();
133 return pingFlag->wait(timeout);
134 }
135
136 sp<IStorage> fs;
137 };
138
139 /**
140 * Ensure garbage collection works on null callback.
141 */
TEST_P(HealthStorageHidlTest,GcNullCallback)142 TEST_P(HealthStorageHidlTest, GcNullCallback) {
143 auto ret = fs->garbageCollect(kDevGcTimeoutSec, nullptr);
144
145 ASSERT_OK(ret);
146
147 // Hold test process because HAL can be single-threaded and doing GC.
148 ASSERT_TRUE(ping(kDevGcTimeout + kDevGcTolerance + kRpcTime))
149 << "Service must be available after "
150 << toString(kDevGcTimeout + kDevGcTolerance + kRpcTime);
151 }
152
153 /**
154 * Ensure garbage collection works on non-null callback.
155 */
TEST_P(HealthStorageHidlTest,GcNonNullCallback)156 TEST_P(HealthStorageHidlTest, GcNonNullCallback) {
157 sp<GcCallback> cb = new GcCallback();
158 auto ret = fs->garbageCollect(kDevGcTimeoutSec, cb);
159 ASSERT_OK(ret);
160 cb->waitForResult(kDevGcTimeout + kDevGcTolerance + kRpcTime, Result::SUCCESS);
161 }
162
163 INSTANTIATE_TEST_SUITE_P(
164 PerInstance, HealthStorageHidlTest,
165 testing::ValuesIn(android::hardware::getAllHalInstanceNames(IStorage::descriptor)),
166 android::hardware::PrintInstanceNameToString);
167
168 } // namespace V1_0
169 } // namespace storage
170 } // namespace health
171 } // namespace hardware
172 } // namespace android
173