| Chris Ye | 48dbcaa | 2020-02-10 13:29:01 -0800 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright (C) 2020 The Android Open Source Project | 
 | 3 |  * | 
 | 4 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 | 5 |  * you may not use this file except in compliance with the License. | 
 | 6 |  * You may obtain a copy of the License at | 
 | 7 |  * | 
 | 8 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 | 9 |  * | 
 | 10 |  * Unless required by applicable law or agreed to in writing, software | 
 | 11 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 | 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | 13 |  * See the License for the specific language governing permissions and | 
 | 14 |  * limitations under the License. | 
 | 15 |  */ | 
 | 16 |  | 
 | 17 | #define LOG_TAG "thermal" | 
 | 18 |  | 
 | 19 | #include <cerrno> | 
 | 20 | #include <thread> | 
| Chris Forbes | 1900bef | 2020-09-15 09:39:18 -0700 | [diff] [blame] | 21 | #include <limits> | 
| Chris Ye | 48dbcaa | 2020-02-10 13:29:01 -0800 | [diff] [blame] | 22 |  | 
 | 23 | #include <android/thermal.h> | 
 | 24 | #include <android/os/BnThermalStatusListener.h> | 
 | 25 | #include <android/os/IThermalService.h> | 
 | 26 | #include <binder/IServiceManager.h> | 
 | 27 | #include <utils/Log.h> | 
 | 28 |  | 
 | 29 | using android::sp; | 
 | 30 |  | 
 | 31 | using namespace android; | 
 | 32 | using namespace android::os; | 
 | 33 |  | 
 | 34 | struct ThermalServiceListener : public BnThermalStatusListener { | 
 | 35 |     public: | 
 | 36 |         virtual binder::Status onStatusChange(int32_t status) override; | 
 | 37 |         ThermalServiceListener(AThermalManager *manager) {mMgr = manager;} | 
 | 38 |     private: | 
 | 39 |         AThermalManager *mMgr; | 
 | 40 | }; | 
 | 41 |  | 
 | 42 | struct ListenerCallback { | 
 | 43 |     AThermal_StatusCallback callback; | 
 | 44 |     void* data; | 
 | 45 | }; | 
 | 46 |  | 
 | 47 | struct AThermalManager { | 
 | 48 |    public: | 
 | 49 |         static AThermalManager* createAThermalManager(); | 
 | 50 |         AThermalManager() = delete; | 
 | 51 |         ~AThermalManager(); | 
 | 52 |         status_t notifyStateChange(int32_t status); | 
 | 53 |         status_t getCurrentThermalStatus(int32_t *status); | 
 | 54 |         status_t addListener(AThermal_StatusCallback, void *data); | 
 | 55 |         status_t removeListener(AThermal_StatusCallback, void *data); | 
| Chris Forbes | 1900bef | 2020-09-15 09:39:18 -0700 | [diff] [blame] | 56 |         status_t getThermalHeadroom(int32_t forecastSeconds, float *result); | 
| Chris Ye | 48dbcaa | 2020-02-10 13:29:01 -0800 | [diff] [blame] | 57 |    private: | 
 | 58 |        AThermalManager(sp<IThermalService> service); | 
 | 59 |        sp<IThermalService> mThermalSvc; | 
 | 60 |        sp<ThermalServiceListener> mServiceListener; | 
 | 61 |        std::vector<ListenerCallback> mListeners; | 
 | 62 |        std::mutex mMutex; | 
 | 63 | }; | 
 | 64 |  | 
 | 65 | binder::Status ThermalServiceListener::onStatusChange(int32_t status) { | 
 | 66 |     if (mMgr != nullptr) { | 
 | 67 |         mMgr->notifyStateChange(status); | 
 | 68 |     } | 
 | 69 |     return binder::Status::ok(); | 
 | 70 | } | 
 | 71 |  | 
 | 72 | AThermalManager* AThermalManager::createAThermalManager() { | 
 | 73 |     sp<IBinder> binder = | 
 | 74 |             defaultServiceManager()->checkService(String16("thermalservice")); | 
 | 75 |  | 
 | 76 |     if (binder == nullptr) { | 
 | 77 |         ALOGE("%s: Thermal service is not ready ", __FUNCTION__); | 
 | 78 |         return nullptr; | 
 | 79 |     } | 
 | 80 |     return new AThermalManager(interface_cast<IThermalService>(binder)); | 
 | 81 | } | 
 | 82 |  | 
 | 83 | AThermalManager::AThermalManager(sp<IThermalService> service) | 
 | 84 |     : mThermalSvc(service), | 
 | 85 |       mServiceListener(nullptr) { | 
 | 86 | } | 
 | 87 |  | 
 | 88 | AThermalManager::~AThermalManager() { | 
 | 89 |     std::unique_lock<std::mutex> lock(mMutex); | 
 | 90 |  | 
 | 91 |     mListeners.clear(); | 
 | 92 |     if (mServiceListener != nullptr) { | 
 | 93 |         bool success = false; | 
 | 94 |         mThermalSvc->unregisterThermalStatusListener(mServiceListener, &success); | 
 | 95 |         mServiceListener = nullptr; | 
 | 96 |     } | 
 | 97 | } | 
 | 98 |  | 
 | 99 | status_t AThermalManager::notifyStateChange(int32_t status) { | 
 | 100 |     std::unique_lock<std::mutex> lock(mMutex); | 
 | 101 |     AThermalStatus thermalStatus = static_cast<AThermalStatus>(status); | 
 | 102 |  | 
 | 103 |     for (auto listener : mListeners) { | 
 | 104 |         listener.callback(listener.data, thermalStatus); | 
 | 105 |     } | 
 | 106 |     return OK; | 
 | 107 | } | 
 | 108 |  | 
 | 109 | status_t AThermalManager::addListener(AThermal_StatusCallback callback, void *data) { | 
 | 110 |     std::unique_lock<std::mutex> lock(mMutex); | 
 | 111 |  | 
 | 112 |     if (callback == nullptr) { | 
 | 113 |         // Callback can not be nullptr | 
 | 114 |         return EINVAL; | 
 | 115 |     } | 
 | 116 |     for (const auto& cb : mListeners) { | 
 | 117 |         // Don't re-add callbacks. | 
 | 118 |         if (callback == cb.callback && data == cb.data) { | 
 | 119 |             return EINVAL; | 
 | 120 |         } | 
 | 121 |     } | 
 | 122 |     mListeners.emplace_back(ListenerCallback{callback, data}); | 
 | 123 |  | 
 | 124 |     if (mServiceListener != nullptr) { | 
 | 125 |         return OK; | 
 | 126 |     } | 
 | 127 |     bool success = false; | 
 | 128 |     mServiceListener = new ThermalServiceListener(this); | 
 | 129 |     if (mServiceListener == nullptr) { | 
 | 130 |         return ENOMEM; | 
 | 131 |     } | 
 | 132 |     auto ret = mThermalSvc->registerThermalStatusListener(mServiceListener, &success); | 
 | 133 |     if (!success || !ret.isOk()) { | 
 | 134 |         ALOGE("Failed in registerThermalStatusListener %d", success); | 
 | 135 |         if (ret.exceptionCode() == binder::Status::EX_SECURITY) { | 
 | 136 |             return EPERM; | 
 | 137 |         } | 
 | 138 |         return EPIPE; | 
 | 139 |     } | 
 | 140 |     return OK; | 
 | 141 | } | 
 | 142 |  | 
 | 143 | status_t AThermalManager::removeListener(AThermal_StatusCallback callback, void *data) { | 
 | 144 |     std::unique_lock<std::mutex> lock(mMutex); | 
 | 145 |  | 
 | 146 |     auto it = std::remove_if(mListeners.begin(), | 
 | 147 |                              mListeners.end(), | 
 | 148 |                              [&](const ListenerCallback& cb) { | 
 | 149 |                                     return callback == cb.callback && | 
 | 150 |                                            data == cb.data; | 
 | 151 |                              }); | 
 | 152 |     if (it == mListeners.end()) { | 
 | 153 |         // If the listener and data pointer were not previously added. | 
 | 154 |         return EINVAL; | 
 | 155 |     } | 
 | 156 |     mListeners.erase(it, mListeners.end()); | 
 | 157 |  | 
 | 158 |     if (!mListeners.empty()) { | 
 | 159 |         return OK; | 
 | 160 |     } | 
 | 161 |     if (mServiceListener == nullptr) { | 
 | 162 |         return OK; | 
 | 163 |     } | 
 | 164 |     bool success = false; | 
 | 165 |     auto ret = mThermalSvc->unregisterThermalStatusListener(mServiceListener, &success); | 
 | 166 |     if (!success || !ret.isOk()) { | 
 | 167 |         ALOGE("Failed in unregisterThermalStatusListener %d", success); | 
 | 168 |         if (ret.exceptionCode() == binder::Status::EX_SECURITY) { | 
 | 169 |             return EPERM; | 
 | 170 |         } | 
 | 171 |         return EPIPE; | 
 | 172 |     } | 
 | 173 |     mServiceListener = nullptr; | 
 | 174 |     return OK; | 
 | 175 | } | 
 | 176 |  | 
 | 177 | status_t AThermalManager::getCurrentThermalStatus(int32_t *status) { | 
 | 178 |     binder::Status ret = mThermalSvc->getCurrentThermalStatus(status); | 
 | 179 |  | 
 | 180 |     if (!ret.isOk()) { | 
 | 181 |         if (ret.exceptionCode() == binder::Status::EX_SECURITY) { | 
 | 182 |             return EPERM; | 
 | 183 |         } | 
 | 184 |         return EPIPE; | 
 | 185 |     } | 
 | 186 |     return OK; | 
 | 187 | } | 
 | 188 |  | 
