xref: /aosp_15_r20/hardware/interfaces/thermal/aidl/vts/VtsHalThermalTargetTest.cpp (revision 4d7e907c777eeecc4c5bd7cf640a754fac206ff7)
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