diff --git a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/CoolingDevice.aidl b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/CoolingDevice.aidl index dff3c4cc49..7e1aed7e3c 100644 --- a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/CoolingDevice.aidl +++ b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/CoolingDevice.aidl @@ -38,4 +38,7 @@ parcelable CoolingDevice { android.hardware.thermal.CoolingType type; String name; long value; + long powerLimitMw; + long powerMw; + long timeWindowMs; } diff --git a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/ICoolingDeviceChangedCallback.aidl b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/ICoolingDeviceChangedCallback.aidl new file mode 100644 index 0000000000..ea75b1c8a9 --- /dev/null +++ b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/ICoolingDeviceChangedCallback.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.thermal; +/* @hide */ +@VintfStability +interface ICoolingDeviceChangedCallback { + oneway void notifyCoolingDeviceChanged(in android.hardware.thermal.CoolingDevice coolingDevice); +} diff --git a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/IThermal.aidl b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/IThermal.aidl index c9b6cab364..904496cdf1 100644 --- a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/IThermal.aidl +++ b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/IThermal.aidl @@ -44,4 +44,6 @@ interface IThermal { void registerThermalChangedCallback(in android.hardware.thermal.IThermalChangedCallback callback); void registerThermalChangedCallbackWithType(in android.hardware.thermal.IThermalChangedCallback callback, in android.hardware.thermal.TemperatureType type); void unregisterThermalChangedCallback(in android.hardware.thermal.IThermalChangedCallback callback); + void registerCoolingDeviceChangedCallbackWithType(in android.hardware.thermal.ICoolingDeviceChangedCallback callback, in android.hardware.thermal.CoolingType type); + void unregisterCoolingDeviceChangedCallback(in android.hardware.thermal.ICoolingDeviceChangedCallback callback); } diff --git a/thermal/aidl/android/hardware/thermal/CoolingDevice.aidl b/thermal/aidl/android/hardware/thermal/CoolingDevice.aidl index 0c5c17d3a9..406733bc5c 100644 --- a/thermal/aidl/android/hardware/thermal/CoolingDevice.aidl +++ b/thermal/aidl/android/hardware/thermal/CoolingDevice.aidl @@ -40,4 +40,16 @@ parcelable CoolingDevice { * means deeper throttling. */ long value; + /** + * Power budget (mW) of the cooling device. + */ + long powerLimitMw; + /** + * Target cooling device's AVG power for the last time_window_ms. + */ + long powerMw; + /** + * The time window (millisecond) to calculate the power consumption + */ + long timeWindowMs; } diff --git a/thermal/aidl/android/hardware/thermal/ICoolingDeviceChangedCallback.aidl b/thermal/aidl/android/hardware/thermal/ICoolingDeviceChangedCallback.aidl new file mode 100644 index 0000000000..e6bb9fe2f9 --- /dev/null +++ b/thermal/aidl/android/hardware/thermal/ICoolingDeviceChangedCallback.aidl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.thermal; + +import android.hardware.thermal.CoolingDevice; +import android.hardware.thermal.Temperature; + +/** + * ICoolingDeviceChangedCallback send cooling device change notification to clients. + * @hide + */ +@VintfStability +interface ICoolingDeviceChangedCallback { + /** + * Send a cooling device change event to all ThermalHAL + * cooling device event listeners. + * + * @param cooling_device The cooling device information associated with the + * change event. + */ + oneway void notifyCoolingDeviceChanged(in CoolingDevice coolingDevice); +} diff --git a/thermal/aidl/android/hardware/thermal/IThermal.aidl b/thermal/aidl/android/hardware/thermal/IThermal.aidl index c94edcda72..4aa4090752 100644 --- a/thermal/aidl/android/hardware/thermal/IThermal.aidl +++ b/thermal/aidl/android/hardware/thermal/IThermal.aidl @@ -18,6 +18,7 @@ package android.hardware.thermal; import android.hardware.thermal.CoolingDevice; import android.hardware.thermal.CoolingType; +import android.hardware.thermal.ICoolingDeviceChangedCallback; import android.hardware.thermal.IThermalChangedCallback; import android.hardware.thermal.Temperature; import android.hardware.thermal.TemperatureThreshold; @@ -188,4 +189,40 @@ interface IThermal { * getMessage() must be populated with human-readable error message. */ void unregisterThermalChangedCallback(in IThermalChangedCallback callback); + + /** + * Register an ICoolingDeviceChangedCallback for a given CoolingType, used by + * the Thermal HAL to receive CDEV events when cooling device status + * changed. + * Multiple registrations with different ICoolingDeviceChangedCallback must be allowed. + * Multiple registrations with same ICoolingDeviceChangedCallback is not allowed, client + * should unregister the given ICoolingDeviceChangedCallback first. + * + * @param callback the ICoolingChangedCallback to use for receiving + * cooling device events. If nullptr callback is given, the status code will be + * STATUS_BAD_VALUE and the operation will fail. + * @param type the type to be filtered. + * + * @throws EX_ILLEGAL_ARGUMENT If the callback is given nullptr or already registered. And the + * getMessage() must be populated with human-readable error message. + * @throws EX_ILLEGAL_STATE If the Thermal HAL is not initialized successfully. And the + * getMessage() must be populated with human-readable error message. + */ + void registerCoolingDeviceChangedCallbackWithType( + in ICoolingDeviceChangedCallback callback, in CoolingType type); + + /** + * Unregister an ICoolingDeviceChangedCallback, used by the Thermal HAL + * to receive CDEV events when cooling device status changed. + * + * @param callback the ICoolingDeviceChangedCallback to use for receiving + * cooling device events. if nullptr callback is given, the status code will be + * STATUS_BAD_VALUE and the operation will fail. + * + * @throws EX_ILLEGAL_ARGUMENT If the callback is given nullptr or not previously registered. + * And the getMessage() must be populated with human-readable error message. + * @throws EX_ILLEGAL_STATE If the Thermal HAL is not initialized successfully. And the + * getMessage() must be populated with human-readable error message. + */ + void unregisterCoolingDeviceChangedCallback(in ICoolingDeviceChangedCallback callback); } diff --git a/thermal/aidl/default/Thermal.cpp b/thermal/aidl/default/Thermal.cpp index f643d22f1f..41d0be8346 100644 --- a/thermal/aidl/default/Thermal.cpp +++ b/thermal/aidl/default/Thermal.cpp @@ -142,4 +142,53 @@ ScopedAStatus Thermal::unregisterThermalChangedCallback( return ScopedAStatus::ok(); } +ScopedAStatus Thermal::registerCoolingDeviceChangedCallbackWithType( + const std::shared_ptr& in_callback, CoolingType in_type) { + LOG(VERBOSE) << __func__ << " ICoolingDeviceChangedCallback: " << in_callback + << ", CoolingType: " << static_cast(in_type); + if (in_callback == nullptr) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "Invalid nullptr callback"); + } + { + std::lock_guard _lock(cdev_callback_mutex_); + if (std::any_of(cdev_callbacks_.begin(), cdev_callbacks_.end(), + [&](const std::shared_ptr& c) { + return interfacesEqual(c, in_callback); + })) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "Callback already registered"); + } + cdev_callbacks_.push_back(in_callback); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus Thermal::unregisterCoolingDeviceChangedCallback( + const std::shared_ptr& in_callback) { + LOG(VERBOSE) << __func__ << " ICoolingDeviceChangedCallback: " << in_callback; + if (in_callback == nullptr) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "Invalid nullptr callback"); + } + { + std::lock_guard _lock(cdev_callback_mutex_); + bool removed = false; + cdev_callbacks_.erase( + std::remove_if(cdev_callbacks_.begin(), cdev_callbacks_.end(), + [&](const std::shared_ptr& c) { + if (interfacesEqual(c, in_callback)) { + removed = true; + return true; + } + return false; + }), + cdev_callbacks_.end()); + if (!removed) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "Callback wasn't registered"); + } + } + return ScopedAStatus::ok(); +} } // namespace aidl::android::hardware::thermal::impl::example diff --git a/thermal/aidl/default/Thermal.h b/thermal/aidl/default/Thermal.h index 8885e631fe..d3d88748ee 100644 --- a/thermal/aidl/default/Thermal.h +++ b/thermal/aidl/default/Thermal.h @@ -46,6 +46,7 @@ class Thermal : public BnThermal { ndk::ScopedAStatus registerThermalChangedCallback( const std::shared_ptr& in_callback) override; + ndk::ScopedAStatus registerThermalChangedCallbackWithType( const std::shared_ptr& in_callback, TemperatureType in_type) override; @@ -53,9 +54,18 @@ class Thermal : public BnThermal { ndk::ScopedAStatus unregisterThermalChangedCallback( const std::shared_ptr& in_callback) override; + ndk::ScopedAStatus registerCoolingDeviceChangedCallbackWithType( + const std::shared_ptr& in_callback, + CoolingType in_type) override; + + ndk::ScopedAStatus unregisterCoolingDeviceChangedCallback( + const std::shared_ptr& in_callback) override; + private: std::mutex thermal_callback_mutex_; std::vector> thermal_callbacks_; + std::mutex cdev_callback_mutex_; + std::vector> cdev_callbacks_; }; } // namespace example diff --git a/thermal/aidl/vts/VtsHalThermalTargetTest.cpp b/thermal/aidl/vts/VtsHalThermalTargetTest.cpp index 4b0eb655b5..403c6c85a7 100644 --- a/thermal/aidl/vts/VtsHalThermalTargetTest.cpp +++ b/thermal/aidl/vts/VtsHalThermalTargetTest.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -59,6 +60,15 @@ static const Temperature kThrottleTemp = { .throttlingStatus = ThrottlingSeverity::CRITICAL, }; +static const CoolingDevice kCoolingDevice = { + .type = CoolingType::CPU, + .name = "test cooling device", + .value = 1, + .powerLimitMw = 300, + .powerMw = 500, + .timeWindowMs = 7000, +}; + // Callback class for receiving thermal event notifications from main class class ThermalCallback : public BnThermalChangedCallback { public: @@ -85,6 +95,33 @@ class ThermalCallback : public BnThermalChangedCallback { bool mInvoke = false; }; +// Callback class for receiving cooling device event notifications from main class +class CoolingDeviceCallback : public BnCoolingDeviceChangedCallback { + public: + ndk::ScopedAStatus notifyCoolingDeviceChanged(const CoolingDevice&) override { + { + std::lock_guard lock(mMutex); + mInvoke = true; + } + mNotifyCoolingDeviceChanged.notify_all(); + return ndk::ScopedAStatus::ok(); + } + + template + [[nodiscard]] bool waitForCallback(std::chrono::duration duration) { + std::unique_lock lock(mMutex); + bool r = mNotifyCoolingDeviceChanged.wait_for(lock, duration, + [this] { return this->mInvoke; }); + mInvoke = false; + return r; + } + + private: + std::mutex mMutex; + std::condition_variable mNotifyCoolingDeviceChanged; + bool mInvoke = false; +}; + // The main test class for THERMAL HIDL HAL. class ThermalAidlTest : public testing::TestWithParam { public: @@ -97,19 +134,42 @@ class ThermalAidlTest : public testing::TestWithParam { ASSERT_NE(mThermalCallback, nullptr); ::ndk::ScopedAStatus status = mThermal->registerThermalChangedCallback(mThermalCallback); ASSERT_TRUE(status.isOk()) << status.getMessage(); + + auto ret = mThermal->getInterfaceVersion(&thermal_version); + ASSERT_TRUE(ret.isOk()) << ret; + if (thermal_version > 1) { + mCoolingDeviceCallback = ndk::SharedRefBase::make(); + ASSERT_NE(mCoolingDeviceCallback, nullptr); + status = mThermal->registerCoolingDeviceChangedCallbackWithType(mCoolingDeviceCallback, + kCoolingDevice.type); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + } } void TearDown() override { ::ndk::ScopedAStatus status = mThermal->unregisterThermalChangedCallback(mThermalCallback); ASSERT_TRUE(status.isOk()) << status.getMessage(); + // Expect to fail if unregister again status = mThermal->unregisterThermalChangedCallback(mThermalCallback); ASSERT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode()); + + auto ret = mThermal->getInterfaceVersion(&thermal_version); + ASSERT_TRUE(ret.isOk()) << ret; + if (thermal_version > 1) { + status = mThermal->unregisterCoolingDeviceChangedCallback(mCoolingDeviceCallback); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + status = mThermal->unregisterCoolingDeviceChangedCallback(mCoolingDeviceCallback); + ASSERT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode()); + } } + // Stores thermal version + int32_t thermal_version; protected: std::shared_ptr mThermal; std::shared_ptr mThermalCallback; + std::shared_ptr mCoolingDeviceCallback; }; // Test ThermalChangedCallback::notifyThrottling(). @@ -121,6 +181,21 @@ TEST_P(ThermalAidlTest, NotifyThrottlingTest) { ASSERT_TRUE(thermalCallback->waitForCallback(200ms)); } +// Test CoolingDeviceChangedCallback::notifyCoolingDeviceChanged(). +// This just calls into and back from our local CoolingDeviceChangedCallback impl. +TEST_P(ThermalAidlTest, NotifyCoolingDeviceChangedTest) { + auto ret = mThermal->getInterfaceVersion(&thermal_version); + ASSERT_TRUE(ret.isOk()) << ret; + if (thermal_version < 2) { + return; + } + std::shared_ptr cdevCallback = + ndk::SharedRefBase::make(); + ::ndk::ScopedAStatus status = cdevCallback->notifyCoolingDeviceChanged(kCoolingDevice); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + ASSERT_TRUE(cdevCallback->waitForCallback(200ms)); +} + // Test Thermal->registerThermalChangedCallback. TEST_P(ThermalAidlTest, RegisterThermalChangedCallbackTest) { // Expect to fail with same callback @@ -169,6 +244,37 @@ TEST_P(ThermalAidlTest, RegisterThermalChangedCallbackWithTypeTest) { || status.getExceptionCode() == EX_NULL_POINTER); } +// Test Thermal->registerCoolingDeviceChangedCallbackWithType. +TEST_P(ThermalAidlTest, RegisterCoolingDeviceChangedCallbackWithTypeTest) { + auto ret = mThermal->getInterfaceVersion(&thermal_version); + ASSERT_TRUE(ret.isOk()) << ret; + if (thermal_version < 2) { + return; + } + + // Expect to fail with same callback + ::ndk::ScopedAStatus status = mThermal->registerCoolingDeviceChangedCallbackWithType( + mCoolingDeviceCallback, CoolingType::CPU); + ASSERT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode()); + // Expect to fail with null callback + status = mThermal->registerCoolingDeviceChangedCallbackWithType(nullptr, CoolingType::CPU); + ASSERT_TRUE(status.getExceptionCode() == EX_ILLEGAL_ARGUMENT || + status.getExceptionCode() == EX_NULL_POINTER); + std::shared_ptr localCoolingDeviceCallback = + ndk::SharedRefBase::make(); + // Expect to succeed with different callback + status = mThermal->registerCoolingDeviceChangedCallbackWithType(localCoolingDeviceCallback, + CoolingType::CPU); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + // Remove the local callback + status = mThermal->unregisterCoolingDeviceChangedCallback(localCoolingDeviceCallback); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + // Expect to fail with null callback + status = mThermal->unregisterCoolingDeviceChangedCallback(nullptr); + ASSERT_TRUE(status.getExceptionCode() == EX_ILLEGAL_ARGUMENT || + status.getExceptionCode() == EX_NULL_POINTER); +} + // Test Thermal->getCurrentTemperatures(). TEST_P(ThermalAidlTest, TemperatureTest) { std::vector ret;