sm6250-common: Update thermal HAL

* From hardware/google/pixel at d774cbb949e98627e4172bf8fc11e8d954599aa7.

Change-Id: I3a3a0c29575d0595e71a30f1e64e33ca34d2eb27
This commit is contained in:
Alexander Winkowski 2021-10-12 13:00:29 +02:00
parent 27f575c973
commit 7f99605ccc
No known key found for this signature in database
GPG Key ID: 72762A66704CDE44
19 changed files with 2884 additions and 252 deletions

View File

@ -386,7 +386,8 @@ PRODUCT_BOOT_JARS += \
# Thermal HAL # Thermal HAL
PRODUCT_PACKAGES += \ PRODUCT_PACKAGES += \
android.hardware.thermal@2.0-service.xiaomi_atoll android.hardware.thermal@2.0-service.xiaomi_atoll \
thermal_symlinks
PRODUCT_COPY_FILES += \ PRODUCT_COPY_FILES += \
$(LOCAL_PATH)/configs/thermal_info_config.json:$(TARGET_COPY_OUT_VENDOR)/etc/thermal_info_config.json $(LOCAL_PATH)/configs/thermal_info_config.json:$(TARGET_COPY_OUT_VENDOR)/etc/thermal_info_config.json

View File

@ -44,3 +44,5 @@
# Thermal # Thermal
/vendor/bin/hw/android\.hardware\.thermal@2\.0-service\.xiaomi_atoll u:object_r:hal_thermal_default_exec:s0 /vendor/bin/hw/android\.hardware\.thermal@2\.0-service\.xiaomi_atoll u:object_r:hal_thermal_default_exec:s0
/vendor/bin/thermal_symlinks u:object_r:init-thermal-symlinks-sh_exec:s0
/dev/thermal(/.*)? u:object_r:thermal_link_device:s0

View File

@ -0,0 +1,12 @@
type init-thermal-symlinks-sh, domain;
type init-thermal-symlinks-sh_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(init-thermal-symlinks-sh)
allow init-thermal-symlinks-sh vendor_toolbox_exec:file rx_file_perms;
allow init-thermal-symlinks-sh thermal_link_device:dir rw_dir_perms;
allow init-thermal-symlinks-sh thermal_link_device:lnk_file create_file_perms;
allow init-thermal-symlinks-sh sysfs_thermal:dir r_dir_perms;
allow init-thermal-symlinks-sh sysfs_thermal:file r_file_perms;
allow init-thermal-symlinks-sh sysfs_thermal:lnk_file r_file_perms;
set_prop(init-thermal-symlinks-sh, vendor_thermal_prop)

View File

@ -16,6 +16,7 @@ cc_binary {
"utils/config_parser.cpp", "utils/config_parser.cpp",
"utils/thermal_files.cpp", "utils/thermal_files.cpp",
"utils/thermal_watcher.cpp", "utils/thermal_watcher.cpp",
"utils/power_files.cpp",
], ],
shared_libs: [ shared_libs: [
"libbase", "libbase",
@ -23,11 +24,12 @@ cc_binary {
"libhidlbase", "libhidlbase",
"libjsoncpp", "libjsoncpp",
"libutils", "libutils",
"libnl",
"libbinder_ndk", "libbinder_ndk",
"android.hardware.thermal@1.0", "android.hardware.thermal@1.0",
"android.hardware.thermal@2.0", "android.hardware.thermal@2.0",
"android.hardware.power-ndk_platform", "android.hardware.power-V1-ndk_platform",
"pixel-power-ext-ndk_platform" "pixel-power-ext-V1-ndk_platform"
], ],
cflags: [ cflags: [
"-Wall", "-Wall",
@ -45,3 +47,12 @@ cc_binary {
"-warnings-as-errors=android-*,clang-analyzer-security*,cert-*" "-warnings-as-errors=android-*,clang-analyzer-security*,cert-*"
], ],
} }
sh_binary {
name: "xiaomi_atoll_thermal_symlinks",
src: "init.thermal.symlinks.sh",
vendor: true,
init_rc: [
"xiaomi_atoll-thermal-symlinks.rc",
],
}

View File

