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