1 /*
2 * Copyright (C) 2021 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 #define LOG_TAG "PerformanceHintNativeTest"
18
19 #include <aidl/android/hardware/power/ChannelConfig.h>
20 #include <aidl/android/hardware/power/SessionConfig.h>
21 #include <aidl/android/hardware/power/SessionTag.h>
22 #include <aidl/android/hardware/power/WorkDuration.h>
23 #include <aidl/android/os/IHintManager.h>
24 #include <android/binder_manager.h>
25 #include <android/binder_status.h>
26 #include <android/performance_hint.h>
27 #include <gmock/gmock.h>
28 #include <gtest/gtest.h>
29 #include <performance_hint_private.h>
30
31 #include <memory>
32 #include <vector>
33
34 namespace hal = aidl::android::hardware::power;
35 using aidl::android::os::IHintManager;
36 using aidl::android::os::IHintSession;
37 using ndk::ScopedAStatus;
38 using ndk::SpAIBinder;
39
40 using namespace android;
41 using namespace testing;
42
43 class MockIHintManager : public IHintManager {
44 public:
45 MOCK_METHOD(ScopedAStatus, createHintSessionWithConfig,
46 (const SpAIBinder& token, const ::std::vector<int32_t>& tids, int64_t durationNanos,
47 hal::SessionTag tag, std::optional<hal::SessionConfig>* config,
48 std::shared_ptr<IHintSession>* _aidl_return),
49 (override));
50 MOCK_METHOD(ScopedAStatus, getHintSessionPreferredRate, (int64_t * _aidl_return), (override));
51 MOCK_METHOD(ScopedAStatus, setHintSessionThreads,
52 (const std::shared_ptr<IHintSession>& hintSession,
53 const ::std::vector<int32_t>& tids),
54 (override));
55 MOCK_METHOD(ScopedAStatus, getHintSessionThreadIds,
56 (const std::shared_ptr<IHintSession>& hintSession, ::std::vector<int32_t>* tids),
57 (override));
58 MOCK_METHOD(ScopedAStatus, getSessionChannel,
59 (const ::ndk::SpAIBinder& in_token, hal::ChannelConfig* _aidl_return), (override));
60 MOCK_METHOD(ScopedAStatus, closeSessionChannel, (), (override));
61 MOCK_METHOD(SpAIBinder, asBinder, (), (override));
62 MOCK_METHOD(bool, isRemote, (), (override));
63 };
64
65 class MockIHintSession : public IHintSession {
66 public:
67 MOCK_METHOD(ScopedAStatus, updateTargetWorkDuration, (int64_t targetDurationNanos), (override));
68 MOCK_METHOD(ScopedAStatus, reportActualWorkDuration,
69 (const ::std::vector<int64_t>& actualDurationNanos,
70 const ::std::vector<int64_t>& timeStampNanos),
71 (override));
72 MOCK_METHOD(ScopedAStatus, sendHint, (int32_t hint), (override));
73 MOCK_METHOD(ScopedAStatus, setMode, (int32_t mode, bool enabled), (override));
74 MOCK_METHOD(ScopedAStatus, close, (), (override));
75 MOCK_METHOD(ScopedAStatus, reportActualWorkDuration2,
76 (const ::std::vector<hal::WorkDuration>& workDurations), (override));
77 MOCK_METHOD(SpAIBinder, asBinder, (), (override));
78 MOCK_METHOD(bool, isRemote, (), (override));
79 };
80
81 class PerformanceHintTest : public Test {
82 public:
SetUp()83 void SetUp() override {
84 mMockIHintManager = ndk::SharedRefBase::make<NiceMock<MockIHintManager>>();
85 APerformanceHint_setIHintManagerForTesting(&mMockIHintManager);
86 }
87
TearDown()88 void TearDown() override {
89 mMockIHintManager = nullptr;
90 // Destroys MockIHintManager.
91 APerformanceHint_setIHintManagerForTesting(nullptr);
92 }
93
createManager()94 APerformanceHintManager* createManager() {
95 ON_CALL(*mMockIHintManager, getHintSessionPreferredRate(_))
96 .WillByDefault(DoAll(SetArgPointee<0>(123L), [] { return ScopedAStatus::ok(); }));
97 return APerformanceHint_getManager();
98 }
createSession(APerformanceHintManager * manager,int64_t targetDuration=56789L,bool isHwui=false)99 APerformanceHintSession* createSession(APerformanceHintManager* manager,
100 int64_t targetDuration = 56789L, bool isHwui = false) {
101 mMockSession = ndk::SharedRefBase::make<NiceMock<MockIHintSession>>();
102 int64_t sessionId = 123;
103 std::vector<int32_t> tids;
104 tids.push_back(1);
105 tids.push_back(2);
106
107 ON_CALL(*mMockIHintManager,
108 createHintSessionWithConfig(_, Eq(tids), Eq(targetDuration), _, _, _))
109 .WillByDefault(DoAll(SetArgPointee<4>(std::make_optional<hal::SessionConfig>(
110 {.id = sessionId})),
111 SetArgPointee<5>(std::shared_ptr<IHintSession>(mMockSession)),
112 [] { return ScopedAStatus::ok(); }));
113
114 ON_CALL(*mMockIHintManager, setHintSessionThreads(_, _)).WillByDefault([] {
115 return ScopedAStatus::ok();
116 });
117 ON_CALL(*mMockSession, sendHint(_)).WillByDefault([] { return ScopedAStatus::ok(); });
118 ON_CALL(*mMockSession, setMode(_, _)).WillByDefault([] { return ScopedAStatus::ok(); });
119 ON_CALL(*mMockSession, close()).WillByDefault([] { return ScopedAStatus::ok(); });
120 ON_CALL(*mMockSession, updateTargetWorkDuration(_)).WillByDefault([] {
121 return ScopedAStatus::ok();
122 });
123 ON_CALL(*mMockSession, reportActualWorkDuration(_, _)).WillByDefault([] {
124 return ScopedAStatus::ok();
125 });
126 ON_CALL(*mMockSession, reportActualWorkDuration2(_)).WillByDefault([] {
127 return ScopedAStatus::ok();
128 });
129 if (isHwui) {
130 return APerformanceHint_createSessionInternal(manager, tids.data(), tids.size(),
131 targetDuration, SessionTag::HWUI);
132 }
133 return APerformanceHint_createSession(manager, tids.data(), tids.size(), targetDuration);
134 }
135
136 std::shared_ptr<NiceMock<MockIHintManager>> mMockIHintManager = nullptr;
137 std::shared_ptr<NiceMock<MockIHintSession>> mMockSession = nullptr;
138 };
139
equalsWithoutTimestamp(hal::WorkDuration lhs,hal::WorkDuration rhs)140 bool equalsWithoutTimestamp(hal::WorkDuration lhs, hal::WorkDuration rhs) {
141 return lhs.workPeriodStartTimestampNanos == rhs.workPeriodStartTimestampNanos &&
142 lhs.cpuDurationNanos == rhs.cpuDurationNanos &&
143 lhs.gpuDurationNanos == rhs.gpuDurationNanos && lhs.durationNanos == rhs.durationNanos;
144 }
145
TEST_F(PerformanceHintTest,TestGetPreferredUpdateRateNanos)146 TEST_F(PerformanceHintTest, TestGetPreferredUpdateRateNanos) {
147 APerformanceHintManager* manager = createManager();
148 int64_t preferredUpdateRateNanos = APerformanceHint_getPreferredUpdateRateNanos(manager);
149 EXPECT_EQ(123L, preferredUpdateRateNanos);
150 }
151
TEST_F(PerformanceHintTest,TestSession)152 TEST_F(PerformanceHintTest, TestSession) {
153 APerformanceHintManager* manager = createManager();
154 APerformanceHintSession* session = createSession(manager);
155 ASSERT_TRUE(session);
156
157 int64_t targetDurationNanos = 10;
158 EXPECT_CALL(*mMockSession, updateTargetWorkDuration(Eq(targetDurationNanos))).Times(Exactly(1));
159 int result = APerformanceHint_updateTargetWorkDuration(session, targetDurationNanos);
160 EXPECT_EQ(0, result);
161
162 usleep(2); // Sleep for longer than preferredUpdateRateNanos.
163 int64_t actualDurationNanos = 20;
164 std::vector<int64_t> actualDurations;
165 actualDurations.push_back(20);
166 EXPECT_CALL(*mMockSession, reportActualWorkDuration2(_)).Times(Exactly(1));
167 result = APerformanceHint_reportActualWorkDuration(session, actualDurationNanos);
168 EXPECT_EQ(0, result);
169
170 result = APerformanceHint_updateTargetWorkDuration(session, -1L);
171 EXPECT_EQ(EINVAL, result);
172 result = APerformanceHint_reportActualWorkDuration(session, -1L);
173 EXPECT_EQ(EINVAL, result);
174
175 SessionHint hintId = SessionHint::CPU_LOAD_RESET;
176 EXPECT_CALL(*mMockSession, sendHint(Eq(hintId))).Times(Exactly(1));
177 result = APerformanceHint_sendHint(session, hintId);
178 EXPECT_EQ(0, result);
179 usleep(110000); // Sleep for longer than the update timeout.
180 EXPECT_CALL(*mMockSession, sendHint(Eq(hintId))).Times(Exactly(1));
181 result = APerformanceHint_sendHint(session, hintId);
182 EXPECT_EQ(0, result);
183 // Expect to get rate limited if we try to send faster than the limiter allows
184 EXPECT_CALL(*mMockSession, sendHint(Eq(hintId))).Times(Exactly(0));
185 result = APerformanceHint_sendHint(session, hintId);
186 EXPECT_EQ(0, result);
187
188 result = APerformanceHint_sendHint(session, static_cast<SessionHint>(-1));
189 EXPECT_EQ(EINVAL, result);
190
191 EXPECT_CALL(*mMockSession, close()).Times(Exactly(1));
192 APerformanceHint_closeSession(session);
193 }
194
TEST_F(PerformanceHintTest,TestUpdatedSessionCreation)195 TEST_F(PerformanceHintTest, TestUpdatedSessionCreation) {
196 EXPECT_CALL(*mMockIHintManager, createHintSessionWithConfig(_, _, _, _, _, _)).Times(1);
197 APerformanceHintManager* manager = createManager();
198 APerformanceHintSession* session = createSession(manager);
199 ASSERT_TRUE(session);
200 APerformanceHint_closeSession(session);
201 }
202
TEST_F(PerformanceHintTest,TestHwuiSessionCreation)203 TEST_F(PerformanceHintTest, TestHwuiSessionCreation) {
204 EXPECT_CALL(*mMockIHintManager,
205 createHintSessionWithConfig(_, _, _, hal::SessionTag::HWUI, _, _))
206 .Times(1);
207 APerformanceHintManager* manager = createManager();
208 APerformanceHintSession* session = createSession(manager, 56789L, true);
209 ASSERT_TRUE(session);
210 APerformanceHint_closeSession(session);
211 }
212
TEST_F(PerformanceHintTest,SetThreads)213 TEST_F(PerformanceHintTest, SetThreads) {
214 APerformanceHintManager* manager = createManager();
215
216 APerformanceHintSession* session = createSession(manager);
217 ASSERT_TRUE(session);
218
219 int32_t emptyTids[2];
220 int result = APerformanceHint_setThreads(session, emptyTids, 0);
221 EXPECT_EQ(EINVAL, result);
222
223 std::vector<int32_t> newTids;
224 newTids.push_back(1);
225 newTids.push_back(3);
226 EXPECT_CALL(*mMockIHintManager, setHintSessionThreads(_, Eq(newTids))).Times(Exactly(1));
227 result = APerformanceHint_setThreads(session, newTids.data(), newTids.size());
228 EXPECT_EQ(0, result);
229
230 testing::Mock::VerifyAndClearExpectations(mMockIHintManager.get());
231 std::vector<int32_t> invalidTids;
232 invalidTids.push_back(4);
233 invalidTids.push_back(6);
234 EXPECT_CALL(*mMockIHintManager, setHintSessionThreads(_, Eq(invalidTids)))
235 .Times(Exactly(1))
236 .WillOnce(Return(ByMove(ScopedAStatus::fromExceptionCode(EX_SECURITY))));
237 result = APerformanceHint_setThreads(session, invalidTids.data(), invalidTids.size());
238 EXPECT_EQ(EPERM, result);
239 }
240
TEST_F(PerformanceHintTest,SetPowerEfficient)241 TEST_F(PerformanceHintTest, SetPowerEfficient) {
242 APerformanceHintManager* manager = createManager();
243 APerformanceHintSession* session = createSession(manager);
244 ASSERT_TRUE(session);
245
246 EXPECT_CALL(*mMockSession, setMode(_, Eq(true))).Times(Exactly(1));
247 int result = APerformanceHint_setPreferPowerEfficiency(session, true);
248 EXPECT_EQ(0, result);
249
250 EXPECT_CALL(*mMockSession, setMode(_, Eq(false))).Times(Exactly(1));
251 result = APerformanceHint_setPreferPowerEfficiency(session, false);
252 EXPECT_EQ(0, result);
253 }
254
TEST_F(PerformanceHintTest,CreateZeroTargetDurationSession)255 TEST_F(PerformanceHintTest, CreateZeroTargetDurationSession) {
256 APerformanceHintManager* manager = createManager();
257 APerformanceHintSession* session = createSession(manager, 0);
258 ASSERT_TRUE(session);
259 }
260
261 MATCHER_P(WorkDurationEq, expected, "") {
262 if (arg.size() != expected.size()) {
263 *result_listener << "WorkDuration vectors are different sizes. Expected: "
264 << expected.size() << ", Actual: " << arg.size();
265 return false;
266 }
267 for (int i = 0; i < expected.size(); ++i) {
268 hal::WorkDuration expectedWorkDuration = expected[i];
269 hal::WorkDuration actualWorkDuration = arg[i];
270 if (!equalsWithoutTimestamp(expectedWorkDuration, actualWorkDuration)) {
271 *result_listener << "WorkDuration at [" << i << "] is different: "
272 << "Expected: " << expectedWorkDuration.toString()
273 << ", Actual: " << actualWorkDuration.toString();
274 return false;
275 }
276 }
277 return true;
278 }
279
TEST_F(PerformanceHintTest,TestAPerformanceHint_reportActualWorkDuration2)280 TEST_F(PerformanceHintTest, TestAPerformanceHint_reportActualWorkDuration2) {
281 APerformanceHintManager* manager = createManager();
282 APerformanceHintSession* session = createSession(manager);
283 ASSERT_TRUE(session);
284
285 int64_t targetDurationNanos = 10;
286 EXPECT_CALL(*mMockSession, updateTargetWorkDuration(Eq(targetDurationNanos))).Times(Exactly(1));
287 int result = APerformanceHint_updateTargetWorkDuration(session, targetDurationNanos);
288 EXPECT_EQ(0, result);
289
290 usleep(2); // Sleep for longer than preferredUpdateRateNanos.
291 struct TestPair {
292 hal::WorkDuration duration;
293 int expectedResult;
294 };
295 std::vector<TestPair> testPairs{
296 {{1, 20, 1, 13, 8}, OK}, {{1, -20, 1, 13, 8}, EINVAL},
297 {{1, 20, -1, 13, 8}, EINVAL}, {{1, -20, 1, -13, 8}, EINVAL},
298 {{1, -20, 1, 13, -8}, EINVAL},
299 };
300 for (auto&& pair : testPairs) {
301 std::vector<hal::WorkDuration> actualWorkDurations;
302 actualWorkDurations.push_back(pair.duration);
303
304 EXPECT_CALL(*mMockSession, reportActualWorkDuration2(WorkDurationEq(actualWorkDurations)))
305 .Times(Exactly(1));
306 result = APerformanceHint_reportActualWorkDuration2(session,
307 reinterpret_cast<AWorkDuration*>(
308 &pair.duration));
309 EXPECT_EQ(pair.expectedResult, result);
310 }
311
312 EXPECT_CALL(*mMockSession, close()).Times(Exactly(1));
313 APerformanceHint_closeSession(session);
314 }
315
TEST_F(PerformanceHintTest,TestAWorkDuration)316 TEST_F(PerformanceHintTest, TestAWorkDuration) {
317 AWorkDuration* aWorkDuration = AWorkDuration_create();
318 ASSERT_NE(aWorkDuration, nullptr);
319
320 AWorkDuration_setWorkPeriodStartTimestampNanos(aWorkDuration, 1);
321 AWorkDuration_setActualTotalDurationNanos(aWorkDuration, 20);
322 AWorkDuration_setActualCpuDurationNanos(aWorkDuration, 13);
323 AWorkDuration_setActualGpuDurationNanos(aWorkDuration, 8);
324 AWorkDuration_release(aWorkDuration);
325 }
326