@ -36,7 +36,6 @@ namespace {
using ::android::hardware::interfacesEqual; using ::android::hardware::interfacesEqual;
using ::android::hardware::thermal::V1_0::ThermalStatus; using ::android::hardware::thermal::V1_0::ThermalStatus;
using ::android::hardware::thermal::V1_0::ThermalStatusCode; using ::android::hardware::thermal::V1_0::ThermalStatusCode;
using ::android::hidl::base::V1_0::IBase;
template <typename T, typename U> template <typename T, typename U>
Return<void> setFailureAndCallback(T _hidl_cb, hidl_vec<U> data, std::string_view debug_msg) { Return<void> setFailureAndCallback(T _hidl_cb, hidl_vec<U> data, std::string_view debug_msg) {
@ -234,15 +233,14 @@ Return<void> Thermal::unregisterThermalChangedCallback(
return Void(); return Void();
} }
void Thermal::sendThermalChangedCallback(const std::vector<Temperature_2_0> &temps) { void Thermal::sendThermalChangedCallback(const Temperature_2_0 &t) {
std::lock_guard<std::mutex> _lock(thermal_callback_mutex_); std::lock_guard<std::mutex> _lock(thermal_callback_mutex_);
for (auto &t : temps) {
LOG(INFO) << "Sending notification: " LOG(VERBOSE) << "Sending notification: "
<< " Type: " << android::hardware::thermal::V2_0::toString(t.type) << " Type: " << android::hardware::thermal::V2_0::toString(t.type)
<< " Name: " << t.name << " CurrentValue: " << t.value << " ThrottlingStatus: " << " Name: " << t.name << " CurrentValue: " << t.value << " ThrottlingStatus: "
<< android::hardware::thermal::V2_0::toString(t.throttlingStatus); << android::hardware::thermal::V2_0::toString(t.throttlingStatus);
thermal_helper_.sendPowerExtHint(t);
callbacks_.erase( callbacks_.erase(
std::remove_if(callbacks_.begin(), callbacks_.end(), std::remove_if(callbacks_.begin(), callbacks_.end(),
[&](const CallbackSetting &c) { [&](const CallbackSetting &c) {
@ -255,6 +253,276 @@ void Thermal::sendThermalChangedCallback(const std::vector<Temperature_2_0> &tem
return false; return false;
}), }),
callbacks_.end()); callbacks_.end());
}
void Thermal::dumpVirtualSensorInfo(std::ostringstream *dump_buf) {
*dump_buf << "VirtualSensorInfo:" << std::endl;
const auto &map = thermal_helper_.GetSensorInfoMap();
for (const auto &sensor_info_pair : map) {
if (sensor_info_pair.second.virtual_sensor_info != nullptr) {
*dump_buf << " Name: " << sensor_info_pair.first << std::endl;
*dump_buf << " LinkedSensorName: [";
for (size_t i = 0;
i < sensor_info_pair.second.virtual_sensor_info->linked_sensors.size(); i++) {
*dump_buf << sensor_info_pair.second.virtual_sensor_info->linked_sensors[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " LinkedSensorCoefficient: [";
for (size_t i = 0; i < sensor_info_pair.second.virtual_sensor_info->coefficients.size();
i++) {
*dump_buf << sensor_info_pair.second.virtual_sensor_info->coefficients[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " Offset: " << sensor_info_pair.second.virtual_sensor_info->offset
<< std::endl;
*dump_buf << " Trigger Sensor: "
<< (sensor_info_pair.second.virtual_sensor_info->trigger_sensor.empty()
? "N/A"
: sensor_info_pair.second.virtual_sensor_info->trigger_sensor)
<< std::endl;
*dump_buf << " Formula: ";
switch (sensor_info_pair.second.virtual_sensor_info->formula) {
case FormulaOption::COUNT_THRESHOLD:
*dump_buf << "COUNT_THRESHOLD";
break;
case FormulaOption::WEIGHTED_AVG:
*dump_buf << "WEIGHTED_AVG";
break;
case FormulaOption::MAXIMUM:
*dump_buf << "MAXIMUM";
break;
case FormulaOption::MINIMUM:
*dump_buf << "MINIMUM";
break;
default:
*dump_buf << "NONE";
break;
}
*dump_buf << std::endl;
}
}
}
void Thermal::dumpThrottlingInfo(std::ostringstream *dump_buf) {
*dump_buf << "Throttling Info:" << std::endl;
const auto &map = thermal_helper_.GetSensorInfoMap();
for (const auto &name_info_pair : map) {
if (name_info_pair.second.throttling_info->binded_cdev_info_map.size()) {
*dump_buf << " Name: " << name_info_pair.first << std::endl;
*dump_buf << " PID Info:" << std::endl;
*dump_buf << " K_po: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << name_info_pair.second.throttling_info->k_po[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " K_pu: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << name_info_pair.second.throttling_info->k_pu[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " K_i: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << name_info_pair.second.throttling_info->k_i[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " K_d: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << name_info_pair.second.throttling_info->k_d[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " i_max: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << name_info_pair.second.throttling_info->i_max[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " max_alloc_power: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << name_info_pair.second.throttling_info->max_alloc_power[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " min_alloc_power: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << name_info_pair.second.throttling_info->min_alloc_power[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " s_power: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << name_info_pair.second.throttling_info->s_power[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " i_cutoff: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << name_info_pair.second.throttling_info->i_cutoff[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " Binded CDEV Info:" << std::endl;
if (name_info_pair.second.throttling_info->binded_cdev_info_map.size()) {
for (const auto &binded_cdev_info_pair :
name_info_pair.second.throttling_info->binded_cdev_info_map) {
*dump_buf << " Cooling device name: " << binded_cdev_info_pair.first
<< std::endl;
*dump_buf << " WeightForPID: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << binded_cdev_info_pair.second.cdev_weight_for_pid[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " Ceiling: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << binded_cdev_info_pair.second.cdev_ceiling[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " Floor with PowerLink: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << binded_cdev_info_pair.second.cdev_floor_with_power_link[i]
<< " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " Hard limit: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << binded_cdev_info_pair.second.limit_info[i] << " ";
}
*dump_buf << "]" << std::endl;
if (!binded_cdev_info_pair.second.power_rail.empty()) {
*dump_buf << " Binded power rail: "
<< binded_cdev_info_pair.second.power_rail << std::endl;
*dump_buf << " Power threshold: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
*dump_buf << binded_cdev_info_pair.second.power_thresholds[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " Release logic: ";
switch (binded_cdev_info_pair.second.release_logic) {
case ReleaseLogic::INCREASE:
*dump_buf << "INCREASE";
break;
case ReleaseLogic::DECREASE:
*dump_buf << "DECREASE";
break;
case ReleaseLogic::STEPWISE:
*dump_buf << "STEPWISE";
break;
case ReleaseLogic::RELEASE_TO_FLOOR:
*dump_buf << "RELEASE_TO_FLOOR";
break;
default:
*dump_buf << "NONE";
break;
}
*dump_buf << std::endl;
*dump_buf << " high_power_check: " << std::boolalpha
<< binded_cdev_info_pair.second.high_power_check << std::endl;
*dump_buf << " throttling_with_power_link: " << std::boolalpha
<< binded_cdev_info_pair.second.throttling_with_power_link
<< std::endl;
}
}
}
}
}
}
void Thermal::dumpThrottlingRequestStatus(std::ostringstream *dump_buf) {
const auto &sensor_status_map = thermal_helper_.GetSensorStatusMap();
const auto &cdev_status_map = thermal_helper_.GetCdevStatusMap();
const auto &release_map = thermal_helper_.GetThrottlingReleaseMap();
*dump_buf << "Throttling Request Status " << std::endl;
for (const auto &cdev_status_pair : cdev_status_map) {
*dump_buf << " Name: " << cdev_status_pair.first << std::endl;
for (const auto &request_pair : cdev_status_pair.second) {
*dump_buf << " Request Sensor: " << request_pair.first << std::endl;
*dump_buf << " Request Throttling State: " << request_pair.second << std::endl;
if (sensor_status_map.at(request_pair.first).pid_request_map.size() &&
sensor_status_map.at(request_pair.first)
.pid_request_map.count(cdev_status_pair.first)) {
*dump_buf << " PID Request State: "
<< sensor_status_map.at(request_pair.first)
.pid_request_map.at(cdev_status_pair.first)
<< std::endl;
}
if (sensor_status_map.at(request_pair.first).hard_limit_request_map.size() &&
sensor_status_map.at(request_pair.first)
.hard_limit_request_map.count(cdev_status_pair.first)) {
*dump_buf << " Hard Limit Request State: "
<< sensor_status_map.at(request_pair.first)
.hard_limit_request_map.at(cdev_status_pair.first)
<< std::endl;
}
if (release_map.count(request_pair.first) &&
release_map.at(request_pair.first).count(cdev_status_pair.first)) {
const auto &cdev_release_info =
release_map.at(request_pair.first).at(cdev_status_pair.first);
*dump_buf << " Release Step: " << cdev_release_info.release_step << std::endl;
}
}
}
}
void Thermal::dumpPowerRailInfo(std::ostringstream *dump_buf) {
const auto &power_rail_info_map = thermal_helper_.GetPowerRailInfoMap();
const auto &power_status_map = thermal_helper_.GetPowerStatusMap();
*dump_buf << "Power Rail Info " << std::endl;
for (const auto &power_rail_pair : power_rail_info_map) {
*dump_buf << " Power Rail: " << power_rail_pair.first << std::endl;
*dump_buf << " Power Sample Count: " << power_rail_pair.second.power_sample_count
<< std::endl;
*dump_buf << " Power Sample Delay: " << power_rail_pair.second.power_sample_delay.count()
<< std::endl;
for (const auto &power_status_pair : power_status_map) {
if (power_status_pair.second.count(power_rail_pair.first)) {
auto power_history =
power_status_pair.second.at(power_rail_pair.first).power_history;
*dump_buf << " Request Sensor: " << power_status_pair.first << std::endl;
*dump_buf
<< " Last Updated AVG Power: "
<< power_status_pair.second.at(power_rail_pair.first).last_updated_avg_power
<< " mW" << std::endl;
if (power_rail_pair.second.virtual_power_rail_info != nullptr) {
*dump_buf << " Formula=";
switch (power_rail_pair.second.virtual_power_rail_info->formula) {
case FormulaOption::COUNT_THRESHOLD:
*dump_buf << "COUNT_THRESHOLD";
break;
case FormulaOption::WEIGHTED_AVG:
*dump_buf << "WEIGHTED_AVG";
break;
case FormulaOption::MAXIMUM:
*dump_buf << "MAXIMUM";
break;
case FormulaOption::MINIMUM:
*dump_buf << "MINIMUM";
break;
default:
*dump_buf << "NONE";
break;
}
*dump_buf << std::endl;
}
for (size_t i = 0; i < power_history.size(); ++i) {
if (power_rail_pair.second.virtual_power_rail_info != nullptr) {
*dump_buf << " Linked power rail "
<< power_rail_pair.second.virtual_power_rail_info
->linked_power_rails[i]
<< std::endl;
*dump_buf << " Coefficient="
<< power_rail_pair.second.virtual_power_rail_info->coefficients[i]
<< std::endl;
*dump_buf << " Power Samples: ";
} else {
*dump_buf << " Power Samples: ";
}
while (power_history[i].size() > 0) {
const auto power_sample = power_history[i].front();
power_history[i].pop();
*dump_buf << "(T=" << power_sample.duration
<< ", uWs=" << power_sample.energy_counter << ") ";
}
*dump_buf << std::endl;
}
}
}
} }
} }
@ -371,24 +639,31 @@ Return<void> Thermal::debug(const hidl_handle &handle, const hidl_vec<hidl_strin
} }
} }
{ {
dump_buf << "Monitor:" << std::endl; dump_buf << "SendCallback" << std::endl;
dump_buf << " Enabled List: ";
const auto &map = thermal_helper_.GetSensorInfoMap(); const auto &map = thermal_helper_.GetSensorInfoMap();
for (const auto &name_info_pair : map) { for (const auto &name_info_pair : map) {
dump_buf << " Name: " << name_info_pair.first; if (name_info_pair.second.send_cb) {
dump_buf << " Monitor: " << std::boolalpha << name_info_pair.second.is_monitor dump_buf << name_info_pair.first << " ";
<< std::noboolalpha << std::endl;
} }
} }
dump_buf << std::endl;
}
{ {
dump_buf << "SendPowerHint:" << std::endl; dump_buf << "SendPowerHint" << std::endl;
dump_buf << " Enabled List: ";
const auto &map = thermal_helper_.GetSensorInfoMap(); const auto &map = thermal_helper_.GetSensorInfoMap();
for (const auto &name_info_pair : map) { for (const auto &name_info_pair : map) {
dump_buf << " Name: " << name_info_pair.first; if (name_info_pair.second.send_powerhint) {
dump_buf << " SendPowerHint: " << std::boolalpha dump_buf << name_info_pair.first << " ";
<< name_info_pair.second.send_powerhint << std::noboolalpha
<< std::endl;
} }
} }
dump_buf << std::endl;
}
dumpVirtualSensorInfo(&dump_buf);
dumpThrottlingInfo(&dump_buf);
dumpThrottlingRequestStatus(&dump_buf);
dumpPowerRailInfo(&dump_buf);
{ {
dump_buf << "AIDL Power Hal exist: " << std::boolalpha dump_buf << "AIDL Power Hal exist: " << std::boolalpha
<< thermal_helper_.isAidlPowerHalExist() << std::endl; << thermal_helper_.isAidlPowerHalExist() << std::endl;

View File

@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#ifndef ANDROID_HARDWARE_THERMAL_V2_0_CROSSHATCH_THERMAL_H
#define ANDROID_HARDWARE_THERMAL_V2_0_CROSSHATCH_THERMAL_H #pragma once
#include <mutex> #include <mutex>
#include <thread> #include <thread>
@ -40,7 +40,7 @@ using ::android::hardware::thermal::V2_0::IThermalChangedCallback;
struct CallbackSetting { struct CallbackSetting {
CallbackSetting(sp<IThermalChangedCallback> callback, bool is_filter_type, CallbackSetting(sp<IThermalChangedCallback> callback, bool is_filter_type,
TemperatureType_2_0 type) TemperatureType_2_0 type)
: callback(callback), is_filter_type(is_filter_type), type(type) {} : callback(std::move(callback)), is_filter_type(is_filter_type), type(type) {}
sp<IThermalChangedCallback> callback; sp<IThermalChangedCallback> callback;
bool is_filter_type; bool is_filter_type;
TemperatureType_2_0 type; TemperatureType_2_0 type;
@ -65,8 +65,8 @@ class Thermal : public IThermal {
getCurrentTemperatures_cb _hidl_cb) override; getCurrentTemperatures_cb _hidl_cb) override;
Return<void> getTemperatureThresholds(bool filterType, TemperatureType_2_0 type, Return<void> getTemperatureThresholds(bool filterType, TemperatureType_2_0 type,
getTemperatureThresholds_cb _hidl_cb) override; getTemperatureThresholds_cb _hidl_cb) override;
Return<void> registerThermalChangedCallback(const sp<IThermalChangedCallback> &callback, Return<void> registerThermalChangedCallback(
bool filterType, TemperatureType_2_0 type, const sp<IThermalChangedCallback> &callback, bool filterType, TemperatureType_2_0 type,
registerThermalChangedCallback_cb _hidl_cb) override; registerThermalChangedCallback_cb _hidl_cb) override;
Return<void> unregisterThermalChangedCallback( Return<void> unregisterThermalChangedCallback(
const sp<IThermalChangedCallback> &callback, const sp<IThermalChangedCallback> &callback,
@ -78,10 +78,14 @@ class Thermal : public IThermal {
Return<void> debug(const hidl_handle &fd, const hidl_vec<hidl_string> &args) override; Return<void> debug(const hidl_handle &fd, const hidl_vec<hidl_string> &args) override;
// Helper function for calling callbacks // Helper function for calling callbacks
void sendThermalChangedCallback(const std::vector<Temperature_2_0> &temps); void sendThermalChangedCallback(const Temperature_2_0 &t);
private: private:
ThermalHelper thermal_helper_; ThermalHelper thermal_helper_;
void dumpVirtualSensorInfo(std::ostringstream *dump_buf);
void dumpThrottlingInfo(std::ostringstream *dump_buf);
void dumpThrottlingRequestStatus(std::ostringstream *dump_buf);
void dumpPowerRailInfo(std::ostringstream *dump_buf);
std::mutex thermal_callback_mutex_; std::mutex thermal_callback_mutex_;
std::vector<CallbackSetting> callbacks_; std::vector<CallbackSetting> callbacks_;
}; };
@ -91,5 +95,3 @@ class Thermal : public IThermal {
} // namespace thermal } // namespace thermal
} // namespace hardware } // namespace hardware
} // namespace android } // namespace android
#endif // ANDROID_HARDWARE_THERMAL_V2_0_CROSSHATCH_THERMAL_H

View File

@ -0,0 +1,13 @@
#!/vendor/bin/sh
for f in /sys/class/thermal/thermal_zone*
do
tz_name=`cat $f/type`
ln -s $f /dev/thermal/tz-by-name/$tz_name
done
for f in /sys/class/thermal/cooling_device*
do
cdev_name=`cat $f/type`
ln -s $f /dev/thermal/cdev-by-name/$cdev_name
done
setprop vendor.thermal.link_ready 1

View File

@ -17,6 +17,8 @@
#include <hidl/HidlTransportSupport.h> #include <hidl/HidlTransportSupport.h>
#include "Thermal.h" #include "Thermal.h"
constexpr std::string_view kThermalLogTag("pixel-thermal");
using ::android::OK; using ::android::OK;
using ::android::status_t; using ::android::status_t;
@ -34,6 +36,7 @@ static int shutdown() {
} }
int main(int /* argc */, char ** /* argv */) { int main(int /* argc */, char ** /* argv */) {
android::base::SetDefaultTag(kThermalLogTag.data());
status_t status; status_t status;
android::sp<IThermal> service = nullptr; android::sp<IThermal> service = nullptr;

File diff suppressed because it is too large Load Diff

View File

@ -27,8 +27,7 @@
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#ifndef THERMAL_THERMAL_HELPER_H__ #pragma once
#define THERMAL_THERMAL_HELPER_H__
#include <array> #include <array>
#include <chrono> #include <chrono>
@ -45,6 +44,7 @@
#include <android/hardware/thermal/2.0/IThermal.h> #include <android/hardware/thermal/2.0/IThermal.h>
#include "utils/config_parser.h" #include "utils/config_parser.h"
#include "utils/power_files.h"
#include "utils/thermal_files.h" #include "utils/thermal_files.h"
#include "utils/thermal_watcher.h" #include "utils/thermal_watcher.h"
@ -69,14 +69,23 @@ using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType;
using ::android::hardware::thermal::V2_0::TemperatureThreshold; using ::android::hardware::thermal::V2_0::TemperatureThreshold;
using ::android::hardware::thermal::V2_0::ThrottlingSeverity; using ::android::hardware::thermal::V2_0::ThrottlingSeverity;
using NotificationCallback = std::function<void(const std::vector<Temperature_2_0> &temps)>; using NotificationCallback = std::function<void(const Temperature_2_0 &t)>;
using NotificationTime = std::chrono::time_point<std::chrono::steady_clock>; using NotificationTime = std::chrono::time_point<std::chrono::steady_clock>;
using CdevRequestStatus = std::unordered_map<std::string, int>;
// Get thermal_zone type
bool getThermalZoneTypeById(int tz_id, std::string *);
struct SensorStatus { struct SensorStatus {
ThrottlingSeverity severity; ThrottlingSeverity severity;
ThrottlingSeverity prev_hot_severity; ThrottlingSeverity prev_hot_severity;
ThrottlingSeverity prev_cold_severity; ThrottlingSeverity prev_cold_severity;
ThrottlingSeverity prev_hint_severity; ThrottlingSeverity prev_hint_severity;
boot_clock::time_point last_update_time;
std::unordered_map<std::string, int> pid_request_map;
std::unordered_map<std::string, int> hard_limit_request_map;
float err_integral;
float prev_err;
}; };
class PowerHalService { class PowerHalService {
@ -99,7 +108,7 @@ class PowerHalService {
class ThermalHelper { class ThermalHelper {
public: public:
ThermalHelper(const NotificationCallback &cb); explicit ThermalHelper(const NotificationCallback &cb);
~ThermalHelper() = default; ~ThermalHelper() = default;
bool fillTemperatures(hidl_vec<Temperature_1_0> *temperatures) const; bool fillTemperatures(hidl_vec<Temperature_1_0> *temperatures) const;
@ -118,52 +127,104 @@ class ThermalHelper {
bool isInitializedOk() const { return is_initialized_; } bool isInitializedOk() const { return is_initialized_; }
// Read the temperature of a single sensor. // Read the temperature of a single sensor.
bool readTemperature(std::string_view sensor_name, Temperature_1_0 *out) const; bool readTemperature(std::string_view sensor_name, Temperature_1_0 *out,
bool is_virtual_sensor = false) const;
bool readTemperature( bool readTemperature(
std::string_view sensor_name, Temperature_2_0 *out, std::string_view sensor_name, Temperature_2_0 *out,
std::pair<ThrottlingSeverity, ThrottlingSeverity> *throtting_status = nullptr) const; std::pair<ThrottlingSeverity, ThrottlingSeverity> *throtting_status = nullptr,
bool is_virtual_sensor = false) const;
bool readTemperatureThreshold(std::string_view sensor_name, TemperatureThreshold *out) const; bool readTemperatureThreshold(std::string_view sensor_name, TemperatureThreshold *out) const;
// Read the value of a single cooling device. // Read the value of a single cooling device.
bool readCoolingDevice(std::string_view cooling_device, CoolingDevice_2_0 *out) const; bool readCoolingDevice(std::string_view cooling_device, CoolingDevice_2_0 *out) const;
// Get SensorInfo Map // Get SensorInfo Map
const std::map<std::string, SensorInfo> &GetSensorInfoMap() const { return sensor_info_map_; } const std::unordered_map<std::string, SensorInfo> &GetSensorInfoMap() const {
return sensor_info_map_;
}
// Get CdevInfo Map
const std::unordered_map<std::string, CdevInfo> &GetCdevInfoMap() const {
return cooling_device_info_map_;
}
// Get PowerRailInfo Map
const std::unordered_map<std::string, PowerRailInfo> &GetPowerRailInfoMap() const {
return power_rail_info_map_;
}
// Get SensorStatus Map
const std::unordered_map<std::string, SensorStatus> &GetSensorStatusMap() const {
std::shared_lock<std::shared_mutex> _lock(sensor_status_map_mutex_);
return sensor_status_map_;
}
// Get CdevStatus Map
const std::unordered_map<std::string, CdevRequestStatus> &GetCdevStatusMap() const {
std::shared_lock<std::shared_mutex> _lock(cdev_status_map_mutex_);
return cdev_status_map_;
}
// Get ThrottlingRelease Map
const std::unordered_map<std::string, CdevReleaseStatus> &GetThrottlingReleaseMap() const {
return power_files_.GetThrottlingReleaseMap();
}
// Get PowerStatus Map
const std::unordered_map<std::string, PowerStatusMap> &GetPowerStatusMap() const {
return power_files_.GetPowerStatusMap();
}
void sendPowerExtHint(const Temperature_2_0 &t); void sendPowerExtHint(const Temperature_2_0 &t);
bool isAidlPowerHalExist() { return power_hal_service_.isAidlPowerHalExist(); } bool isAidlPowerHalExist() { return power_hal_service_.isAidlPowerHalExist(); }
bool isPowerHalConnected() { return power_hal_service_.isPowerHalConnected(); } bool isPowerHalConnected() { return power_hal_service_.isPowerHalConnected(); }
bool isPowerHalExtConnected() { return power_hal_service_.isPowerHalExtConnected(); } bool isPowerHalExtConnected() { return power_hal_service_.isPowerHalExtConnected(); }
private: private:
bool initializeSensorMap(const std::map<std::string, std::string> &path_map); bool initializeSensorMap(const std::unordered_map<std::string, std::string> &path_map);
bool initializeCoolingDevices(const std::map<std::string, std::string> &path_map); bool initializeCoolingDevices(const std::unordered_map<std::string, std::string> &path_map);
bool initializeTrip(const std::map<std::string, std::string> &path_map); void setMinTimeout(SensorInfo *sensor_info);
void initializeTrip(const std::unordered_map<std::string, std::string> &path_map,
std::set<std::string> *monitored_sensors, bool thermal_genl_enabled);
// For thermal_watcher_'s polling thread // For thermal_watcher_'s polling thread, return the sleep interval
bool thermalWatcherCallbackFunc(const std::set<std::string> &uevent_sensors); std::chrono::milliseconds thermalWatcherCallbackFunc(
const std::set<std::string> &uevent_sensors);
// Return hot and cold severity status as std::pair // Return hot and cold severity status as std::pair
std::pair<ThrottlingSeverity, ThrottlingSeverity> getSeverityFromThresholds( std::pair<ThrottlingSeverity, ThrottlingSeverity> getSeverityFromThresholds(
const ThrottlingArray &hot_thresholds, const ThrottlingArray &cold_thresholds, const ThrottlingArray &hot_thresholds, const ThrottlingArray &cold_thresholds,
const ThrottlingArray &hot_hysteresis, const ThrottlingArray &cold_hysteresis, const ThrottlingArray &hot_hysteresis, const ThrottlingArray &cold_hysteresis,
ThrottlingSeverity prev_hot_severity, ThrottlingSeverity prev_cold_severity, ThrottlingSeverity prev_hot_severity, ThrottlingSeverity prev_cold_severity,
float value) const; float value) const;
bool checkVirtualSensor(std::string_view sensor_name, std::string *temp) const;
// Return the target state of PID algorithm
size_t getTargetStateOfPID(const SensorInfo &sensor_info, const SensorStatus &sensor_status);
// Return the power budget which is computed by PID algorithm
float pidPowerCalculator(const Temperature_2_0 &temp, const SensorInfo &sensor_info,
SensorStatus *sensor_status,
const std::chrono::milliseconds time_elapsed_ms, size_t target_state);
bool connectToPowerHal(); bool connectToPowerHal();
void updateSupportedPowerHints(); void updateSupportedPowerHints();
bool requestCdevByPower(std::string_view sensor_name, SensorStatus *sensor_status,
const SensorInfo &sensor_info, float total_power_budget,
size_t target_state);
void requestCdevBySeverity(std::string_view sensor_name, SensorStatus *sensor_status,
const SensorInfo &sensor_info);
void computeCoolingDevicesRequest(std::string_view sensor_name, const SensorInfo &sensor_info,
const SensorStatus &sensor_status,
std::vector<std::string> *cooling_devices_to_update);
void updateCoolingDevices(const std::vector<std::string> &cooling_devices_to_update);
sp<ThermalWatcher> thermal_watcher_; sp<ThermalWatcher> thermal_watcher_;
PowerFiles power_files_;
ThermalFiles thermal_sensors_; ThermalFiles thermal_sensors_;
ThermalFiles cooling_devices_; ThermalFiles cooling_devices_;
bool is_initialized_; bool is_initialized_;
const NotificationCallback cb_; const NotificationCallback cb_;
const std::map<std::string, CoolingType> cooling_device_info_map_; std::unordered_map<std::string, CdevInfo> cooling_device_info_map_;
const std::map<std::string, SensorInfo> sensor_info_map_; std::unordered_map<std::string, SensorInfo> sensor_info_map_;
std::map<std::string, std::map<ThrottlingSeverity, ThrottlingSeverity>> std::unordered_map<std::string, PowerRailInfo> power_rail_info_map_;
std::unordered_map<std::string, std::map<ThrottlingSeverity, ThrottlingSeverity>>
supported_powerhint_map_; supported_powerhint_map_;
PowerHalService power_hal_service_; PowerHalService power_hal_service_;
mutable std::shared_mutex sensor_status_map_mutex_; mutable std::shared_mutex sensor_status_map_mutex_;
std::map<std::string, SensorStatus> sensor_status_map_; std::unordered_map<std::string, SensorStatus> sensor_status_map_;
mutable std::shared_mutex cdev_status_map_mutex_;
std::unordered_map<std::string, CdevRequestStatus> cdev_status_map_;
}; };
} // namespace implementation } // namespace implementation
@ -171,5 +232,3 @@ class ThermalHelper {
} // namespace thermal } // namespace thermal
} // namespace hardware } // namespace hardware
} // namespace android } // namespace android
#endif // THERMAL_THERMAL_HELPER_H__

View File

@ -15,9 +15,10 @@
*/ */
#include <android-base/file.h> #include <android-base/file.h>
#include <android-base/logging.h> #include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/strings.h> #include <android-base/strings.h>
#include <cmath> #include <cmath>
#include <set> #include <unordered_set>
#include <json/reader.h> #include <json/reader.h>
#include <json/value.h> #include <json/value.h>
@ -30,6 +31,8 @@ namespace thermal {
namespace V2_0 { namespace V2_0 {
namespace implementation { namespace implementation {
constexpr std::string_view kPowerLinkDisabledProperty("vendor.disable.thermal.powerlink");
using ::android::hardware::hidl_enum_range; using ::android::hardware::hidl_enum_range;
using ::android::hardware::thermal::V2_0::toString; using ::android::hardware::thermal::V2_0::toString;
using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType; using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType;
@ -57,27 +60,103 @@ float getFloatFromValue(const Json::Value &value) {
} }
} }
int getIntFromValue(const Json::Value &value) {
if (value.isString()) {
return (value.asString() == "max") ? std::numeric_limits<int>::max()
: std::stoul(value.asString());
} else {
return value.asInt();
}
}
bool getIntFromJsonValues(const Json::Value &values, CdevArray *out, bool inc_check,
bool dec_check) {
CdevArray ret;
if (inc_check && dec_check) {
LOG(ERROR) << "Cannot enable inc_check and dec_check at the same time";
return false;
}
if (values.size() != kThrottlingSeverityCount) {
LOG(ERROR) << "Values size is invalid";
return false;
} else {
int last;
for (Json::Value::ArrayIndex i = 0; i < kThrottlingSeverityCount; ++i) {
ret[i] = getIntFromValue(values[i]);
if (inc_check && ret[i] < last) {
LOG(FATAL) << "Invalid array[" << i << "]" << ret[i] << " min=" << last;
return false;
}
if (dec_check && ret[i] > last) {
LOG(FATAL) << "Invalid array[" << i << "]" << ret[i] << " max=" << last;
return false;
}
last = ret[i];
LOG(INFO) << "[" << i << "]: " << ret[i];
}
}
*out = ret;
return true;
}
bool getFloatFromJsonValues(const Json::Value &values, ThrottlingArray *out, bool inc_check,
bool dec_check) {
ThrottlingArray ret;
if (inc_check && dec_check) {
LOG(ERROR) << "Cannot enable inc_check and dec_check at the same time";
return false;
}
if (values.size() != kThrottlingSeverityCount) {
LOG(ERROR) << "Values size is invalid";
return false;
} else {
float last = std::nanf("");
for (Json::Value::ArrayIndex i = 0; i < kThrottlingSeverityCount; ++i) {
ret[i] = getFloatFromValue(values[i]);
if (inc_check && !std::isnan(last) && !std::isnan(ret[i]) && ret[i] < last) {
LOG(FATAL) << "Invalid array[" << i << "]" << ret[i] << " min=" << last;
return false;
}
if (dec_check && !std::isnan(last) && !std::isnan(ret[i]) && ret[i] > last) {
LOG(FATAL) << "Invalid array[" << i << "]" << ret[i] << " max=" << last;
return false;
}
last = std::isnan(ret[i]) ? last : ret[i];
LOG(INFO) << "[" << i << "]: " << ret[i];
}
}
*out = ret;
return true;
}
} // namespace } // namespace
std::map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path) { std::unordered_map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path) {
std::string json_doc; std::string json_doc;
std::map<std::string, SensorInfo> sensors_parsed; std::unordered_map<std::string, SensorInfo> sensors_parsed;
if (!android::base::ReadFileToString(config_path.data(), &json_doc)) { if (!android::base::ReadFileToString(config_path.data(), &json_doc)) {
LOG(ERROR) << "Failed to read JSON config from " << config_path; LOG(ERROR) << "Failed to read JSON config from " << config_path;
return sensors_parsed; return sensors_parsed;
} }
Json::Value root; Json::Value root;
Json::Reader reader; Json::CharReaderBuilder builder;
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
std::string errorMessage;
if (!reader.parse(json_doc, root)) { if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
LOG(ERROR) << "Failed to parse JSON config"; LOG(ERROR) << "Failed to parse JSON config: " << errorMessage;
return sensors_parsed; return sensors_parsed;
} }
Json::Value sensors = root["Sensors"]; Json::Value sensors = root["Sensors"];
std::size_t total_parsed = 0; std::size_t total_parsed = 0;
std::set<std::string> sensors_name_parsed; std::unordered_set<std::string> sensors_name_parsed;
for (Json::Value::ArrayIndex i = 0; i < sensors.size(); ++i) { for (Json::Value::ArrayIndex i = 0; i < sensors.size(); ++i) {
const std::string &name = sensors[i]["Name"].asString(); const std::string &name = sensors[i]["Name"].asString();
@ -107,6 +186,24 @@ std::map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path)
return sensors_parsed; return sensors_parsed;
} }
bool send_cb = false;
if (sensors[i]["Monitor"].empty() || !sensors[i]["Monitor"].isBool()) {
LOG(INFO) << "Failed to read Sensor[" << name << "]'s Monitor, set to 'false'";
} else if (sensors[i]["Monitor"].asBool()) {
send_cb = true;
}
LOG(INFO) << "Sensor[" << name << "]'s SendCallback: " << std::boolalpha << send_cb
<< std::noboolalpha;
bool send_powerhint = false;
if (sensors[i]["SendPowerHint"].empty() || !sensors[i]["SendPowerHint"].isBool()) {
LOG(INFO) << "Failed to read Sensor[" << name << "]'s SendPowerHint, set to 'false'";
} else if (sensors[i]["SendPowerHint"].asBool()) {
send_powerhint = true;
}
LOG(INFO) << "Sensor[" << name << "]'s SendPowerHint: " << std::boolalpha << send_powerhint
<< std::noboolalpha;
std::array<float, kThrottlingSeverityCount> hot_thresholds; std::array<float, kThrottlingSeverityCount> hot_thresholds;
hot_thresholds.fill(NAN); hot_thresholds.fill(NAN);
std::array<float, kThrottlingSeverityCount> cold_thresholds; std::array<float, kThrottlingSeverityCount> cold_thresholds;
@ -115,7 +212,18 @@ std::map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path)
hot_hysteresis.fill(0.0); hot_hysteresis.fill(0.0);
std::array<float, kThrottlingSeverityCount> cold_hysteresis; std::array<float, kThrottlingSeverityCount> cold_hysteresis;
cold_hysteresis.fill(0.0); cold_hysteresis.fill(0.0);
std::vector<std::string> linked_sensors;
std::vector<float> coefficients;
float offset = 0;
std::string trigger_sensor;
FormulaOption formula = FormulaOption::COUNT_THRESHOLD;
bool is_virtual_sensor = false;
if (sensors[i]["VirtualSensor"].empty() || !sensors[i]["VirtualSensor"].isBool()) {
LOG(INFO) << "Failed to read Sensor[" << name << "]'s VirtualSensor, set to 'false'";
} else {
is_virtual_sensor = sensors[i]["VirtualSensor"].asBool();
}
Json::Value values = sensors[i]["HotThreshold"]; Json::Value values = sensors[i]["HotThreshold"];
if (values.size() != kThrottlingSeverityCount) { if (values.size() != kThrottlingSeverityCount) {
LOG(ERROR) << "Invalid " LOG(ERROR) << "Invalid "
@ -201,6 +309,63 @@ std::map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path)
} }
} }
if (is_virtual_sensor) {
values = sensors[i]["Combination"];
if (values.size()) {
linked_sensors.reserve(values.size());
for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
linked_sensors.emplace_back(values[j].asString());
LOG(INFO) << "Sensor[" << name << "]'s combination[" << j
<< "]: " << linked_sensors[j];
}
} else {
sensors_parsed.clear();
return sensors_parsed;
}
values = sensors[i]["Coefficient"];
if (values.size()) {
coefficients.reserve(values.size());
for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
coefficients.emplace_back(getFloatFromValue(values[j]));
LOG(INFO) << "Sensor[" << name << "]'s coefficient[" << j
<< "]: " << coefficients[j];
}
} else {
sensors_parsed.clear();
return sensors_parsed;
}
if (!sensors[i]["Offset"].empty()) {
offset = sensors[i]["Offset"].asFloat();
}
if (linked_sensors.size() != coefficients.size()) {
sensors_parsed.clear();
return sensors_parsed;
}
trigger_sensor = sensors[i]["TriggerSensor"].asString();
if (sensors[i]["Formula"].asString().compare("COUNT_THRESHOLD") == 0) {
formula = FormulaOption::COUNT_THRESHOLD;
} else if (sensors[i]["Formula"].asString().compare("WEIGHTED_AVG") == 0) {
formula = FormulaOption::WEIGHTED_AVG;
} else if (sensors[i]["Formula"].asString().compare("MAXIMUM") == 0) {
formula = FormulaOption::MAXIMUM;
} else if (sensors[i]["Formula"].asString().compare("MINIMUM") == 0) {
formula = FormulaOption::MINIMUM;
} else {
sensors_parsed.clear();
return sensors_parsed;
}
}
std::string temp_path;
if (!sensors[i]["TempPath"].empty()) {
temp_path = sensors[i]["TempPath"].asString();
LOG(INFO) << "Sensor[" << name << "]'s TempPath: " << temp_path;
}
float vr_threshold = NAN; float vr_threshold = NAN;
vr_threshold = getFloatFromValue(sensors[i]["VrThreshold"]); vr_threshold = getFloatFromValue(sensors[i]["VrThreshold"]);
LOG(INFO) << "Sensor[" << name << "]'s VrThreshold: " << vr_threshold; LOG(INFO) << "Sensor[" << name << "]'s VrThreshold: " << vr_threshold;
@ -208,23 +373,287 @@ std::map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path)
float multiplier = sensors[i]["Multiplier"].asFloat(); float multiplier = sensors[i]["Multiplier"].asFloat();
LOG(INFO) << "Sensor[" << name << "]'s Multiplier: " << multiplier; LOG(INFO) << "Sensor[" << name << "]'s Multiplier: " << multiplier;
bool is_monitor = false; std::chrono::milliseconds polling_delay;
if (sensors[i]["Monitor"].empty() || !sensors[i]["Monitor"].isBool()) { if (sensors[i]["PollingDelay"].empty()) {
LOG(INFO) << "Failed to read Sensor[" << name << "]'s Monitor, set to 'false'"; polling_delay = kUeventPollTimeoutMs;
} else { } else {
is_monitor = sensors[i]["Monitor"].asBool(); polling_delay = std::chrono::milliseconds(getIntFromValue(sensors[i]["PollingDelay"]));
} }
LOG(INFO) << "Sensor[" << name << "]'s Monitor: " << std::boolalpha << is_monitor LOG(INFO) << "Sensor[" << name << "]'s Polling delay: " << polling_delay.count();
<< std::noboolalpha;
bool send_powerhint = false; std::chrono::milliseconds passive_delay;
if (sensors[i]["SendPowerHint"].empty() || !sensors[i]["SendPowerHint"].isBool()) { if (sensors[i]["PassiveDelay"].empty()) {
LOG(INFO) << "Failed to read Sensor[" << name << "]'s SendPowerHint, set to 'false'"; passive_delay = kMinPollIntervalMs;
} else { } else {
send_powerhint = sensors[i]["SendPowerHint"].asBool(); passive_delay = std::chrono::milliseconds(getIntFromValue(sensors[i]["PassiveDelay"]));
} }
LOG(INFO) << "Sensor[" << name << "]'s SendPowerHint: " << std::boolalpha << send_powerhint LOG(INFO) << "Sensor[" << name << "]'s Passive delay: " << passive_delay.count();
<< std::noboolalpha;
bool support_pid = false;
std::array<float, kThrottlingSeverityCount> k_po;
k_po.fill(0.0);
std::array<float, kThrottlingSeverityCount> k_pu;
k_pu.fill(0.0);
std::array<float, kThrottlingSeverityCount> k_i;
k_i.fill(0.0);
std::array<float, kThrottlingSeverityCount> k_d;
k_d.fill(0.0);
std::array<float, kThrottlingSeverityCount> i_max;
i_max.fill(NAN);
std::array<float, kThrottlingSeverityCount> max_alloc_power;
max_alloc_power.fill(NAN);
std::array<float, kThrottlingSeverityCount> min_alloc_power;
min_alloc_power.fill(NAN);
std::array<float, kThrottlingSeverityCount> s_power;
s_power.fill(NAN);
std::array<float, kThrottlingSeverityCount> i_cutoff;
i_cutoff.fill(NAN);
// Parse PID parameters
if (!sensors[i]["PIDInfo"].empty()) {
LOG(INFO) << "Start to parse"
<< " Sensor[" << name << "]'s K_Po";
if (sensors[i]["PIDInfo"]["K_Po"].empty() ||
!getFloatFromJsonValues(sensors[i]["PIDInfo"]["K_Po"], &k_po, false, false)) {
LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_Po";
sensors_parsed.clear();
return sensors_parsed;
}
LOG(INFO) << "Start to parse"
<< " Sensor[" << name << "]'s K_Pu";
if (sensors[i]["PIDInfo"]["K_Pu"].empty() ||
!getFloatFromJsonValues(sensors[i]["PIDInfo"]["K_Pu"], &k_pu, false, false)) {
LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_Pu";
sensors_parsed.clear();
return sensors_parsed;
}
LOG(INFO) << "Start to parse"
<< " Sensor[" << name << "]'s K_I";
if (sensors[i]["PIDInfo"]["K_I"].empty() ||
!getFloatFromJsonValues(sensors[i]["PIDInfo"]["K_I"], &k_i, false, false)) {
LOG(INFO) << "Sensor[" << name << "]: Failed to parse K_I";
sensors_parsed.clear();
return sensors_parsed;
}
LOG(INFO) << "Start to parse"
<< " Sensor[" << name << "]'s K_D";
if (sensors[i]["PIDInfo"]["K_D"].empty() ||
!getFloatFromJsonValues(sensors[i]["PIDInfo"]["K_D"], &k_d, false, false)) {
LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_D";
sensors_parsed.clear();
return sensors_parsed;
}
LOG(INFO) << "Start to parse"
<< " Sensor[" << name << "]'s I_Max";
if (sensors[i]["PIDInfo"]["I_Max"].empty() ||
!getFloatFromJsonValues(sensors[i]["PIDInfo"]["I_Max"], &i_max, false, false)) {
LOG(ERROR) << "Sensor[" << name << "]: Failed to parse I_Max";
sensors_parsed.clear();
return sensors_parsed;
}
LOG(INFO) << "Start to parse"
<< " Sensor[" << name << "]'s MaxAllocPower";
if (sensors[i]["PIDInfo"]["MaxAllocPower"].empty() ||
!getFloatFromJsonValues(sensors[i]["PIDInfo"]["MaxAllocPower"], &max_alloc_power,
false, true)) {
LOG(ERROR) << "Sensor[" << name << "]: Failed to parse MaxAllocPower";
sensors_parsed.clear();
return sensors_parsed;
}
LOG(INFO) << "Start to parse"
<< " Sensor[" << name << "]'s MinAllocPower";
if (sensors[i]["PIDInfo"]["MinAllocPower"].empty() ||
!getFloatFromJsonValues(sensors[i]["PIDInfo"]["MinAllocPower"], &min_alloc_power,
false, true)) {
LOG(ERROR) << "Sensor[" << name << "]: Failed to parse MinAllocPower";
sensors_parsed.clear();
return sensors_parsed;
}
LOG(INFO) << "Start to parse"
<< " Sensor[" << name << "]'s S_Power";
if (sensors[i]["PIDInfo"]["S_Power"].empty() ||
!getFloatFromJsonValues(sensors[i]["PIDInfo"]["S_Power"], &s_power, false, true)) {
LOG(ERROR) << "Sensor[" << name << "]: Failed to parse S_Power";
sensors_parsed.clear();
return sensors_parsed;
}
LOG(INFO) << "Start to parse"
<< " Sensor[" << name << "]'s I_Cutoff";
if (sensors[i]["PIDInfo"]["I_Cutoff"].empty() ||
!getFloatFromJsonValues(sensors[i]["PIDInfo"]["I_Cutoff"], &i_cutoff, false,
false)) {
LOG(ERROR) << "Sensor[" << name << "]: Failed to parse I_Cutoff";
sensors_parsed.clear();
return sensors_parsed;
}
// Confirm we have at least one valid PID combination
bool valid_pid_combination = false;
for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
if (!std::isnan(s_power[j])) {
if (std::isnan(k_po[j]) || std::isnan(k_pu[j]) || std::isnan(k_i[j]) ||
std::isnan(k_d[j]) || std::isnan(i_max[j]) ||
std::isnan(max_alloc_power[j]) || std::isnan(min_alloc_power[j]) ||
std::isnan(i_cutoff[j])) {
valid_pid_combination = false;
break;
} else {
valid_pid_combination = true;
}
}
}
if (!valid_pid_combination) {
LOG(ERROR) << "Sensor[" << name << "]: Invalid PID parameters combinations";
sensors_parsed.clear();
return sensors_parsed;
} else {
support_pid = true;
}
}
// Parse binded cooling device
bool support_hard_limit = false;
std::unordered_map<std::string, BindedCdevInfo> binded_cdev_info_map;
values = sensors[i]["BindedCdevInfo"];
for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
Json::Value sub_values;
const std::string &cdev_name = values[j]["CdevRequest"].asString();
ThrottlingArray cdev_weight_for_pid;
cdev_weight_for_pid.fill(NAN);
CdevArray cdev_ceiling;
cdev_ceiling.fill(std::numeric_limits<int>::max());
if (support_pid) {
if (!values[j]["CdevWeightForPID"].empty()) {
LOG(INFO) << "Sensor[" << name << "]: Star to parse " << cdev_name
<< "'s CdevWeightForPID";
if (!getFloatFromJsonValues(values[j]["CdevWeightForPID"], &cdev_weight_for_pid,
false, false)) {
LOG(ERROR) << "Failed to parse CdevWeightForPID";
sensors_parsed.clear();
return sensors_parsed;
}
}
if (!values[j]["CdevCeiling"].empty()) {
LOG(INFO) << "Sensor[" << name
<< "]: Start to parse CdevCeiling: " << cdev_name;
if (!getIntFromJsonValues(values[j]["CdevCeiling"], &cdev_ceiling, false,
false)) {
LOG(ERROR) << "Failed to parse CdevCeiling";
sensors_parsed.clear();
return sensors_parsed;
}
}
}
CdevArray limit_info;
limit_info.fill(0);
ThrottlingArray power_thresholds;
power_thresholds.fill(NAN);
ReleaseLogic release_logic = ReleaseLogic::NONE;
sub_values = values[j]["LimitInfo"];
if (sub_values.size()) {
LOG(INFO) << "Sensor[" << name << "]: Start to parse LimitInfo: " << cdev_name;
if (!getIntFromJsonValues(sub_values, &limit_info, false, false)) {
LOG(ERROR) << "Failed to parse LimitInfo";
sensors_parsed.clear();
return sensors_parsed;
}
support_hard_limit = true;
}
// Parse linked power info
bool is_power_data_invalid = false;
std::string power_rail;
bool high_power_check = false;
bool throttling_with_power_link = false;
CdevArray cdev_floor_with_power_link;
cdev_floor_with_power_link.fill(0);
const bool power_link_disabled =
android::base::GetBoolProperty(kPowerLinkDisabledProperty.data(), false);
if (!power_link_disabled) {
power_rail = values[j]["BindedPowerRail"].asString();
if (values[j]["HighPowerCheck"].asBool()) {
high_power_check = true;
}
LOG(INFO) << "Highpowercheck: " << std::boolalpha << high_power_check;
if (values[j]["ThrottlingWithPowerLink"].asBool()) {
throttling_with_power_link = true;
}
LOG(INFO) << "ThrottlingwithPowerLink: " << std::boolalpha
<< throttling_with_power_link;
sub_values = values[j]["CdevFloorWithPowerLink"];
if (sub_values.size()) {
LOG(INFO) << "Sensor[" << name << "]: Start to parse " << cdev_name
<< "'s CdevFloorWithPowerLink";
if (!getIntFromJsonValues(sub_values, &cdev_floor_with_power_link, false,
false)) {
LOG(ERROR) << "Failed to parse CdevFloor";
is_power_data_invalid = true;
}
}
sub_values = values[j]["PowerThreshold"];
if (sub_values.size()) {
LOG(INFO) << "Sensor[" << name << "]: Start to parse " << cdev_name
<< "'s PowerThreshold";
if (!getFloatFromJsonValues(sub_values, &power_thresholds, false, false)) {
LOG(ERROR) << "Failed to parse power thresholds";
is_power_data_invalid = true;
}
if (values[j]["ReleaseLogic"].asString() == "INCREASE") {
release_logic = ReleaseLogic::INCREASE;
LOG(INFO) << "Release logic: INCREASE";
} else if (values[j]["ReleaseLogic"].asString() == "DECREASE") {
release_logic = ReleaseLogic::DECREASE;
LOG(INFO) << "Release logic: DECREASE";
} else if (values[j]["ReleaseLogic"].asString() == "STEPWISE") {
release_logic = ReleaseLogic::STEPWISE;
LOG(INFO) << "Release logic: STEPWISE";
} else if (values[j]["ReleaseLogic"].asString() == "RELEASE_TO_FLOOR") {
release_logic = ReleaseLogic::RELEASE_TO_FLOOR;
LOG(INFO) << "Release logic: RELEASE_TO_FLOOR";
} else {
LOG(ERROR) << "Release logic is invalid";
is_power_data_invalid = true;
}
if (is_power_data_invalid) {
LOG(ERROR) << cdev_name << "'s power rail " << power_rail << " is invalid";
sensors_parsed.clear();
return sensors_parsed;
}
}
}
binded_cdev_info_map[cdev_name] = {
.limit_info = limit_info,
.power_thresholds = power_thresholds,
.release_logic = release_logic,
.high_power_check = high_power_check,
.throttling_with_power_link = throttling_with_power_link,
.cdev_weight_for_pid = cdev_weight_for_pid,
.cdev_ceiling = cdev_ceiling,
.cdev_floor_with_power_link = cdev_floor_with_power_link,
.power_rail = power_rail,
};
}
bool is_monitor = (send_cb | send_powerhint | support_pid | support_hard_limit);
LOG(INFO) << "Sensor[" << name << "]'s Monitor: " << std::boolalpha << is_monitor;
std::unique_ptr<VirtualSensorInfo> virtual_sensor_info;
if (is_virtual_sensor) {
virtual_sensor_info.reset(new VirtualSensorInfo{linked_sensors, coefficients, offset,
trigger_sensor, formula});
}
std::unique_ptr<ThrottlingInfo> throttling_info(
new ThrottlingInfo{k_po, k_pu, k_i, k_d, i_max, max_alloc_power, min_alloc_power,
s_power, i_cutoff, binded_cdev_info_map});
sensors_parsed[name] = { sensors_parsed[name] = {
.type = sensor_type, .type = sensor_type,
@ -232,11 +661,18 @@ std::map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path)
.cold_thresholds = cold_thresholds, .cold_thresholds = cold_thresholds,
.hot_hysteresis = hot_hysteresis, .hot_hysteresis = hot_hysteresis,
.cold_hysteresis = cold_hysteresis, .cold_hysteresis = cold_hysteresis,
.temp_path = temp_path,
.vr_threshold = vr_threshold, .vr_threshold = vr_threshold,
.multiplier = multiplier, .multiplier = multiplier,
.is_monitor = is_monitor, .polling_delay = polling_delay,
.passive_delay = passive_delay,
.send_cb = send_cb,
.send_powerhint = send_powerhint, .send_powerhint = send_powerhint,
.is_monitor = is_monitor,
.virtual_sensor_info = std::move(virtual_sensor_info),
.throttling_info = std::move(throttling_info),
}; };
++total_parsed; ++total_parsed;
} }
@ -244,25 +680,27 @@ std::map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path)
return sensors_parsed; return sensors_parsed;
} }
std::map<std::string, CoolingType> ParseCoolingDevice(std::string_view config_path) { std::unordered_map<std::string, CdevInfo> ParseCoolingDevice(std::string_view config_path) {
std::string json_doc; std::string json_doc;
std::map<std::string, CoolingType> cooling_devices_parsed; std::unordered_map<std::string, CdevInfo> cooling_devices_parsed;
if (!android::base::ReadFileToString(config_path.data(), &json_doc)) { if (!android::base::ReadFileToString(config_path.data(), &json_doc)) {
LOG(ERROR) << "Failed to read JSON config from " << config_path; LOG(ERROR) << "Failed to read JSON config from " << config_path;
return cooling_devices_parsed; return cooling_devices_parsed;
} }
Json::Value root; Json::Value root;
Json::Reader reader; Json::CharReaderBuilder builder;
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
std::string errorMessage;
if (!reader.parse(json_doc, root)) { if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
LOG(ERROR) << "Failed to parse JSON config"; LOG(ERROR) << "Failed to parse JSON config";
return cooling_devices_parsed; return cooling_devices_parsed;
} }
Json::Value cooling_devices = root["CoolingDevices"]; Json::Value cooling_devices = root["CoolingDevices"];
std::size_t total_parsed = 0; std::size_t total_parsed = 0;
std::set<std::string> cooling_devices_name_parsed; std::unordered_set<std::string> cooling_devices_name_parsed;
for (Json::Value::ArrayIndex i = 0; i < cooling_devices.size(); ++i) { for (Json::Value::ArrayIndex i = 0; i < cooling_devices.size(); ++i) {
const std::string &name = cooling_devices[i]["Name"].asString(); const std::string &name = cooling_devices[i]["Name"].asString();
@ -292,8 +730,35 @@ std::map<std::string, CoolingType> ParseCoolingDevice(std::string_view config_pa
return cooling_devices_parsed; return cooling_devices_parsed;
} }
cooling_devices_parsed[name] = cooling_device_type; const std::string &read_path = cooling_devices[i]["ReadPath"].asString();
LOG(INFO) << "Cdev Read Path: " << (read_path.empty() ? "default" : read_path);
const std::string &write_path = cooling_devices[i]["WritePath"].asString();
LOG(INFO) << "Cdev Write Path: " << (write_path.empty() ? "default" : write_path);
std::vector<float> state2power;
Json::Value values = cooling_devices[i]["State2Power"];
if (values.size()) {
state2power.reserve(values.size());
for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
state2power.emplace_back(getFloatFromValue(values[j]));
LOG(INFO) << "Cooling device[" << name << "]'s Power2State[" << j
<< "]: " << state2power[j];
}
} else {
LOG(INFO) << "CoolingDevice[" << i << "]'s Name: " << name
<< " does not support Power2State";
}
const std::string &power_rail = cooling_devices[i]["PowerRail"].asString();
LOG(INFO) << "Cooling device power rail : " << power_rail;
cooling_devices_parsed[name] = {
.type = cooling_device_type,
.read_path = read_path,
.write_path = write_path,
.state2power = state2power,
};
++total_parsed; ++total_parsed;
} }
@ -301,6 +766,139 @@ std::map<std::string, CoolingType> ParseCoolingDevice(std::string_view config_pa
return cooling_devices_parsed; return cooling_devices_parsed;
} }
std::unordered_map<std::string, PowerRailInfo> ParsePowerRailInfo(std::string_view config_path) {
std::string json_doc;
std::unordered_map<std::string, PowerRailInfo> power_rails_parsed;
if (!android::base::ReadFileToString(config_path.data(), &json_doc)) {
LOG(ERROR) << "Failed to read JSON config from " << config_path;
return power_rails_parsed;
}
Json::Value root;
Json::CharReaderBuilder builder;
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
std::string errorMessage;
if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
LOG(ERROR) << "Failed to parse JSON config";
return power_rails_parsed;
}
Json::Value power_rails = root["PowerRails"];
std::size_t total_parsed = 0;
std::unordered_set<std::string> power_rails_name_parsed;
for (Json::Value::ArrayIndex i = 0; i < power_rails.size(); ++i) {
const std::string &name = power_rails[i]["Name"].asString();
LOG(INFO) << "PowerRail[" << i << "]'s Name: " << name;
if (name.empty()) {
LOG(ERROR) << "Failed to read "
<< "PowerRail[" << i << "]'s Name";
power_rails_parsed.clear();
return power_rails_parsed;
}
std::string rail;
if (power_rails[i]["Rail"].empty()) {
rail = name;
} else {
rail = power_rails[i]["Rail"].asString();
}
LOG(INFO) << "PowerRail[" << i << "]'s Rail: " << rail;
std::vector<std::string> linked_power_rails;
std::vector<float> coefficients;
float offset = 0;
FormulaOption formula = FormulaOption::COUNT_THRESHOLD;
bool is_virtual_power_rail = false;
Json::Value values;
int power_sample_count = 0;
std::chrono::milliseconds power_sample_delay;
if (!power_rails[i]["VirtualRails"].empty() && power_rails[i]["VirtualRails"].isBool()) {
is_virtual_power_rail = power_rails[i]["VirtualRails"].asBool();
LOG(INFO) << "PowerRails[" << name << "]'s VirtualRail, set to 'true'";
}
if (is_virtual_power_rail) {
values = power_rails[i]["Combination"];
if (values.size()) {
linked_power_rails.reserve(values.size());
for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
linked_power_rails.emplace_back(values[j].asString());
LOG(INFO) << "PowerRail[" << name << "]'s combination[" << j
<< "]: " << linked_power_rails[j];
}
} else {
power_rails_parsed.clear();
return power_rails_parsed;
}
values = power_rails[i]["Coefficient"];
if (values.size()) {
coefficients.reserve(values.size());
for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
coefficients.emplace_back(getFloatFromValue(values[j]));
LOG(INFO) << "PowerRail[" << name << "]'s coefficient[" << j
<< "]: " << coefficients[j];
}
} else {
power_rails_parsed.clear();
return power_rails_parsed;
}
if (!power_rails[i]["Offset"].empty()) {
offset = power_rails[i]["Offset"].asFloat();
}
if (linked_power_rails.size() != coefficients.size()) {
power_rails_parsed.clear();
return power_rails_parsed;
}
if (power_rails[i]["Formula"].asString().compare("COUNT_THRESHOLD") == 0) {
formula = FormulaOption::COUNT_THRESHOLD;
} else if (power_rails[i]["Formula"].asString().compare("WEIGHTED_AVG") == 0) {
formula = FormulaOption::WEIGHTED_AVG;
} else if (power_rails[i]["Formula"].asString().compare("MAXIMUM") == 0) {
formula = FormulaOption::MAXIMUM;
} else if (power_rails[i]["Formula"].asString().compare("MINIMUM") == 0) {
formula = FormulaOption::MINIMUM;
} else {
power_rails_parsed.clear();
return power_rails_parsed;
}
}
std::unique_ptr<VirtualPowerRailInfo> virtual_power_rail_info;
if (is_virtual_power_rail) {
virtual_power_rail_info.reset(
new VirtualPowerRailInfo{linked_power_rails, coefficients, offset, formula});
}
power_sample_count = power_rails[i]["PowerSampleCount"].asInt();
LOG(INFO) << "Power sample Count: " << power_sample_count;
if (!power_rails[i]["PowerSampleDelay"]) {
power_sample_delay = std::chrono::milliseconds::max();
} else {
power_sample_delay =
std::chrono::milliseconds(getIntFromValue(power_rails[i]["PowerSampleDelay"]));
}
power_rails_parsed[name] = {
.rail = rail,
.power_sample_count = power_sample_count,
.power_sample_delay = power_sample_delay,
.virtual_power_rail_info = std::move(virtual_power_rail_info),
};
++total_parsed;
}
LOG(INFO) << total_parsed << " PowerRails parsed successfully";
return power_rails_parsed;
}
} // namespace implementation } // namespace implementation
} // namespace V2_0 } // namespace V2_0
} // namespace thermal } // namespace thermal

