Add native Thermal Throttling API to libandroid.
Add native thermal manager API of thermal mananger service into libandroid. Export Thermal API as NDK library. Bug: 137151587 Bug: 136285293 Test: build, atest thermalmanager-test atest CtsThermalTestCases Change-Id: Ia49fb2133624ffcd6168af804ae612ef2bb190f2
This commit is contained in:
12
Android.bp
12
Android.bp
@@ -453,6 +453,18 @@ filegroup {
|
||||
path: "core/java",
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "libpowermanager_aidl",
|
||||
srcs: [
|
||||
"core/java/android/os/Temperature.aidl",
|
||||
"core/java/android/os/CoolingDevice.aidl",
|
||||
"core/java/android/os/IThermalEventListener.aidl",
|
||||
"core/java/android/os/IThermalStatusListener.aidl",
|
||||
"core/java/android/os/IThermalService.aidl",
|
||||
],
|
||||
path: "core/java",
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "framework-minus-apex",
|
||||
defaults: ["framework-defaults"],
|
||||
|
||||
@@ -16,4 +16,4 @@
|
||||
|
||||
package android.os;
|
||||
|
||||
parcelable CoolingDevice;
|
||||
parcelable CoolingDevice cpp_header "android/CoolingDevice.h";
|
||||
|
||||
@@ -56,7 +56,7 @@ interface IThermalService {
|
||||
* @return list of {@link android.os.Temperature}.
|
||||
* {@hide}
|
||||
*/
|
||||
List<Temperature> getCurrentTemperatures();
|
||||
Temperature[] getCurrentTemperatures();
|
||||
|
||||
/**
|
||||
* Get current temperature with its throttling status on given temperature type.
|
||||
@@ -64,7 +64,7 @@ interface IThermalService {
|
||||
* @return list of {@link android.os.Temperature}.
|
||||
* {@hide}
|
||||
*/
|
||||
List<Temperature> getCurrentTemperaturesWithType(in int type);
|
||||
Temperature[] getCurrentTemperaturesWithType(in int type);
|
||||
|
||||
/**
|
||||
* Register a listener for thermal status change.
|
||||
@@ -94,7 +94,7 @@ interface IThermalService {
|
||||
* @return list of {@link android.os.CoolingDevice}.
|
||||
* {@hide}
|
||||
*/
|
||||
List<CoolingDevice> getCurrentCoolingDevices();
|
||||
CoolingDevice[] getCurrentCoolingDevices();
|
||||
|
||||
/**
|
||||
* Get current cooling devices on given type.
|
||||
@@ -102,7 +102,8 @@ interface IThermalService {
|
||||
* @return list of {@link android.os.CoolingDevice}.
|
||||
* {@hide}
|
||||
*/
|
||||
List<CoolingDevice> getCurrentCoolingDevicesWithType(in int type);
|
||||
|
||||
CoolingDevice[] getCurrentCoolingDevicesWithType(in int type);
|
||||
|
||||
/**
|
||||
* @param forecastSeconds how many seconds ahead to forecast the provided headroom
|
||||
|
||||
@@ -16,4 +16,4 @@
|
||||
|
||||
package android.os;
|
||||
|
||||
parcelable Temperature;
|
||||
parcelable Temperature cpp_header "android/Temperature.h";
|
||||
|
||||
@@ -51,6 +51,7 @@ cc_library_shared {
|
||||
"surface_control.cpp",
|
||||
"system_fonts.cpp",
|
||||
"trace.cpp",
|
||||
"thermal.cpp"
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
@@ -72,6 +73,7 @@ cc_library_shared {
|
||||
"libxml2",
|
||||
"libEGL",
|
||||
"libGLESv2",
|
||||
"libpowermanager",
|
||||
"android.hardware.configstore@1.0",
|
||||
"android.hardware.configstore-utils",
|
||||
],
|
||||
|
||||
@@ -280,6 +280,11 @@ LIBANDROID {
|
||||
android_res_nquery; # introduced=29
|
||||
android_res_nresult; # introduced=29
|
||||
android_res_nsend; # introduced=29
|
||||
AThermal_acquireManager; # introduced=30
|
||||
AThermal_releaseManager; # introduced=30
|
||||
AThermal_getCurrentThermalStatus; # introduced=30
|
||||
AThermal_registerThermalStatusListener; # introduced=30
|
||||
AThermal_unregisterThermalStatusListener; # introduced=30
|
||||
local:
|
||||
*;
|
||||
};
|
||||
|
||||
261
native/android/thermal.cpp
Normal file
261
native/android/thermal.cpp
Normal file
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "thermal"
|
||||
|
||||
#include <cerrno>
|
||||
#include <thread>
|
||||
|
||||
#include <android/thermal.h>
|
||||
#include <android/os/BnThermalStatusListener.h>
|
||||
#include <android/os/IThermalService.h>
|
||||
#include <binder/IServiceManager.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
using android::sp;
|
||||
|
||||
using namespace android;
|
||||
using namespace android::os;
|
||||
|
||||
struct ThermalServiceListener : public BnThermalStatusListener {
|
||||
public:
|
||||
virtual binder::Status onStatusChange(int32_t status) override;
|
||||
ThermalServiceListener(AThermalManager *manager) {mMgr = manager;}
|
||||
private:
|
||||
AThermalManager *mMgr;
|
||||
};
|
||||
|
||||
struct ListenerCallback {
|
||||
AThermal_StatusCallback callback;
|
||||
void* data;
|
||||
};
|
||||
|
||||
struct AThermalManager {
|
||||
public:
|
||||
static AThermalManager* createAThermalManager();
|
||||
AThermalManager() = delete;
|
||||
~AThermalManager();
|
||||
status_t notifyStateChange(int32_t status);
|
||||
status_t getCurrentThermalStatus(int32_t *status);
|
||||
status_t addListener(AThermal_StatusCallback, void *data);
|
||||
status_t removeListener(AThermal_StatusCallback, void *data);
|
||||
private:
|
||||
AThermalManager(sp<IThermalService> service);
|
||||
sp<IThermalService> mThermalSvc;
|
||||
sp<ThermalServiceListener> mServiceListener;
|
||||
std::vector<ListenerCallback> mListeners;
|
||||
std::mutex mMutex;
|
||||
};
|
||||
|
||||
binder::Status ThermalServiceListener::onStatusChange(int32_t status) {
|
||||
if (mMgr != nullptr) {
|
||||
mMgr->notifyStateChange(status);
|
||||
}
|
||||
return binder::Status::ok();
|
||||
}
|
||||
|
||||
AThermalManager* AThermalManager::createAThermalManager() {
|
||||
sp<IBinder> binder =
|
||||
defaultServiceManager()->checkService(String16("thermalservice"));
|
||||
|
||||
if (binder == nullptr) {
|
||||
ALOGE("%s: Thermal service is not ready ", __FUNCTION__);
|
||||
return nullptr;
|
||||
}
|
||||
return new AThermalManager(interface_cast<IThermalService>(binder));
|
||||
}
|
||||
|
||||
AThermalManager::AThermalManager(sp<IThermalService> service)
|
||||
: mThermalSvc(service),
|
||||
mServiceListener(nullptr) {
|
||||
}
|
||||
|
||||
AThermalManager::~AThermalManager() {
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
|
||||
mListeners.clear();
|
||||
if (mServiceListener != nullptr) {
|
||||
bool success = false;
|
||||
mThermalSvc->unregisterThermalStatusListener(mServiceListener, &success);
|
||||
mServiceListener = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
status_t AThermalManager::notifyStateChange(int32_t status) {
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
AThermalStatus thermalStatus = static_cast<AThermalStatus>(status);
|
||||
|
||||
for (auto listener : mListeners) {
|
||||
listener.callback(listener.data, thermalStatus);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t AThermalManager::addListener(AThermal_StatusCallback callback, void *data) {
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
|
||||
if (callback == nullptr) {
|
||||
// Callback can not be nullptr
|
||||
return EINVAL;
|
||||
}
|
||||
for (const auto& cb : mListeners) {
|
||||
// Don't re-add callbacks.
|
||||
if (callback == cb.callback && data == cb.data) {
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
mListeners.emplace_back(ListenerCallback{callback, data});
|
||||
|
||||
if (mServiceListener != nullptr) {
|
||||
return OK;
|
||||
}
|
||||
bool success = false;
|
||||
mServiceListener = new ThermalServiceListener(this);
|
||||
if (mServiceListener == nullptr) {
|
||||
return ENOMEM;
|
||||
}
|
||||
auto ret = mThermalSvc->registerThermalStatusListener(mServiceListener, &success);
|
||||
if (!success || !ret.isOk()) {
|
||||
ALOGE("Failed in registerThermalStatusListener %d", success);
|
||||
if (ret.exceptionCode() == binder::Status::EX_SECURITY) {
|
||||
return EPERM;
|
||||
}
|
||||
return EPIPE;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t AThermalManager::removeListener(AThermal_StatusCallback callback, void *data) {
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
|
||||
auto it = std::remove_if(mListeners.begin(),
|
||||
mListeners.end(),
|
||||
[&](const ListenerCallback& cb) {
|
||||
return callback == cb.callback &&
|
||||
data == cb.data;
|
||||
});
|
||||
if (it == mListeners.end()) {
|
||||
// If the listener and data pointer were not previously added.
|
||||
return EINVAL;
|
||||
}
|
||||
mListeners.erase(it, mListeners.end());
|
||||
|
||||
if (!mListeners.empty()) {
|
||||
return OK;
|
||||
}
|
||||
if (mServiceListener == nullptr) {
|
||||
return OK;
|
||||
}
|
||||
bool success = false;
|
||||
auto ret = mThermalSvc->unregisterThermalStatusListener(mServiceListener, &success);
|
||||
if (!success || !ret.isOk()) {
|
||||
ALOGE("Failed in unregisterThermalStatusListener %d", success);
|
||||
if (ret.exceptionCode() == binder::Status::EX_SECURITY) {
|
||||
return EPERM;
|
||||
}
|
||||
return EPIPE;
|
||||
}
|
||||
mServiceListener = nullptr;
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t AThermalManager::getCurrentThermalStatus(int32_t *status) {
|
||||
binder::Status ret = mThermalSvc->getCurrentThermalStatus(status);
|
||||
|
||||
if (!ret.isOk()) {
|
||||
if (ret.exceptionCode() == binder::Status::EX_SECURITY) {
|
||||
return EPERM;
|
||||
}
|
||||
return EPIPE;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire an instance of the thermal manager. This must be freed using
|
||||
* {@link AThermal_releaseManager}.
|
||||
*
|
||||
* @return manager instance on success, nullptr on failure.
|
||||
*/
|
||||
AThermalManager* AThermal_acquireManager() {
|
||||
auto manager = AThermalManager::createAThermalManager();
|
||||
|
||||
return manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the thermal manager pointer acquired by
|
||||
* {@link AThermal_acquireManager}.
|
||||
*
|
||||
* @param manager The manager to be released.
|
||||
*
|
||||
*/
|
||||
void AThermal_releaseManager(AThermalManager *manager) {
|
||||
delete manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current thermal status.
|
||||
*
|
||||
* @param manager The manager instance to use to query the thermal status,
|
||||
* acquired by {@link AThermal_acquireManager}.
|
||||
*
|
||||
* @return current thermal status, ATHERMAL_STATUS_ERROR on failure.
|
||||
*/
|
||||
AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager) {
|
||||
int32_t status = 0;
|
||||
status_t ret = manager->getCurrentThermalStatus(&status);
|
||||
if (ret != OK) {
|
||||
return AThermalStatus::ATHERMAL_STATUS_ERROR;
|
||||
}
|
||||
return static_cast<AThermalStatus>(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the thermal status listener for thermal status change.
|
||||
*
|
||||
* @param manager The manager instance to use to register.
|
||||
* acquired by {@link AThermal_acquireManager}.
|
||||
* @param callback The callback function to be called when thermal status updated.
|
||||
* @param data The data pointer to be passed when callback is called.
|
||||
*
|
||||
* @return 0 on success
|
||||
* EINVAL if the listener and data pointer were previously added and not removed.
|
||||
* EPERM if the required permission is not held.
|
||||
* EPIPE if communication with the system service has failed.
|
||||
*/
|
||||
int AThermal_registerThermalStatusListener(AThermalManager *manager,
|
||||
AThermal_StatusCallback callback, void *data) {
|
||||
return manager->addListener(callback, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the thermal status listener previously resgistered.
|
||||
*
|
||||
* @param manager The manager instance to use to unregister.
|
||||
* acquired by {@link AThermal_acquireManager}.
|
||||
* @param callback The callback function to be called when thermal status updated.
|
||||
* @param data The data pointer to be passed when callback is called.
|
||||
*
|
||||
* @return 0 on success
|
||||
* EINVAL if the listener and data pointer were not previously added.
|
||||
* EPERM if the required permission is not held.
|
||||
* EPIPE if communication with the system service has failed.
|
||||
*/
|
||||
int AThermal_unregisterThermalStatusListener(AThermalManager *manager,
|
||||
AThermal_StatusCallback callback, void *data) {
|
||||
return manager->removeListener(callback, data);
|
||||
}
|
||||
@@ -370,30 +370,33 @@ public class ThermalManagerService extends SystemService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Temperature> getCurrentTemperatures() {
|
||||
public Temperature[] getCurrentTemperatures() {
|
||||
getContext().enforceCallingOrSelfPermission(
|
||||
android.Manifest.permission.DEVICE_POWER, null);
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
if (!mHalReady.get()) {
|
||||
return new ArrayList<>();
|
||||
return new Temperature[0];
|
||||
}
|
||||
return mHalWrapper.getCurrentTemperatures(false, 0 /* not used */);
|
||||
final List<Temperature> curr = mHalWrapper.getCurrentTemperatures(
|
||||
false, 0 /* not used */);
|
||||
return curr.toArray(new Temperature[curr.size()]);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Temperature> getCurrentTemperaturesWithType(int type) {
|
||||
public Temperature[] getCurrentTemperaturesWithType(int type) {
|
||||
getContext().enforceCallingOrSelfPermission(
|
||||
android.Manifest.permission.DEVICE_POWER, null);
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
if (!mHalReady.get()) {
|
||||
return new ArrayList<>();
|
||||
return new Temperature[0];
|
||||
}
|
||||
return mHalWrapper.getCurrentTemperatures(true, type);
|
||||
final List<Temperature> curr = mHalWrapper.getCurrentTemperatures(true, type);
|
||||
return curr.toArray(new Temperature[curr.size()]);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
@@ -443,30 +446,34 @@ public class ThermalManagerService extends SystemService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CoolingDevice> getCurrentCoolingDevices() {
|
||||
public CoolingDevice[] getCurrentCoolingDevices() {
|
||||
getContext().enforceCallingOrSelfPermission(
|
||||
android.Manifest.permission.DEVICE_POWER, null);
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
if (!mHalReady.get()) {
|
||||
return new ArrayList<>();
|
||||
return new CoolingDevice[0];
|
||||
}
|
||||
return mHalWrapper.getCurrentCoolingDevices(false, 0);
|
||||
final List<CoolingDevice> devList = mHalWrapper.getCurrentCoolingDevices(
|
||||
false, 0);
|
||||
return devList.toArray(new CoolingDevice[devList.size()]);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CoolingDevice> getCurrentCoolingDevicesWithType(int type) {
|
||||
public CoolingDevice[] getCurrentCoolingDevicesWithType(int type) {
|
||||
getContext().enforceCallingOrSelfPermission(
|
||||
android.Manifest.permission.DEVICE_POWER, null);
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
if (!mHalReady.get()) {
|
||||
return new ArrayList<>();
|
||||
return new CoolingDevice[0];
|
||||
}
|
||||
return mHalWrapper.getCurrentCoolingDevices(true, type);
|
||||
final List<CoolingDevice> devList = mHalWrapper.getCurrentCoolingDevices(
|
||||
true, type);
|
||||
return devList.toArray(new CoolingDevice[devList.size()]);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
|
||||
@@ -1515,7 +1515,7 @@ public class StatsPullAtomService extends SystemService {
|
||||
}
|
||||
final long callingToken = Binder.clearCallingIdentity();
|
||||
try {
|
||||
List<Temperature> temperatures = thermalService.getCurrentTemperatures();
|
||||
Temperature temperatures[] = thermalService.getCurrentTemperatures();
|
||||
for (Temperature temp : temperatures) {
|
||||
StatsEvent e = StatsEvent.newBuilder()
|
||||
.setAtomId(atomTag)
|
||||
@@ -1553,7 +1553,7 @@ public class StatsPullAtomService extends SystemService {
|
||||
}
|
||||
final long callingToken = Binder.clearCallingIdentity();
|
||||
try {
|
||||
List<CoolingDevice> devices = thermalService.getCurrentCoolingDevices();
|
||||
CoolingDevice devices[] = thermalService.getCurrentCoolingDevices();
|
||||
for (CoolingDevice device : devices) {
|
||||
StatsEvent e = StatsEvent.newBuilder()
|
||||
.setAtomId(atomTag)
|
||||
|
||||
@@ -297,9 +297,11 @@ public class ThermalManagerServiceTest {
|
||||
@Test
|
||||
public void testGetCurrentTemperatures() throws RemoteException {
|
||||
assertListEqualsIgnoringOrder(mFakeHal.getCurrentTemperatures(false, 0),
|
||||
mService.mService.getCurrentTemperatures());
|
||||
assertListEqualsIgnoringOrder(mFakeHal.getCurrentTemperatures(true, Temperature.TYPE_SKIN),
|
||||
mService.mService.getCurrentTemperaturesWithType(Temperature.TYPE_SKIN));
|
||||
Arrays.asList(mService.mService.getCurrentTemperatures()));
|
||||
assertListEqualsIgnoringOrder(
|
||||
mFakeHal.getCurrentTemperatures(true, Temperature.TYPE_SKIN),
|
||||
Arrays.asList(mService.mService.getCurrentTemperaturesWithType(
|
||||
Temperature.TYPE_SKIN)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -331,21 +333,22 @@ public class ThermalManagerServiceTest {
|
||||
assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1));
|
||||
assertTrue(mService.mService.unregisterThermalEventListener(mEventListener1));
|
||||
assertTrue(mService.mService.unregisterThermalStatusListener(mStatusListener1));
|
||||
assertEquals(0, mService.mService.getCurrentTemperatures().size());
|
||||
assertEquals(0,
|
||||
mService.mService.getCurrentTemperaturesWithType(Temperature.TYPE_SKIN).size());
|
||||
assertEquals(0, Arrays.asList(mService.mService.getCurrentTemperatures()).size());
|
||||
assertEquals(0, Arrays.asList(mService.mService.getCurrentTemperaturesWithType(
|
||||
Temperature.TYPE_SKIN)).size());
|
||||
assertEquals(Temperature.THROTTLING_NONE, mService.mService.getCurrentThermalStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCurrentCoolingDevices() throws RemoteException {
|
||||
assertListEqualsIgnoringOrder(mFakeHal.getCurrentCoolingDevices(false, 0),
|
||||
mService.mService.getCurrentCoolingDevices());
|
||||
Arrays.asList(mService.mService.getCurrentCoolingDevices()));
|
||||
assertListEqualsIgnoringOrder(
|
||||
mFakeHal.getCurrentCoolingDevices(false, CoolingDevice.TYPE_BATTERY),
|
||||
mService.mService.getCurrentCoolingDevices());
|
||||
Arrays.asList(mService.mService.getCurrentCoolingDevices()));
|
||||
assertListEqualsIgnoringOrder(
|
||||
mFakeHal.getCurrentCoolingDevices(true, CoolingDevice.TYPE_CPU),
|
||||
mService.mService.getCurrentCoolingDevicesWithType(CoolingDevice.TYPE_CPU));
|
||||
Arrays.asList(mService.mService.getCurrentCoolingDevicesWithType(
|
||||
CoolingDevice.TYPE_CPU)));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user