summaryrefslogtreecommitdiff
path: root/native/android
diff options
context:
space:
mode:
Diffstat (limited to 'native/android')
-rw-r--r--native/android/Android.bp4
-rw-r--r--native/android/display_luts.cpp136
-rw-r--r--native/android/dynamic_instrumentation_manager.cpp204
-rw-r--r--native/android/include_platform/android/dynamic_instrumentation_manager.h133
-rw-r--r--native/android/libandroid.map.txt59
-rw-r--r--native/android/performance_hint.cpp756
-rw-r--r--native/android/surface_control.cpp65
-rw-r--r--native/android/system_fonts.cpp2
-rw-r--r--native/android/system_health.cpp278
-rw-r--r--native/android/tests/performance_hint/PerformanceHintNativeTest.cpp162
-rw-r--r--native/android/tests/thermal/NativeThermalUnitTest.cpp311
-rw-r--r--native/android/thermal.cpp323
12 files changed, 2175 insertions, 258 deletions
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 3eb99c3387f7..129d6163010e 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -55,6 +55,8 @@ cc_library_shared {
"surface_control_input_receiver.cpp",
"choreographer.cpp",
"configuration.cpp",
+ "display_luts.cpp",
+ "dynamic_instrumentation_manager.cpp",
"hardware_buffer_jni.cpp",
"input.cpp",
"input_transfer_token.cpp",
@@ -71,6 +73,7 @@ cc_library_shared {
"surface_control.cpp",
"surface_texture.cpp",
"system_fonts.cpp",
+ "system_health.cpp",
"trace.cpp",
"thermal.cpp",
],
@@ -100,6 +103,7 @@ cc_library_shared {
"android.hardware.configstore@1.0",
"android.hardware.configstore-utils",
"android.os.flags-aconfig-cc",
+ "dynamic_instrumentation_manager_aidl-cpp",
"libnativedisplay",
"libfmq",
],
diff --git a/native/android/display_luts.cpp b/native/android/display_luts.cpp
new file mode 100644
index 000000000000..b03a718d4a65
--- /dev/null
+++ b/native/android/display_luts.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2024 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 "DisplayLuts"
+
+#include <android/display_luts.h>
+#include <display_luts_private.h>
+#include <utils/Log.h>
+
+#include <cmath>
+
+#define ADISPLAYLUTS_BUFFER_LENGTH_LIMIT (100000)
+
+#define CHECK_NOT_NULL(name) \
+ LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument");
+
+ADisplayLutsEntry* ADisplayLutsEntry_createEntry(float* buffer, int32_t length,
+ ADisplayLuts_Dimension dimension,
+ ADisplayLuts_SamplingKey key) {
+ CHECK_NOT_NULL(buffer);
+ LOG_ALWAYS_FATAL_IF(length >= ADISPLAYLUTS_BUFFER_LENGTH_LIMIT,
+ "the lut raw buffer length is too big to handle");
+ if (dimension != ADISPLAYLUTS_ONE_DIMENSION && dimension != ADISPLAYLUTS_THREE_DIMENSION) {
+ LOG_ALWAYS_FATAL("the lut dimension is be either 1 or 3");
+ }
+ int32_t size = 0;
+ if (dimension == ADISPLAYLUTS_THREE_DIMENSION) {
+ LOG_ALWAYS_FATAL_IF(length % 3 != 0, "the 3d lut raw buffer is not divisible by 3");
+ int32_t lengthPerChannel = length / 3;
+ float sizeForDim = std::cbrt(static_cast<float>(lengthPerChannel));
+ LOG_ALWAYS_FATAL_IF(sizeForDim != (int)(sizeForDim),
+ "the 3d lut buffer length is incorrect");
+ size = (int)sizeForDim;
+ } else {
+ size = length;
+ }
+ LOG_ALWAYS_FATAL_IF(size < 2, "the lut size for each dimension is too small");
+
+ ADisplayLutsEntry* entry = new ADisplayLutsEntry();
+ entry->buffer.data.resize(length);
+ std::copy(buffer, buffer + length, entry->buffer.data.begin());
+ entry->properties = {dimension, size, key};
+
+ entry->incStrong((void*)ADisplayLutsEntry_createEntry);
+ return static_cast<ADisplayLutsEntry*>(entry);
+}
+
+void ADisplayLutsEntry_destroy(ADisplayLutsEntry* entry) {
+ if (entry != NULL) {
+ entry->decStrong((void*)ADisplayLutsEntry_createEntry);
+ }
+}
+
+ADisplayLuts_Dimension ADisplayLutsEntry_getDimension(const ADisplayLutsEntry* entry) {
+ CHECK_NOT_NULL(entry);
+ return entry->properties.dimension;
+}
+
+int32_t ADisplayLutsEntry_getSize(const ADisplayLutsEntry* entry) {
+ CHECK_NOT_NULL(entry);
+ return entry->properties.size;
+}
+
+ADisplayLuts_SamplingKey ADisplayLutsEntry_getSamplingKey(const ADisplayLutsEntry* entry) {
+ CHECK_NOT_NULL(entry);
+ return entry->properties.samplingKey;
+}
+
+const float* ADisplayLutsEntry_getBuffer(const ADisplayLutsEntry* _Nonnull entry) {
+ CHECK_NOT_NULL(entry);
+ return entry->buffer.data.data();
+}
+
+ADisplayLuts* ADisplayLuts_create() {
+ ADisplayLuts* luts = new ADisplayLuts();
+ if (luts == NULL) {
+ delete luts;
+ return NULL;
+ }
+ luts->incStrong((void*)ADisplayLuts_create);
+ return static_cast<ADisplayLuts*>(luts);
+}
+
+void ADisplayLuts_clearLuts(ADisplayLuts* luts) {
+ for (auto& entry : luts->entries) {
+ entry->decStrong((void*)ADisplayLuts_setEntries); // Decrement ref count
+ }
+ luts->entries.clear();
+ luts->offsets.clear();
+ luts->totalBufferSize = 0;
+}
+
+void ADisplayLuts_destroy(ADisplayLuts* luts) {
+ if (luts != NULL) {
+ ADisplayLuts_clearLuts(luts);
+ luts->decStrong((void*)ADisplayLuts_create);
+ }
+}
+
+void ADisplayLuts_setEntries(ADisplayLuts* luts, ADisplayLutsEntry** entries, int32_t numEntries) {
+ CHECK_NOT_NULL(luts);
+ // always clear the previously set lut(s)
+ ADisplayLuts_clearLuts(luts);
+
+ // do nothing
+ if (!entries || numEntries == 0) {
+ return;
+ }
+
+ LOG_ALWAYS_FATAL_IF(numEntries > 2, "The number of entries should be not over 2!");
+ if (numEntries == 2 && entries[0]->properties.dimension != ADISPLAYLUTS_ONE_DIMENSION &&
+ entries[1]->properties.dimension != ADISPLAYLUTS_THREE_DIMENSION) {
+ LOG_ALWAYS_FATAL("The entries should be 1D and 3D in order!");
+ }
+
+ luts->offsets.reserve(numEntries);
+ luts->entries.reserve(numEntries);
+ for (int32_t i = 0; i < numEntries; i++) {
+ luts->offsets.emplace_back(luts->totalBufferSize);
+ luts->totalBufferSize += entries[i]->buffer.data.size();
+ luts->entries.emplace_back(entries[i]);
+ luts->entries.back()->incStrong((void*)ADisplayLuts_setEntries);
+ }
+} \ No newline at end of file
diff --git a/native/android/dynamic_instrumentation_manager.cpp b/native/android/dynamic_instrumentation_manager.cpp
new file mode 100644
index 000000000000..074973188c66
--- /dev/null
+++ b/native/android/dynamic_instrumentation_manager.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2024 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 "ADynamicInstrumentationManager"
+#include <android-base/properties.h>
+#include <android/dynamic_instrumentation_manager.h>
+#include <android/os/instrumentation/BnOffsetCallback.h>
+#include <android/os/instrumentation/ExecutableMethodFileOffsets.h>
+#include <android/os/instrumentation/IDynamicInstrumentationManager.h>
+#include <android/os/instrumentation/MethodDescriptor.h>
+#include <android/os/instrumentation/TargetProcess.h>
+#include <binder/Binder.h>
+#include <binder/IServiceManager.h>
+#include <utils/Log.h>
+#include <utils/StrongPointer.h>
+
+#include <future>
+#include <mutex>
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace android::dynamicinstrumentationmanager {
+
+using android::os::instrumentation::BnOffsetCallback;
+using android::os::instrumentation::ExecutableMethodFileOffsets;
+
+// Global instance of IDynamicInstrumentationManager, service is obtained only on first use.
+static std::mutex mLock;
+static sp<os::instrumentation::IDynamicInstrumentationManager> mService;
+
+sp<os::instrumentation::IDynamicInstrumentationManager> getService() {
+ std::lock_guard<std::mutex> scoped_lock(mLock);
+ if (mService == nullptr || !IInterface::asBinder(mService)->isBinderAlive()) {
+ sp<IBinder> binder =
+ defaultServiceManager()->waitForService(String16("dynamic_instrumentation"));
+ mService = interface_cast<os::instrumentation::IDynamicInstrumentationManager>(binder);
+ }
+ return mService;
+}
+
+} // namespace android::dynamicinstrumentationmanager
+
+using namespace android;
+using namespace dynamicinstrumentationmanager;
+
+struct ADynamicInstrumentationManager_TargetProcess {
+ uid_t uid;
+ uid_t pid;
+ std::string processName;
+
+ ADynamicInstrumentationManager_TargetProcess(uid_t uid, pid_t pid, const char* processName)
+ : uid(uid), pid(pid), processName(processName) {}
+};
+
+ADynamicInstrumentationManager_TargetProcess* ADynamicInstrumentationManager_TargetProcess_create(
+ uid_t uid, pid_t pid, const char* processName) {
+ return new ADynamicInstrumentationManager_TargetProcess(uid, pid, processName);
+}
+
+void ADynamicInstrumentationManager_TargetProcess_destroy(
+ const ADynamicInstrumentationManager_TargetProcess* instance) {
+ delete instance;
+}
+
+struct ADynamicInstrumentationManager_MethodDescriptor {
+ std::string fqcn;
+ std::string methodName;
+ std::vector<std::string> fqParameters;
+
+ ADynamicInstrumentationManager_MethodDescriptor(const char* fqcn, const char* methodName,
+ const char* fullyQualifiedParameters[],
+ size_t numParameters)
+ : fqcn(fqcn), methodName(methodName) {
+ std::vector<std::string> fqParameters;
+ fqParameters.reserve(numParameters);
+ std::copy_n(fullyQualifiedParameters, numParameters, std::back_inserter(fqParameters));
+ this->fqParameters = std::move(fqParameters);
+ }
+};
+
+ADynamicInstrumentationManager_MethodDescriptor*
+ADynamicInstrumentationManager_MethodDescriptor_create(const char* fullyQualifiedClassName,
+ const char* methodName,
+ const char* fullyQualifiedParameters[],
+ size_t numParameters) {
+ return new ADynamicInstrumentationManager_MethodDescriptor(fullyQualifiedClassName, methodName,
+ fullyQualifiedParameters,
+ numParameters);
+}
+
+void ADynamicInstrumentationManager_MethodDescriptor_destroy(
+ const ADynamicInstrumentationManager_MethodDescriptor* instance) {
+ delete instance;
+}
+
+struct ADynamicInstrumentationManager_ExecutableMethodFileOffsets {
+ std::string containerPath;
+ uint64_t containerOffset;
+ uint64_t methodOffset;
+};
+
+ADynamicInstrumentationManager_ExecutableMethodFileOffsets*
+ADynamicInstrumentationManager_ExecutableMethodFileOffsets_create() {
+ return new ADynamicInstrumentationManager_ExecutableMethodFileOffsets();
+}
+
+const char* ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerPath(
+ const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* instance) {
+ return instance->containerPath.c_str();
+}
+
+uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerOffset(
+ const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* instance) {
+ return instance->containerOffset;
+}
+
+uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getMethodOffset(
+ const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* instance) {
+ return instance->methodOffset;
+}
+
+void ADynamicInstrumentationManager_ExecutableMethodFileOffsets_destroy(
+ const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* instance) {
+ delete instance;
+}
+
+class ResultCallback : public BnOffsetCallback {
+public:
+ ::android::binder::Status onResult(
+ const ::std::optional<ExecutableMethodFileOffsets>& offsets) override {
+ promise_.set_value(offsets);
+ return android::binder::Status::ok();
+ }
+
+ std::optional<ExecutableMethodFileOffsets> waitForResult() {
+ std::future<std::optional<ExecutableMethodFileOffsets>> futureResult =
+ promise_.get_future();
+ auto futureStatus = futureResult.wait_for(
+ std::chrono::seconds(1 * android::base::HwTimeoutMultiplier()));
+ if (futureStatus == std::future_status::ready) {
+ return futureResult.get();
+ } else {
+ return std::nullopt;
+ }
+ }
+
+private:
+ std::promise<std::optional<ExecutableMethodFileOffsets>> promise_;
+};
+
+int32_t ADynamicInstrumentationManager_getExecutableMethodFileOffsets(
+ const ADynamicInstrumentationManager_TargetProcess* targetProcess,
+ const ADynamicInstrumentationManager_MethodDescriptor* methodDescriptor,
+ const ADynamicInstrumentationManager_ExecutableMethodFileOffsets** out) {
+ android::os::instrumentation::TargetProcess targetProcessParcel;
+ targetProcessParcel.uid = targetProcess->uid;
+ targetProcessParcel.pid = targetProcess->pid;
+ targetProcessParcel.processName = targetProcess->processName;
+
+ android::os::instrumentation::MethodDescriptor methodDescriptorParcel;
+ methodDescriptorParcel.fullyQualifiedClassName = methodDescriptor->fqcn;
+ methodDescriptorParcel.methodName = methodDescriptor->methodName;
+ methodDescriptorParcel.fullyQualifiedParameters = methodDescriptor->fqParameters;
+
+ sp<os::instrumentation::IDynamicInstrumentationManager> service = getService();
+ if (service == nullptr) {
+ return INVALID_OPERATION;
+ }
+
+ android::sp<ResultCallback> resultCallback = android::sp<ResultCallback>::make();
+ binder_status_t result =
+ service->getExecutableMethodFileOffsets(targetProcessParcel, methodDescriptorParcel,
+ resultCallback)
+ .exceptionCode();
+ if (result != OK) {
+ return result;
+ }
+ std::optional<ExecutableMethodFileOffsets> offsets = resultCallback->waitForResult();
+ if (offsets != std::nullopt) {
+ auto* value = new ADynamicInstrumentationManager_ExecutableMethodFileOffsets();
+ value->containerPath = offsets->containerPath;
+ value->containerOffset = offsets->containerOffset;
+ value->methodOffset = offsets->methodOffset;
+ *out = value;
+ } else {
+ *out = nullptr;
+ }
+
+ return result;
+}
diff --git a/native/android/include_platform/android/dynamic_instrumentation_manager.h b/native/android/include_platform/android/dynamic_instrumentation_manager.h
new file mode 100644
index 000000000000..ab9f37034a22
--- /dev/null
+++ b/native/android/include_platform/android/dynamic_instrumentation_manager.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#ifndef __ADYNAMICINSTRUMENTATIONMANAGER_H__
+#define __ADYNAMICINSTRUMENTATIONMANAGER_H__
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+struct ADynamicInstrumentationManager_MethodDescriptor;
+typedef struct ADynamicInstrumentationManager_MethodDescriptor
+ ADynamicInstrumentationManager_MethodDescriptor;
+
+struct ADynamicInstrumentationManager_TargetProcess;
+typedef struct ADynamicInstrumentationManager_TargetProcess
+ ADynamicInstrumentationManager_TargetProcess;
+
+struct ADynamicInstrumentationManager_ExecutableMethodFileOffsets;
+typedef struct ADynamicInstrumentationManager_ExecutableMethodFileOffsets
+ ADynamicInstrumentationManager_ExecutableMethodFileOffsets;
+
+/**
+ * Initializes an ADynamicInstrumentationManager_TargetProcess. Caller must clean up when they are
+ * done with ADynamicInstrumentationManager_TargetProcess_destroy.
+ *
+ * @param uid of targeted process.
+ * @param pid of targeted process.
+ * @param processName to disambiguate from corner cases that may arise from pid reuse.
+ */
+ADynamicInstrumentationManager_TargetProcess* _Nonnull
+ ADynamicInstrumentationManager_TargetProcess_create(
+ uid_t uid, pid_t pid, const char* _Nonnull processName) __INTRODUCED_IN(36);
+/**
+ * Clean up an ADynamicInstrumentationManager_TargetProcess.
+ *
+ * @param instance returned from ADynamicInstrumentationManager_TargetProcess_create.
+ */
+void ADynamicInstrumentationManager_TargetProcess_destroy(
+ const ADynamicInstrumentationManager_TargetProcess* _Nonnull instance) __INTRODUCED_IN(36);
+
+/**
+ * Initializes an ADynamicInstrumentationManager_MethodDescriptor. Caller must clean up when they
+ * are done with ADynamicInstrumentationManager_MethodDescriptor_Destroy.
+ *
+ * @param fullyQualifiedClassName fqcn of class containing the method.
+ * @param methodName
+ * @param fullyQualifiedParameters fqcn of parameters of the method's signature, or e.g. "int" for
+ * primitives.
+ * @param numParameters length of `fullyQualifiedParameters` array.
+ */
+ADynamicInstrumentationManager_MethodDescriptor* _Nonnull
+ ADynamicInstrumentationManager_MethodDescriptor_create(
+ const char* _Nonnull fullyQualifiedClassName, const char* _Nonnull methodName,
+ const char* _Nonnull fullyQualifiedParameters[_Nonnull], size_t numParameters)
+ __INTRODUCED_IN(36);
+/**
+ * Clean up an ADynamicInstrumentationManager_MethodDescriptor.
+ *
+ * @param instance returned from ADynamicInstrumentationManager_MethodDescriptor_create.
+ */
+void ADynamicInstrumentationManager_MethodDescriptor_destroy(
+ const ADynamicInstrumentationManager_MethodDescriptor* _Nonnull instance)
+ __INTRODUCED_IN(36);
+
+/**
+ * Get the containerPath calculated by
+ * ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
+ * @param instance created with ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
+ * @return The OS path of the containing file.
+ */
+const char* _Nullable ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerPath(
+ const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance)
+ __INTRODUCED_IN(36);
+/**
+ * Get the containerOffset calculated by
+ * ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
+ * @param instance created with ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
+ * @return The offset of the containing file within the process' memory.
+ */
+uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerOffset(
+ const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance)
+ __INTRODUCED_IN(36);
+/**
+ * Get the methodOffset calculated by ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
+ * @param instance created with ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
+ * @return The offset of the method within the containing file.
+ */
+uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getMethodOffset(
+ const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance)
+ __INTRODUCED_IN(36);
+/**
+ * Clean up an ADynamicInstrumentationManager_ExecutableMethodFileOffsets.
+ *
+ * @param instance returned from ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
+ */
+void ADynamicInstrumentationManager_ExecutableMethodFileOffsets_destroy(
+ const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance)
+ __INTRODUCED_IN(36);
+/**
+ * Provides ART metadata about the described java method within the target process.
+ *
+ * @param targetProcess describes for which process the data is requested.
+ * @param methodDescriptor describes the targeted method.
+ * @param out will be populated with the data if successful. A nullptr combined
+ * with an OK status means that the program method is defined, but the offset
+ * info was unavailable because it is not AOT compiled.
+ * @return status indicating success or failure. The values correspond to the `binder_exception_t`
+ * enum values from <android/binder_status.h>.
+ */
+int32_t ADynamicInstrumentationManager_getExecutableMethodFileOffsets(
+ const ADynamicInstrumentationManager_TargetProcess* _Nonnull targetProcess,
+ const ADynamicInstrumentationManager_MethodDescriptor* _Nonnull methodDescriptor,
+ const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nullable* _Nonnull out)
+ __INTRODUCED_IN(36);
+
+__END_DECLS
+
+#endif // __ADYNAMICINSTRUMENTATIONMANAGER_H__
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 202535d45191..1ccadf90c2a9 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -4,6 +4,15 @@ LIBANDROID {
AActivityManager_removeUidImportanceListener; # systemapi introduced=31
AActivityManager_isUidActive; # systemapi introduced=31
AActivityManager_getUidImportance; # systemapi introduced=31
+ ADynamicInstrumentationManager_TargetProcess_create; # systemapi
+ ADynamicInstrumentationManager_TargetProcess_destroy; # systemapi
+ ADynamicInstrumentationManager_MethodDescriptor_create; # systemapi
+ ADynamicInstrumentationManager_MethodDescriptor_destroy; # systemapi
+ ADynamicInstrumentationManager_getExecutableMethodFileOffsets; # systemapi
+ ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerPath; # systemapi
+ ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerOffset; # systemapi
+ ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getMethodOffset; # systemapi
+ ADynamicInstrumentationManager_ExecutableMethodFileOffsets_destroy; # systemapi
AAssetDir_close;
AAssetDir_getNextFileName;
AAssetDir_rewind;
@@ -86,6 +95,15 @@ LIBANDROID {
AConfiguration_setTouchscreen;
AConfiguration_setUiModeNight;
AConfiguration_setUiModeType;
+ ADisplayLuts_create; # introduced=36
+ ADisplayLuts_setEntries; # introduced=36
+ ADisplayLuts_destroy; # introduced=36
+ ADisplayLutsEntry_createEntry; # introduced=36
+ ADisplayLutsEntry_getDimension; # introduced=36
+ ADisplayLutsEntry_getSize; # introduced=36
+ ADisplayLutsEntry_getSamplingKey; # introduced=36
+ ADisplayLutsEntry_getBuffer; # introduced=36
+ ADisplayLutsEntry_destroy; # introduced=36
AInputEvent_getDeviceId;
AInputEvent_getSource;
AInputEvent_getType;
@@ -290,6 +308,7 @@ LIBANDROID {
ASurfaceTransaction_setHdrMetadata_smpte2086; # introduced=29
ASurfaceTransaction_setExtendedRangeBrightness; # introduced=UpsideDownCake
ASurfaceTransaction_setDesiredHdrHeadroom; # introduced=VanillaIceCream
+ ASurfaceTransaction_setLuts; # introduced=36
ASurfaceTransaction_setOnComplete; # introduced=29
ASurfaceTransaction_setOnCommit; # introduced=31
ASurfaceTransaction_setPosition; # introduced=31
@@ -301,6 +320,23 @@ LIBANDROID {
ASystemFontIterator_open; # introduced=29
ASystemFontIterator_close; # introduced=29
ASystemFontIterator_next; # introduced=29
+ ASystemHealth_getCpuHeadroom; # introduced=36
+ ASystemHealth_getGpuHeadroom; # introduced=36
+ ASystemHealth_getCpuHeadroomMinIntervalMillis; # introduced=36
+ ASystemHealth_getGpuHeadroomMinIntervalMillis; # introduced=36
+ ACpuHeadroomParams_create; # introduced=36
+ ACpuHeadroomParams_destroy; # introduced=36
+ ACpuHeadroomParams_setCalculationType; # introduced=36
+ ACpuHeadroomParams_getCalculationType; # introduced=36
+ ACpuHeadroomParams_setCalculationWindowMillis; # introduced=36
+ ACpuHeadroomParams_getCalculationWindowMillis; # introduced=36
+ ACpuHeadroomParams_setTids; # introduced=36
+ AGpuHeadroomParams_create; # introduced=36
+ AGpuHeadroomParams_destroy; # introduced=36
+ AGpuHeadroomParams_setCalculationType; # introduced=36
+ AGpuHeadroomParams_getCalculationType; # introduced=36
+ AGpuHeadroomParams_setCalculationWindowMillis; # introduced=36
+ AGpuHeadroomParams_getCalculationWindowMillis; # introduced=36
AFont_close; # introduced=29
AFont_getFontFilePath; # introduced=29
AFont_getWeight; # introduced=29
@@ -342,21 +378,38 @@ LIBANDROID {
AThermal_unregisterThermalStatusListener; # introduced=30
AThermal_getThermalHeadroom; # introduced=31
AThermal_getThermalHeadroomThresholds; # introduced=VanillaIceCream
+ AThermal_registerThermalHeadroomListener; # introduced=36
+ AThermal_unregisterThermalHeadroomListener; # introduced=36
APerformanceHint_getManager; # introduced=Tiramisu
APerformanceHint_createSession; # introduced=Tiramisu
APerformanceHint_getPreferredUpdateRateNanos; # introduced=Tiramisu
+ APerformanceHint_getMaxGraphicsPipelineThreadsCount; # introduced=36
APerformanceHint_updateTargetWorkDuration; # introduced=Tiramisu
APerformanceHint_reportActualWorkDuration; # introduced=Tiramisu
APerformanceHint_closeSession; # introduced=Tiramisu
APerformanceHint_setThreads; # introduced=UpsideDownCake
APerformanceHint_setPreferPowerEfficiency; # introduced=VanillaIceCream
APerformanceHint_reportActualWorkDuration2; # introduced=VanillaIceCream
+ APerformanceHint_createSessionUsingConfig; # introduced=36
+ APerformanceHint_notifyWorkloadIncrease; # introduced=36
+ APerformanceHint_notifyWorkloadReset; # introduced=36
+ APerformanceHint_notifyWorkloadSpike; # introduced=36
+ APerformanceHint_borrowSessionFromJava; # introduced=36
+ APerformanceHint_setNativeSurfaces; # introduced=36
AWorkDuration_create; # introduced=VanillaIceCream
AWorkDuration_release; # introduced=VanillaIceCream
AWorkDuration_setWorkPeriodStartTimestampNanos; # introduced=VanillaIceCream
AWorkDuration_setActualTotalDurationNanos; # introduced=VanillaIceCream
AWorkDuration_setActualCpuDurationNanos; # introduced=VanillaIceCream
AWorkDuration_setActualGpuDurationNanos; # introduced=VanillaIceCream
+ ASessionCreationConfig_create; # introduced=36
+ ASessionCreationConfig_release; # introduced=36
+ ASessionCreationConfig_setTids; # introduced=36
+ ASessionCreationConfig_setTargetWorkDurationNanos; # introduced=36
+ ASessionCreationConfig_setPreferPowerEfficiency; # introduced=36
+ ASessionCreationConfig_setGraphicsPipeline; # introduced=36
+ ASessionCreationConfig_setNativeSurfaces; # introduced=36
+ ASessionCreationConfig_setUseAutoTiming; # introduced=36
local:
*;
};
@@ -366,9 +419,15 @@ LIBANDROID_PLATFORM {
AThermal_setIThermalServiceForTesting;
APerformanceHint_setIHintManagerForTesting;
APerformanceHint_sendHint;
+ APerformanceHint_setUseGraphicsPipelineForTesting;
APerformanceHint_getThreadIds;
APerformanceHint_createSessionInternal;
+ APerformanceHint_createSessionUsingConfigInternal;
APerformanceHint_setUseFMQForTesting;
+ APerformanceHint_getRateLimiterPropertiesForTesting;
+ APerformanceHint_setUseNewLoadHintBehaviorForTesting;
+ APerformanceHint_closeSessionFromJava;
+ APerformanceHint_createSessionFromJava;
extern "C++" {
ASurfaceControl_registerSurfaceStatsListener*;
ASurfaceControl_unregisterSurfaceStatsListener*;
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 095d7d1145ae..9257901bcd1f 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -22,23 +22,35 @@
#include <aidl/android/hardware/power/SessionHint.h>
#include <aidl/android/hardware/power/SessionMode.h>
#include <aidl/android/hardware/power/SessionTag.h>
+#include <aidl/android/hardware/power/SupportInfo.h>
#include <aidl/android/hardware/power/WorkDuration.h>
#include <aidl/android/hardware/power/WorkDurationFixedV1.h>
#include <aidl/android/os/IHintManager.h>
#include <aidl/android/os/IHintSession.h>
+#include <aidl/android/os/SessionCreationConfig.h>
#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
+#include <android/binder_libbinder.h>
#include <android/binder_manager.h>
#include <android/binder_status.h>
+#include <android/native_window.h>
#include <android/performance_hint.h>
+#include <android/surface_control.h>
#include <android/trace.h>
#include <android_os.h>
+#include <cutils/trace.h>
#include <fmq/AidlMessageQueue.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
#include <inttypes.h>
+#include <jni_wrappers.h>
#include <performance_hint_private.h>
#include <utils/SystemClock.h>
#include <chrono>
+#include <format>
+#include <future>
#include <set>
#include <utility>
#include <vector>
@@ -61,6 +73,34 @@ struct APerformanceHintSession;
constexpr int64_t SEND_HINT_TIMEOUT = std::chrono::nanoseconds(100ms).count();
struct AWorkDuration : public hal::WorkDuration {};
+struct ASessionCreationConfig : public SessionCreationConfig {
+ std::vector<wp<IBinder>> layers{};
+ bool hasMode(hal::SessionMode&& mode) {
+ return std::find(modesToEnable.begin(), modesToEnable.end(), mode) != modesToEnable.end();
+ }
+};
+
+bool kForceGraphicsPipeline = false;
+
+bool useGraphicsPipeline() {
+ return android::os::adpf_graphics_pipeline() || kForceGraphicsPipeline;
+}
+
+// A pair of values that determine the behavior of the
+// load hint rate limiter, to only allow "X hints every Y seconds"
+constexpr double kLoadHintInterval = std::chrono::nanoseconds(2s).count();
+constexpr double kMaxLoadHintsPerInterval = 20;
+constexpr double kReplenishRate = kMaxLoadHintsPerInterval / kLoadHintInterval;
+bool kForceNewHintBehavior = false;
+
+template <class T>
+constexpr int32_t enum_size() {
+ return static_cast<int32_t>(*(ndk::enum_range<T>().end() - 1)) + 1;
+}
+
+bool useNewLoadHintBehavior() {
+ return android::os::adpf_use_load_hints() || kForceNewHintBehavior;
+}
// Shared lock for the whole PerformanceHintManager and sessions
static std::mutex sHintMutex = std::mutex{};
@@ -75,7 +115,8 @@ public:
hal::WorkDuration* durations, size_t count) REQUIRES(sHintMutex);
bool updateTargetWorkDuration(std::optional<hal::SessionConfig>& config,
int64_t targetDurationNanos) REQUIRES(sHintMutex);
- bool sendHint(std::optional<hal::SessionConfig>& config, SessionHint hint) REQUIRES(sHintMutex);
+ bool sendHints(std::optional<hal::SessionConfig>& config, std::vector<hal::SessionHint>& hint,
+ int64_t now) REQUIRES(sHintMutex);
bool setMode(std::optional<hal::SessionConfig>& config, hal::SessionMode, bool enabled)
REQUIRES(sHintMutex);
void setToken(ndk::SpAIBinder& token);
@@ -85,10 +126,11 @@ public:
private:
template <HalChannelMessageContents::Tag T, bool urgent = false,
class C = HalChannelMessageContents::_at<T>>
- bool sendMessages(std::optional<hal::SessionConfig>& config, C* message, size_t count = 1)
- REQUIRES(sHintMutex);
+ bool sendMessages(std::optional<hal::SessionConfig>& config, C* message, size_t count = 1,
+ int64_t now = ::android::uptimeNanos()) REQUIRES(sHintMutex);
template <HalChannelMessageContents::Tag T, class C = HalChannelMessageContents::_at<T>>
- void writeBuffer(C* message, hal::SessionConfig& config, size_t count) REQUIRES(sHintMutex);
+ void writeBuffer(C* message, hal::SessionConfig& config, size_t count, int64_t now)
+ REQUIRES(sHintMutex);
bool isActiveLocked() REQUIRES(sHintMutex);
bool updatePersistentTransaction() REQUIRES(sHintMutex);
@@ -104,56 +146,101 @@ private:
size_t mAvailableSlots GUARDED_BY(sHintMutex) = 0;
bool mHalSupported = true;
HalMessageQueue::MemTransaction mFmqTransaction GUARDED_BY(sHintMutex);
+ std::future<bool> mChannelCreationFinished;
+};
+
+class SupportInfoWrapper {
+public:
+ SupportInfoWrapper(hal::SupportInfo& info);
+ bool isSessionModeSupported(hal::SessionMode mode);
+ bool isSessionHintSupported(hal::SessionHint hint);
+
+private:
+ template <class T>
+ bool getEnumSupportFromBitfield(T& enumValue, int64_t& supportBitfield) {
+ // extract the bit corresponding to the enum by shifting the bitfield
+ // over that much and cutting off any extra values
+ return (supportBitfield >> static_cast<int>(enumValue)) % 2;
+ }
+ hal::SupportInfo mSupportInfo;
+};
+
+class HintManagerClient : public IHintManager::BnHintManagerClient {
+public:
+ // Currently a no-op that exists for FMQ init to call in the future
+ ndk::ScopedAStatus receiveChannelConfig(const hal::ChannelConfig&) {
+ return ndk::ScopedAStatus::ok();
+ }
};
struct APerformanceHintManager {
public:
static APerformanceHintManager* getInstance();
- APerformanceHintManager(std::shared_ptr<IHintManager>& service, int64_t preferredRateNanos);
+ APerformanceHintManager(std::shared_ptr<IHintManager>& service,
+ IHintManager::HintManagerClientData&& clientData,
+ std::shared_ptr<HintManagerClient> callbackClient);
APerformanceHintManager() = delete;
~APerformanceHintManager();
APerformanceHintSession* createSession(const int32_t* threadIds, size_t size,
int64_t initialTargetWorkDurationNanos,
- hal::SessionTag tag = hal::SessionTag::APP);
+ hal::SessionTag tag = hal::SessionTag::APP,
+ bool isJava = false);
+ APerformanceHintSession* getSessionFromJava(JNIEnv* _Nonnull env, jobject _Nonnull sessionObj);
+
+ APerformanceHintSession* createSessionUsingConfig(ASessionCreationConfig* sessionCreationConfig,
+ hal::SessionTag tag = hal::SessionTag::APP,
+ bool isJava = false);
int64_t getPreferredRateNanos() const;
+ int32_t getMaxGraphicsPipelineThreadsCount();
FMQWrapper& getFMQWrapper();
+ bool canSendLoadHints(std::vector<hal::SessionHint>& hints, int64_t now) REQUIRES(sHintMutex);
+ void initJava(JNIEnv* _Nonnull env);
+ template <class T>
+ static void layersFromNativeSurfaces(ANativeWindow** windows, int numWindows,
+ ASurfaceControl** controls, int numSurfaceControls,
+ std::vector<T>& out);
+ ndk::SpAIBinder& getToken();
+ SupportInfoWrapper& getSupportInfo();
private:
- // Necessary to create an empty binder object
- static void* tokenStubOnCreate(void*) {
- return nullptr;
- }
- static void tokenStubOnDestroy(void*) {}
- static binder_status_t tokenStubOnTransact(AIBinder*, transaction_code_t, const AParcel*,
- AParcel*) {
- return STATUS_OK;
- }
-
static APerformanceHintManager* create(std::shared_ptr<IHintManager> iHintManager);
std::shared_ptr<IHintManager> mHintManager;
+ std::shared_ptr<HintManagerClient> mCallbackClient;
+ IHintManager::HintManagerClientData mClientData;
+ SupportInfoWrapper mSupportInfoWrapper;
ndk::SpAIBinder mToken;
- const int64_t mPreferredRateNanos;
FMQWrapper mFMQWrapper;
+ double mHintBudget = kMaxLoadHintsPerInterval;
+ int64_t mLastBudgetReplenish = 0;
+ bool mJavaInitialized = false;
+ jclass mJavaSessionClazz;
+ jfieldID mJavaSessionNativePtr;
};
struct APerformanceHintSession {
public:
APerformanceHintSession(std::shared_ptr<IHintManager> hintManager,
std::shared_ptr<IHintSession> session, int64_t preferredRateNanos,
- int64_t targetDurationNanos,
+ int64_t targetDurationNanos, bool isJava,
std::optional<hal::SessionConfig> sessionConfig);
APerformanceHintSession() = delete;
~APerformanceHintSession();
int updateTargetWorkDuration(int64_t targetDurationNanos);
int reportActualWorkDuration(int64_t actualDurationNanos);
- int sendHint(SessionHint hint);
+ int sendHints(std::vector<hal::SessionHint>& hints, int64_t now, const char* debugName);
+ int notifyWorkloadIncrease(bool cpu, bool gpu, const char* debugName);
+ int notifyWorkloadReset(bool cpu, bool gpu, const char* debugName);
+ int notifyWorkloadSpike(bool cpu, bool gpu, const char* debugName);
int setThreads(const int32_t* threadIds, size_t size);
int getThreadIds(int32_t* const threadIds, size_t* size);
int setPreferPowerEfficiency(bool enabled);
int reportActualWorkDuration(AWorkDuration* workDuration);
+ bool isJava();
+ status_t setNativeSurfaces(ANativeWindow** windows, int numWindows, ASurfaceControl** controls,
+ int numSurfaceControls);
private:
friend struct APerformanceHintManager;
@@ -171,17 +258,23 @@ private:
// Last target hit timestamp
int64_t mLastTargetMetTimestamp GUARDED_BY(sHintMutex);
// Last hint reported from sendHint indexed by hint value
+ // This is only used by the old rate limiter impl and is replaced
+ // with the new rate limiter under a flag
std::vector<int64_t> mLastHintSentTimestamp GUARDED_BY(sHintMutex);
// Cached samples
std::vector<hal::WorkDuration> mActualWorkDurations GUARDED_BY(sHintMutex);
+ // Is this session backing an SDK wrapper object
+ const bool mIsJava;
std::string mSessionName;
static int64_t sIDCounter GUARDED_BY(sHintMutex);
// The most recent set of thread IDs
std::vector<int32_t> mLastThreadIDs GUARDED_BY(sHintMutex);
- std::optional<hal::SessionConfig> mSessionConfig GUARDED_BY(sHintMutex);
+ std::optional<hal::SessionConfig> mSessionConfig;
// Tracing helpers
- void traceThreads(std::vector<int32_t>& tids) REQUIRES(sHintMutex);
+ void traceThreads(const std::vector<int32_t>& tids) REQUIRES(sHintMutex);
void tracePowerEfficient(bool powerEfficient);
+ void traceGraphicsPipeline(bool graphicsPipeline);
+ void traceModes(const std::vector<hal::SessionMode>& modesToEnable);
void traceActualDuration(int64_t actualDuration);
void traceBatchSize(size_t batchSize);
void traceTargetDuration(int64_t targetDuration);
@@ -199,14 +292,27 @@ static FMQWrapper& getFMQ() {
return APerformanceHintManager::getInstance()->getFMQWrapper();
}
+// ===================================== SupportInfoWrapper implementation
+
+SupportInfoWrapper::SupportInfoWrapper(hal::SupportInfo& info) : mSupportInfo(info) {}
+
+bool SupportInfoWrapper::isSessionHintSupported(hal::SessionHint hint) {
+ return getEnumSupportFromBitfield(hint, mSupportInfo.sessionHints);
+}
+
+bool SupportInfoWrapper::isSessionModeSupported(hal::SessionMode mode) {
+ return getEnumSupportFromBitfield(mode, mSupportInfo.sessionModes);
+}
+
// ===================================== APerformanceHintManager implementation
APerformanceHintManager::APerformanceHintManager(std::shared_ptr<IHintManager>& manager,
- int64_t preferredRateNanos)
- : mHintManager(std::move(manager)), mPreferredRateNanos(preferredRateNanos) {
- static AIBinder_Class* tokenBinderClass =
- AIBinder_Class_define("phm_token", tokenStubOnCreate, tokenStubOnDestroy,
- tokenStubOnTransact);
- mToken = ndk::SpAIBinder(AIBinder_new(tokenBinderClass, nullptr));
+ IHintManager::HintManagerClientData&& clientData,
+ std::shared_ptr<HintManagerClient> callbackClient)
+ : mHintManager(std::move(manager)),
+ mCallbackClient(callbackClient),
+ mClientData(clientData),
+ mSupportInfoWrapper(clientData.supportInfo),
+ mToken(callbackClient->asBinder()) {
if (mFMQWrapper.isSupported()) {
mFMQWrapper.setToken(mToken);
mFMQWrapper.startChannel(mHintManager.get());
@@ -218,6 +324,8 @@ APerformanceHintManager::~APerformanceHintManager() {
}
APerformanceHintManager* APerformanceHintManager::getInstance() {
+ static std::once_flag creationFlag;
+ static APerformanceHintManager* instance = nullptr;
if (gHintManagerForTesting) {
return gHintManagerForTesting.get();
}
@@ -226,7 +334,7 @@ APerformanceHintManager* APerformanceHintManager::getInstance() {
std::shared_ptr<APerformanceHintManager>(create(*gIHintManagerForTesting));
return gHintManagerForTesting.get();
}
- static APerformanceHintManager* instance = create(nullptr);
+ std::call_once(creationFlag, []() { instance = create(nullptr); });
return instance;
}
@@ -239,62 +347,146 @@ APerformanceHintManager* APerformanceHintManager::create(std::shared_ptr<IHintMa
ALOGE("%s: PerformanceHint service is not ready ", __FUNCTION__);
return nullptr;
}
- int64_t preferredRateNanos = -1L;
- ndk::ScopedAStatus ret = manager->getHintSessionPreferredRate(&preferredRateNanos);
+ std::shared_ptr<HintManagerClient> client = ndk::SharedRefBase::make<HintManagerClient>();
+ IHintManager::HintManagerClientData clientData;
+ ndk::ScopedAStatus ret = manager->registerClient(client, &clientData);
if (!ret.isOk()) {
- ALOGE("%s: PerformanceHint cannot get preferred rate. %s", __FUNCTION__, ret.getMessage());
+ ALOGE("%s: PerformanceHint is not supported. %s", __FUNCTION__, ret.getMessage());
return nullptr;
}
- if (preferredRateNanos <= 0) {
- preferredRateNanos = -1L;
+ if (clientData.preferredRateNanos <= 0) {
+ clientData.preferredRateNanos = -1L;
+ }
+ return new APerformanceHintManager(manager, std::move(clientData), client);
+}
+
+bool APerformanceHintManager::canSendLoadHints(std::vector<hal::SessionHint>& hints, int64_t now) {
+ mHintBudget =
+ std::min(kMaxLoadHintsPerInterval,
+ mHintBudget +
+ static_cast<double>(now - mLastBudgetReplenish) * kReplenishRate);
+ mLastBudgetReplenish = now;
+
+ // If this youngest timestamp isn't older than the timeout time, we can't send
+ if (hints.size() > mHintBudget) {
+ return false;
}
- return new APerformanceHintManager(manager, preferredRateNanos);
+ mHintBudget -= hints.size();
+ return true;
}
APerformanceHintSession* APerformanceHintManager::createSession(
const int32_t* threadIds, size_t size, int64_t initialTargetWorkDurationNanos,
- hal::SessionTag tag) {
- std::vector<int32_t> tids(threadIds, threadIds + size);
- std::shared_ptr<IHintSession> session;
+ hal::SessionTag tag, bool isJava) {
ndk::ScopedAStatus ret;
hal::SessionConfig sessionConfig{.id = -1};
- ret = mHintManager->createHintSessionWithConfig(mToken, tids, initialTargetWorkDurationNanos,
- tag, &sessionConfig, &session);
+
+ ASessionCreationConfig creationConfig{{
+ .tids = std::vector<int32_t>(threadIds, threadIds + size),
+ .targetWorkDurationNanos = initialTargetWorkDurationNanos,
+ }};
+
+ return APerformanceHintManager::createSessionUsingConfig(&creationConfig, tag, isJava);
+}
+
+APerformanceHintSession* APerformanceHintManager::createSessionUsingConfig(
+ ASessionCreationConfig* sessionCreationConfig, hal::SessionTag tag, bool isJava) {
+ std::shared_ptr<IHintSession> session;
+ hal::SessionConfig sessionConfig{.id = -1};
+ ndk::ScopedAStatus ret;
+
+ // Hold the tokens weakly until we actually need them,
+ // then promote them, then drop all strong refs after
+ if (!sessionCreationConfig->layers.empty()) {
+ for (auto&& layerIter = sessionCreationConfig->layers.begin();
+ layerIter != sessionCreationConfig->layers.end();) {
+ sp<IBinder> promoted = layerIter->promote();
+ if (promoted == nullptr) {
+ layerIter = sessionCreationConfig->layers.erase(layerIter);
+ } else {
+ sessionCreationConfig->layerTokens.push_back(
+ ndk::SpAIBinder(AIBinder_fromPlatformBinder(promoted.get())));
+ ++layerIter;
+ }
+ }
+ }
+
+ ret = mHintManager->createHintSessionWithConfig(mToken, tag,
+ *static_cast<SessionCreationConfig*>(
+ sessionCreationConfig),
+ &sessionConfig, &session);
+
+ sessionCreationConfig->layerTokens.clear();
if (!ret.isOk() || !session) {
ALOGE("%s: PerformanceHint cannot create session. %s", __FUNCTION__, ret.getMessage());
return nullptr;
}
- auto out = new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos,
- initialTargetWorkDurationNanos,
+
+ auto out = new APerformanceHintSession(mHintManager, std::move(session),
+ mClientData.preferredRateNanos,
+ sessionCreationConfig->targetWorkDurationNanos, isJava,
sessionConfig.id == -1
? std::nullopt
: std::make_optional<hal::SessionConfig>(
std::move(sessionConfig)));
std::scoped_lock lock(sHintMutex);
- out->traceThreads(tids);
- out->traceTargetDuration(initialTargetWorkDurationNanos);
- out->tracePowerEfficient(false);
+ out->traceThreads(sessionCreationConfig->tids);
+ out->traceTargetDuration(sessionCreationConfig->targetWorkDurationNanos);
+ out->traceModes(sessionCreationConfig->modesToEnable);
+
+ return out;
+}
+
+APerformanceHintSession* APerformanceHintManager::getSessionFromJava(JNIEnv* env,
+ jobject sessionObj) {
+ initJava(env);
+ LOG_ALWAYS_FATAL_IF(!env->IsInstanceOf(sessionObj, mJavaSessionClazz),
+ "Wrong java type passed to APerformanceHint_getSessionFromJava");
+ APerformanceHintSession* out = reinterpret_cast<APerformanceHintSession*>(
+ env->GetLongField(sessionObj, mJavaSessionNativePtr));
+ LOG_ALWAYS_FATAL_IF(out == nullptr, "Java-wrapped native hint session is nullptr");
+ LOG_ALWAYS_FATAL_IF(!out->isJava(), "Unmanaged native hint session returned from Java SDK");
return out;
}
int64_t APerformanceHintManager::getPreferredRateNanos() const {
- return mPreferredRateNanos;
+ return mClientData.preferredRateNanos;
+}
+
+int32_t APerformanceHintManager::getMaxGraphicsPipelineThreadsCount() {
+ return mClientData.maxGraphicsPipelineThreads;
}
FMQWrapper& APerformanceHintManager::getFMQWrapper() {
return mFMQWrapper;
}
-// ===================================== APerformanceHintSession implementation
+void APerformanceHintManager::initJava(JNIEnv* _Nonnull env) {
+ if (mJavaInitialized) {
+ return;
+ }
+ jclass sessionClazz = FindClassOrDie(env, "android/os/PerformanceHintManager$Session");
+ mJavaSessionClazz = MakeGlobalRefOrDie(env, sessionClazz);
+ mJavaSessionNativePtr = GetFieldIDOrDie(env, mJavaSessionClazz, "mNativeSessionPtr", "J");
+ mJavaInitialized = true;
+}
-constexpr int kNumEnums =
- ndk::enum_range<hal::SessionHint>().end() - ndk::enum_range<hal::SessionHint>().begin();
+ndk::SpAIBinder& APerformanceHintManager::getToken() {
+ return mToken;
+}
+
+SupportInfoWrapper& APerformanceHintManager::getSupportInfo() {
+ return mSupportInfoWrapper;
+}
+// ===================================== APerformanceHintSession implementation
+
+constexpr int kNumEnums = enum_size<hal::SessionHint>();
APerformanceHintSession::APerformanceHintSession(std::shared_ptr<IHintManager> hintManager,
std::shared_ptr<IHintSession> session,
int64_t preferredRateNanos,
- int64_t targetDurationNanos,
+ int64_t targetDurationNanos, bool isJava,
std::optional<hal::SessionConfig> sessionConfig)
: mHintManager(hintManager),
mHintSession(std::move(session)),
@@ -303,6 +495,7 @@ APerformanceHintSession::APerformanceHintSession(std::shared_ptr<IHintManager> h
mFirstTargetMetTimestamp(0),
mLastTargetMetTimestamp(0),
mLastHintSentTimestamp(std::vector<int64_t>(kNumEnums, 0)),
+ mIsJava(isJava),
mSessionConfig(sessionConfig) {
if (sessionConfig->id > INT32_MAX) {
ALOGE("Session ID too large, must fit 32-bit integer");
@@ -357,31 +550,100 @@ int APerformanceHintSession::reportActualWorkDuration(int64_t actualDurationNano
return reportActualWorkDurationInternal(static_cast<AWorkDuration*>(&workDuration));
}
-int APerformanceHintSession::sendHint(SessionHint hint) {
+bool APerformanceHintSession::isJava() {
+ return mIsJava;
+}
+
+int APerformanceHintSession::sendHints(std::vector<hal::SessionHint>& hints, int64_t now,
+ const char*) {
std::scoped_lock lock(sHintMutex);
- if (hint < 0 || hint >= static_cast<int32_t>(mLastHintSentTimestamp.size())) {
- ALOGE("%s: invalid session hint %d", __FUNCTION__, hint);
+ if (hints.empty()) {
return EINVAL;
}
- int64_t now = uptimeNanos();
+ for (auto&& hint : hints) {
+ if (static_cast<int32_t>(hint) < 0 || static_cast<int32_t>(hint) >= kNumEnums) {
+ ALOGE("%s: invalid session hint %d", __FUNCTION__, hint);
+ return EINVAL;
+ }
+ }
- // Limit sendHint to a pre-detemined rate for safety
- if (now < (mLastHintSentTimestamp[hint] + SEND_HINT_TIMEOUT)) {
- return 0;
+ if (useNewLoadHintBehavior()) {
+ if (!APerformanceHintManager::getInstance()->canSendLoadHints(hints, now)) {
+ return EBUSY;
+ }
+ }
+ // keep old rate limiter behavior for legacy flag
+ else {
+ for (auto&& hint : hints) {
+ if (now < (mLastHintSentTimestamp[static_cast<int32_t>(hint)] + SEND_HINT_TIMEOUT)) {
+ return EBUSY;
+ }
+ }
}
- if (!getFMQ().sendHint(mSessionConfig, hint)) {
- ndk::ScopedAStatus ret = mHintSession->sendHint(hint);
+ if (!getFMQ().sendHints(mSessionConfig, hints, now)) {
+ for (auto&& hint : hints) {
+ ndk::ScopedAStatus ret = mHintSession->sendHint(static_cast<int32_t>(hint));
- if (!ret.isOk()) {
- ALOGE("%s: HintSession sendHint failed: %s", __FUNCTION__, ret.getMessage());
- return EPIPE;
+ if (!ret.isOk()) {
+ ALOGE("%s: HintSession sendHint failed: %s", __FUNCTION__, ret.getMessage());
+ return EPIPE;
+ }
}
}
- mLastHintSentTimestamp[hint] = now;
+
+ if (!useNewLoadHintBehavior()) {
+ for (auto&& hint : hints) {
+ mLastHintSentTimestamp[static_cast<int32_t>(hint)] = now;
+ }
+ }
+
+ if (ATrace_isEnabled()) {
+ ATRACE_INSTANT("Sending load hint");
+ }
+
return 0;
}
+int APerformanceHintSession::notifyWorkloadIncrease(bool cpu, bool gpu, const char* debugName) {
+ std::vector<hal::SessionHint> hints(2);
+ hints.clear();
+ if (cpu) {
+ hints.push_back(hal::SessionHint::CPU_LOAD_UP);
+ }
+ if (gpu) {
+ hints.push_back(hal::SessionHint::GPU_LOAD_UP);
+ }
+ int64_t now = ::android::uptimeNanos();
+ return sendHints(hints, now, debugName);
+}
+
+int APerformanceHintSession::notifyWorkloadReset(bool cpu, bool gpu, const char* debugName) {
+ std::vector<hal::SessionHint> hints(2);
+ hints.clear();
+ if (cpu) {
+ hints.push_back(hal::SessionHint::CPU_LOAD_RESET);
+ }
+ if (gpu) {
+ hints.push_back(hal::SessionHint::GPU_LOAD_RESET);
+ }
+ int64_t now = ::android::uptimeNanos();
+ return sendHints(hints, now, debugName);
+}
+
+int APerformanceHintSession::notifyWorkloadSpike(bool cpu, bool gpu, const char* debugName) {
+ std::vector<hal::SessionHint> hints(2);
+ hints.clear();
+ if (cpu) {
+ hints.push_back(hal::SessionHint::CPU_LOAD_SPIKE);
+ }
+ if (gpu) {
+ hints.push_back(hal::SessionHint::GPU_LOAD_SPIKE);
+ }
+ int64_t now = ::android::uptimeNanos();
+ return sendHints(hints, now, debugName);
+}
+
int APerformanceHintSession::setThreads(const int32_t* threadIds, size_t size) {
if (size == 0) {
ALOGE("%s: the list of thread ids must not be empty.", __FUNCTION__);
@@ -495,6 +757,57 @@ int APerformanceHintSession::reportActualWorkDurationInternal(AWorkDuration* wor
return 0;
}
+status_t APerformanceHintSession::setNativeSurfaces(ANativeWindow** windows, int numWindows,
+ ASurfaceControl** controls,
+ int numSurfaceControls) {
+ if (!mSessionConfig.has_value()) {
+ return ENOTSUP;
+ }
+
+ std::vector<sp<IBinder>> layerHandles;
+ APerformanceHintManager::layersFromNativeSurfaces<sp<IBinder>>(windows, numWindows, controls,
+ numSurfaceControls,
+ layerHandles);
+
+ std::vector<ndk::SpAIBinder> ndkLayerHandles;
+ for (auto&& handle : layerHandles) {
+ ndkLayerHandles.emplace_back(ndk::SpAIBinder(AIBinder_fromPlatformBinder(handle)));
+ }
+
+ mHintSession->associateToLayers(ndkLayerHandles);
+ return 0;
+}
+
+template <class T>
+void APerformanceHintManager::layersFromNativeSurfaces(ANativeWindow** windows, int numWindows,
+ ASurfaceControl** controls,
+ int numSurfaceControls,
+ std::vector<T>& out) {
+ std::scoped_lock lock(sHintMutex);
+ if (windows != nullptr) {
+ std::vector<ANativeWindow*> windowVec(windows, windows + numWindows);
+ for (auto&& window : windowVec) {
+ Surface* surface = static_cast<Surface*>(window);
+ if (Surface::isValid(surface)) {
+ const sp<IBinder>& handle = surface->getSurfaceControlHandle();
+ if (handle != nullptr) {
+ out.push_back(handle);
+ }
+ }
+ }
+ }
+
+ if (controls != nullptr) {
+ std::vector<ASurfaceControl*> controlVec(controls, controls + numSurfaceControls);
+ for (auto&& aSurfaceControl : controlVec) {
+ SurfaceControl* control = reinterpret_cast<SurfaceControl*>(aSurfaceControl);
+ if (control->isValid()) {
+ out.push_back(control->getHandle());
+ }
+ }
+ }
+}
+
// ===================================== FMQ wrapper implementation
bool FMQWrapper::isActive() {
@@ -522,25 +835,28 @@ bool FMQWrapper::isSupported() {
}
bool FMQWrapper::startChannel(IHintManager* manager) {
- if (isSupported() && !isActive()) {
- std::optional<hal::ChannelConfig> config;
- auto ret = manager->getSessionChannel(mToken, &config);
- if (ret.isOk() && config.has_value()) {
- std::scoped_lock lock{sHintMutex};
- mQueue = std::make_shared<HalMessageQueue>(config->channelDescriptor, true);
- if (config->eventFlagDescriptor.has_value()) {
- mFlagQueue = std::make_shared<HalFlagQueue>(*config->eventFlagDescriptor, true);
- android::hardware::EventFlag::createEventFlag(mFlagQueue->getEventFlagWord(),
- &mEventFlag);
- mWriteMask = config->writeFlagBitmask;
+ if (isSupported() && !isActive() && manager->isRemote()) {
+ mChannelCreationFinished = std::async(std::launch::async, [&, this, manager]() {
+ std::optional<hal::ChannelConfig> config;
+ auto ret = manager->getSessionChannel(mToken, &config);
+ if (ret.isOk() && config.has_value()) {
+ std::scoped_lock lock{sHintMutex};
+ mQueue = std::make_shared<HalMessageQueue>(config->channelDescriptor, true);
+ if (config->eventFlagDescriptor.has_value()) {
+ mFlagQueue = std::make_shared<HalFlagQueue>(*config->eventFlagDescriptor, true);
+ android::hardware::EventFlag::createEventFlag(mFlagQueue->getEventFlagWord(),
+ &mEventFlag);
+ mWriteMask = config->writeFlagBitmask;
+ }
+ updatePersistentTransaction();
+ } else if (ret.isOk() && !config.has_value()) {
+ ALOGV("FMQ channel enabled but unsupported.");
+ setUnsupported();
+ } else {
+ ALOGE("%s: FMQ channel initialization failed: %s", __FUNCTION__, ret.getMessage());
}
- updatePersistentTransaction();
- } else if (ret.isOk() && !config.has_value()) {
- ALOGV("FMQ channel enabled but unsupported.");
- setUnsupported();
- } else {
- ALOGE("%s: FMQ channel initialization failed: %s", __FUNCTION__, ret.getMessage());
- }
+ return true;
+ });
}
return isActive();
}
@@ -558,24 +874,25 @@ void FMQWrapper::stopChannel(IHintManager* manager) {
}
template <HalChannelMessageContents::Tag T, class C>
-void FMQWrapper::writeBuffer(C* message, hal::SessionConfig& config, size_t) {
- new (mFmqTransaction.getSlot(0)) hal::ChannelMessage{
- .sessionID = static_cast<int32_t>(config.id),
- .timeStampNanos = ::android::uptimeNanos(),
- .data = HalChannelMessageContents::make<T, C>(std::move(*message)),
- };
+void FMQWrapper::writeBuffer(C* message, hal::SessionConfig& config, size_t count, int64_t now) {
+ for (size_t i = 0; i < count; ++i) {
+ new (mFmqTransaction.getSlot(i)) hal::ChannelMessage{
+ .sessionID = static_cast<int32_t>(config.id),
+ .timeStampNanos = now,
+ .data = HalChannelMessageContents::make<T, C>(std::move(*(message + i))),
+ };
+ }
}
template <>
void FMQWrapper::writeBuffer<HalChannelMessageContents::workDuration>(hal::WorkDuration* messages,
hal::SessionConfig& config,
- size_t count) {
+ size_t count, int64_t now) {
for (size_t i = 0; i < count; ++i) {
hal::WorkDuration& message = messages[i];
new (mFmqTransaction.getSlot(i)) hal::ChannelMessage{
.sessionID = static_cast<int32_t>(config.id),
- .timeStampNanos =
- (i == count - 1) ? ::android::uptimeNanos() : message.timeStampNanos,
+ .timeStampNanos = (i == count - 1) ? now : message.timeStampNanos,
.data = HalChannelMessageContents::make<HalChannelMessageContents::workDuration,
hal::WorkDurationFixedV1>({
.durationNanos = message.cpuDurationNanos,
@@ -588,7 +905,8 @@ void FMQWrapper::writeBuffer<HalChannelMessageContents::workDuration>(hal::WorkD
}
template <HalChannelMessageContents::Tag T, bool urgent, class C>
-bool FMQWrapper::sendMessages(std::optional<hal::SessionConfig>& config, C* message, size_t count) {
+bool FMQWrapper::sendMessages(std::optional<hal::SessionConfig>& config, C* message, size_t count,
+ int64_t now) {
if (!isActiveLocked() || !config.has_value() || mCorrupted) {
return false;
}
@@ -602,7 +920,7 @@ bool FMQWrapper::sendMessages(std::optional<hal::SessionConfig>& config, C* mess
return false;
}
}
- writeBuffer<T, C>(message, *config, count);
+ writeBuffer<T, C>(message, *config, count, now);
mQueue->commitWrite(count);
mEventFlag->wake(mWriteMask);
// Re-create the persistent transaction after writing
@@ -634,10 +952,9 @@ bool FMQWrapper::updateTargetWorkDuration(std::optional<hal::SessionConfig>& con
return sendMessages<HalChannelMessageContents::targetDuration>(config, &targetDurationNanos);
}
-bool FMQWrapper::sendHint(std::optional<hal::SessionConfig>& config, SessionHint hint) {
- return sendMessages<HalChannelMessageContents::hint>(config,
- reinterpret_cast<hal::SessionHint*>(
- &hint));
+bool FMQWrapper::sendHints(std::optional<hal::SessionConfig>& config,
+ std::vector<hal::SessionHint>& hints, int64_t now) {
+ return sendMessages<HalChannelMessageContents::hint>(config, hints.data(), hints.size(), now);
}
bool FMQWrapper::setMode(std::optional<hal::SessionConfig>& config, hal::SessionMode mode,
@@ -649,7 +966,7 @@ bool FMQWrapper::setMode(std::optional<hal::SessionConfig>& config, hal::Session
// ===================================== Tracing helpers
-void APerformanceHintSession::traceThreads(std::vector<int32_t>& tids) {
+void APerformanceHintSession::traceThreads(const std::vector<int32_t>& tids) {
std::set<int32_t> tidSet{tids.begin(), tids.end()};
// Disable old TID tracing
@@ -675,6 +992,30 @@ void APerformanceHintSession::tracePowerEfficient(bool powerEfficient) {
ATrace_setCounter((mSessionName + " power efficiency mode").c_str(), powerEfficient);
}
+void APerformanceHintSession::traceGraphicsPipeline(bool graphicsPipeline) {
+ ATrace_setCounter((mSessionName + " graphics pipeline mode").c_str(), graphicsPipeline);
+}
+
+void APerformanceHintSession::traceModes(const std::vector<hal::SessionMode>& modesToEnable) {
+ // Iterate through all modes to trace, set to enable for all modes in modesToEnable,
+ // and set to disable for those are not.
+ for (hal::SessionMode mode :
+ {hal::SessionMode::POWER_EFFICIENCY, hal::SessionMode::GRAPHICS_PIPELINE}) {
+ bool isEnabled =
+ find(modesToEnable.begin(), modesToEnable.end(), mode) != modesToEnable.end();
+ switch (mode) {
+ case hal::SessionMode::POWER_EFFICIENCY:
+ tracePowerEfficient(isEnabled);
+ break;
+ case hal::SessionMode::GRAPHICS_PIPELINE:
+ traceGraphicsPipeline(isEnabled);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
void APerformanceHintSession::traceActualDuration(int64_t actualDuration) {
ATrace_setCounter((mSessionName + " actual duration").c_str(), actualDuration);
}
@@ -717,6 +1058,22 @@ APerformanceHintSession* APerformanceHint_createSession(APerformanceHintManager*
return manager->createSession(threadIds, size, initialTargetWorkDurationNanos);
}
+APerformanceHintSession* APerformanceHint_createSessionUsingConfig(
+ APerformanceHintManager* manager, ASessionCreationConfig* sessionCreationConfig) {
+ VALIDATE_PTR(manager);
+ VALIDATE_PTR(sessionCreationConfig);
+ return manager->createSessionUsingConfig(sessionCreationConfig);
+}
+
+APerformanceHintSession* APerformanceHint_createSessionUsingConfigInternal(
+ APerformanceHintManager* manager, ASessionCreationConfig* sessionCreationConfig,
+ SessionTag tag) {
+ VALIDATE_PTR(manager);
+ VALIDATE_PTR(sessionCreationConfig);
+ return manager->createSessionUsingConfig(sessionCreationConfig,
+ static_cast<hal::SessionTag>(tag));
+}
+
APerformanceHintSession* APerformanceHint_createSessionInternal(
APerformanceHintManager* manager, const int32_t* threadIds, size_t size,
int64_t initialTargetWorkDurationNanos, SessionTag tag) {
@@ -726,11 +1083,31 @@ APerformanceHintSession* APerformanceHint_createSessionInternal(
static_cast<hal::SessionTag>(tag));
}
+APerformanceHintSession* APerformanceHint_createSessionFromJava(
+ APerformanceHintManager* manager, const int32_t* threadIds, size_t size,
+ int64_t initialTargetWorkDurationNanos) {
+ VALIDATE_PTR(manager)
+ VALIDATE_PTR(threadIds)
+ return manager->createSession(threadIds, size, initialTargetWorkDurationNanos,
+ hal::SessionTag::APP, true);
+}
+
+APerformanceHintSession* APerformanceHint_borrowSessionFromJava(JNIEnv* env, jobject sessionObj) {
+ VALIDATE_PTR(env)
+ VALIDATE_PTR(sessionObj)
+ return APerformanceHintManager::getInstance()->getSessionFromJava(env, sessionObj);
+}
+
int64_t APerformanceHint_getPreferredUpdateRateNanos(APerformanceHintManager* manager) {
VALIDATE_PTR(manager)
return manager->getPreferredRateNanos();
}
+int APerformanceHint_getMaxGraphicsPipelineThreadsCount(APerformanceHintManager* manager) {
+ VALIDATE_PTR(manager);
+ return manager->getMaxGraphicsPipelineThreadsCount();
+}
+
int APerformanceHint_updateTargetWorkDuration(APerformanceHintSession* session,
int64_t targetDurationNanos) {
VALIDATE_PTR(session)
@@ -746,12 +1123,24 @@ int APerformanceHint_reportActualWorkDuration(APerformanceHintSession* session,
void APerformanceHint_closeSession(APerformanceHintSession* session) {
VALIDATE_PTR(session)
+ if (session->isJava()) {
+ LOG_ALWAYS_FATAL("%s: Java-owned PerformanceHintSession cannot be closed in native",
+ __FUNCTION__);
+ return;
+ }
+ delete session;
+}
+
+void APerformanceHint_closeSessionFromJava(APerformanceHintSession* session) {
+ VALIDATE_PTR(session)
delete session;
}
int APerformanceHint_sendHint(APerformanceHintSession* session, SessionHint hint) {
VALIDATE_PTR(session)
- return session->sendHint(hint);
+ std::vector<hal::SessionHint> hints{static_cast<hal::SessionHint>(hint)};
+ int64_t now = ::android::uptimeNanos();
+ return session->sendHints(hints, now, "HWUI hint");
}
int APerformanceHint_setThreads(APerformanceHintSession* session, const pid_t* threadIds,
@@ -784,6 +1173,44 @@ int APerformanceHint_reportActualWorkDuration2(APerformanceHintSession* session,
return session->reportActualWorkDuration(workDurationPtr);
}
+int APerformanceHint_notifyWorkloadIncrease(APerformanceHintSession* session, bool cpu, bool gpu,
+ const char* debugName) {
+ VALIDATE_PTR(session)
+ VALIDATE_PTR(debugName)
+ if (!useNewLoadHintBehavior()) {
+ return ENOTSUP;
+ }
+ return session->notifyWorkloadIncrease(cpu, gpu, debugName);
+}
+
+int APerformanceHint_notifyWorkloadReset(APerformanceHintSession* session, bool cpu, bool gpu,
+ const char* debugName) {
+ VALIDATE_PTR(session)
+ VALIDATE_PTR(debugName)
+ if (!useNewLoadHintBehavior()) {
+ return ENOTSUP;
+ }
+ return session->notifyWorkloadReset(cpu, gpu, debugName);
+}
+
+int APerformanceHint_notifyWorkloadSpike(APerformanceHintSession* session, bool cpu, bool gpu,
+ const char* debugName) {
+ VALIDATE_PTR(session)
+ VALIDATE_PTR(debugName)
+ if (!useNewLoadHintBehavior()) {
+ return ENOTSUP;
+ }
+ return session->notifyWorkloadSpike(cpu, gpu, debugName);
+}
+
+int APerformanceHint_setNativeSurfaces(APerformanceHintSession* session,
+ ANativeWindow** nativeWindows, int nativeWindowsSize,
+ ASurfaceControl** surfaceControls, int surfaceControlsSize) {
+ VALIDATE_PTR(session)
+ return session->setNativeSurfaces(nativeWindows, nativeWindowsSize, surfaceControls,
+ surfaceControlsSize);
+}
+
AWorkDuration* AWorkDuration_create() {
return new AWorkDuration();
}
@@ -831,3 +1258,138 @@ void APerformanceHint_setIHintManagerForTesting(void* iManager) {
void APerformanceHint_setUseFMQForTesting(bool enabled) {
gForceFMQEnabled = enabled;
}
+
+ASessionCreationConfig* ASessionCreationConfig_create() {
+ return new ASessionCreationConfig();
+}
+
+void ASessionCreationConfig_release(ASessionCreationConfig* config) {
+ VALIDATE_PTR(config)
+
+ delete config;
+}
+
+int ASessionCreationConfig_setTids(ASessionCreationConfig* config, const pid_t* tids, size_t size) {
+ VALIDATE_PTR(config)
+ VALIDATE_PTR(tids)
+
+ if (!useGraphicsPipeline()) {
+ return ENOTSUP;
+ }
+
+ if (size <= 0) {
+ LOG_ALWAYS_FATAL_IF(size <= 0,
+ "%s: Invalid value. Thread id list size should be greater than zero.",
+ __FUNCTION__);
+ return EINVAL;
+ }
+ config->tids = std::vector<int32_t>(tids, tids + size);
+ return 0;
+}
+
+int ASessionCreationConfig_setTargetWorkDurationNanos(ASessionCreationConfig* config,
+ int64_t targetWorkDurationNanos) {
+ VALIDATE_PTR(config)
+ VALIDATE_INT(targetWorkDurationNanos, >= 0)
+
+ if (!useGraphicsPipeline()) {
+ return ENOTSUP;
+ }
+
+ config->targetWorkDurationNanos = targetWorkDurationNanos;
+ return 0;
+}
+
+int ASessionCreationConfig_setPreferPowerEfficiency(ASessionCreationConfig* config, bool enabled) {
+ VALIDATE_PTR(config)
+
+ if (!useGraphicsPipeline()) {
+ return ENOTSUP;
+ }
+
+ if (enabled) {
+ config->modesToEnable.push_back(hal::SessionMode::POWER_EFFICIENCY);
+ } else {
+ std::erase(config->modesToEnable, hal::SessionMode::POWER_EFFICIENCY);
+ }
+ return 0;
+}
+
+int ASessionCreationConfig_setGraphicsPipeline(ASessionCreationConfig* config, bool enabled) {
+ VALIDATE_PTR(config)
+
+ if (!useGraphicsPipeline()) {
+ return ENOTSUP;
+ }
+
+ if (enabled) {
+ config->modesToEnable.push_back(hal::SessionMode::GRAPHICS_PIPELINE);
+ } else {
+ std::erase(config->modesToEnable, hal::SessionMode::GRAPHICS_PIPELINE);
+
+ // Remove automatic timing modes if we turn off GRAPHICS_PIPELINE,
+ // as it is a strict pre-requisite for these to run
+ std::erase(config->modesToEnable, hal::SessionMode::AUTO_CPU);
+ std::erase(config->modesToEnable, hal::SessionMode::AUTO_GPU);
+ }
+ return 0;
+}
+
+void APerformanceHint_setUseGraphicsPipelineForTesting(bool enabled) {
+ kForceGraphicsPipeline = enabled;
+}
+
+void APerformanceHint_getRateLimiterPropertiesForTesting(int32_t* maxLoadHintsPerInterval,
+ int64_t* loadHintInterval) {
+ *maxLoadHintsPerInterval = kMaxLoadHintsPerInterval;
+ *loadHintInterval = kLoadHintInterval;
+}
+
+void APerformanceHint_setUseNewLoadHintBehaviorForTesting(bool newBehavior) {
+ kForceNewHintBehavior = newBehavior;
+}
+
+int ASessionCreationConfig_setNativeSurfaces(ASessionCreationConfig* config,
+ ANativeWindow** nativeWindows, int nativeWindowsSize,
+ ASurfaceControl** surfaceControls,
+ int surfaceControlsSize) {
+ VALIDATE_PTR(config)
+
+ APerformanceHintManager::layersFromNativeSurfaces<wp<IBinder>>(nativeWindows, nativeWindowsSize,
+ surfaceControls,
+ surfaceControlsSize,
+ config->layers);
+
+ if (config->layers.empty()) {
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+int ASessionCreationConfig_setUseAutoTiming(ASessionCreationConfig* _Nonnull config, bool cpu,
+ bool gpu) {
+ VALIDATE_PTR(config)
+ if ((cpu || gpu) && !config->hasMode(hal::SessionMode::GRAPHICS_PIPELINE)) {
+ ALOGE("Automatic timing is not supported unless graphics pipeline mode is enabled first");
+ return ENOTSUP;
+ }
+
+ if (config->hasMode(hal::SessionMode::AUTO_CPU)) {
+ if (!cpu) {
+ std::erase(config->modesToEnable, hal::SessionMode::AUTO_CPU);
+ }
+ } else if (cpu) {
+ config->modesToEnable.push_back(static_cast<hal::SessionMode>(hal::SessionMode::AUTO_CPU));
+ }
+
+ if (config->hasMode(hal::SessionMode::AUTO_GPU)) {
+ if (!gpu) {
+ std::erase(config->modesToEnable, hal::SessionMode::AUTO_GPU);
+ }
+ } else if (gpu) {
+ config->modesToEnable.push_back(static_cast<hal::SessionMode>(hal::SessionMode::AUTO_GPU));
+ }
+
+ return 0;
+}
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index e46db6bb3727..4fe0b80f3951 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -14,12 +14,15 @@
* limitations under the License.
*/
+#include <android/gui/LutProperties.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/native_window.h>
#include <android/surface_control.h>
#include <android/surface_control_jni.h>
#include <android_runtime/android_view_SurfaceControl.h>
#include <configstore/Utils.h>
+#include <cutils/ashmem.h>
+#include <display_luts_private.h>
#include <gui/HdrMetadata.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
@@ -53,6 +56,16 @@ static_assert(static_cast<int>(ADATASPACE_SCRGB) == static_cast<int>(HAL_DATASPA
static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) ==
static_cast<int>(HAL_DATASPACE_DISPLAY_P3));
static_assert(static_cast<int>(ADATASPACE_BT2020_PQ) == static_cast<int>(HAL_DATASPACE_BT2020_PQ));
+static_assert(static_cast<int>(ADISPLAYLUTS_ONE_DIMENSION) ==
+ static_cast<int>(android::gui::LutProperties::Dimension::ONE_D));
+static_assert(static_cast<int>(ADISPLAYLUTS_THREE_DIMENSION) ==
+ static_cast<int>(android::gui::LutProperties::Dimension::THREE_D));
+static_assert(static_cast<int>(ADISPLAYLUTS_SAMPLINGKEY_RGB) ==
+ static_cast<int>(android::gui::LutProperties::SamplingKey::RGB));
+static_assert(static_cast<int>(ADISPLAYLUTS_SAMPLINGKEY_MAX_RGB) ==
+ static_cast<int>(android::gui::LutProperties::SamplingKey::MAX_RGB));
+static_assert(static_cast<int>(ADISPLAYLUTS_SAMPLINGKEY_CIE_Y) ==
+ static_cast<int>(android::gui::LutProperties::SamplingKey::CIE_Y));
Transaction* ASurfaceTransaction_to_Transaction(ASurfaceTransaction* aSurfaceTransaction) {
return reinterpret_cast<Transaction*>(aSurfaceTransaction);
@@ -693,6 +706,58 @@ void ASurfaceTransaction_setDesiredHdrHeadroom(ASurfaceTransaction* aSurfaceTran
transaction->setDesiredHdrHeadroom(surfaceControl, desiredRatio);
}
+void ASurfaceTransaction_setLuts(ASurfaceTransaction* aSurfaceTransaction,
+ ASurfaceControl* aSurfaceControl,
+ const struct ADisplayLuts* luts) {
+ CHECK_NOT_NULL(aSurfaceTransaction);
+ CHECK_NOT_NULL(aSurfaceControl);
+
+ sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+ Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+ int fd = -1;
+ std::vector<int32_t> offsets;
+ std::vector<int32_t> dimensions;
+ std::vector<int32_t> sizes;
+ std::vector<int32_t> samplingKeys;
+
+ if (luts) {
+ std::vector<float> buffer(luts->totalBufferSize);
+ int32_t count = luts->offsets.size();
+ offsets = luts->offsets;
+
+ dimensions.reserve(count);
+ sizes.reserve(count);
+ samplingKeys.reserve(count);
+ for (int32_t i = 0; i < count; i++) {
+ dimensions.emplace_back(luts->entries[i]->properties.dimension);
+ sizes.emplace_back(luts->entries[i]->properties.size);
+ samplingKeys.emplace_back(luts->entries[i]->properties.samplingKey);
+ std::copy(luts->entries[i]->buffer.data.begin(), luts->entries[i]->buffer.data.end(),
+ buffer.begin() + offsets[i]);
+ }
+
+ // mmap
+ fd = ashmem_create_region("lut_shared_mem", luts->totalBufferSize * sizeof(float));
+ if (fd < 0) {
+ LOG_ALWAYS_FATAL("setLuts, ashmem_create_region() failed");
+ return;
+ }
+ void* ptr = mmap(nullptr, luts->totalBufferSize * sizeof(float), PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, 0);
+ if (ptr == MAP_FAILED) {
+ LOG_ALWAYS_FATAL("setLuts, Failed to map the shared memory");
+ return;
+ }
+
+ memcpy(ptr, buffer.data(), luts->totalBufferSize * sizeof(float));
+ munmap(ptr, luts->totalBufferSize * sizeof(float));
+ }
+
+ transaction->setLuts(surfaceControl, base::unique_fd(fd), offsets, dimensions, sizes,
+ samplingKeys);
+}
+
void ASurfaceTransaction_setColor(ASurfaceTransaction* aSurfaceTransaction,
ASurfaceControl* aSurfaceControl,
float r, float g, float b, float alpha,
diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp
index 91f78ce6f950..0c07b2acbb0c 100644
--- a/native/android/system_fonts.cpp
+++ b/native/android/system_fonts.cpp
@@ -327,7 +327,7 @@ AFont* _Nonnull AFontMatcher_match(
result->mWeight = font->style().weight();
result->mItalic = font->style().slant() == minikin::FontStyle::Slant::ITALIC;
result->mCollectionIndex = minikinFontSkia->GetFontIndex();
- const std::vector<minikin::FontVariation>& axes = minikinFontSkia->GetAxes();
+ const minikin::VariationSettings& axes = minikinFontSkia->GetAxes();
result->mAxes.reserve(axes.size());
for (auto axis : axes) {
result->mAxes.push_back(std::make_pair(axis.axisTag, axis.value));
diff --git a/native/android/system_health.cpp b/native/android/system_health.cpp
new file mode 100644
index 000000000000..f3fa9f6836d5
--- /dev/null
+++ b/native/android/system_health.cpp
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2024 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 <aidl/android/hardware/power/CpuHeadroomParams.h>
+#include <aidl/android/hardware/power/GpuHeadroomParams.h>
+#include <aidl/android/os/CpuHeadroomParamsInternal.h>
+#include <aidl/android/os/GpuHeadroomParamsInternal.h>
+#include <aidl/android/os/IHintManager.h>
+#include <android/binder_manager.h>
+#include <android/system_health.h>
+#include <binder/IServiceManager.h>
+#include <binder/Status.h>
+
+using namespace android;
+using namespace aidl::android::os;
+namespace hal = aidl::android::hardware::power;
+
+struct ACpuHeadroomParams : public CpuHeadroomParamsInternal {};
+struct AGpuHeadroomParams : public GpuHeadroomParamsInternal {};
+
+const int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50;
+const int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000;
+const int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50;
+const int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000;
+const int CPU_HEADROOM_MAX_TID_COUNT = 5;
+
+struct ASystemHealthManager {
+public:
+ static ASystemHealthManager* getInstance();
+ ASystemHealthManager(std::shared_ptr<IHintManager>& hintManager);
+ ASystemHealthManager() = delete;
+ ~ASystemHealthManager();
+ int getCpuHeadroom(const ACpuHeadroomParams* params, float* outHeadroom);
+ int getGpuHeadroom(const AGpuHeadroomParams* params, float* outHeadroom);
+ int getCpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis);
+ int getGpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis);
+
+private:
+ static ASystemHealthManager* create(std::shared_ptr<IHintManager> hintManager);
+ std::shared_ptr<IHintManager> mHintManager;
+};
+
+ASystemHealthManager* ASystemHealthManager::getInstance() {
+ static std::once_flag creationFlag;
+ static ASystemHealthManager* instance = nullptr;
+ std::call_once(creationFlag, []() { instance = create(nullptr); });
+ return instance;
+}
+
+ASystemHealthManager::ASystemHealthManager(std::shared_ptr<IHintManager>& hintManager)
+ : mHintManager(std::move(hintManager)) {}
+
+ASystemHealthManager::~ASystemHealthManager() {}
+
+ASystemHealthManager* ASystemHealthManager::create(std::shared_ptr<IHintManager> hintManager) {
+ if (!hintManager) {
+ hintManager = IHintManager::fromBinder(
+ ndk::SpAIBinder(AServiceManager_waitForService("performance_hint")));
+ }
+ if (hintManager == nullptr) {
+ ALOGE("%s: PerformanceHint service is not ready ", __FUNCTION__);
+ return nullptr;
+ }
+ return new ASystemHealthManager(hintManager);
+}
+
+ASystemHealthManager* ASystemHealth_acquireManager() {
+ return ASystemHealthManager::getInstance();
+}
+
+int ASystemHealthManager::getCpuHeadroom(const ACpuHeadroomParams* params, float* outHeadroom) {
+ std::optional<hal::CpuHeadroomResult> res;
+ ::ndk::ScopedAStatus ret;
+ CpuHeadroomParamsInternal internalParams;
+ if (!params) {
+ ret = mHintManager->getCpuHeadroom(internalParams, &res);
+ } else {
+ ret = mHintManager->getCpuHeadroom(*params, &res);
+ }
+ if (!ret.isOk()) {
+ LOG_ALWAYS_FATAL_IF(ret.getExceptionCode() == EX_ILLEGAL_ARGUMENT,
+ "Invalid ACpuHeadroomParams: %s", ret.getMessage());
+ ALOGE("ASystemHealth_getCpuHeadroom fails: %s", ret.getMessage());
+ if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+ return ENOTSUP;
+ } else if (ret.getExceptionCode() == EX_SECURITY) {
+ return EPERM;
+ }
+ return EPIPE;
+ }
+ *outHeadroom = res->get<hal::CpuHeadroomResult::Tag::globalHeadroom>();
+ return OK;
+}
+
+int ASystemHealthManager::getGpuHeadroom(const AGpuHeadroomParams* params, float* outHeadroom) {
+ std::optional<hal::GpuHeadroomResult> res;
+ ::ndk::ScopedAStatus ret;
+ GpuHeadroomParamsInternal internalParams;
+ if (!params) {
+ ret = mHintManager->getGpuHeadroom(internalParams, &res);
+ } else {
+ ret = mHintManager->getGpuHeadroom(*params, &res);
+ }
+ if (!ret.isOk()) {
+ LOG_ALWAYS_FATAL_IF(ret.getExceptionCode() == EX_ILLEGAL_ARGUMENT,
+ "Invalid AGpuHeadroomParams: %s", ret.getMessage());
+ ALOGE("ASystemHealth_getGpuHeadroom fails: %s", ret.getMessage());
+ if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+ return ENOTSUP;
+ }
+ return EPIPE;
+ }
+ *outHeadroom = res->get<hal::GpuHeadroomResult::Tag::globalHeadroom>();
+ return OK;
+}
+
+int ASystemHealthManager::getCpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis) {
+ int64_t minIntervalMillis = 0;
+ ::ndk::ScopedAStatus ret = mHintManager->getCpuHeadroomMinIntervalMillis(&minIntervalMillis);
+ if (!ret.isOk()) {
+ ALOGE("ASystemHealth_getCpuHeadroomMinIntervalMillis fails: %s", ret.getMessage());
+ if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+ return ENOTSUP;
+ }
+ return EPIPE;
+ }
+ *outMinIntervalMillis = minIntervalMillis;
+ return OK;
+}
+
+int ASystemHealthManager::getGpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis) {
+ int64_t minIntervalMillis = 0;
+ ::ndk::ScopedAStatus ret = mHintManager->getGpuHeadroomMinIntervalMillis(&minIntervalMillis);
+ if (!ret.isOk()) {
+ ALOGE("ASystemHealth_getGpuHeadroomMinIntervalMillis fails: %s", ret.getMessage());
+ if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+ return ENOTSUP;
+ }
+ return EPIPE;
+ }
+ *outMinIntervalMillis = minIntervalMillis;
+ return OK;
+}
+
+int ASystemHealth_getCpuHeadroom(const ACpuHeadroomParams* _Nullable params,
+ float* _Nonnull outHeadroom) {
+ LOG_ALWAYS_FATAL_IF(outHeadroom == nullptr, "%s: outHeadroom should not be null", __FUNCTION__);
+ auto manager = ASystemHealthManager::getInstance();
+ if (manager == nullptr) return ENOTSUP;
+ return manager->getCpuHeadroom(params, outHeadroom);
+}
+
+int ASystemHealth_getGpuHeadroom(const AGpuHeadroomParams* _Nullable params,
+ float* _Nonnull outHeadroom) {
+ LOG_ALWAYS_FATAL_IF(outHeadroom == nullptr, "%s: outHeadroom should not be null", __FUNCTION__);
+ auto manager = ASystemHealthManager::getInstance();
+ if (manager == nullptr) return ENOTSUP;
+ return manager->getGpuHeadroom(params, outHeadroom);
+}
+
+int ASystemHealth_getCpuHeadroomMinIntervalMillis(int64_t* _Nonnull outMinIntervalMillis) {
+ LOG_ALWAYS_FATAL_IF(outMinIntervalMillis == nullptr,
+ "%s: outMinIntervalMillis should not be null", __FUNCTION__);
+ auto manager = ASystemHealthManager::getInstance();
+ if (manager == nullptr) return ENOTSUP;
+ return manager->getCpuHeadroomMinIntervalMillis(outMinIntervalMillis);
+}
+
+int ASystemHealth_getGpuHeadroomMinIntervalMillis(int64_t* _Nonnull outMinIntervalMillis) {
+ LOG_ALWAYS_FATAL_IF(outMinIntervalMillis == nullptr,
+ "%s: outMinIntervalMillis should not be null", __FUNCTION__);
+ auto manager = ASystemHealthManager::getInstance();
+ if (manager == nullptr) return ENOTSUP;
+ return manager->getGpuHeadroomMinIntervalMillis(outMinIntervalMillis);
+}
+
+void ACpuHeadroomParams_setCalculationWindowMillis(ACpuHeadroomParams* _Nonnull params,
+ int windowMillis) {
+ LOG_ALWAYS_FATAL_IF(windowMillis < CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN ||
+ windowMillis > CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX,
+ "%s: windowMillis should be in range [50, 10000] but got %d", __FUNCTION__,
+ windowMillis);
+ params->calculationWindowMillis = windowMillis;
+}
+
+void AGpuHeadroomParams_setCalculationWindowMillis(AGpuHeadroomParams* _Nonnull params,
+ int windowMillis) {
+ LOG_ALWAYS_FATAL_IF(windowMillis < GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN ||
+ windowMillis > GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX,
+ "%s: windowMillis should be in range [50, 10000] but got %d", __FUNCTION__,
+ windowMillis);
+ params->calculationWindowMillis = windowMillis;
+}
+
+int ACpuHeadroomParams_getCalculationWindowMillis(ACpuHeadroomParams* _Nonnull params) {
+ return params->calculationWindowMillis;
+}
+
+int AGpuHeadroomParams_getCalculationWindowMillis(AGpuHeadroomParams* _Nonnull params) {
+ return params->calculationWindowMillis;
+}
+
+void ACpuHeadroomParams_setTids(ACpuHeadroomParams* _Nonnull params, const int* _Nonnull tids,
+ int tidsSize) {
+ LOG_ALWAYS_FATAL_IF(tids == nullptr, "%s: tids should not be null", __FUNCTION__);
+ LOG_ALWAYS_FATAL_IF(tidsSize > CPU_HEADROOM_MAX_TID_COUNT, "%s: tids size should not exceed 5",
+ __FUNCTION__);
+ params->tids.resize(tidsSize);
+ params->tids.clear();
+ for (int i = 0; i < tidsSize; ++i) {
+ LOG_ALWAYS_FATAL_IF(tids[i] <= 0, "ACpuHeadroomParams_setTids: Invalid non-positive tid %d",
+ tids[i]);
+ params->tids[i] = tids[i];
+ }
+}
+
+void ACpuHeadroomParams_setCalculationType(ACpuHeadroomParams* _Nonnull params,
+ ACpuHeadroomCalculationType calculationType) {
+ LOG_ALWAYS_FATAL_IF(calculationType < ACpuHeadroomCalculationType::
+ ACPU_HEADROOM_CALCULATION_TYPE_MIN ||
+ calculationType > ACpuHeadroomCalculationType::
+ ACPU_HEADROOM_CALCULATION_TYPE_AVERAGE,
+ "%s: calculationType should be one of ACpuHeadroomCalculationType values "
+ "but got %d",
+ __FUNCTION__, calculationType);
+ params->calculationType = static_cast<hal::CpuHeadroomParams::CalculationType>(calculationType);
+}
+
+ACpuHeadroomCalculationType ACpuHeadroomParams_getCalculationType(
+ ACpuHeadroomParams* _Nonnull params) {
+ return static_cast<ACpuHeadroomCalculationType>(params->calculationType);
+}
+
+void AGpuHeadroomParams_setCalculationType(AGpuHeadroomParams* _Nonnull params,
+ AGpuHeadroomCalculationType calculationType) {
+ LOG_ALWAYS_FATAL_IF(calculationType < AGpuHeadroomCalculationType::
+ AGPU_HEADROOM_CALCULATION_TYPE_MIN ||
+ calculationType > AGpuHeadroomCalculationType::
+ AGPU_HEADROOM_CALCULATION_TYPE_AVERAGE,
+ "%s: calculationType should be one of AGpuHeadroomCalculationType values "
+ "but got %d",
+ __FUNCTION__, calculationType);
+ params->calculationType = static_cast<hal::GpuHeadroomParams::CalculationType>(calculationType);
+}
+
+AGpuHeadroomCalculationType AGpuHeadroomParams_getCalculationType(
+ AGpuHeadroomParams* _Nonnull params) {
+ return static_cast<AGpuHeadroomCalculationType>(params->calculationType);
+}
+
+ACpuHeadroomParams* _Nonnull ACpuHeadroomParams_create() {
+ return new ACpuHeadroomParams();
+}
+
+AGpuHeadroomParams* _Nonnull AGpuHeadroomParams_create() {
+ return new AGpuHeadroomParams();
+}
+
+void ACpuHeadroomParams_destroy(ACpuHeadroomParams* _Nonnull params) {
+ delete params;
+}
+
+void AGpuHeadroomParams_destroy(AGpuHeadroomParams* _Nonnull params) {
+ delete params;
+}
diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
index 9de3a6f525e6..e3c10f63abb4 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -18,9 +18,11 @@
#include <aidl/android/hardware/power/ChannelConfig.h>
#include <aidl/android/hardware/power/SessionConfig.h>
+#include <aidl/android/hardware/power/SessionMode.h>
#include <aidl/android/hardware/power/SessionTag.h>
#include <aidl/android/hardware/power/WorkDuration.h>
#include <aidl/android/os/IHintManager.h>
+#include <aidl/android/os/SessionCreationConfig.h>
#include <android/binder_manager.h>
#include <android/binder_status.h>
#include <android/performance_hint.h>
@@ -36,6 +38,7 @@ using namespace std::chrono_literals;
namespace hal = aidl::android::hardware::power;
using aidl::android::os::IHintManager;
using aidl::android::os::IHintSession;
+using aidl::android::os::SessionCreationConfig;
using ndk::ScopedAStatus;
using ndk::SpAIBinder;
using HalChannelMessageContents = hal::ChannelMessage::ChannelMessageContents;
@@ -49,11 +52,10 @@ using namespace testing;
class MockIHintManager : public IHintManager {
public:
MOCK_METHOD(ScopedAStatus, createHintSessionWithConfig,
- (const SpAIBinder& token, const ::std::vector<int32_t>& tids, int64_t durationNanos,
- hal::SessionTag tag, hal::SessionConfig* config,
+ (const SpAIBinder& token, hal::SessionTag tag,
+ const SessionCreationConfig& creationConfig, hal::SessionConfig* config,
std::shared_ptr<IHintSession>* _aidl_return),
(override));
- MOCK_METHOD(ScopedAStatus, getHintSessionPreferredRate, (int64_t * _aidl_return), (override));
MOCK_METHOD(ScopedAStatus, setHintSessionThreads,
(const std::shared_ptr<IHintSession>& hintSession,
const ::std::vector<int32_t>& tids),
@@ -66,6 +68,24 @@ public:
std::optional<hal::ChannelConfig>* _aidl_return),
(override));
MOCK_METHOD(ScopedAStatus, closeSessionChannel, (), (override));
+ MOCK_METHOD(ScopedAStatus, getCpuHeadroom,
+ (const ::aidl::android::os::CpuHeadroomParamsInternal& in_params,
+ std::optional<hal::CpuHeadroomResult>* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, getCpuHeadroomMinIntervalMillis, (int64_t* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, getGpuHeadroom,
+ (const ::aidl::android::os::GpuHeadroomParamsInternal& in_params,
+ std::optional<hal::GpuHeadroomResult>* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, getGpuHeadroomMinIntervalMillis, (int64_t* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, passSessionManagerBinder, (const SpAIBinder& sessionManager));
+ MOCK_METHOD(ScopedAStatus, registerClient,
+ (const std::shared_ptr<::aidl::android::os::IHintManager::IHintManagerClient>&
+ clientDataIn,
+ ::aidl::android::os::IHintManager::HintManagerClientData* _aidl_return),
+ (override));
MOCK_METHOD(SpAIBinder, asBinder, (), (override));
MOCK_METHOD(bool, isRemote, (), (override));
};
@@ -82,6 +102,8 @@ public:
MOCK_METHOD(ScopedAStatus, close, (), (override));
MOCK_METHOD(ScopedAStatus, reportActualWorkDuration2,
(const ::std::vector<hal::WorkDuration>& workDurations), (override));
+ MOCK_METHOD(ScopedAStatus, associateToLayers,
+ (const std::vector<::ndk::SpAIBinder>& in_layerTokens), (override));
MOCK_METHOD(SpAIBinder, asBinder, (), (override));
MOCK_METHOD(bool, isRemote, (), (override));
};
@@ -90,7 +112,11 @@ class PerformanceHintTest : public Test {
public:
void SetUp() override {
mMockIHintManager = ndk::SharedRefBase::make<NiceMock<MockIHintManager>>();
+ APerformanceHint_getRateLimiterPropertiesForTesting(&mMaxLoadHintsPerInterval,
+ &mLoadHintInterval);
APerformanceHint_setIHintManagerForTesting(&mMockIHintManager);
+ APerformanceHint_setUseGraphicsPipelineForTesting(true);
+ APerformanceHint_setUseNewLoadHintBehaviorForTesting(true);
}
void TearDown() override {
@@ -101,23 +127,23 @@ public:
APerformanceHintManager* createManager() {
APerformanceHint_setUseFMQForTesting(mUsingFMQ);
- ON_CALL(*mMockIHintManager, getHintSessionPreferredRate(_))
- .WillByDefault(DoAll(SetArgPointee<0>(123L), [] { return ScopedAStatus::ok(); }));
+ ON_CALL(*mMockIHintManager, registerClient(_, _))
+ .WillByDefault(
+ DoAll(SetArgPointee<1>(mClientData), [] { return ScopedAStatus::ok(); }));
return APerformanceHint_getManager();
}
APerformanceHintSession* createSession(APerformanceHintManager* manager,
int64_t targetDuration = 56789L, bool isHwui = false) {
mMockSession = ndk::SharedRefBase::make<NiceMock<MockIHintSession>>();
- int64_t sessionId = 123;
+ const int64_t sessionId = 123;
std::vector<int32_t> tids;
tids.push_back(1);
tids.push_back(2);
- ON_CALL(*mMockIHintManager,
- createHintSessionWithConfig(_, Eq(tids), Eq(targetDuration), _, _, _))
- .WillByDefault(DoAll(SetArgPointee<4>(hal::SessionConfig({.id = sessionId})),
- SetArgPointee<5>(std::shared_ptr<IHintSession>(mMockSession)),
+ ON_CALL(*mMockIHintManager, createHintSessionWithConfig(_, _, _, _, _))
+ .WillByDefault(DoAll(SetArgPointee<3>(hal::SessionConfig({.id = sessionId})),
+ SetArgPointee<4>(std::shared_ptr<IHintSession>(mMockSession)),
[] { return ScopedAStatus::ok(); }));
ON_CALL(*mMockIHintManager, setHintSessionThreads(_, _)).WillByDefault([] {
@@ -142,6 +168,43 @@ public:
return APerformanceHint_createSession(manager, tids.data(), tids.size(), targetDuration);
}
+ APerformanceHintSession* createSessionUsingConfig(APerformanceHintManager* manager,
+ SessionCreationConfig config,
+ bool isHwui = false) {
+ mMockSession = ndk::SharedRefBase::make<NiceMock<MockIHintSession>>();
+ const int64_t sessionId = 123;
+
+ ON_CALL(*mMockIHintManager, createHintSessionWithConfig(_, _, _, _, _))
+ .WillByDefault(DoAll(SetArgPointee<3>(hal::SessionConfig({.id = sessionId})),
+ SetArgPointee<4>(std::shared_ptr<IHintSession>(mMockSession)),
+ [] { return ScopedAStatus::ok(); }));
+
+ ON_CALL(*mMockIHintManager, setHintSessionThreads(_, _)).WillByDefault([] {
+ return ScopedAStatus::ok();
+ });
+ ON_CALL(*mMockSession, sendHint(_)).WillByDefault([] { return ScopedAStatus::ok(); });
+ ON_CALL(*mMockSession, setMode(_, true)).WillByDefault([] { return ScopedAStatus::ok(); });
+ ON_CALL(*mMockSession, close()).WillByDefault([] { return ScopedAStatus::ok(); });
+ ON_CALL(*mMockSession, updateTargetWorkDuration(_)).WillByDefault([] {
+ return ScopedAStatus::ok();
+ });
+ ON_CALL(*mMockSession, reportActualWorkDuration(_, _)).WillByDefault([] {
+ return ScopedAStatus::ok();
+ });
+ ON_CALL(*mMockSession, reportActualWorkDuration2(_)).WillByDefault([] {
+ return ScopedAStatus::ok();
+ });
+
+ if (isHwui) {
+ return APerformanceHint_createSessionUsingConfigInternal(
+ manager, reinterpret_cast<ASessionCreationConfig*>(&config), SessionTag::HWUI);
+ }
+
+ return APerformanceHint_createSessionUsingConfig(manager,
+ reinterpret_cast<ASessionCreationConfig*>(
+ &config));
+ }
+
void setFMQEnabled(bool enabled) {
mUsingFMQ = enabled;
if (enabled) {
@@ -176,6 +239,23 @@ public:
int kMockQueueSize = 20;
bool mUsingFMQ = false;
+ IHintManager::HintManagerClientData mClientData{
+ .powerHalVersion = 6,
+ .maxGraphicsPipelineThreads = 5,
+ .preferredRateNanos = 123L,
+ .supportInfo{
+ .usesSessions = true,
+ .boosts = 0,
+ .modes = 0,
+ .sessionHints = -1,
+ .sessionModes = -1,
+ .sessionTags = -1,
+ },
+ };
+
+ int32_t mMaxLoadHintsPerInterval;
+ int64_t mLoadHintInterval;
+
template <HalChannelMessageContents::Tag T, class C = HalChannelMessageContents::_at<T>>
void expectToReadFromFmq(C expected) {
hal::ChannelMessage readData;
@@ -191,12 +271,6 @@ bool equalsWithoutTimestamp(hal::WorkDuration lhs, hal::WorkDuration rhs) {
lhs.gpuDurationNanos == rhs.gpuDurationNanos && lhs.durationNanos == rhs.durationNanos;
}
-TEST_F(PerformanceHintTest, TestGetPreferredUpdateRateNanos) {
- APerformanceHintManager* manager = createManager();
- int64_t preferredUpdateRateNanos = APerformanceHint_getPreferredUpdateRateNanos(manager);
- EXPECT_EQ(123L, preferredUpdateRateNanos);
-}
-
TEST_F(PerformanceHintTest, TestSession) {
APerformanceHintManager* manager = createManager();
APerformanceHintSession* session = createSession(manager);
@@ -218,7 +292,6 @@ TEST_F(PerformanceHintTest, TestSession) {
EXPECT_CALL(*mMockSession, reportActualWorkDuration2(_)).Times(Exactly(1));
result = APerformanceHint_reportActualWorkDuration(session, actualDurationNanos);
EXPECT_EQ(0, result);
-
result = APerformanceHint_updateTargetWorkDuration(session, -1L);
EXPECT_EQ(EINVAL, result);
result = APerformanceHint_reportActualWorkDuration(session, -1L);
@@ -228,33 +301,57 @@ TEST_F(PerformanceHintTest, TestSession) {
EXPECT_CALL(*mMockSession, sendHint(Eq(hintId))).Times(Exactly(1));
result = APerformanceHint_sendHint(session, hintId);
EXPECT_EQ(0, result);
- usleep(110000); // Sleep for longer than the update timeout.
- EXPECT_CALL(*mMockSession, sendHint(Eq(hintId))).Times(Exactly(1));
- result = APerformanceHint_sendHint(session, hintId);
+ EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::CPU_LOAD_UP))).Times(Exactly(1));
+ result = APerformanceHint_notifyWorkloadIncrease(session, true, false, "Test hint");
EXPECT_EQ(0, result);
- // Expect to get rate limited if we try to send faster than the limiter allows
- EXPECT_CALL(*mMockSession, sendHint(Eq(hintId))).Times(Exactly(0));
- result = APerformanceHint_sendHint(session, hintId);
+ EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::CPU_LOAD_RESET))).Times(Exactly(1));
+ EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::GPU_LOAD_RESET))).Times(Exactly(1));
+ result = APerformanceHint_notifyWorkloadReset(session, true, true, "Test hint");
+ EXPECT_EQ(0, result);
+ EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::CPU_LOAD_SPIKE))).Times(Exactly(1));
+ EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::GPU_LOAD_SPIKE))).Times(Exactly(1));
+ result = APerformanceHint_notifyWorkloadSpike(session, true, true, "Test hint");
EXPECT_EQ(0, result);
result = APerformanceHint_sendHint(session, static_cast<SessionHint>(-1));
EXPECT_EQ(EINVAL, result);
+ Mock::VerifyAndClearExpectations(mMockSession.get());
+ for (int i = 0; i < mMaxLoadHintsPerInterval; ++i) {
+ APerformanceHint_sendHint(session, hintId);
+ }
+
+ // Expect to get rate limited if we try to send faster than the limiter allows
+ EXPECT_CALL(*mMockSession, sendHint(_)).Times(Exactly(0));
+ result = APerformanceHint_notifyWorkloadIncrease(session, true, true, "Test hint");
+ EXPECT_EQ(result, EBUSY);
+ EXPECT_CALL(*mMockSession, sendHint(_)).Times(Exactly(0));
+ result = APerformanceHint_notifyWorkloadReset(session, true, true, "Test hint");
EXPECT_CALL(*mMockSession, close()).Times(Exactly(1));
APerformanceHint_closeSession(session);
}
TEST_F(PerformanceHintTest, TestUpdatedSessionCreation) {
- EXPECT_CALL(*mMockIHintManager, createHintSessionWithConfig(_, _, _, _, _, _)).Times(1);
+ EXPECT_CALL(*mMockIHintManager, createHintSessionWithConfig(_, _, _, _, _)).Times(1);
APerformanceHintManager* manager = createManager();
APerformanceHintSession* session = createSession(manager);
ASSERT_TRUE(session);
APerformanceHint_closeSession(session);
}
+TEST_F(PerformanceHintTest, TestSessionCreationUsingConfig) {
+ EXPECT_CALL(*mMockIHintManager, createHintSessionWithConfig(_, _, _, _, _)).Times(1);
+ SessionCreationConfig config{.tids = std::vector<int32_t>(1, 2),
+ .targetWorkDurationNanos = 5678,
+ .modesToEnable = std::vector<hal::SessionMode>(0)};
+ APerformanceHintManager* manager = createManager();
+ APerformanceHintSession* session = createSessionUsingConfig(manager, config);
+ ASSERT_TRUE(session);
+ APerformanceHint_closeSession(session);
+}
+
TEST_F(PerformanceHintTest, TestHwuiSessionCreation) {
- EXPECT_CALL(*mMockIHintManager,
- createHintSessionWithConfig(_, _, _, hal::SessionTag::HWUI, _, _))
+ EXPECT_CALL(*mMockIHintManager, createHintSessionWithConfig(_, hal::SessionTag::HWUI, _, _, _))
.Times(1);
APerformanceHintManager* manager = createManager();
APerformanceHintSession* session = createSession(manager, 56789L, true);
@@ -420,3 +517,16 @@ TEST_F(PerformanceHintTest, TestReportActualUsingFMQ) {
reinterpret_cast<AWorkDuration*>(&duration));
expectToReadFromFmq<HalChannelMessageContents::Tag::workDuration>(durationExpected);
}
+
+TEST_F(PerformanceHintTest, TestASessionCreationConfig) {
+ ASessionCreationConfig* config = ASessionCreationConfig_create();
+ ASSERT_NE(config, nullptr);
+
+ const int32_t testTids[2] = {1, 2};
+ const size_t size = 2;
+ EXPECT_EQ(ASessionCreationConfig_setTids(config, testTids, size), 0);
+ EXPECT_EQ(ASessionCreationConfig_setTargetWorkDurationNanos(config, 20), 0);
+ EXPECT_EQ(ASessionCreationConfig_setPreferPowerEfficiency(config, true), 0);
+ EXPECT_EQ(ASessionCreationConfig_setGraphicsPipeline(config, true), 0);
+ ASessionCreationConfig_release(config);
+}
diff --git a/native/android/tests/thermal/NativeThermalUnitTest.cpp b/native/android/tests/thermal/NativeThermalUnitTest.cpp
index 6d6861a3026a..923ad011de41 100644
--- a/native/android/tests/thermal/NativeThermalUnitTest.cpp
+++ b/native/android/tests/thermal/NativeThermalUnitTest.cpp
@@ -67,14 +67,72 @@ public:
MOCK_METHOD(Status, getThermalHeadroomThresholds, (::std::vector<float> * _aidl_return),
(override));
MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+ MOCK_METHOD(Status, registerThermalHeadroomListener,
+ (const ::android::sp<::android::os::IThermalHeadroomListener>& listener,
+ bool* _aidl_return),
+ (override));
+ MOCK_METHOD(Status, unregisterThermalHeadroomListener,
+ (const ::android::sp<::android::os::IThermalHeadroomListener>& listener,
+ bool* _aidl_return),
+ (override));
};
+struct HeadroomCallbackData {
+ void* data;
+ float headroom;
+ float forecast;
+ int32_t forecastSeconds;
+ const std::vector<float> thresholds;
+};
+
+struct StatusCallbackData {
+ void* data;
+ AThermalStatus status;
+};
+
+static std::optional<HeadroomCallbackData> headroomCalled1;
+static std::optional<HeadroomCallbackData> headroomCalled2;
+static std::optional<StatusCallbackData> statusCalled1;
+static std::optional<StatusCallbackData> statusCalled2;
+
+static std::vector<float> convertThresholds(const AThermalHeadroomThreshold* thresholds,
+ size_t size) {
+ std::vector<float> ret;
+ for (int i = 0; i < (int)size; i++) {
+ ret.emplace_back(thresholds[i].headroom);
+ }
+ return ret;
+};
+
+static void onHeadroomChange1(void* data, float headroom, float forecast, int32_t forecastSeconds,
+ const AThermalHeadroomThreshold* thresholds, size_t size) {
+ headroomCalled1.emplace(data, headroom, forecast, forecastSeconds,
+ convertThresholds(thresholds, size));
+}
+
+static void onHeadroomChange2(void* data, float headroom, float forecast, int32_t forecastSeconds,
+ const AThermalHeadroomThreshold* thresholds, size_t size) {
+ headroomCalled2.emplace(data, headroom, forecast, forecastSeconds,
+ convertThresholds(thresholds, size));
+}
+
+static void onStatusChange1(void* data, AThermalStatus status) {
+ statusCalled1.emplace(data, status);
+}
+static void onStatusChange2(void* data, AThermalStatus status) {
+ statusCalled2.emplace(data, status);
+}
+
class NativeThermalUnitTest : public Test {
public:
void SetUp() override {
mMockIThermalService = new StrictMock<MockIThermalService>();
AThermal_setIThermalServiceForTesting(mMockIThermalService);
mThermalManager = AThermal_acquireManager();
+ headroomCalled1.reset();
+ headroomCalled2.reset();
+ statusCalled1.reset();
+ statusCalled2.reset();
}
void TearDown() override {
@@ -109,9 +167,11 @@ TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholds) {
size_t size1;
ASSERT_EQ(OK, AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds1, &size1));
checkThermalHeadroomThresholds(expected, thresholds1, size1);
- // following calls should be cached
- EXPECT_CALL(*mMockIThermalService, getThermalHeadroomThresholds(_)).Times(0);
-
+ // following calls should not be cached
+ expected = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
+ EXPECT_CALL(*mMockIThermalService, getThermalHeadroomThresholds(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(expected), Return(Status())));
const AThermalHeadroomThreshold* thresholds2 = nullptr;
size_t size2;
ASSERT_EQ(OK, AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds2, &size2));
@@ -156,3 +216,248 @@ TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholdsFailedWithNonEmpty
ASSERT_EQ(EINVAL, AThermal_getThermalHeadroomThresholds(mThermalManager, &initialized, &size));
delete[] initialized;
}
+
+TEST_F(NativeThermalUnitTest, TestRegisterThermalHeadroomListener) {
+ EXPECT_CALL(*mMockIThermalService, registerThermalHeadroomListener(_, _))
+ .Times(Exactly(2))
+ .WillOnce(Return(
+ Status::fromExceptionCode(binder::Status::Exception::EX_TRANSACTION_FAILED)));
+ float data1 = 1.0f;
+ float data2 = 2.0f;
+ ASSERT_EQ(EPIPE,
+ AThermal_registerThermalHeadroomListener(mThermalManager, onHeadroomChange1, &data1));
+ ASSERT_EQ(EPIPE,
+ AThermal_registerThermalHeadroomListener(mThermalManager, onHeadroomChange2, &data2));
+
+ // verify only 1 service call to register a global listener
+ sp<IThermalHeadroomListener> capturedServiceListener;
+ Mock::VerifyAndClearExpectations(mMockIThermalService);
+ EXPECT_CALL(*mMockIThermalService, registerThermalHeadroomListener(_, _))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(testing::SaveArg<0>(&capturedServiceListener),
+ testing::Invoke([](const sp<IThermalHeadroomListener>&,
+ bool* aidl_return) { *aidl_return = true; }),
+ Return(Status::ok())));
+ ASSERT_EQ(0,
+ AThermal_registerThermalHeadroomListener(mThermalManager, onHeadroomChange1, &data1));
+ ASSERT_EQ(EINVAL,
+ AThermal_registerThermalHeadroomListener(mThermalManager, onHeadroomChange1, &data1));
+ ASSERT_EQ(0,
+ AThermal_registerThermalHeadroomListener(mThermalManager, onHeadroomChange2, &data2));
+ const ::std::vector<float> thresholds = {0.1f, 0.2f};
+ capturedServiceListener->onHeadroomChange(0.1f, 0.3f, 20, thresholds);
+ ASSERT_TRUE(headroomCalled1.has_value());
+ EXPECT_EQ(headroomCalled1->data, &data1);
+ EXPECT_EQ(headroomCalled1->headroom, 0.1f);
+ EXPECT_EQ(headroomCalled1->forecast, 0.3f);
+ EXPECT_EQ(headroomCalled1->forecastSeconds, 20);
+ EXPECT_EQ(headroomCalled1->thresholds, thresholds);
+ ASSERT_TRUE(headroomCalled2.has_value());
+ EXPECT_EQ(headroomCalled2->data, &data2);
+ EXPECT_EQ(headroomCalled2->headroom, 0.1f);
+ EXPECT_EQ(headroomCalled2->forecast, 0.3f);
+ EXPECT_EQ(headroomCalled2->forecastSeconds, 20);
+ EXPECT_EQ(headroomCalled2->thresholds, thresholds);
+
+ // after test finished the global service listener should be unregistered
+ EXPECT_CALL(*mMockIThermalService, unregisterThermalHeadroomListener(_, _))
+ .Times(Exactly(1))
+ .WillOnce(Return(binder::Status::ok()));
+}
+
+TEST_F(NativeThermalUnitTest, TestUnregisterThermalHeadroomListener) {
+ sp<IThermalHeadroomListener> capturedServiceListener;
+ EXPECT_CALL(*mMockIThermalService, registerThermalHeadroomListener(_, _))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(testing::SaveArg<0>(&capturedServiceListener),
+ testing::Invoke([](const sp<IThermalHeadroomListener>&,
+ bool* aidl_return) { *aidl_return = true; }),
+ Return(Status::ok())));
+ float data1 = 1.0f;
+ float data2 = 2.0f;
+ ASSERT_EQ(0,
+ AThermal_registerThermalHeadroomListener(mThermalManager, onHeadroomChange1, &data1));
+ ASSERT_EQ(0,
+ AThermal_registerThermalHeadroomListener(mThermalManager, onHeadroomChange2, &data2));
+ capturedServiceListener->onHeadroomChange(0.1f, 0.3f, 20, {});
+ ASSERT_TRUE(headroomCalled1.has_value());
+ ASSERT_TRUE(headroomCalled2.has_value());
+
+ EXPECT_CALL(*mMockIThermalService, unregisterThermalHeadroomListener(_, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(binder::Status::Exception::EX_TRANSACTION_FAILED)));
+
+ // callback 1 should be unregistered and callback 2 unregistration should fail due to service
+ // listener unregistration call failure
+ ASSERT_EQ(0,
+ AThermal_unregisterThermalHeadroomListener(mThermalManager, onHeadroomChange1,
+ &data1));
+ ASSERT_EQ(EPIPE,
+ AThermal_unregisterThermalHeadroomListener(mThermalManager, onHeadroomChange2,
+ &data2));
+ // verify only callback 2 is called after callback 1 is unregistered
+ std::vector<float> thresholds = {0.1f, 0.2f};
+ headroomCalled1.reset();
+ headroomCalled2.reset();
+ capturedServiceListener->onHeadroomChange(0.1f, 0.3f, 20, thresholds);
+ ASSERT_TRUE(!headroomCalled1.has_value());
+ ASSERT_TRUE(headroomCalled2.has_value());
+
+ // verify only 1 service call to unregister global service listener
+ Mock::VerifyAndClearExpectations(mMockIThermalService);
+ EXPECT_CALL(*mMockIThermalService, unregisterThermalHeadroomListener(_, _))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(testing::Invoke([](const sp<IThermalHeadroomListener>&,
+ bool* aidl_return) { *aidl_return = true; }),
+ Return(Status::ok())));
+ ASSERT_EQ(EINVAL,
+ AThermal_unregisterThermalHeadroomListener(mThermalManager, onHeadroomChange1,
+ &data1));
+ ASSERT_EQ(0,
+ AThermal_unregisterThermalHeadroomListener(mThermalManager, onHeadroomChange2,
+ &data2));
+ // verify neither callback is called after global service listener is unregistered
+ headroomCalled1.reset();
+ headroomCalled2.reset();
+ capturedServiceListener->onHeadroomChange(0.1f, 0.3f, 20, thresholds);
+ ASSERT_TRUE(!headroomCalled1.has_value());
+ ASSERT_TRUE(!headroomCalled2.has_value());
+
+ // verify adding a new callback will still work
+ Mock::VerifyAndClearExpectations(mMockIThermalService);
+ EXPECT_CALL(*mMockIThermalService, registerThermalHeadroomListener(_, _))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(testing::SaveArg<0>(&capturedServiceListener),
+ testing::Invoke([](const sp<IThermalHeadroomListener>&,
+ bool* aidl_return) { *aidl_return = true; }),
+ Return(Status::ok())));
+ ASSERT_EQ(0,
+ AThermal_registerThermalHeadroomListener(mThermalManager, onHeadroomChange1, &data1));
+ headroomCalled1.reset();
+ capturedServiceListener->onHeadroomChange(0.1f, 0.3f, 20, thresholds);
+ ASSERT_TRUE(headroomCalled1.has_value());
+ EXPECT_EQ(headroomCalled1->data, &data1);
+ EXPECT_EQ(headroomCalled1->headroom, 0.1f);
+ EXPECT_EQ(headroomCalled1->forecast, 0.3f);
+ EXPECT_EQ(headroomCalled1->forecastSeconds, 20);
+ EXPECT_EQ(headroomCalled1->thresholds, thresholds);
+
+ // after test finished the global service listener should be unregistered
+ EXPECT_CALL(*mMockIThermalService, unregisterThermalHeadroomListener(_, _))
+ .Times(Exactly(1))
+ .WillOnce(Return(binder::Status::ok()));
+}
+
+TEST_F(NativeThermalUnitTest, TestRegisterThermalStatusListener) {
+ EXPECT_CALL(*mMockIThermalService, registerThermalStatusListener(_, _))
+ .Times(Exactly(2))
+ .WillOnce(Return(
+ Status::fromExceptionCode(binder::Status::Exception::EX_TRANSACTION_FAILED)));
+ int data1 = 1;
+ int data2 = 2;
+ ASSERT_EQ(EPIPE,
+ AThermal_registerThermalStatusListener(mThermalManager, onStatusChange1, &data1));
+ ASSERT_EQ(EPIPE,
+ AThermal_registerThermalStatusListener(mThermalManager, onStatusChange2, &data2));
+
+ // verify only 1 service call to register a global listener
+ sp<IThermalStatusListener> capturedServiceListener;
+ Mock::VerifyAndClearExpectations(mMockIThermalService);
+ EXPECT_CALL(*mMockIThermalService, registerThermalStatusListener(_, _))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(testing::SaveArg<0>(&capturedServiceListener),
+ testing::Invoke([](const sp<IThermalStatusListener>&,
+ bool* aidl_return) { *aidl_return = true; }),
+ Return(Status::ok())));
+ ASSERT_EQ(0, AThermal_registerThermalStatusListener(mThermalManager, onStatusChange1, &data1));
+ ASSERT_EQ(EINVAL,
+ AThermal_registerThermalStatusListener(mThermalManager, onStatusChange1, &data1));
+ ASSERT_EQ(0, AThermal_registerThermalStatusListener(mThermalManager, onStatusChange2, &data2));
+
+ capturedServiceListener->onStatusChange(AThermalStatus::ATHERMAL_STATUS_LIGHT);
+ ASSERT_TRUE(statusCalled1.has_value());
+ EXPECT_EQ(statusCalled1->data, &data1);
+ EXPECT_EQ(statusCalled1->status, AThermalStatus::ATHERMAL_STATUS_LIGHT);
+ ASSERT_TRUE(statusCalled2.has_value());
+ EXPECT_EQ(statusCalled2->data, &data2);
+ EXPECT_EQ(statusCalled2->status, AThermalStatus::ATHERMAL_STATUS_LIGHT);
+
+ // after test finished the callback should be unregistered
+ EXPECT_CALL(*mMockIThermalService, unregisterThermalStatusListener(_, _))
+ .Times(Exactly(1))
+ .WillOnce(Return(binder::Status::ok()));
+}
+
+TEST_F(NativeThermalUnitTest, TestUnregisterThermalStatusListener) {
+ sp<IThermalStatusListener> capturedServiceListener;
+ EXPECT_CALL(*mMockIThermalService, registerThermalStatusListener(_, _))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(testing::SaveArg<0>(&capturedServiceListener),
+ testing::Invoke([](const sp<IThermalStatusListener>&,
+ bool* aidl_return) { *aidl_return = true; }),
+ Return(Status::ok())));
+ int data1 = 1;
+ int data2 = 2;
+ ASSERT_EQ(0, AThermal_registerThermalStatusListener(mThermalManager, onStatusChange1, &data1));
+ ASSERT_EQ(0, AThermal_registerThermalStatusListener(mThermalManager, onStatusChange2, &data2));
+ capturedServiceListener->onStatusChange(AThermalStatus::ATHERMAL_STATUS_LIGHT);
+ ASSERT_TRUE(statusCalled1.has_value());
+ ASSERT_TRUE(statusCalled2.has_value());
+
+ EXPECT_CALL(*mMockIThermalService, unregisterThermalStatusListener(_, _))
+ .Times(Exactly(1))
+ .WillOnce(Return(
+ Status::fromExceptionCode(binder::Status::Exception::EX_TRANSACTION_FAILED)));
+ // callback 1 should be unregistered and callback 2 unregistration should fail due to service
+ // listener unregistration call failure
+ ASSERT_EQ(0,
+ AThermal_unregisterThermalStatusListener(mThermalManager, onStatusChange1, &data1));
+ ASSERT_EQ(EPIPE,
+ AThermal_unregisterThermalStatusListener(mThermalManager, onStatusChange2, &data2));
+
+ // verify only callback 2 is called after callback 1 is unregistered
+ statusCalled1.reset();
+ statusCalled2.reset();
+ capturedServiceListener->onStatusChange(AThermalStatus::ATHERMAL_STATUS_LIGHT);
+ ASSERT_TRUE(!statusCalled1.has_value());
+ ASSERT_TRUE(statusCalled2.has_value());
+
+ // verify only 1 service call to unregister global service listener
+ Mock::VerifyAndClearExpectations(mMockIThermalService);
+ EXPECT_CALL(*mMockIThermalService, unregisterThermalStatusListener(_, _))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(testing::Invoke([](const sp<IThermalStatusListener>&,
+ bool* aidl_return) { *aidl_return = true; }),
+ Return(Status::ok())));
+ ASSERT_EQ(EINVAL,
+ AThermal_unregisterThermalStatusListener(mThermalManager, onStatusChange1, &data1));
+ ASSERT_EQ(0,
+ AThermal_unregisterThermalStatusListener(mThermalManager, onStatusChange2, &data2));
+ // verify neither callback is called after global service listener is unregistered
+ statusCalled1.reset();
+ statusCalled2.reset();
+ capturedServiceListener->onStatusChange(AThermalStatus::ATHERMAL_STATUS_LIGHT);
+ ASSERT_TRUE(!statusCalled1.has_value());
+ ASSERT_TRUE(!statusCalled2.has_value());
+
+ // verify adding a new callback will still work
+ Mock::VerifyAndClearExpectations(mMockIThermalService);
+ EXPECT_CALL(*mMockIThermalService, registerThermalStatusListener(_, _))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(testing::SaveArg<0>(&capturedServiceListener),
+ testing::Invoke([](const sp<IThermalStatusListener>&,
+ bool* aidl_return) { *aidl_return = true; }),
+ Return(Status::ok())));
+ ASSERT_EQ(0, AThermal_registerThermalStatusListener(mThermalManager, onStatusChange1, &data1));
+ statusCalled1.reset();
+ capturedServiceListener->onStatusChange(AThermalStatus::ATHERMAL_STATUS_LIGHT);
+ ASSERT_TRUE(statusCalled1.has_value());
+ EXPECT_EQ(statusCalled1->data, &data1);
+ EXPECT_EQ(statusCalled1->status, AThermalStatus::ATHERMAL_STATUS_LIGHT);
+
+ // after test finished the global service listener should be unregistered
+ EXPECT_CALL(*mMockIThermalService, unregisterThermalStatusListener(_, _))
+ .Times(Exactly(1))
+ .WillOnce(Return(binder::Status::ok()));
+}
diff --git a/native/android/thermal.cpp b/native/android/thermal.cpp
index f7a3537d3f4a..cefcaf7766bb 100644
--- a/native/android/thermal.cpp
+++ b/native/android/thermal.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "thermal"
#include <android-base/thread_annotations.h>
+#include <android/os/BnThermalHeadroomListener.h>
#include <android/os/BnThermalStatusListener.h>
#include <android/os/IThermalService.h>
#include <android/thermal.h>
@@ -33,10 +34,10 @@ using android::sp;
using namespace android;
using namespace android::os;
-struct ThermalServiceListener : public BnThermalStatusListener {
+struct ThermalServiceStatusListener : public BnThermalStatusListener {
public:
virtual binder::Status onStatusChange(int32_t status) override;
- ThermalServiceListener(AThermalManager *manager) {
+ ThermalServiceStatusListener(AThermalManager *manager) {
mMgr = manager;
}
@@ -44,11 +45,29 @@ private:
AThermalManager *mMgr;
};
-struct ListenerCallback {
+struct ThermalServiceHeadroomListener : public BnThermalHeadroomListener {
+public:
+ virtual binder::Status onHeadroomChange(float headroom, float forecastHeadroom,
+ int32_t forecastSeconds,
+ const ::std::vector<float> &thresholds) override;
+ ThermalServiceHeadroomListener(AThermalManager *manager) {
+ mMgr = manager;
+ }
+
+private:
+ AThermalManager *mMgr;
+};
+
+struct StatusListenerCallback {
AThermal_StatusCallback callback;
void* data;
};
+struct HeadroomListenerCallback {
+ AThermal_HeadroomCallback callback;
+ void *data;
+};
+
static IThermalService *gIThermalServiceForTesting = nullptr;
struct AThermalManager {
@@ -57,30 +76,44 @@ public:
AThermalManager() = delete;
~AThermalManager();
status_t notifyStateChange(int32_t status);
+ status_t notifyHeadroomChange(float headroom, float forecastHeadroom, int32_t forecastSeconds,
+ const ::std::vector<float> &thresholds);
status_t getCurrentThermalStatus(int32_t *status);
- status_t addListener(AThermal_StatusCallback, void *data);
- status_t removeListener(AThermal_StatusCallback, void *data);
+ status_t addStatusListener(AThermal_StatusCallback, void *data);
+ status_t removeStatusListener(AThermal_StatusCallback, void *data);
status_t getThermalHeadroom(int32_t forecastSeconds, float *result);
status_t getThermalHeadroomThresholds(const AThermalHeadroomThreshold **, size_t *size);
+ status_t addHeadroomListener(AThermal_HeadroomCallback, void *data);
+ status_t removeHeadroomListener(AThermal_HeadroomCallback, void *data);
private:
AThermalManager(sp<IThermalService> service);
sp<IThermalService> mThermalSvc;
- std::mutex mListenerMutex;
- sp<ThermalServiceListener> mServiceListener GUARDED_BY(mListenerMutex);
- std::vector<ListenerCallback> mListeners GUARDED_BY(mListenerMutex);
- std::mutex mThresholdsMutex;
- const AThermalHeadroomThreshold *mThresholds = nullptr; // GUARDED_BY(mThresholdsMutex)
- size_t mThresholdsCount GUARDED_BY(mThresholdsMutex);
+ std::mutex mStatusListenerMutex;
+ sp<ThermalServiceStatusListener> mServiceStatusListener GUARDED_BY(mStatusListenerMutex);
+ std::vector<StatusListenerCallback> mStatusListeners GUARDED_BY(mStatusListenerMutex);
+
+ std::mutex mHeadroomListenerMutex;
+ sp<ThermalServiceHeadroomListener> mServiceHeadroomListener GUARDED_BY(mHeadroomListenerMutex);
+ std::vector<HeadroomListenerCallback> mHeadroomListeners GUARDED_BY(mHeadroomListenerMutex);
};
-binder::Status ThermalServiceListener::onStatusChange(int32_t status) {
+binder::Status ThermalServiceStatusListener::onStatusChange(int32_t status) {
if (mMgr != nullptr) {
mMgr->notifyStateChange(status);
}
return binder::Status::ok();
}
+binder::Status ThermalServiceHeadroomListener::onHeadroomChange(
+ float headroom, float forecastHeadroom, int32_t forecastSeconds,
+ const ::std::vector<float> &thresholds) {
+ if (mMgr != nullptr) {
+ mMgr->notifyHeadroomChange(headroom, forecastHeadroom, forecastSeconds, thresholds);
+ }
+ return binder::Status::ok();
+}
+
AThermalManager* AThermalManager::createAThermalManager() {
if (gIThermalServiceForTesting) {
return new AThermalManager(gIThermalServiceForTesting);
@@ -96,97 +129,183 @@ AThermalManager* AThermalManager::createAThermalManager() {
}
AThermalManager::AThermalManager(sp<IThermalService> service)
- : mThermalSvc(std::move(service)), mServiceListener(nullptr) {}
+ : mThermalSvc(std::move(service)),
+ mServiceStatusListener(nullptr),
+ mServiceHeadroomListener(nullptr) {}
AThermalManager::~AThermalManager() {
{
- std::scoped_lock<std::mutex> listenerLock(mListenerMutex);
- mListeners.clear();
- if (mServiceListener != nullptr) {
+ std::scoped_lock<std::mutex> listenerLock(mStatusListenerMutex);
+ mStatusListeners.clear();
+ if (mServiceStatusListener != nullptr) {
bool success = false;
- mThermalSvc->unregisterThermalStatusListener(mServiceListener, &success);
- mServiceListener = nullptr;
+ mThermalSvc->unregisterThermalStatusListener(mServiceStatusListener, &success);
+ mServiceStatusListener = nullptr;
+ }
+ }
+ {
+ std::scoped_lock<std::mutex> headroomListenerLock(mHeadroomListenerMutex);
+ mHeadroomListeners.clear();
+ if (mServiceHeadroomListener != nullptr) {
+ bool success = false;
+ mThermalSvc->unregisterThermalHeadroomListener(mServiceHeadroomListener, &success);
+ mServiceHeadroomListener = nullptr;
}
}
- std::scoped_lock<std::mutex> lock(mThresholdsMutex);
- delete[] mThresholds;
}
status_t AThermalManager::notifyStateChange(int32_t status) {
- std::scoped_lock<std::mutex> lock(mListenerMutex);
+ std::scoped_lock<std::mutex> lock(mStatusListenerMutex);
AThermalStatus thermalStatus = static_cast<AThermalStatus>(status);
- for (auto listener : mListeners) {
+ for (auto listener : mStatusListeners) {
listener.callback(listener.data, thermalStatus);
}
return OK;
}
-status_t AThermalManager::addListener(AThermal_StatusCallback callback, void *data) {
- std::scoped_lock<std::mutex> lock(mListenerMutex);
+status_t AThermalManager::notifyHeadroomChange(float headroom, float forecastHeadroom,
+ int32_t forecastSeconds,
+ const ::std::vector<float> &thresholds) {
+ std::scoped_lock<std::mutex> lock(mHeadroomListenerMutex);
+ size_t thresholdsCount = thresholds.size();
+ auto t = new AThermalHeadroomThreshold[thresholdsCount];
+ for (int i = 0; i < (int)thresholdsCount; i++) {
+ t[i].headroom = thresholds[i];
+ t[i].thermalStatus = static_cast<AThermalStatus>(i);
+ }
+ for (auto listener : mHeadroomListeners) {
+ listener.callback(listener.data, headroom, forecastHeadroom, forecastSeconds, t,
+ thresholdsCount);
+ }
+ delete[] t;
+ return OK;
+}
+
+status_t AThermalManager::addStatusListener(AThermal_StatusCallback callback, void *data) {
+ std::scoped_lock<std::mutex> lock(mStatusListenerMutex);
if (callback == nullptr) {
// Callback can not be nullptr
return EINVAL;
}
- for (const auto& cb : mListeners) {
+ for (const auto &cb : mStatusListeners) {
// Don't re-add callbacks.
if (callback == cb.callback && data == cb.data) {
return EINVAL;
}
}
- mListeners.emplace_back(ListenerCallback{callback, data});
- if (mServiceListener != nullptr) {
+ if (mServiceStatusListener != nullptr) {
+ mStatusListeners.emplace_back(StatusListenerCallback{callback, data});
return OK;
}
bool success = false;
- mServiceListener = new ThermalServiceListener(this);
- if (mServiceListener == nullptr) {
+ mServiceStatusListener = new ThermalServiceStatusListener(this);
+ if (mServiceStatusListener == nullptr) {
return ENOMEM;
}
- auto ret = mThermalSvc->registerThermalStatusListener(mServiceListener, &success);
+ auto ret = mThermalSvc->registerThermalStatusListener(mServiceStatusListener, &success);
if (!success || !ret.isOk()) {
+ mServiceStatusListener = nullptr;
ALOGE("Failed in registerThermalStatusListener %d", success);
if (ret.exceptionCode() == binder::Status::EX_SECURITY) {
return EPERM;
}
return EPIPE;
}
+ mStatusListeners.emplace_back(StatusListenerCallback{callback, data});
return OK;
}
-status_t AThermalManager::removeListener(AThermal_StatusCallback callback, void *data) {
- std::scoped_lock<std::mutex> lock(mListenerMutex);
+status_t AThermalManager::removeStatusListener(AThermal_StatusCallback callback, void *data) {
+ std::scoped_lock<std::mutex> lock(mStatusListenerMutex);
- auto it = std::remove_if(mListeners.begin(),
- mListeners.end(),
- [&](const ListenerCallback& cb) {
- return callback == cb.callback &&
- data == cb.data;
+ auto it = std::remove_if(mStatusListeners.begin(), mStatusListeners.end(),
+ [&](const StatusListenerCallback &cb) {
+ return callback == cb.callback && data == cb.data;
});
- if (it == mListeners.end()) {
+ if (it == mStatusListeners.end()) {
// If the listener and data pointer were not previously added.
return EINVAL;
}
- mListeners.erase(it, mListeners.end());
+ if (mServiceStatusListener == nullptr || mStatusListeners.size() > 1) {
+ mStatusListeners.erase(it, mStatusListeners.end());
+ return OK;
+ }
- if (!mListeners.empty()) {
+ bool success = false;
+ auto ret = mThermalSvc->unregisterThermalStatusListener(mServiceStatusListener, &success);
+ if (!success || !ret.isOk()) {
+ ALOGE("Failed in unregisterThermalStatusListener %d", success);
+ if (ret.exceptionCode() == binder::Status::EX_SECURITY) {
+ return EPERM;
+ }
+ return EPIPE;
+ }
+ mServiceStatusListener = nullptr;
+ mStatusListeners.erase(it, mStatusListeners.end());
+ return OK;
+}
+
+status_t AThermalManager::addHeadroomListener(AThermal_HeadroomCallback callback, void *data) {
+ std::scoped_lock<std::mutex> lock(mHeadroomListenerMutex);
+ if (callback == nullptr) {
+ return EINVAL;
+ }
+ for (const auto &cb : mHeadroomListeners) {
+ if (callback == cb.callback && data == cb.data) {
+ return EINVAL;
+ }
+ }
+
+ if (mServiceHeadroomListener != nullptr) {
+ mHeadroomListeners.emplace_back(HeadroomListenerCallback{callback, data});
return OK;
}
- if (mServiceListener == nullptr) {
+ bool success = false;
+ mServiceHeadroomListener = new ThermalServiceHeadroomListener(this);
+ if (mServiceHeadroomListener == nullptr) {
+ return ENOMEM;
+ }
+ auto ret = mThermalSvc->registerThermalHeadroomListener(mServiceHeadroomListener, &success);
+ if (!success || !ret.isOk()) {
+ ALOGE("Failed in registerThermalHeadroomListener %d", success);
+ mServiceHeadroomListener = nullptr;
+ if (ret.exceptionCode() == binder::Status::EX_SECURITY) {
+ return EPERM;
+ }
+ return EPIPE;
+ }
+ mHeadroomListeners.emplace_back(HeadroomListenerCallback{callback, data});
+ return OK;
+}
+
+status_t AThermalManager::removeHeadroomListener(AThermal_HeadroomCallback callback, void *data) {
+ std::scoped_lock<std::mutex> lock(mHeadroomListenerMutex);
+
+ auto it = std::remove_if(mHeadroomListeners.begin(), mHeadroomListeners.end(),
+ [&](const HeadroomListenerCallback &cb) {
+ return callback == cb.callback && data == cb.data;
+ });
+ if (it == mHeadroomListeners.end()) {
+ return EINVAL;
+ }
+ if (mServiceHeadroomListener == nullptr || mHeadroomListeners.size() > 1) {
+ mHeadroomListeners.erase(it, mHeadroomListeners.end());
return OK;
}
bool success = false;
- auto ret = mThermalSvc->unregisterThermalStatusListener(mServiceListener, &success);
+ auto ret = mThermalSvc->unregisterThermalHeadroomListener(mServiceHeadroomListener, &success);
if (!success || !ret.isOk()) {
- ALOGE("Failed in unregisterThermalStatusListener %d", success);
+ ALOGE("Failed in unregisterThermalHeadroomListener %d", success);
if (ret.exceptionCode() == binder::Status::EX_SECURITY) {
return EPERM;
}
return EPIPE;
}
- mServiceListener = nullptr;
+ mServiceHeadroomListener = nullptr;
+ mHeadroomListeners.erase(it, mHeadroomListeners.end());
return OK;
}
@@ -216,61 +335,36 @@ status_t AThermalManager::getThermalHeadroom(int32_t forecastSeconds, float *res
status_t AThermalManager::getThermalHeadroomThresholds(const AThermalHeadroomThreshold **result,
size_t *size) {
- std::scoped_lock<std::mutex> lock(mThresholdsMutex);
- if (mThresholds == nullptr) {
- auto thresholds = std::make_unique<std::vector<float>>();
- binder::Status ret = mThermalSvc->getThermalHeadroomThresholds(thresholds.get());
- if (!ret.isOk()) {
- if (ret.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
- // feature is not enabled
- return ENOSYS;
- }
- return EPIPE;
- }
- mThresholdsCount = thresholds->size();
- auto t = new AThermalHeadroomThreshold[mThresholdsCount];
- for (int i = 0; i < (int)mThresholdsCount; i++) {
- t[i].headroom = (*thresholds)[i];
- t[i].thermalStatus = static_cast<AThermalStatus>(i);
+ auto thresholds = std::make_unique<std::vector<float>>();
+ binder::Status ret = mThermalSvc->getThermalHeadroomThresholds(thresholds.get());
+ if (!ret.isOk()) {
+ if (ret.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ // feature is not enabled
+ return ENOSYS;
}
- mThresholds = t;
+ return EPIPE;
+ }
+ size_t thresholdsCount = thresholds->size();
+ auto t = new AThermalHeadroomThreshold[thresholdsCount];
+ for (int i = 0; i < (int)thresholdsCount; i++) {
+ t[i].headroom = (*thresholds)[i];
+ t[i].thermalStatus = static_cast<AThermalStatus>(i);
}
- *size = mThresholdsCount;
- *result = mThresholds;
+ *size = thresholdsCount;
+ *result = t;
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);
@@ -280,59 +374,16 @@ AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager) {
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);
+ AThermal_StatusCallback callback, void *data) {
+ return manager->addStatusListener(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);
+ AThermal_StatusCallback callback, void *data) {
+ return manager->removeStatusListener(callback, data);
}
-/**
- * Provides an estimate of how much thermal headroom the device currently has
- * before hitting severe throttling.
- *
- * Note that this only attempts to track the headroom of slow-moving sensors,
- * such as the skin temperature sensor. This means that there is no benefit to
- * calling this function more frequently than about once per second, and attempts
- * to call significantly more frequently may result in the function returning {@code NaN}.
- *
- * See also PowerManager#getThermalHeadroom.
- *
- * @param manager The manager instance to use
- * @param forecastSeconds how many seconds in the future to forecast
- * @return a value greater than or equal to 0.0 where 1.0 indicates the SEVERE throttling
- * threshold. Returns NaN if the device does not support this functionality or if
- * this function is called significantly faster than once per second.
- */
float AThermal_getThermalHeadroom(AThermalManager *manager, int forecastSeconds) {
float result = 0.0f;
status_t ret = manager->getThermalHeadroom(forecastSeconds, &result);
@@ -354,3 +405,13 @@ int AThermal_getThermalHeadroomThresholds(AThermalManager *manager,
void AThermal_setIThermalServiceForTesting(void *iThermalService) {
gIThermalServiceForTesting = static_cast<IThermalService *>(iThermalService);
}
+
+int AThermal_registerThermalHeadroomListener(AThermalManager *manager,
+ AThermal_HeadroomCallback callback, void *data) {
+ return manager->addHeadroomListener(callback, data);
+}
+
+int AThermal_unregisterThermalHeadroomListener(AThermalManager *manager,
+ AThermal_HeadroomCallback callback, void *data) {
+ return manager->removeHeadroomListener(callback, data);
+}