View File

@ -14,11 +14,10 @@
* limitations under the License. * limitations under the License.
*/ */
#ifndef THERMAL_UTILS_CONFIG_PARSER_H__ #pragma once
#define THERMAL_UTILS_CONFIG_PARSER_H__
#include <map>
#include <string> #include <string>
#include <unordered_map>
#include <android/hardware/thermal/2.0/IThermal.h> #include <android/hardware/thermal/2.0/IThermal.h>
@ -29,12 +28,73 @@ namespace V2_0 {
namespace implementation { namespace implementation {
using ::android::hardware::hidl_enum_range; using ::android::hardware::hidl_enum_range;
using ::android::hardware::thermal::V2_0::CoolingType; using CoolingType_2_0 = ::android::hardware::thermal::V2_0::CoolingType;
using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType; using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType;
using ::android::hardware::thermal::V2_0::ThrottlingSeverity; using ::android::hardware::thermal::V2_0::ThrottlingSeverity;
constexpr size_t kThrottlingSeverityCount = std::distance( constexpr size_t kThrottlingSeverityCount = std::distance(
hidl_enum_range<ThrottlingSeverity>().begin(), hidl_enum_range<ThrottlingSeverity>().end()); hidl_enum_range<ThrottlingSeverity>().begin(), hidl_enum_range<ThrottlingSeverity>().end());
using ThrottlingArray = std::array<float, static_cast<size_t>(kThrottlingSeverityCount)>; using ThrottlingArray = std::array<float, static_cast<size_t>(kThrottlingSeverityCount)>;
using CdevArray = std::array<int, static_cast<size_t>(kThrottlingSeverityCount)>;
constexpr std::chrono::milliseconds kMinPollIntervalMs = std::chrono::milliseconds(2000);
constexpr std::chrono::milliseconds kUeventPollTimeoutMs = std::chrono::milliseconds(300000);
enum FormulaOption : uint32_t {
COUNT_THRESHOLD = 0,
WEIGHTED_AVG,
MAXIMUM,
MINIMUM,
};
struct VirtualSensorInfo {
std::vector<std::string> linked_sensors;
std::vector<float> coefficients;
float offset;
std::string trigger_sensor;
FormulaOption formula;
};
struct VirtualPowerRailInfo {
std::vector<std::string> linked_power_rails;
std::vector<float> coefficients;
float offset;
FormulaOption formula;
};
// The method when the ODPM power is lower than threshold
enum ReleaseLogic : uint32_t {
INCREASE = 0, // Increase throttling by step
DECREASE, // Decrease throttling by step
STEPWISE, // Support both increase and decrease logix
RELEASE_TO_FLOOR, // Release throttling to floor directly
NONE,
};
struct BindedCdevInfo {
CdevArray limit_info;
ThrottlingArray power_thresholds;
ReleaseLogic release_logic;
ThrottlingArray cdev_weight_for_pid;
CdevArray cdev_ceiling;
CdevArray cdev_floor_with_power_link;
std::string power_rail;
// The flag for activate release logic when power is higher than power threshold
bool high_power_check;
// The flag for only triggering throttling until all power samples are collected
bool throttling_with_power_link;
};
struct ThrottlingInfo {
ThrottlingArray k_po;
ThrottlingArray k_pu;
ThrottlingArray k_i;
ThrottlingArray k_d;
ThrottlingArray i_max;
ThrottlingArray max_alloc_power;
ThrottlingArray min_alloc_power;
ThrottlingArray s_power;
ThrottlingArray i_cutoff;
std::unordered_map<std::string, BindedCdevInfo> binded_cdev_info_map;
};
struct SensorInfo { struct SensorInfo {
TemperatureType_2_0 type; TemperatureType_2_0 type;
@ -42,19 +102,38 @@ struct SensorInfo {
ThrottlingArray cold_thresholds; ThrottlingArray cold_thresholds;
ThrottlingArray hot_hysteresis; ThrottlingArray hot_hysteresis;
ThrottlingArray cold_hysteresis; ThrottlingArray cold_hysteresis;
std::string temp_path;
float vr_threshold; float vr_threshold;
float multiplier; float multiplier;
bool is_monitor; std::chrono::milliseconds polling_delay;
std::chrono::milliseconds passive_delay;
bool send_cb;
bool send_powerhint; bool send_powerhint;
bool is_monitor;
std::unique_ptr<VirtualSensorInfo> virtual_sensor_info;
std::unique_ptr<ThrottlingInfo> throttling_info;
}; };
std::map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path); struct CdevInfo {
std::map<std::string, CoolingType> ParseCoolingDevice(std::string_view config_path); CoolingType_2_0 type;
std::string read_path;
std::string write_path;
std::vector<float> state2power;
int max_state;
};
struct PowerRailInfo {
std::string rail;
int power_sample_count;
std::chrono::milliseconds power_sample_delay;
std::unique_ptr<VirtualPowerRailInfo> virtual_power_rail_info;
};
std::unordered_map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path);
std::unordered_map<std::string, CdevInfo> ParseCoolingDevice(std::string_view config_path);
std::unordered_map<std::string, PowerRailInfo> ParsePowerRailInfo(std::string_view config_path);
} // namespace implementation } // namespace implementation
} // namespace V2_0 } // namespace V2_0
} // namespace thermal } // namespace thermal
} // namespace hardware } // namespace hardware
} // namespace android } // namespace android
#endif // THERMAL_UTILS_CONFIG_PARSER_H__

