1 /*
2 * Copyright (C) 2022 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 <algorithm>
18 #include <chrono>
19 #include <cmath>
20 #include <memory>
21 #include <string>
22 #include <thread>
23 #include <unordered_map>
24 #include <vector>
25
26 #define LOG_TAG "thermal_aidl_hal_test"
27
28 #include <VtsCoreUtil.h>
29 #include <aidl/Gtest.h>
30 #include <aidl/Vintf.h>
31 #include <aidl/android/hardware/thermal/BnCoolingDeviceChangedCallback.h>
32 #include <aidl/android/hardware/thermal/BnThermal.h>
33 #include <aidl/android/hardware/thermal/BnThermalChangedCallback.h>
34 #include <android-base/logging.h>
35 #include <android-base/properties.h>
36 #include <android/binder_ibinder.h>
37 #include <android/binder_interface_utils.h>
38 #include <android/binder_manager.h>
39 #include <android/binder_process.h>
40 #include <android/binder_status.h>
41 #include <gtest/gtest.h>
42
43 #include <unistd.h>
44
45 namespace aidl::android::hardware::thermal {
46
47 namespace {
48
49 using ::android::sp;
50 using android::hardware::thermal::CoolingDevice;
51 using android::hardware::thermal::IThermal;
52 using android::hardware::thermal::Temperature;
53 using android::hardware::thermal::TemperatureType;
54
55 using namespace std::string_literals;
56 using namespace std::chrono_literals;
57
58 static const Temperature kThrottleTemp = {
59 .type = TemperatureType::SKIN,
60 .name = "test temperature sensor",
61 .value = 98.6,
62 .throttlingStatus = ThrottlingSeverity::CRITICAL,
63 };
64
65 static const CoolingDevice kCoolingDevice = {
66 .type = CoolingType::CPU,
67 .name = "test cooling device",
68 .value = 1,
69 .powerLimitMw = 300,
70 .powerMw = 500,
71 .timeWindowMs = 7000,
72 };
73
74 static const std::string FEATURE_WATCH = "android.hardware.type.watch";
75 static const std::string FEATURE_TELEVISION = "android.hardware.type.television";
76 static const std::string FEATURE_LEANBACK = "android.software.leanback";
77 static const std::string FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
78 static const std::string FEATURE_PC = "android.hardware.type.pc";
79 static const std::string FEATURE_EMBEDDED = "android.hardware.type.embedded";
80 static const std::string kNonHandheldFeatures[] = {FEATURE_AUTOMOTIVE, FEATURE_LEANBACK,
81 FEATURE_PC, FEATURE_TELEVISION,
82 FEATURE_WATCH, FEATURE_EMBEDDED};
83
84 // Callback class for receiving thermal event notifications from main class
85 class ThermalCallback : public BnThermalChangedCallback {
86 public:
notifyThrottling(const Temperature &)87 ndk::ScopedAStatus notifyThrottling(const Temperature&) override {
88 {
89 std::lock_guard<std::mutex> lock(mMutex);
90 mInvoke = true;
91 }
92 mNotifyThrottling.notify_all();
93 return ndk::ScopedAStatus::ok();
94 }
95
notifyThresholdChanged(const TemperatureThreshold &)96 ndk::ScopedAStatus notifyThresholdChanged(const TemperatureThreshold&) {
97 return ndk::ScopedAStatus::ok();
98 }
99
100 template <typename R, typename P>
waitForCallback(std::chrono::duration<R,P> duration)101 [[nodiscard]] bool waitForCallback(std::chrono::duration<R, P> duration) {
102 std::unique_lock<std::mutex> lock(mMutex);
103 bool r = mNotifyThrottling.wait_for(lock, duration, [this] { return this->mInvoke; });
104 mInvoke = false;
105 return r;
106 }
107
108 private:
109 std::mutex mMutex;
110 std::condition_variable mNotifyThrottling;
111 bool mInvoke = false;
112 };
113
114 // Callback class for receiving cooling device event notifications from main class
115 class CoolingDeviceCallback : public BnCoolingDeviceChangedCallback {
116 public:
notifyCoolingDeviceChanged(const CoolingDevice &)117 ndk::ScopedAStatus notifyCoolingDeviceChanged(const CoolingDevice&) override {
118 {
119 std::lock_guard<std::mutex> lock(mMutex);
120 mInvoke = true;
121 }
122 mNotifyCoolingDeviceChanged.notify_all();
123 return ndk::ScopedAStatus::ok();
124 }
125
126 template <typename R, typename P>
waitForCallback(std::chrono::duration<R,P> duration)127 [[nodiscard]] bool waitForCallback(std::chrono::duration<R, P> duration) {
128 std::unique_lock<std::mutex> lock(mMutex);
129 bool r = mNotifyCoolingDeviceChanged.wait_for(lock, duration,
130 [this] { return this->mInvoke; });
131 mInvoke = false;
132 return r;
133 }
134
135 private:
136 std::mutex mMutex;
137 std::condition_variable mNotifyCoolingDeviceChanged;
138 bool mInvoke = false;
139 };
140
141 // The main test class for THERMAL HIDL HAL.
142 class ThermalAidlTest : public testing::TestWithParam<std::string> {
143 public:
SetUp()144 void SetUp() override {
145 AIBinder* binder = AServiceManager_waitForService(GetParam().c_str());
146 ASSERT_NE(binder, nullptr);
147 mThermal = IThermal::fromBinder(ndk::SpAIBinder(binder));
148
149 mThermalCallback = ndk::SharedRefBase::make<ThermalCallback>();
150 ASSERT_NE(mThermalCallback, nullptr);
151 ::ndk::ScopedAStatus status = mThermal->registerThermalChangedCallback(mThermalCallback);
152 ASSERT_TRUE(status.isOk()) << status.getMessage();
153
154 auto ret = mThermal->getInterfaceVersion(&thermal_version);
155 ASSERT_TRUE(ret.isOk()) << ret;
156 if (thermal_version > 1) {
157 mCoolingDeviceCallback = ndk::SharedRefBase::make<CoolingDeviceCallback>();
158 ASSERT_NE(mCoolingDeviceCallback, nullptr);
159 status = mThermal->registerCoolingDeviceChangedCallbackWithType(mCoolingDeviceCallback,
160 kCoolingDevice.type);
161 ASSERT_TRUE(status.isOk()) << status.getMessage();
162 }
163 }
164
TearDown()165 void TearDown() override {
166 ::ndk::ScopedAStatus status = mThermal->unregisterThermalChangedCallback(mThermalCallback);
167 ASSERT_TRUE(status.isOk()) << status.getMessage();
168
169 // Expect to fail if unregister again
170 status = mThermal->unregisterThermalChangedCallback(mThermalCallback);
171 ASSERT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode());
172
173 auto ret = mThermal->getInterfaceVersion(&thermal_version);
174 ASSERT_TRUE(ret.isOk()) << ret;
175 if (thermal_version > 1) {
176 status = mThermal->unregisterCoolingDeviceChangedCallback(mCoolingDeviceCallback);
177 ASSERT_TRUE(status.isOk()) << status.getMessage();
178 status = mThermal->unregisterCoolingDeviceChangedCallback(mCoolingDeviceCallback);
179 ASSERT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode());
180 }
181 }
182 // Stores thermal version
183 int32_t thermal_version;
184
185 protected:
186 std::shared_ptr<IThermal> mThermal;
187 std::shared_ptr<ThermalCallback> mThermalCallback;
188 std::shared_ptr<CoolingDeviceCallback> mCoolingDeviceCallback;
189 };
190
191 // Test ThermalChangedCallback::notifyThrottling().
192 // This just calls into and back from our local ThermalChangedCallback impl.
TEST_P(ThermalAidlTest,NotifyThrottlingTest)193 TEST_P(ThermalAidlTest, NotifyThrottlingTest) {
194 std::shared_ptr<ThermalCallback> thermalCallback = ndk::SharedRefBase::make<ThermalCallback>();
195 ::ndk::ScopedAStatus status = thermalCallback->notifyThrottling(kThrottleTemp);
196 ASSERT_TRUE(status.isOk()) << status.getMessage();
197 ASSERT_TRUE(thermalCallback->waitForCallback(200ms));
198 }
199
200 // Test CoolingDeviceChangedCallback::notifyCoolingDeviceChanged().
201 // This just calls into and back from our local CoolingDeviceChangedCallback impl.
TEST_P(ThermalAidlTest,NotifyCoolingDeviceChangedTest)202 TEST_P(ThermalAidlTest, NotifyCoolingDeviceChangedTest) {
203 auto ret = mThermal->getInterfaceVersion(&thermal_version);
204 ASSERT_TRUE(ret.isOk()) << ret;
205 if (thermal_version < 2) {
206 return;
207 }
208 std::shared_ptr<CoolingDeviceCallback> cdevCallback =
209 ndk::SharedRefBase::make<CoolingDeviceCallback>();
210 ::ndk::ScopedAStatus status = cdevCallback->notifyCoolingDeviceChanged(kCoolingDevice);
211 ASSERT_TRUE(status.isOk()) << status.getMessage();
212 ASSERT_TRUE(cdevCallback->waitForCallback(200ms));
213 }
214
215 // Test Thermal->registerThermalChangedCallback.
TEST_P(ThermalAidlTest,RegisterThermalChangedCallbackTest)216 TEST_P(ThermalAidlTest, RegisterThermalChangedCallbackTest) {
217 // Expect to fail with same callback
218 ::ndk::ScopedAStatus status = mThermal->registerThermalChangedCallback(mThermalCallback);
219 ASSERT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode());
220 // Expect to fail with null callback
221 status = mThermal->registerThermalChangedCallback(nullptr);
222 ASSERT_TRUE(status.getExceptionCode() == EX_ILLEGAL_ARGUMENT
223 || status.getExceptionCode() == EX_NULL_POINTER);
224 std::shared_ptr<ThermalCallback> localThermalCallback =
225 ndk::SharedRefBase::make<ThermalCallback>();
226 // Expect to succeed with different callback
227 status = mThermal->registerThermalChangedCallback(localThermalCallback);
228 ASSERT_TRUE(status.isOk()) << status.getMessage();
229 // Remove the local callback
230 status = mThermal->unregisterThermalChangedCallback(localThermalCallback);
231 ASSERT_TRUE(status.isOk()) << status.getMessage();
232 // Expect to fail with null callback
233 status = mThermal->unregisterThermalChangedCallback(nullptr);
234 ASSERT_TRUE(status.getExceptionCode() == EX_ILLEGAL_ARGUMENT
235 || status.getExceptionCode() == EX_NULL_POINTER);
236 }
237
238 // Test Thermal->registerThermalChangedCallbackWithType.
TEST_P(ThermalAidlTest,RegisterThermalChangedCallbackWithTypeTest)239 TEST_P(ThermalAidlTest, RegisterThermalChangedCallbackWithTypeTest) {
240 // Expect to fail with same callback
241 ::ndk::ScopedAStatus status = mThermal->registerThermalChangedCallbackWithType(
242 mThermalCallback, TemperatureType::SKIN);
243 ASSERT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode());
244 // Expect to fail with null callback
245 status = mThermal->registerThermalChangedCallbackWithType(nullptr, TemperatureType::SKIN);
246 ASSERT_TRUE(status.getExceptionCode() == EX_ILLEGAL_ARGUMENT
247 || status.getExceptionCode() == EX_NULL_POINTER);
248 std::shared_ptr<ThermalCallback> localThermalCallback =
249 ndk::SharedRefBase::make<ThermalCallback>();
250 // Expect to succeed with different callback
251 status = mThermal->registerThermalChangedCallbackWithType(localThermalCallback,
252 TemperatureType::SKIN);
253 ASSERT_TRUE(status.isOk()) << status.getMessage();
254 // Remove the local callback
255 status = mThermal->unregisterThermalChangedCallback(localThermalCallback);
256 ASSERT_TRUE(status.isOk()) << status.getMessage();
257 // Expect to fail with null callback
258 status = mThermal->unregisterThermalChangedCallback(nullptr);
259 ASSERT_TRUE(status.getExceptionCode() == EX_ILLEGAL_ARGUMENT
260 || status.getExceptionCode() == EX_NULL_POINTER);
261 }
262
263 // Test Thermal->registerCoolingDeviceChangedCallbackWithType.
TEST_P(ThermalAidlTest,RegisterCoolingDeviceChangedCallbackWithTypeTest)264 TEST_P(ThermalAidlTest, RegisterCoolingDeviceChangedCallbackWithTypeTest) {
265 auto ret = mThermal->getInterfaceVersion(&thermal_version);
266 ASSERT_TRUE(ret.isOk()) << ret;
267 if (thermal_version < 2) {
268 return;
269 }
270
271 // Expect to fail with same callback
272 ::ndk::ScopedAStatus status = mThermal->registerCoolingDeviceChangedCallbackWithType(
273 mCoolingDeviceCallback, CoolingType::CPU);
274 ASSERT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode());
275 // Expect to fail with null callback
276 status = mThermal->registerCoolingDeviceChangedCallbackWithType(nullptr, CoolingType::CPU);
277 ASSERT_TRUE(status.getExceptionCode() == EX_ILLEGAL_ARGUMENT ||
278 status.getExceptionCode() == EX_NULL_POINTER);
279 std::shared_ptr<CoolingDeviceCallback> localCoolingDeviceCallback =
280 ndk::SharedRefBase::make<CoolingDeviceCallback>();
281 // Expect to succeed with different callback
282 status = mThermal->registerCoolingDeviceChangedCallbackWithType(localCoolingDeviceCallback,
283 CoolingType::CPU);
284 ASSERT_TRUE(status.isOk()) << status.getMessage();
285 // Remove the local callback
286 status = mThermal->unregisterCoolingDeviceChangedCallback(localCoolingDeviceCallback);
287 ASSERT_TRUE(status.isOk()) << status.getMessage();
288 // Expect to fail with null callback
289 status = mThermal->unregisterCoolingDeviceChangedCallback(nullptr);
290 ASSERT_TRUE(status.getExceptionCode() == EX_ILLEGAL_ARGUMENT ||
291 status.getExceptionCode() == EX_NULL_POINTER);
292 }
293
294 // Test Thermal->getCurrentTemperatures().
TEST_P(ThermalAidlTest,TemperatureTest)295 TEST_P(ThermalAidlTest, TemperatureTest) {
296 std::vector<Temperature> ret;
297 ::ndk::ScopedAStatus status = mThermal->getTemperatures(&ret);
298 if (status.isOk()) {
299 for (auto& i : ret) {
300 EXPECT_LT(0u, i.name.size());
301 LOG(INFO) << i.name + " " + toString(i.type) << "\n";
302 }
303 } else {
304 ASSERT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode());
305 }
306
307 auto types = ::ndk::enum_range<TemperatureType>();
308 for (const auto& type : types) {
309 status = mThermal->getTemperaturesWithType(type, &ret);
310
311 if (status.isOk()) {
312 for (auto& i : ret) {
313 EXPECT_EQ(type, i.type) << "Expect type " + toString(type) + " but got " +
314 toString(i.type) + " for " + i.name;
315 EXPECT_LT(0u, i.name.size());
316 }
317 } else {
318 ASSERT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode());
319 }
320 }
321 }
322
323 // Test Thermal->getTemperatureThresholds().
TEST_P(ThermalAidlTest,TemperatureThresholdTest)324 TEST_P(ThermalAidlTest, TemperatureThresholdTest) {
325 std::vector<TemperatureThreshold> ret;
326 ::ndk::ScopedAStatus status = mThermal->getTemperatureThresholds(&ret);
327 if (status.isOk()) {
328 for (auto& i : ret) {
329 EXPECT_LT(0u, i.name.size());
330 LOG(INFO) << i.name + " " + toString(i.type) << "\n";
331 }
332 } else {
333 ASSERT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode());
334 }
335
336 auto types = ::ndk::enum_range<TemperatureType>();
337 for (const auto& type : types) {
338 status = mThermal->getTemperatureThresholdsWithType(type, &ret);
339
340 if (status.isOk()) {
341 for (auto& i : ret) {
342 EXPECT_EQ(type, i.type) << "Expect type " + toString(type) + " but got " +
343 toString(i.type) + " for " + i.name;
344 EXPECT_LT(0u, i.name.size());
345 }
346 } else {
347 ASSERT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode());
348 }
349 }
350 }
351
352 // Test Thermal->getTemperatureThresholdsWithType(SKIN).
353 // @VsrTest = GMS-VSR-3.2.5-001
354 // @VsrTest = VSR-3.2.5-001
355 // @VsrTest = GMS-VSR-3.2.5-002
356 // @VsrTest = VSR-3.2.5-002
TEST_P(ThermalAidlTest,SkinTemperatureThresholdsTest)357 TEST_P(ThermalAidlTest, SkinTemperatureThresholdsTest) {
358 auto apiLevel = ::android::base::GetIntProperty<int32_t>("ro.vendor.api_level", 0);
359 if (apiLevel < 202404) {
360 GTEST_SKIP() << "Skipping test as the vendor level is below 202404: " << apiLevel;
361 }
362 for (const auto& feature : kNonHandheldFeatures) {
363 if (::testing::deviceSupportsFeature(feature.c_str())) {
364 GTEST_SKIP() << "Skipping test as the device has feature: " << feature;
365 }
366 }
367 std::vector<Temperature> temperatures;
368 ::ndk::ScopedAStatus status =
369 mThermal->getTemperaturesWithType(TemperatureType::SKIN, &temperatures);
370 ASSERT_TRUE(status.isOk()) << "getTemperaturesWithType(SKIN) failed";
371 ASSERT_FALSE(temperatures.empty()) << "getTemperaturesWithType(SKIN) returns empty";
372 ASSERT_EQ(1, temperatures.size())
373 << "getTemperaturesWithType(SKIN) returns multiple temperatures";
374
375 std::vector<TemperatureThreshold> thresholds;
376 status = mThermal->getTemperatureThresholdsWithType(TemperatureType::SKIN, &thresholds);
377 ASSERT_TRUE(status.isOk()) << "getTemperatureThresholdsWithType(SKIN) failed";
378 ASSERT_FALSE(thresholds.empty()) << "getTemperatureThresholdsWithType(SKIN) returns empty";
379 ASSERT_EQ(1, thresholds.size())
380 << "getTemperatureThresholdsWithType(SKIN) returns multiple thresholds";
381 auto temperature = temperatures[0];
382 auto threshold = thresholds[0];
383 ASSERT_EQ(temperature.name, threshold.name);
384 auto severities = ::ndk::enum_range<ThrottlingSeverity>();
385 auto cardinality = std::distance(severities.begin(), severities.end());
386 ASSERT_NE(NAN, temperature.value);
387 ASSERT_EQ(cardinality, threshold.hotThrottlingThresholds.size());
388 float lastThreshold = threshold.hotThrottlingThresholds[1];
389 // skip NONE, and check that the rest should be set and non-decreasing
390 for (auto i = 2; i < cardinality; i++) {
391 float t = threshold.hotThrottlingThresholds[i];
392 ASSERT_NE(NAN, t);
393 ASSERT_TRUE(t >= lastThreshold) << "Temperature thresholds should be non-decreasing "
394 << "but got " << t << " for status " << i << " and "
395 << lastThreshold << " for status " << i - 1;
396 lastThreshold = t;
397 }
398 }
399
400 // Test Thermal->getCoolingDevices().
TEST_P(ThermalAidlTest,CoolingDeviceTest)401 TEST_P(ThermalAidlTest, CoolingDeviceTest) {
402 std::vector<CoolingDevice> ret;
403 ::ndk::ScopedAStatus status = mThermal->getCoolingDevices(&ret);
404 if (status.isOk()) {
405 for (auto& i : ret) {
406 EXPECT_LT(0u, i.name.size());
407 LOG(INFO) << i.name + " " + toString(i.type) << "\n";
408 }
409 } else {
410 ASSERT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode());
411 }
412
413 auto types = ::ndk::enum_range<CoolingType>();
414 for (const auto& type : types) {
415 status = mThermal->getCoolingDevicesWithType(type, &ret);
416 if (status.isOk()) {
417 ASSERT_TRUE(status.isOk());
418 for (auto& i : ret) {
419 EXPECT_EQ(type, i.type) << "Expect type " + toString(type) + " but got " +
420 toString(i.type) + " for " + i.name;
421 EXPECT_LT(0u, i.name.size());
422 }
423 } else {
424 ASSERT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode());
425 }
426 }
427 }
428
429 // Test Thermal->forecastSkinTemperature.
TEST_P(ThermalAidlTest,ForecastSkinTemperatureTest)430 TEST_P(ThermalAidlTest, ForecastSkinTemperatureTest) {
431 auto apiLevel = ::android::base::GetIntProperty<int32_t>("ro.vendor.api_level", 0);
432 if (apiLevel < 202504) {
433 GTEST_SKIP() << "Skipping test as the vendor level is below 202504: " << apiLevel;
434 }
435 float temperature = 0.0f;
436 ::ndk::ScopedAStatus status = mThermal->forecastSkinTemperature(1, &temperature);
437 if (status.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
438 GTEST_SKIP() << "Skipping test as temperature forecast is not supported";
439 }
440 for (int i = 0; i <= 60; i++) {
441 status = mThermal->forecastSkinTemperature(i, &temperature);
442 ASSERT_NE(NAN, temperature);
443 }
444 }
445
446 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ThermalAidlTest);
447 INSTANTIATE_TEST_SUITE_P(
448 Thermal, ThermalAidlTest,
449 testing::ValuesIn(::android::getAidlHalInstanceNames(IThermal::descriptor)),
450 ::android::PrintInstanceNameToString);
451
452 } // namespace
453 } // namespace aidl::android::hardware::thermal
454
main(int argc,char ** argv)455 int main(int argc, char** argv) {
456 ::testing::InitGoogleTest(&argc, argv);
457 ABinderProcess_setThreadPoolMaxThreadCount(1);
458 ABinderProcess_startThreadPool();
459 return RUN_ALL_TESTS();
460 }
461