| Chris Forbes | 1900bef | 2020-09-15 09:39:18 -0700 | [diff] [blame] | 189 | status_t AThermalManager::getThermalHeadroom(int32_t forecastSeconds, float *result) { | 
 | 190 |     binder::Status ret = mThermalSvc->getThermalHeadroom(forecastSeconds, result); | 
 | 191 |  | 
 | 192 |     if (!ret.isOk()) { | 
 | 193 |         if (ret.exceptionCode() == binder::Status::EX_SECURITY) { | 
 | 194 |             return EPERM; | 
 | 195 |         } | 
 | 196 |         return EPIPE; | 
 | 197 |     } | 
 | 198 |     return OK; | 
 | 199 | } | 
 | 200 |  | 
| Chris Ye | 48dbcaa | 2020-02-10 13:29:01 -0800 | [diff] [blame] | 201 | /** | 
 | 202 |   * Acquire an instance of the thermal manager. This must be freed using | 
 | 203 |   * {@link AThermal_releaseManager}. | 
 | 204 |   * | 
 | 205 |   * @return manager instance on success, nullptr on failure. | 
 | 206 |  */ | 
 | 207 | AThermalManager* AThermal_acquireManager() { | 
 | 208 |     auto manager = AThermalManager::createAThermalManager(); | 
 | 209 |  | 
 | 210 |     return manager; | 
 | 211 | } | 
 | 212 |  | 
 | 213 | /** | 
 | 214 |  * Release the thermal manager pointer acquired by | 
 | 215 |  * {@link AThermal_acquireManager}. | 
 | 216 |  * | 
 | 217 |  * @param manager The manager to be released. | 
 | 218 |  * | 
 | 219 |  */ | 
 | 220 | void AThermal_releaseManager(AThermalManager *manager) { | 
 | 221 |     delete manager; | 
 | 222 | } | 
 | 223 |  | 
 | 224 | /** | 
 | 225 |   * Gets the current thermal status. | 
 | 226 |   * | 
 | 227 |   * @param manager The manager instance to use to query the thermal status, | 
 | 228 |   * acquired by {@link AThermal_acquireManager}. | 
 | 229 |   * | 
 | 230 |   * @return current thermal status, ATHERMAL_STATUS_ERROR on failure. | 
 | 231 | */ | 
 | 232 | AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager) { | 
 | 233 |     int32_t status = 0; | 
 | 234 |     status_t ret = manager->getCurrentThermalStatus(&status); | 
 | 235 |     if (ret != OK) { | 
 | 236 |         return AThermalStatus::ATHERMAL_STATUS_ERROR; | 
 | 237 |     } | 
 | 238 |     return static_cast<AThermalStatus>(status); | 
 | 239 | } | 
 | 240 |  | 
 | 241 | /** | 
 | 242 |  * Register the thermal status listener for thermal status change. | 
 | 243 |  * | 
 | 244 |  * @param manager The manager instance to use to register. | 
 | 245 |  * acquired by {@link AThermal_acquireManager}. | 
 | 246 |  * @param callback The callback function to be called when thermal status updated. | 
 | 247 |  * @param data The data pointer to be passed when callback is called. | 
 | 248 |  * | 
 | 249 |  * @return 0 on success | 
 | 250 |  *         EINVAL if the listener and data pointer were previously added and not removed. | 
 | 251 |  *         EPERM if the required permission is not held. | 
 | 252 |  *         EPIPE if communication with the system service has failed. | 
 | 253 |  */ | 
 | 254 | int AThermal_registerThermalStatusListener(AThermalManager *manager, | 
 | 255 |         AThermal_StatusCallback callback, void *data) { | 
 | 256 |     return manager->addListener(callback, data); | 
 | 257 | } | 
 | 258 |  | 
 | 259 | /** | 
 | 260 |  * Unregister the thermal status listener previously resgistered. | 
 | 261 |  * | 
 | 262 |  * @param manager The manager instance to use to unregister. | 
 | 263 |  * acquired by {@link AThermal_acquireManager}. | 
 | 264 |  * @param callback The callback function to be called when thermal status updated. | 
 | 265 |  * @param data The data pointer to be passed when callback is called. | 
 | 266 |  * | 
 | 267 |  * @return 0 on success | 
 | 268 |  *         EINVAL if the listener and data pointer were not previously added. | 
 | 269 |  *         EPERM if the required permission is not held. | 
 | 270 |  *         EPIPE if communication with the system service has failed. | 
 | 271 |  */ | 
 | 272 | int AThermal_unregisterThermalStatusListener(AThermalManager *manager, | 
 | 273 |         AThermal_StatusCallback callback, void *data) { | 
 | 274 |     return manager->removeListener(callback, data); | 
 | 275 | } | 