View File

@ -0,0 +1,444 @@
/*
* Copyright (C) 2021 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.
*/
#include <dirent.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include "power_files.h"
namespace android {
namespace hardware {
namespace thermal {
namespace V2_0 {
namespace implementation {
constexpr std::string_view kDeviceType("iio:device");
constexpr std::string_view kIioRootDir("/sys/bus/iio/devices");
constexpr std::string_view kEnergyValueNode("energy_value");
using android::base::ReadFileToString;
using android::base::StringPrintf;
void PowerFiles::setPowerDataToDefault(std::string_view sensor_name) {
std::unique_lock<std::shared_mutex> _lock(throttling_release_map_mutex_);
if (!throttling_release_map_.count(sensor_name.data()) ||
!power_status_map_.count(sensor_name.data())) {
return;
}
auto &cdev_release_map = throttling_release_map_.at(sensor_name.data());
PowerSample power_sample = {};
for (auto &power_status_pair : power_status_map_.at(sensor_name.data())) {
for (size_t i = 0; i < power_status_pair.second.power_history.size(); ++i) {
for (size_t j = 0; j < power_status_pair.second.power_history[i].size(); ++j) {
power_status_pair.second.power_history[i].pop();
power_status_pair.second.power_history[i].emplace(power_sample);
}
}
power_status_pair.second.last_updated_avg_power = NAN;
}
for (auto &cdev_release_pair : cdev_release_map) {
cdev_release_pair.second.release_step = 0;
}
}
int PowerFiles::getReleaseStep(std::string_view sensor_name, std::string_view cdev_name) {
int release_step = 0;
std::shared_lock<std::shared_mutex> _lock(throttling_release_map_mutex_);
if (throttling_release_map_.count(sensor_name.data()) &&
throttling_release_map_[sensor_name.data()].count(cdev_name.data())) {
release_step = throttling_release_map_[sensor_name.data()][cdev_name.data()].release_step;
}
return release_step;
}
bool PowerFiles::registerPowerRailsToWatch(std::string_view sensor_name, std::string_view cdev_name,
const BindedCdevInfo &binded_cdev_info,
const CdevInfo &cdev_info,
const PowerRailInfo &power_rail_info) {
std::vector<std::queue<PowerSample>> power_history;
PowerSample power_sample = {
.energy_counter = 0,
.duration = 0,
};
if (throttling_release_map_.count(sensor_name.data()) &&
throttling_release_map_[sensor_name.data()].count(binded_cdev_info.power_rail)) {
return true;
}
if (!energy_info_map_.size() && !updateEnergyValues()) {
LOG(ERROR) << "Faield to update energy info";
return false;
}
if (power_rail_info.virtual_power_rail_info != nullptr &&
power_rail_info.virtual_power_rail_info->linked_power_rails.size()) {
for (size_t i = 0; i < power_rail_info.virtual_power_rail_info->linked_power_rails.size();
++i) {
if (energy_info_map_.count(
power_rail_info.virtual_power_rail_info->linked_power_rails[i])) {
power_history.emplace_back(std::queue<PowerSample>());
for (int j = 0; j < power_rail_info.power_sample_count; j++) {
power_history[i].emplace(power_sample);
}
}
}
} else {
if (energy_info_map_.count(power_rail_info.rail)) {
power_history.emplace_back(std::queue<PowerSample>());
for (int j = 0; j < power_rail_info.power_sample_count; j++) {
power_history[0].emplace(power_sample);
}
}
}
if (power_history.size()) {
throttling_release_map_[sensor_name.data()][cdev_name.data()] = {
.release_step = 0,
.max_release_step = cdev_info.max_state,
};
power_status_map_[sensor_name.data()][binded_cdev_info.power_rail] = {
.power_history = power_history,
.time_remaining = power_rail_info.power_sample_delay,
.last_updated_avg_power = NAN,
};
} else {
return false;
}
LOG(INFO) << "Sensor " << sensor_name.data() << " successfully registers power rail "
<< binded_cdev_info.power_rail << " for cooling device " << cdev_name.data();
return true;
}
bool PowerFiles::findEnergySourceToWatch(void) {
std::string devicePath;
if (energy_path_set_.size()) {
return true;
}
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kIioRootDir.data()), closedir);
if (!dir) {
PLOG(ERROR) << "Error opening directory" << kIioRootDir;
return false;
}
// Find any iio:devices that support energy_value
while (struct dirent *ent = readdir(dir.get())) {
std::string devTypeDir = ent->d_name;
if (devTypeDir.find(kDeviceType) != std::string::npos) {
devicePath = StringPrintf("%s/%s", kIioRootDir.data(), devTypeDir.data());
std::string deviceEnergyContent;
if (!ReadFileToString(StringPrintf("%s/%s", devicePath.data(), kEnergyValueNode.data()),
&deviceEnergyContent)) {
} else if (deviceEnergyContent.size()) {
energy_path_set_.emplace(
StringPrintf("%s/%s", devicePath.data(), kEnergyValueNode.data()));
}
}
}
if (!energy_path_set_.size()) {
return false;
}
return true;
}
void PowerFiles::clearEnergyInfoMap(void) {
energy_info_map_.clear();
}
bool PowerFiles::updateEnergyValues(void) {
std::string deviceEnergyContent;
std::string deviceEnergyContents;
std::string line;
for (const auto &path : energy_path_set_) {
if (!android::base::ReadFileToString(path, &deviceEnergyContent)) {
LOG(ERROR) << "Failed to read energy content from " << path;
return false;
} else {
deviceEnergyContents.append(deviceEnergyContent);
}
}
std::istringstream energyData(deviceEnergyContents);
clearEnergyInfoMap();
while (std::getline(energyData, line)) {
/* Read rail energy */
uint64_t energy_counter = 0;
uint64_t duration = 0;
/* Format example: CH3(T=358356)[S2M_VDD_CPUCL2], 761330 */
auto start_pos = line.find("T=");
auto end_pos = line.find(')');
if (start_pos != std::string::npos) {
duration =
strtoul(line.substr(start_pos + 2, end_pos - start_pos - 2).c_str(), NULL, 10);
} else {
continue;
}
start_pos = line.find(")[");
end_pos = line.find(']');
std::string railName;
if (start_pos != std::string::npos) {
railName = line.substr(start_pos + 2, end_pos - start_pos - 2);
} else {
continue;
}
start_pos = line.find("],");
if (start_pos != std::string::npos) {
energy_counter = strtoul(line.substr(start_pos + 2).c_str(), NULL, 10);
} else {
continue;
}
energy_info_map_[railName] = {
.energy_counter = energy_counter,
.duration = duration,
};
}
return true;
}
bool PowerFiles::getAveragePower(std::string_view power_rail,
std::queue<PowerSample> *power_history, bool power_sample_update,
float *avg_power) {
const auto curr_sample = energy_info_map_.at(power_rail.data());
bool ret = true;
const auto last_sample = power_history->front();
const auto duration = curr_sample.duration - last_sample.duration;
const auto deltaEnergy = curr_sample.energy_counter - last_sample.energy_counter;
if (!last_sample.duration) {
LOG(VERBOSE) << "Power rail " << power_rail.data() << ": the last energy timestamp is zero";
} else if (duration <= 0 || deltaEnergy < 0) {
LOG(ERROR) << "Power rail " << power_rail.data() << " is invalid: duration = " << duration
<< ", deltaEnergy = " << deltaEnergy;
ret = false;
} else {
*avg_power = static_cast<float>(deltaEnergy) / static_cast<float>(duration);
LOG(VERBOSE) << "Power rail " << power_rail.data() << ", avg power = " << *avg_power
<< ", duration = " << duration << ", deltaEnergy = " << deltaEnergy;
}
if (power_sample_update) {
power_history->pop();
power_history->push(curr_sample);
}
return ret;
}
bool PowerFiles::computeAveragePower(const PowerRailInfo &power_rail_info,
PowerStatus *power_status, bool power_sample_update,
float *avg_power) {
bool ret = true;
float avg_power_val = -1;
float offset = power_rail_info.virtual_power_rail_info->offset;
for (size_t i = 0; i < power_rail_info.virtual_power_rail_info->linked_power_rails.size();
i++) {
float coefficient = power_rail_info.virtual_power_rail_info->coefficients[i];
float avg_power_number = -1;
if (!getAveragePower(power_rail_info.virtual_power_rail_info->linked_power_rails[i],
&power_status->power_history[i], power_sample_update,
&avg_power_number)) {
ret = false;
continue;
} else if (avg_power_number < 0) {
continue;
}
switch (power_rail_info.virtual_power_rail_info->formula) {
case FormulaOption::COUNT_THRESHOLD:
if ((coefficient < 0 && avg_power_number < -coefficient) ||
(coefficient >= 0 && avg_power_number >= coefficient))
avg_power_val += 1;
break;
case FormulaOption::WEIGHTED_AVG:
avg_power_val += avg_power_number * coefficient;
break;
case FormulaOption::MAXIMUM:
if (i == 0)
avg_power_val = std::numeric_limits<float>::lowest();
if (avg_power_number * coefficient > avg_power_val)
avg_power_val = avg_power_number * coefficient;
break;
case FormulaOption::MINIMUM:
if (i == 0)
avg_power_val = std::numeric_limits<float>::max();
if (avg_power_number * coefficient < avg_power_val)
avg_power_val = avg_power_number * coefficient;
break;
default:
break;
}
}
if (avg_power_val >= 0) {
avg_power_val = avg_power_val + offset;
}
*avg_power = avg_power_val;
return ret;
}
bool PowerFiles::throttlingReleaseUpdate(std::string_view sensor_name, std::string_view cdev_name,
const ThrottlingSeverity severity,
const std::chrono::milliseconds time_elapsed_ms,
const BindedCdevInfo &binded_cdev_info,
const PowerRailInfo &power_rail_info,
bool power_sample_update, bool severity_changed) {
std::unique_lock<std::shared_mutex> _lock(throttling_release_map_mutex_);
float avg_power = -1;
if (!throttling_release_map_.count(sensor_name.data()) ||
!throttling_release_map_[sensor_name.data()].count(cdev_name.data()) ||
!power_status_map_.count(sensor_name.data()) ||
!power_status_map_[sensor_name.data()].count(binded_cdev_info.power_rail)) {
return false;
}
auto &release_status = throttling_release_map_[sensor_name.data()].at(cdev_name.data());
auto &power_status = power_status_map_[sensor_name.data()].at(binded_cdev_info.power_rail);
if (power_sample_update) {
if (time_elapsed_ms > power_status.time_remaining) {
power_status.time_remaining = power_rail_info.power_sample_delay;
} else {
power_status.time_remaining = power_status.time_remaining - time_elapsed_ms;
LOG(VERBOSE) << "Power rail " << binded_cdev_info.power_rail
<< " : timeout remaining = " << power_status.time_remaining.count();
if (!severity_changed) {
return true;
} else {
// get the cached average power when thermal severity is changed
power_sample_update = false;
}
}
} else if (!severity_changed &&
power_status.time_remaining != power_rail_info.power_sample_delay) {
return false;
}
if (!energy_info_map_.size() && !updateEnergyValues()) {
LOG(ERROR) << "Failed to update energy values";
release_status.release_step = 0;
return false;
}
if (!power_sample_update && !std::isnan(power_status.last_updated_avg_power)) {
avg_power = power_status.last_updated_avg_power;
} else {
// Return false if we cannot get the average power of the target power rail
if (!((power_rail_info.virtual_power_rail_info == nullptr)
? getAveragePower(binded_cdev_info.power_rail, &power_status.power_history[0],
power_sample_update, &avg_power)
: computeAveragePower(power_rail_info, &power_status, power_sample_update,
&avg_power))) {
release_status.release_step = 0;
if (binded_cdev_info.throttling_with_power_link) {
release_status.release_step = release_status.max_release_step;
}
return false;
} else if (avg_power < 0) {
if (binded_cdev_info.throttling_with_power_link) {
release_status.release_step = release_status.max_release_step;
}
return true;
}
}
power_status.last_updated_avg_power = avg_power;
bool is_over_budget = true;
if (!binded_cdev_info.high_power_check) {
if (avg_power < binded_cdev_info.power_thresholds[static_cast<int>(severity)]) {
is_over_budget = false;
}
} else {
if (avg_power > binded_cdev_info.power_thresholds[static_cast<int>(severity)]) {
is_over_budget = false;
}
}
LOG(INFO) << "Power rail " << binded_cdev_info.power_rail << ": power threshold = "
<< binded_cdev_info.power_thresholds[static_cast<int>(severity)]
<< ", avg power = " << avg_power;
switch (binded_cdev_info.release_logic) {
case ReleaseLogic::INCREASE:
if (!is_over_budget) {
if (std::abs(release_status.release_step) <
static_cast<int>(release_status.max_release_step)) {
release_status.release_step--;
}
} else {
release_status.release_step = 0;
}
break;
case ReleaseLogic::DECREASE:
if (!is_over_budget) {
if (release_status.release_step <
static_cast<int>(release_status.max_release_step)) {
release_status.release_step++;
}
} else {
release_status.release_step = 0;
}
break;
case ReleaseLogic::STEPWISE:
if (!is_over_budget) {
if (release_status.release_step <
static_cast<int>(release_status.max_release_step)) {
release_status.release_step++;
}
} else {
if (std::abs(release_status.release_step) <
static_cast<int>(release_status.max_release_step)) {
release_status.release_step--;
}
}
break;
case ReleaseLogic::RELEASE_TO_FLOOR:
release_status.release_step = is_over_budget ? 0 : release_status.max_release_step;
break;
case ReleaseLogic::NONE:
default:
break;
}
return true;
}
} // namespace implementation
} // namespace V2_0
} // namespace thermal
} // namespace hardware
} // namespace android

