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