| Chris Forbes | 1900bef | 2020-09-15 09:39:18 -0700 | [diff] [blame] | 276 |  | 
 | 277 | /** | 
 | 278 |  * Provides an estimate of how much thermal headroom the device currently has | 
 | 279 |  * before hitting severe throttling. | 
 | 280 |  * | 
 | 281 |  * Note that this only attempts to track the headroom of slow-moving sensors, | 
 | 282 |  * such as the skin temperature sensor. This means that there is no benefit to | 
 | 283 |  * calling this function more frequently than about once per second, and attempts | 
 | 284 |  * to call significantly more frequently may result in the function returning {@code NaN}. | 
 | 285 |  * | 
 | 286 |  * See also PowerManager#getThermalHeadroom. | 
 | 287 |  * | 
 | 288 |  * @param manager The manager instance to use | 
 | 289 |  * @param forecastSeconds how many seconds in the future to forecast | 
 | 290 |  * @return a value greater than or equal to 0.0 where 1.0 indicates the SEVERE throttling | 
 | 291 |  *  	   threshold. Returns NaN if the device does not support this functionality or if | 
 | 292 |  * 	       this function is called significantly faster than once per second. | 
 | 293 |  */ | 
 | 294 | float AThermal_getThermalHeadroom(AThermalManager *manager, | 
 | 295 |         int forecastSeconds) { | 
 | 296 |     float result = 0.0f; | 
 | 297 |     status_t ret = manager->getThermalHeadroom(forecastSeconds, &result); | 
 | 298 |  | 
 | 299 |     if (ret != OK) { | 
 | 300 |         result = std::numeric_limits<float>::quiet_NaN(); | 
 | 301 |     } | 
 | 302 |  | 
 | 303 |     return result; | 
 | 304 | } |