127
thermal/utils/power_files.h Normal file
View File

@ -0,0 +1,127 @@
/*
* Copyright (C) 2021 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.
*/
#pragma once
#include <queue>
#include <shared_mutex>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include "config_parser.h"
namespace android {
namespace hardware {
namespace thermal {
namespace V2_0 {
namespace implementation {
struct PowerSample {
uint64_t energy_counter;
uint64_t duration;
};
struct ReleaseStatus {
int release_step;
int max_release_step;
};
struct PowerStatus {
std::chrono::milliseconds time_remaining;
// A vector to record the queues of power sample history.
std::vector<std::queue<PowerSample>> power_history;
float last_updated_avg_power;
};
using CdevReleaseStatus = std::unordered_map<std::string, ReleaseStatus>;
using PowerStatusMap = std::unordered_map<std::string, PowerStatus>;
// A helper class for monitoring power rails.
class PowerFiles {
public:
PowerFiles() = default;
~PowerFiles() = default;
// Disallow copy and assign.
PowerFiles(const PowerFiles &) = delete;
void operator=(const PowerFiles &) = delete;
// Register a map for the throttling release decision of target power rail
// Return false if the power_rail is not supported.
bool registerPowerRailsToWatch(std::string_view sensor_name, std::string_view cdev_name,
const BindedCdevInfo &binded_cdev_info,
const CdevInfo &cdev_info, const PowerRailInfo &power_rail_info);
// Find the energy source path, return false if no energy source found.
bool findEnergySourceToWatch(void);
// Clear the data of energy_info_map_.
void clearEnergyInfoMap(void);
// Update energy value to energy_info_map_, return false if the value is failed to update.
bool updateEnergyValues(void);
bool getAveragePower(std::string_view power_rail, std::queue<PowerSample> *power_history,
bool power_sample_update, float *avg_power);
bool computeAveragePower(const PowerRailInfo &power_rail_info, PowerStatus *power_status,
bool power_sample_update, float *avg_power);
// Update the throttling release status according to the average power, return true if power
// rail is updated.
bool throttlingReleaseUpdate(std::string_view sensor_name, std::string_view cdev_name,
const ThrottlingSeverity severity,
const std::chrono::milliseconds time_elapsed_ms,
const BindedCdevInfo &binded_cdev_info,
const PowerRailInfo &power_rail_info, bool power_sample_update,
bool severity_changed);
// Get the throttling release status for the targer power rail which is binded in specific
// sensor.
int getReleaseStep(std::string_view sensor_name, std::string_view cdev_name);
// Clear the data of throttling_release_map_.
void setPowerDataToDefault(std::string_view sensor_name);
// Get throttling release status map
const std::unordered_map<std::string, CdevReleaseStatus> &GetThrottlingReleaseMap() const {
std::shared_lock<std::shared_mutex> _lock(throttling_release_map_mutex_);
return throttling_release_map_;
}
// Get Power status map
const std::unordered_map<std::string, PowerStatusMap> &GetPowerStatusMap() const {
std::shared_lock<std::shared_mutex> _lock(power_status_map_mutex_);
return power_status_map_;
}
private:
// The map to record the energy info for each power rail.
std::unordered_map<std::string, PowerSample> energy_info_map_;
// The map to record the throttling release status for each thermal sensor.
std::unordered_map<std::string, CdevReleaseStatus> throttling_release_map_;
mutable std::shared_mutex throttling_release_map_mutex_;
// The map to record the power data for each thermal sensor.
std::unordered_map<std::string, PowerStatusMap> power_status_map_;
mutable std::shared_mutex power_status_map_mutex_;
// The set to store the energy source paths
std::unordered_set<std::string> energy_path_set_;
};
} // namespace implementation
} // namespace V2_0
} // namespace thermal
} // namespace hardware
} // namespace android

View File

@ -19,6 +19,7 @@
#include <android-base/file.h> #include <android-base/file.h>
#include <android-base/logging.h> #include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h> #include <android-base/strings.h>
#include "thermal_files.h" #include "thermal_files.h"
@ -45,6 +46,7 @@ bool ThermalFiles::readThermalFile(std::string_view thermal_name, std::string *d
std::string file_path = getThermalFilePath(std::string_view(thermal_name)); std::string file_path = getThermalFilePath(std::string_view(thermal_name));
*data = ""; *data = "";
if (file_path.empty()) { if (file_path.empty()) {
PLOG(WARNING) << "Failed to find " << thermal_name << "'s path";
return false; return false;
} }
@ -58,6 +60,18 @@ bool ThermalFiles::readThermalFile(std::string_view thermal_name, std::string *d
return true; return true;
} }
bool ThermalFiles::writeCdevFile(std::string_view cdev_name, std::string_view data) {
std::string file_path =
getThermalFilePath(android::base::StringPrintf("%s_%s", cdev_name.data(), "w"));
if (!android::base::WriteStringToFile(data.data(), file_path)) {
PLOG(WARNING) << "Failed to write cdev: " << cdev_name << " to " << data.data();
return false;
}
return true;
}
} // namespace implementation } // namespace implementation
} // namespace V2_0 } // namespace V2_0
} // namespace thermal } // namespace thermal

View File

@ -14,8 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
#ifndef THERMAL_UTILS_THERMAL_FILES_H_ #pragma once
#define THERMAL_UTILS_THERMAL_FILES_H_
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
@ -40,6 +39,7 @@ class ThermalFiles {
// data to empty and return false. If the thermal_name is found and its content // data to empty and return false. If the thermal_name is found and its content
// is read, this function will fill in data accordingly then return true. // is read, this function will fill in data accordingly then return true.
bool readThermalFile(std::string_view thermal_name, std::string *data) const; bool readThermalFile(std::string_view thermal_name, std::string *data) const;
bool writeCdevFile(std::string_view thermal_name, std::string_view data);
size_t getNumThermalFiles() const { return thermal_name_to_path_map_.size(); } size_t getNumThermalFiles() const { return thermal_name_to_path_map_.size(); }
private: private:
@ -51,5 +51,3 @@ class ThermalFiles {
} // namespace thermal } // namespace thermal
} // namespace hardware } // namespace hardware
} // namespace android } // namespace android
#endif // THERMAL_UTILS_THERMAL_FILES_H_

View File

@ -15,6 +15,11 @@
*/ */
#include <cutils/uevent.h> #include <cutils/uevent.h>
#include <dirent.h> #include <dirent.h>
#include <linux/genetlink.h>
#include <linux/netlink.h>
#include <linux/thermal.h>
#include <netlink/genl/ctrl.h>
#include <netlink/genl/genl.h>
#include <sys/inotify.h> #include <sys/inotify.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <sys/types.h> #include <sys/types.h>
@ -23,8 +28,10 @@
#include <android-base/file.h> #include <android-base/file.h>
#include <android-base/logging.h> #include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h> #include <android-base/strings.h>
#include "thermal-helper.h"
#include "thermal_watcher.h" #include "thermal_watcher.h"
namespace android { namespace android {
@ -33,27 +40,360 @@ namespace thermal {
namespace V2_0 { namespace V2_0 {
namespace implementation { namespace implementation {
using std::chrono_literals::operator""ms; namespace {
void ThermalWatcher::registerFilesToWatch(const std::set<std::string> &sensors_to_watch, static int nlErrorHandle(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) {
bool uevent_monitor) { int *ret = reinterpret_cast<int *>(arg);
monitored_sensors_.insert(sensors_to_watch.begin(), sensors_to_watch.end()); *ret = err->error;
if (!uevent_monitor) { LOG(ERROR) << __func__ << "nl_groups: " << nla->nl_groups << ", nl_pid: " << nla->nl_pid;
is_polling_ = true;
return; return NL_STOP;
}
static int nlFinishHandle(struct nl_msg *msg, void *arg) {
int *ret = reinterpret_cast<int *>(arg);
*ret = 1;
struct nlmsghdr *nlh = nlmsg_hdr(msg);
LOG(VERBOSE) << __func__ << ": nlmsg type: " << nlh->nlmsg_type;
return NL_OK;
}
static int nlAckHandle(struct nl_msg *msg, void *arg) {
int *ret = reinterpret_cast<int *>(arg);
*ret = 1;
struct nlmsghdr *nlh = nlmsg_hdr(msg);
LOG(VERBOSE) << __func__ << ": nlmsg type: " << nlh->nlmsg_type;
return NL_OK;
}
static int nlSeqCheckHandle(struct nl_msg *msg, void *arg) {
int *ret = reinterpret_cast<int *>(arg);
*ret = 1;
struct nlmsghdr *nlh = nlmsg_hdr(msg);
LOG(VERBOSE) << __func__ << ": nlmsg type: " << nlh->nlmsg_type;
return NL_OK;
}
struct HandlerArgs {
const char *group;
int id;
};
static int nlSendMsg(struct nl_sock *sock, struct nl_msg *msg,
int (*rx_handler)(struct nl_msg *, void *), void *data) {
int err, done = 0;
std::unique_ptr<nl_cb, decltype(&nl_cb_put)> cb(nl_cb_alloc(NL_CB_DEFAULT), nl_cb_put);
err = nl_send_auto_complete(sock, msg);
if (err < 0)
return err;
err = 0;
nl_cb_err(cb.get(), NL_CB_CUSTOM, nlErrorHandle, &err);
nl_cb_set(cb.get(), NL_CB_FINISH, NL_CB_CUSTOM, nlFinishHandle, &done);
nl_cb_set(cb.get(), NL_CB_ACK, NL_CB_CUSTOM, nlAckHandle, &done);
if (rx_handler != NULL)
nl_cb_set(cb.get(), NL_CB_VALID, NL_CB_CUSTOM, rx_handler, data);
while (err == 0 && done == 0) nl_recvmsgs(sock, cb.get());
return err;
}
static int nlFamilyHandle(struct nl_msg *msg, void *arg) {
struct HandlerArgs *grp = reinterpret_cast<struct HandlerArgs *>(arg);
struct nlattr *tb[CTRL_ATTR_MAX + 1];
struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg));
struct nlattr *mcgrp;
int rem_mcgrp;
nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);
if (!tb[CTRL_ATTR_MCAST_GROUPS]) {
LOG(ERROR) << __func__ << "Multicast group not found";
return -1;
} }
nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp) {
struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1];
nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX, reinterpret_cast<nlattr *>(nla_data(mcgrp)),
nla_len(mcgrp), NULL);
if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME] || !tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID])
continue;
if (strncmp(reinterpret_cast<char *>(nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME])),
grp->group, nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME])) != 0)
continue;
grp->id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]);
break;
}
return 0;
}
static int nlGetMulticastId(struct nl_sock *sock, const char *family, const char *group) {
int err = 0, ctrlid;
struct HandlerArgs grp = {
.group = group,
.id = -ENOENT,
};
std::unique_ptr<nl_msg, decltype(&nlmsg_free)> msg(nlmsg_alloc(), nlmsg_free);
ctrlid = genl_ctrl_resolve(sock, "nlctrl");
genlmsg_put(msg.get(), 0, 0, ctrlid, 0, 0, CTRL_CMD_GETFAMILY, 0);
nla_put_string(msg.get(), CTRL_ATTR_FAMILY_NAME, family);
err = nlSendMsg(sock, msg.get(), nlFamilyHandle, &grp);
if (err)
return err;
err = grp.id;
LOG(INFO) << group << " multicast_id: " << grp.id;
return err;
}
static bool socketAddMembership(struct nl_sock *sock, const char *group) {
int mcid = nlGetMulticastId(sock, THERMAL_GENL_FAMILY_NAME, group);
if (mcid < 0) {
LOG(ERROR) << "Failed to get multicast id: " << group;
return false;
}
if (nl_socket_add_membership(sock, mcid)) {
LOG(ERROR) << "Failed to add netlink socket membership: " << group;
return false;
}
LOG(INFO) << "Added netlink socket membership: " << group;
return true;
}
static int handleEvent(struct nl_msg *n, void *arg) {
struct nlmsghdr *nlh = nlmsg_hdr(n);
struct genlmsghdr *glh = genlmsg_hdr(nlh);
struct nlattr *attrs[THERMAL_GENL_ATTR_MAX + 1];
int *tz_id = reinterpret_cast<int *>(arg);
genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL);
if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_UP) {
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_UP";
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
}
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID])
LOG(INFO) << "Thermal zone trip id: "
<< nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]);
}
if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_DOWN) {
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_DOWN";
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
}
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID])
LOG(INFO) << "Thermal zone trip id: "
<< nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]);
}
if (glh->cmd == THERMAL_GENL_EVENT_TZ_GOV_CHANGE) {
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_GOV_CHANGE";
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
}
if (attrs[THERMAL_GENL_ATTR_GOV_NAME])
LOG(INFO) << "Governor name: " << nla_get_string(attrs[THERMAL_GENL_ATTR_GOV_NAME]);
}
if (glh->cmd == THERMAL_GENL_EVENT_TZ_CREATE) {
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_CREATE";
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
}
if (attrs[THERMAL_GENL_ATTR_TZ_NAME])
LOG(INFO) << "Thermal zone name: " << nla_get_string(attrs[THERMAL_GENL_ATTR_TZ_NAME]);
}
if (glh->cmd == THERMAL_GENL_EVENT_TZ_DELETE) {
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_DELETE";
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
}
}
if (glh->cmd == THERMAL_GENL_EVENT_TZ_DISABLE) {
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_DISABLE";
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
}
}
if (glh->cmd == THERMAL_GENL_EVENT_TZ_ENABLE) {
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_ENABLE";
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
}
}
if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_CHANGE) {
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_CHANGE";
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
}
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID])
LOG(INFO) << "Trip id:: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]);
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE])
LOG(INFO) << "Trip type: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]);
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP])
LOG(INFO) << "Trip temp: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]);
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST])
LOG(INFO) << "Trip hyst: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]);
}
if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_ADD) {
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_ADD";
if (attrs[THERMAL_GENL_ATTR_TZ_ID])
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID])
LOG(INFO) << "Trip id:: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]);
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE])
LOG(INFO) << "Trip type: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]);
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP])
LOG(INFO) << "Trip temp: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]);
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST])
LOG(INFO) << "Trip hyst: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]);
}
if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_DELETE) {
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_DELETE";
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
}
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID])
LOG(INFO) << "Trip id:: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]);
}
if (glh->cmd == THERMAL_GENL_EVENT_CDEV_STATE_UPDATE) {
LOG(INFO) << "THERMAL_GENL_EVENT_CDEV_STATE_UPDATE";
if (attrs[THERMAL_GENL_ATTR_CDEV_ID])
LOG(INFO) << "Cooling device id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]);
if (attrs[THERMAL_GENL_ATTR_CDEV_CUR_STATE])
LOG(INFO) << "Cooling device current state: "
<< nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_CUR_STATE]);
}
if (glh->cmd == THERMAL_GENL_EVENT_CDEV_ADD) {
LOG(INFO) << "THERMAL_GENL_EVENT_CDEV_ADD";
if (attrs[THERMAL_GENL_ATTR_CDEV_NAME])
LOG(INFO) << "Cooling device name: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_NAME]);
if (attrs[THERMAL_GENL_ATTR_CDEV_ID])
LOG(INFO) << "Cooling device id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]);
if (attrs[THERMAL_GENL_ATTR_CDEV_MAX_STATE])
LOG(INFO) << "Cooling device max state: "
<< nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_MAX_STATE]);
}
if (glh->cmd == THERMAL_GENL_EVENT_CDEV_DELETE) {
LOG(INFO) << "THERMAL_GENL_EVENT_CDEV_DELETE";
if (attrs[THERMAL_GENL_ATTR_CDEV_ID])
LOG(INFO) << "Cooling device id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]);
}
if (glh->cmd == THERMAL_GENL_SAMPLING_TEMP) {
LOG(INFO) << "THERMAL_GENL_SAMPLING_TEMP";
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
}
if (attrs[THERMAL_GENL_ATTR_TZ_TEMP])
LOG(INFO) << "Thermal zone temp: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]);
}
return 0;
}
} // namespace
void ThermalWatcher::registerFilesToWatch(const std::set<std::string> &sensors_to_watch) {
LOG(INFO) << "Uevent register file to watch...";
monitored_sensors_.insert(sensors_to_watch.begin(), sensors_to_watch.end());
uevent_fd_.reset((TEMP_FAILURE_RETRY(uevent_open_socket(64 * 1024, true)))); uevent_fd_.reset((TEMP_FAILURE_RETRY(uevent_open_socket(64 * 1024, true))));
if (uevent_fd_.get() < 0) { if (uevent_fd_.get() < 0) {
LOG(ERROR) << "failed to open uevent socket"; LOG(ERROR) << "failed to open uevent socket";
is_polling_ = true;
return; return;
} }
fcntl(uevent_fd_, F_SETFL, O_NONBLOCK); fcntl(uevent_fd_, F_SETFL, O_NONBLOCK);
looper_->addFd(uevent_fd_.get(), 0, Looper::EVENT_INPUT, nullptr, nullptr); looper_->addFd(uevent_fd_.get(), 0, Looper::EVENT_INPUT, nullptr, nullptr);
is_polling_ = false; sleep_ms_ = std::chrono::milliseconds(0);
thermal_triggered_ = true; last_update_time_ = boot_clock::now();
}
void ThermalWatcher::registerFilesToWatchNl(const std::set<std::string> &sensors_to_watch) {
LOG(INFO) << "Thermal genl register file to watch...";
monitored_sensors_.insert(sensors_to_watch.begin(), sensors_to_watch.end());
sk_thermal = nl_socket_alloc();
if (!sk_thermal) {
LOG(ERROR) << "nl_socket_alloc failed";
return;
}
if (genl_connect(sk_thermal)) {
LOG(ERROR) << "genl_connect failed: sk_thermal";
return;
}
thermal_genl_fd_.reset(nl_socket_get_fd(sk_thermal));
if (thermal_genl_fd_.get() < 0) {
LOG(ERROR) << "Failed to create thermal netlink socket";
return;
}
if (!socketAddMembership(sk_thermal, THERMAL_GENL_EVENT_GROUP_NAME)) {
return;
}
/*
* Currently, only the update_temperature() will send thermal genl samlping events
* from kernel. To avoid thermal-hal busy because samlping events are sent
* too frequently, ignore thermal genl samlping events until we figure out how to use it.
*
if (!socketAddMembership(sk_thermal, THERMAL_GENL_SAMPLING_GROUP_NAME)) {
return;
}
*/
fcntl(thermal_genl_fd_, F_SETFL, O_NONBLOCK);
looper_->addFd(thermal_genl_fd_.get(), 0, Looper::EVENT_INPUT, nullptr, nullptr);
sleep_ms_ = std::chrono::milliseconds(0);
last_update_time_ = boot_clock::now(); last_update_time_ = boot_clock::now();
} }
@ -96,9 +436,10 @@ void ThermalWatcher::parseUevent(std::set<std::string> *sensors_set) {
cp = msg; cp = msg;
while (*cp) { while (*cp) {
std::string uevent = cp; std::string uevent = cp;
auto findSubSystemThermal = uevent.find("SUBSYSTEM=thermal");
if (!thermal_event) { if (!thermal_event) {
if (uevent.find("SUBSYSTEM=") == 0) { if (!uevent.find("SUBSYSTEM=")) {
if (uevent.find("SUBSYSTEM=thermal") != std::string::npos) { if (findSubSystemThermal != std::string::npos) {
thermal_event = true; thermal_event = true;
} else { } else {
break; break;
@ -122,34 +463,64 @@ void ThermalWatcher::parseUevent(std::set<std::string> *sensors_set) {
} }
} }
// TODO(b/175367921): Consider for potentially adding more type of event in the function
// instead of just add the sensors to the list.
void ThermalWatcher::parseGenlink(std::set<std::string> *sensors_set) {
int err = 0, done = 0, tz_id = -1;
std::unique_ptr<nl_cb, decltype(&nl_cb_put)> cb(nl_cb_alloc(NL_CB_DEFAULT), nl_cb_put);
nl_cb_err(cb.get(), NL_CB_CUSTOM, nlErrorHandle, &err);
nl_cb_set(cb.get(), NL_CB_FINISH, NL_CB_CUSTOM, nlFinishHandle, &done);
nl_cb_set(cb.get(), NL_CB_ACK, NL_CB_CUSTOM, nlAckHandle, &done);
nl_cb_set(cb.get(), NL_CB_SEQ_CHECK, NL_CB_CUSTOM, nlSeqCheckHandle, &done);
nl_cb_set(cb.get(), NL_CB_VALID, NL_CB_CUSTOM, handleEvent, &tz_id);
while (!done && !err) {
nl_recvmsgs(sk_thermal, cb.get());
if (tz_id < 0) {
break;
}
std::string name;
if (getThermalZoneTypeById(tz_id, &name) &&
std::find(monitored_sensors_.begin(), monitored_sensors_.end(), name) !=
monitored_sensors_.end()) {
sensors_set->insert(name);
}
}
}
void ThermalWatcher::wake() { void ThermalWatcher::wake() {
looper_->wake(); looper_->wake();
} }
bool ThermalWatcher::threadLoop() { bool ThermalWatcher::threadLoop() {
LOG(VERBOSE) << "ThermalWatcher polling..."; LOG(VERBOSE) << "ThermalWatcher polling...";
// Polling interval 2s
static constexpr int kMinPollIntervalMs = 2000;
// Max uevent timeout 5mins
static constexpr int kUeventPollTimeoutMs = 300000;
int fd; int fd;
std::set<std::string> sensors; std::set<std::string> sensors;
auto time_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - auto time_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() -
last_update_time_) last_update_time_);
.count();
int timeout = (thermal_triggered_ || is_polling_) ? kMinPollIntervalMs : kUeventPollTimeoutMs; if (time_elapsed_ms < sleep_ms_ &&
if (time_elapsed_ms < timeout && looper_->pollOnce(timeout, &fd, nullptr, nullptr) >= 0) { looper_->pollOnce(sleep_ms_.count(), &fd, nullptr, nullptr) >= 0) {
if (fd != uevent_fd_.get()) { if (fd != uevent_fd_.get() && fd != thermal_genl_fd_.get()) {
return true; return true;
} } else if (fd == thermal_genl_fd_.get()) {
parseGenlink(&sensors);
} else if (fd == uevent_fd_.get()) {
parseUevent(&sensors); parseUevent(&sensors);
}
// Ignore cb_ if uevent is not from monitored sensors // Ignore cb_ if uevent is not from monitored sensors
if (sensors.size() == 0) { if (sensors.size() == 0) {
return true; return true;
} }
} }
thermal_triggered_ = cb_(sensors);
sleep_ms_ = cb_(sensors);
last_update_time_ = boot_clock::now(); last_update_time_ = boot_clock::now();
return true; return true;
} }

View File

@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#ifndef THERMAL_UTILS_THERMAL_WATCHER_H_
#define THERMAL_UTILS_THERMAL_WATCHER_H_ #pragma once
#include <chrono> #include <chrono>
#include <condition_variable> #include <condition_variable>
@ -40,12 +40,12 @@ namespace implementation {
using android::base::boot_clock; using android::base::boot_clock;
using android::base::unique_fd; using android::base::unique_fd;
using WatcherCallback = std::function<bool(const std::set<std::string> &name)>; using WatcherCallback = std::function<std::chrono::milliseconds(const std::set<std::string> &name)>;
// A helper class for monitoring thermal files changes. // A helper class for monitoring thermal files changes.
class ThermalWatcher : public ::android::Thread { class ThermalWatcher : public ::android::Thread {
public: public:
ThermalWatcher(const WatcherCallback &cb) explicit ThermalWatcher(const WatcherCallback &cb)
: Thread(false), cb_(cb), looper_(new Looper(true)) {} : Thread(false), cb_(cb), looper_(new Looper(true)) {}
~ThermalWatcher() = default; ~ThermalWatcher() = default;
@ -58,7 +58,10 @@ class ThermalWatcher : public ::android::Thread {
// Give the file watcher a list of files to start watching. This helper // Give the file watcher a list of files to start watching. This helper
// class will by default wait for modifications to the file with a looper. // class will by default wait for modifications to the file with a looper.
// This should be called before starting watcher thread. // This should be called before starting watcher thread.
void registerFilesToWatch(const std::set<std::string> &sensors_to_watch, bool uevent_monitor); // For monitoring uevents.
void registerFilesToWatch(const std::set<std::string> &sensors_to_watch);
// For monitoring thermal genl events.
void registerFilesToWatchNl(const std::set<std::string> &sensors_to_watch);
// Wake up the looper thus the worker thread, immediately. This can be called // Wake up the looper thus the worker thread, immediately. This can be called
// in any thread. // in any thread.
void wake(); void wake();
@ -73,6 +76,9 @@ class ThermalWatcher : public ::android::Thread {
// Parse uevent message // Parse uevent message
void parseUevent(std::set<std::string> *sensor_name); void parseUevent(std::set<std::string> *sensor_name);
// Parse thermal netlink message
void parseGenlink(std::set<std::string> *sensor_name);
// Maps watcher filer descriptor to watched file path. // Maps watcher filer descriptor to watched file path.
std::unordered_map<int, std::string> watch_to_file_path_map_; std::unordered_map<int, std::string> watch_to_file_path_map_;
@ -86,14 +92,16 @@ class ThermalWatcher : public ::android::Thread {
// For uevent socket registration. // For uevent socket registration.
android::base::unique_fd uevent_fd_; android::base::unique_fd uevent_fd_;
// For thermal genl socket registration.
android::base::unique_fd thermal_genl_fd_;
// Sensor list which monitor flag is enabled. // Sensor list which monitor flag is enabled.
std::set<std::string> monitored_sensors_; std::set<std::string> monitored_sensors_;
// Flag to point out if any sensor across the first threshold. // Sleep interval voting result
bool thermal_triggered_; std::chrono::milliseconds sleep_ms_;
// Flag to point out if device can support uevent notify.
bool is_polling_;
// Timestamp for last thermal update // Timestamp for last thermal update
boot_clock::time_point last_update_time_; boot_clock::time_point last_update_time_;
// For thermal genl socket object.
struct nl_sock *sk_thermal;
}; };
} // namespace implementation } // namespace implementation
@ -101,5 +109,3 @@ class ThermalWatcher : public ::android::Thread {
} // namespace thermal } // namespace thermal
} // namespace hardware } // namespace hardware
} // namespace android } // namespace android
#endif // THERMAL_UTILS_THERMAL_WATCHER_H_

View File

@ -0,0 +1,11 @@
on early-boot
mkdir /dev/thermal 0750 system system
mkdir /dev/thermal/tz-by-name 0750 system system
mkdir /dev/thermal/cdev-by-name 0750 system system
start vendor.thermal.symlinks
service vendor.thermal.symlinks /vendor/bin/thermal_symlinks
class hal
user system
group system
oneshot