Add ADPF FMQ implementation for PerformanceHintManager
This patch adds an implementation for ADPF FMQ to the
PerformanceHintManager, along with tests and updates to the
HintManagerService to better handle FMQ configs without additional
event flags.
Flag: android.os.adpf_use_fmq_channel_fixed
Test: atest PerformanceHintNativeTestCases
Test: atest HintManagerServiceTest
Bug: 315894228
Change-Id: I2bb2ed7bf4f8e87520bdd9a20b7a7847ba0907eb
diff --git a/core/java/android/os/IHintManager.aidl b/core/java/android/os/IHintManager.aidl
index 360b2ac..73cdd56 100644
--- a/core/java/android/os/IHintManager.aidl
+++ b/core/java/android/os/IHintManager.aidl
@@ -33,7 +33,7 @@
* if creation is supported but fails.
*/
IHintSession createHintSessionWithConfig(in IBinder token, in int[] threadIds,
- in long durationNanos, in SessionTag tag, out @nullable SessionConfig config);
+ in long durationNanos, in SessionTag tag, out SessionConfig config);
/**
* Get preferred rate limit in nanoseconds.
@@ -48,6 +48,6 @@
*
* Throws IllegalStateException if FMQ channel creation fails.
*/
- ChannelConfig getSessionChannel(in IBinder token);
+ @nullable ChannelConfig getSessionChannel(in IBinder token);
oneway void closeSessionChannel();
}
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index f026997..115ee1b 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -115,14 +115,6 @@
}
flag {
- name: "adpf_fmq_eager_send"
- namespace: "game"
- description: "Guards the use of an eager-sending optimization in FMQ for low-latency messages"
- is_fixed_read_only: true
- bug: "315894228"
-}
-
-flag {
name: "adpf_hwui_gpu"
namespace: "game"
description: "Guards use of the FMQ channel for ADPF"
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 346c87d..25c063d6 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -367,6 +367,7 @@
APerformanceHint_sendHint;
APerformanceHint_getThreadIds;
APerformanceHint_createSessionInternal;
+ APerformanceHint_setUseFMQForTesting;
extern "C++" {
ASurfaceControl_registerSurfaceStatsListener*;
ASurfaceControl_unregisterSurfaceStatsListener*;
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index e91c7a9..095d7d1 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -16,10 +16,14 @@
#define LOG_TAG "perf_hint"
+#include <aidl/android/hardware/power/ChannelConfig.h>
+#include <aidl/android/hardware/power/ChannelMessage.h>
+#include <aidl/android/hardware/power/SessionConfig.h>
#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/WorkDuration.h>
+#include <aidl/android/hardware/power/WorkDurationFixedV1.h>
#include <aidl/android/os/IHintManager.h>
#include <aidl/android/os/IHintSession.h>
#include <android-base/stringprintf.h>
@@ -28,6 +32,8 @@
#include <android/binder_status.h>
#include <android/performance_hint.h>
#include <android/trace.h>
+#include <android_os.h>
+#include <fmq/AidlMessageQueue.h>
#include <inttypes.h>
#include <performance_hint_private.h>
#include <utils/SystemClock.h>
@@ -45,6 +51,10 @@
// Namespace for AIDL types coming from the PowerHAL
namespace hal = aidl::android::hardware::power;
+using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
+using HalChannelMessageContents = hal::ChannelMessage::ChannelMessageContents;
+using HalMessageQueue = ::android::AidlMessageQueue<hal::ChannelMessage, SynchronizedReadWrite>;
+using HalFlagQueue = ::android::AidlMessageQueue<int8_t, SynchronizedReadWrite>;
using android::base::StringPrintf;
struct APerformanceHintSession;
@@ -54,18 +64,60 @@
// Shared lock for the whole PerformanceHintManager and sessions
static std::mutex sHintMutex = std::mutex{};
+class FMQWrapper {
+public:
+ bool isActive();
+ bool isSupported();
+ bool startChannel(IHintManager* manager);
+ void stopChannel(IHintManager* manager);
+ // Number of elements the FMQ can hold
+ bool reportActualWorkDurations(std::optional<hal::SessionConfig>& config,
+ 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 setMode(std::optional<hal::SessionConfig>& config, hal::SessionMode, bool enabled)
+ REQUIRES(sHintMutex);
+ void setToken(ndk::SpAIBinder& token);
+ void attemptWake();
+ void setUnsupported();
+
+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);
+ template <HalChannelMessageContents::Tag T, class C = HalChannelMessageContents::_at<T>>
+ void writeBuffer(C* message, hal::SessionConfig& config, size_t count) REQUIRES(sHintMutex);
+
+ bool isActiveLocked() REQUIRES(sHintMutex);
+ bool updatePersistentTransaction() REQUIRES(sHintMutex);
+ std::shared_ptr<HalMessageQueue> mQueue GUARDED_BY(sHintMutex) = nullptr;
+ std::shared_ptr<HalFlagQueue> mFlagQueue GUARDED_BY(sHintMutex) = nullptr;
+ // android::hardware::EventFlag* mEventFlag GUARDED_BY(sHintMutex) = nullptr;
+ android::hardware::EventFlag* mEventFlag = nullptr;
+ int32_t mWriteMask;
+ ndk::SpAIBinder mToken = nullptr;
+ // Used to track if operating on the fmq consistently fails
+ bool mCorrupted = false;
+ // Used to keep a persistent transaction open with FMQ to reduce latency a bit
+ size_t mAvailableSlots GUARDED_BY(sHintMutex) = 0;
+ bool mHalSupported = true;
+ HalMessageQueue::MemTransaction mFmqTransaction GUARDED_BY(sHintMutex);
+};
struct APerformanceHintManager {
public:
static APerformanceHintManager* getInstance();
- APerformanceHintManager(std::shared_ptr<IHintManager> service, int64_t preferredRateNanos);
+ APerformanceHintManager(std::shared_ptr<IHintManager>& service, int64_t preferredRateNanos);
APerformanceHintManager() = delete;
- ~APerformanceHintManager() = default;
+ ~APerformanceHintManager();
APerformanceHintSession* createSession(const int32_t* threadIds, size_t size,
int64_t initialTargetWorkDurationNanos,
hal::SessionTag tag = hal::SessionTag::APP);
int64_t getPreferredRateNanos() const;
+ FMQWrapper& getFMQWrapper();
private:
// Necessary to create an empty binder object
@@ -83,6 +135,7 @@
std::shared_ptr<IHintManager> mHintManager;
ndk::SpAIBinder mToken;
const int64_t mPreferredRateNanos;
+ FMQWrapper mFMQWrapper;
};
struct APerformanceHintSession {
@@ -121,40 +174,57 @@
std::vector<int64_t> mLastHintSentTimestamp GUARDED_BY(sHintMutex);
// Cached samples
std::vector<hal::WorkDuration> mActualWorkDurations GUARDED_BY(sHintMutex);
- std::string mSessionName GUARDED_BY(sHintMutex);
+ 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);
// Tracing helpers
void traceThreads(std::vector<int32_t>& tids) REQUIRES(sHintMutex);
- void tracePowerEfficient(bool powerEfficient) REQUIRES(sHintMutex);
- void traceActualDuration(int64_t actualDuration) REQUIRES(sHintMutex);
- void traceBatchSize(size_t batchSize) REQUIRES(sHintMutex);
- void traceTargetDuration(int64_t targetDuration) REQUIRES(sHintMutex);
+ void tracePowerEfficient(bool powerEfficient);
+ void traceActualDuration(int64_t actualDuration);
+ void traceBatchSize(size_t batchSize);
+ void traceTargetDuration(int64_t targetDuration);
};
static std::shared_ptr<IHintManager>* gIHintManagerForTesting = nullptr;
-static APerformanceHintManager* gHintManagerForTesting = nullptr;
+static std::shared_ptr<APerformanceHintManager> gHintManagerForTesting = nullptr;
+
+static std::optional<bool> gForceFMQEnabled = std::nullopt;
+
// Start above the int32 range so we don't collide with config sessions
int64_t APerformanceHintSession::sIDCounter = INT32_MAX;
+static FMQWrapper& getFMQ() {
+ return APerformanceHintManager::getInstance()->getFMQWrapper();
+}
+
// ===================================== APerformanceHintManager implementation
-APerformanceHintManager::APerformanceHintManager(std::shared_ptr<IHintManager> manager,
+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));
+ if (mFMQWrapper.isSupported()) {
+ mFMQWrapper.setToken(mToken);
+ mFMQWrapper.startChannel(mHintManager.get());
+ }
+}
+
+APerformanceHintManager::~APerformanceHintManager() {
+ mFMQWrapper.stopChannel(mHintManager.get());
}
APerformanceHintManager* APerformanceHintManager::getInstance() {
- if (gHintManagerForTesting) return gHintManagerForTesting;
+ if (gHintManagerForTesting) {
+ return gHintManagerForTesting.get();
+ }
if (gIHintManagerForTesting) {
- APerformanceHintManager* manager = create(*gIHintManagerForTesting);
- gIHintManagerForTesting = nullptr;
- return manager;
+ gHintManagerForTesting =
+ std::shared_ptr<APerformanceHintManager>(create(*gIHintManagerForTesting));
+ return gHintManagerForTesting.get();
}
static APerformanceHintManager* instance = create(nullptr);
return instance;
@@ -178,7 +248,7 @@
if (preferredRateNanos <= 0) {
preferredRateNanos = -1L;
}
- return new APerformanceHintManager(std::move(manager), preferredRateNanos);
+ return new APerformanceHintManager(manager, preferredRateNanos);
}
APerformanceHintSession* APerformanceHintManager::createSession(
@@ -187,15 +257,20 @@
std::vector<int32_t> tids(threadIds, threadIds + size);
std::shared_ptr<IHintSession> session;
ndk::ScopedAStatus ret;
- std::optional<hal::SessionConfig> sessionConfig;
+ hal::SessionConfig sessionConfig{.id = -1};
ret = mHintManager->createHintSessionWithConfig(mToken, tids, initialTargetWorkDurationNanos,
tag, &sessionConfig, &session);
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, sessionConfig);
+ initialTargetWorkDurationNanos,
+ sessionConfig.id == -1
+ ? std::nullopt
+ : std::make_optional<hal::SessionConfig>(
+ std::move(sessionConfig)));
std::scoped_lock lock(sHintMutex);
out->traceThreads(tids);
out->traceTargetDuration(initialTargetWorkDurationNanos);
@@ -207,8 +282,15 @@
return mPreferredRateNanos;
}
+FMQWrapper& APerformanceHintManager::getFMQWrapper() {
+ return mFMQWrapper;
+}
+
// ===================================== APerformanceHintSession implementation
+constexpr int kNumEnums =
+ ndk::enum_range<hal::SessionHint>().end() - ndk::enum_range<hal::SessionHint>().begin();
+
APerformanceHintSession::APerformanceHintSession(std::shared_ptr<IHintManager> hintManager,
std::shared_ptr<IHintSession> session,
int64_t preferredRateNanos,
@@ -220,14 +302,11 @@
mTargetDurationNanos(targetDurationNanos),
mFirstTargetMetTimestamp(0),
mLastTargetMetTimestamp(0),
+ mLastHintSentTimestamp(std::vector<int64_t>(kNumEnums, 0)),
mSessionConfig(sessionConfig) {
if (sessionConfig->id > INT32_MAX) {
ALOGE("Session ID too large, must fit 32-bit integer");
}
- std::scoped_lock lock(sHintMutex);
- constexpr int numEnums =
- ndk::enum_range<hal::SessionHint>().end() - ndk::enum_range<hal::SessionHint>().begin();
- mLastHintSentTimestamp = std::vector<int64_t>(numEnums, 0);
int64_t traceId = sessionConfig.has_value() ? sessionConfig->id : ++sIDCounter;
mSessionName = android::base::StringPrintf("ADPF Session %" PRId64, traceId);
}
@@ -244,19 +323,18 @@
ALOGE("%s: targetDurationNanos must be positive", __FUNCTION__);
return EINVAL;
}
- {
- std::scoped_lock lock(sHintMutex);
- if (mTargetDurationNanos == targetDurationNanos) {
- return 0;
+ std::scoped_lock lock(sHintMutex);
+ if (mTargetDurationNanos == targetDurationNanos) {
+ return 0;
+ }
+ if (!getFMQ().updateTargetWorkDuration(mSessionConfig, targetDurationNanos)) {
+ ndk::ScopedAStatus ret = mHintSession->updateTargetWorkDuration(targetDurationNanos);
+ if (!ret.isOk()) {
+ ALOGE("%s: HintSession updateTargetWorkDuration failed: %s", __FUNCTION__,
+ ret.getMessage());
+ return EPIPE;
}
}
- ndk::ScopedAStatus ret = mHintSession->updateTargetWorkDuration(targetDurationNanos);
- if (!ret.isOk()) {
- ALOGE("%s: HintSession updateTargetWorkDuration failed: %s", __FUNCTION__,
- ret.getMessage());
- return EPIPE;
- }
- std::scoped_lock lock(sHintMutex);
mTargetDurationNanos = targetDurationNanos;
/**
* Most of the workload is target_duration dependent, so now clear the cached samples
@@ -292,11 +370,13 @@
return 0;
}
- ndk::ScopedAStatus ret = mHintSession->sendHint(hint);
+ if (!getFMQ().sendHint(mSessionConfig, hint)) {
+ ndk::ScopedAStatus ret = mHintSession->sendHint(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;
return 0;
@@ -369,10 +449,10 @@
int APerformanceHintSession::reportActualWorkDurationInternal(AWorkDuration* workDuration) {
int64_t actualTotalDurationNanos = workDuration->durationNanos;
+ traceActualDuration(workDuration->durationNanos);
int64_t now = uptimeNanos();
workDuration->timeStampNanos = now;
std::scoped_lock lock(sHintMutex);
- traceActualDuration(workDuration->durationNanos);
mActualWorkDurations.push_back(std::move(*workDuration));
if (actualTotalDurationNanos >= mTargetDurationNanos) {
@@ -396,20 +476,177 @@
mLastTargetMetTimestamp = now;
}
- ndk::ScopedAStatus ret = mHintSession->reportActualWorkDuration2(mActualWorkDurations);
- if (!ret.isOk()) {
- ALOGE("%s: HintSession reportActualWorkDuration failed: %s", __FUNCTION__,
- ret.getMessage());
- mFirstTargetMetTimestamp = 0;
- mLastTargetMetTimestamp = 0;
- traceBatchSize(mActualWorkDurations.size());
- return ret.getExceptionCode() == EX_ILLEGAL_ARGUMENT ? EINVAL : EPIPE;
+ if (!getFMQ().reportActualWorkDurations(mSessionConfig, mActualWorkDurations.data(),
+ mActualWorkDurations.size())) {
+ ndk::ScopedAStatus ret = mHintSession->reportActualWorkDuration2(mActualWorkDurations);
+ if (!ret.isOk()) {
+ ALOGE("%s: HintSession reportActualWorkDuration failed: %s", __FUNCTION__,
+ ret.getMessage());
+ mFirstTargetMetTimestamp = 0;
+ mLastTargetMetTimestamp = 0;
+ traceBatchSize(mActualWorkDurations.size());
+ return ret.getExceptionCode() == EX_ILLEGAL_ARGUMENT ? EINVAL : EPIPE;
+ }
}
+
mActualWorkDurations.clear();
traceBatchSize(0);
return 0;
}
+
+// ===================================== FMQ wrapper implementation
+
+bool FMQWrapper::isActive() {
+ std::scoped_lock lock{sHintMutex};
+ return isActiveLocked();
+}
+
+bool FMQWrapper::isActiveLocked() {
+ return mQueue != nullptr;
+}
+
+void FMQWrapper::setUnsupported() {
+ mHalSupported = false;
+}
+
+bool FMQWrapper::isSupported() {
+ if (!mHalSupported) {
+ return false;
+ }
+ // Used for testing
+ if (gForceFMQEnabled.has_value()) {
+ return *gForceFMQEnabled;
+ }
+ return android::os::adpf_use_fmq_channel_fixed();
+}
+
+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;
+ }
+ 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 isActive();
+}
+
+void FMQWrapper::stopChannel(IHintManager* manager) {
+ {
+ std::scoped_lock lock{sHintMutex};
+ if (!isActiveLocked()) {
+ return;
+ }
+ mFlagQueue = nullptr;
+ mQueue = nullptr;
+ }
+ manager->closeSessionChannel();
+}
+
+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)),
+ };
+}
+
+template <>
+void FMQWrapper::writeBuffer<HalChannelMessageContents::workDuration>(hal::WorkDuration* messages,
+ hal::SessionConfig& config,
+ size_t count) {
+ 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,
+ .data = HalChannelMessageContents::make<HalChannelMessageContents::workDuration,
+ hal::WorkDurationFixedV1>({
+ .durationNanos = message.cpuDurationNanos,
+ .workPeriodStartTimestampNanos = message.workPeriodStartTimestampNanos,
+ .cpuDurationNanos = message.cpuDurationNanos,
+ .gpuDurationNanos = message.gpuDurationNanos,
+ }),
+ };
+ }
+}
+
+template <HalChannelMessageContents::Tag T, bool urgent, class C>
+bool FMQWrapper::sendMessages(std::optional<hal::SessionConfig>& config, C* message, size_t count) {
+ if (!isActiveLocked() || !config.has_value() || mCorrupted) {
+ return false;
+ }
+ // If we didn't reserve enough space, try re-creating the transaction
+ if (count > mAvailableSlots) {
+ if (!updatePersistentTransaction()) {
+ return false;
+ }
+ // If we actually don't have enough space, give up
+ if (count > mAvailableSlots) {
+ return false;
+ }
+ }
+ writeBuffer<T, C>(message, *config, count);
+ mQueue->commitWrite(count);
+ mEventFlag->wake(mWriteMask);
+ // Re-create the persistent transaction after writing
+ updatePersistentTransaction();
+ return true;
+}
+
+void FMQWrapper::setToken(ndk::SpAIBinder& token) {
+ mToken = token;
+}
+
+bool FMQWrapper::updatePersistentTransaction() {
+ mAvailableSlots = mQueue->availableToWrite();
+ if (mAvailableSlots > 0 && !mQueue->beginWrite(mAvailableSlots, &mFmqTransaction)) {
+ ALOGE("ADPF FMQ became corrupted, falling back to binder calls!");
+ mCorrupted = true;
+ return false;
+ }
+ return true;
+}
+
+bool FMQWrapper::reportActualWorkDurations(std::optional<hal::SessionConfig>& config,
+ hal::WorkDuration* durations, size_t count) {
+ return sendMessages<HalChannelMessageContents::workDuration>(config, durations, count);
+}
+
+bool FMQWrapper::updateTargetWorkDuration(std::optional<hal::SessionConfig>& config,
+ int64_t targetDurationNanos) {
+ 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::setMode(std::optional<hal::SessionConfig>& config, hal::SessionMode mode,
+ bool enabled) {
+ hal::ChannelMessage::ChannelMessageContents::SessionModeSetter modeObj{.modeInt = mode,
+ .enabled = enabled};
+ return sendMessages<HalChannelMessageContents::mode, true>(config, &modeObj);
+}
+
// ===================================== Tracing helpers
void APerformanceHintSession::traceThreads(std::vector<int32_t>& tids) {
@@ -585,7 +822,12 @@
}
void APerformanceHint_setIHintManagerForTesting(void* iManager) {
- delete gHintManagerForTesting;
- gHintManagerForTesting = nullptr;
+ if (iManager == nullptr) {
+ gHintManagerForTesting = nullptr;
+ }
gIHintManagerForTesting = static_cast<std::shared_ptr<IHintManager>*>(iManager);
}
+
+void APerformanceHint_setUseFMQForTesting(bool enabled) {
+ gForceFMQEnabled = enabled;
+}
diff --git a/native/android/tests/performance_hint/Android.bp b/native/android/tests/performance_hint/Android.bp
index 608d5d8..f6f1da1 100644
--- a/native/android/tests/performance_hint/Android.bp
+++ b/native/android/tests/performance_hint/Android.bp
@@ -36,10 +36,13 @@
srcs: ["PerformanceHintNativeTest.cpp"],
shared_libs: [
+ "android.hardware.common.fmq-V1-ndk",
"libandroid",
- "liblog",
"libbinder",
"libbinder_ndk",
+ "libcutils",
+ "libfmq",
+ "liblog",
"libpowermanager",
"libutils",
],
@@ -56,8 +59,8 @@
],
cflags: [
- "-Werror",
"-Wall",
+ "-Werror",
],
header_libs: [
diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
index d19fa98..9de3a6f 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -24,6 +24,7 @@
#include <android/binder_manager.h>
#include <android/binder_status.h>
#include <android/performance_hint.h>
+#include <fmq/AidlMessageQueue.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <performance_hint_private.h>
@@ -31,11 +32,16 @@
#include <memory>
#include <vector>
+using namespace std::chrono_literals;
namespace hal = aidl::android::hardware::power;
using aidl::android::os::IHintManager;
using aidl::android::os::IHintSession;
using ndk::ScopedAStatus;
using ndk::SpAIBinder;
+using HalChannelMessageContents = hal::ChannelMessage::ChannelMessageContents;
+
+using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
+using HalFlagQueue = ::android::AidlMessageQueue<int8_t, SynchronizedReadWrite>;
using namespace android;
using namespace testing;
@@ -44,7 +50,7 @@
public:
MOCK_METHOD(ScopedAStatus, createHintSessionWithConfig,
(const SpAIBinder& token, const ::std::vector<int32_t>& tids, int64_t durationNanos,
- hal::SessionTag tag, std::optional<hal::SessionConfig>* config,
+ hal::SessionTag tag, hal::SessionConfig* config,
std::shared_ptr<IHintSession>* _aidl_return),
(override));
MOCK_METHOD(ScopedAStatus, getHintSessionPreferredRate, (int64_t * _aidl_return), (override));
@@ -56,7 +62,9 @@
(const std::shared_ptr<IHintSession>& hintSession, ::std::vector<int32_t>* tids),
(override));
MOCK_METHOD(ScopedAStatus, getSessionChannel,
- (const ::ndk::SpAIBinder& in_token, hal::ChannelConfig* _aidl_return), (override));
+ (const ::ndk::SpAIBinder& in_token,
+ std::optional<hal::ChannelConfig>* _aidl_return),
+ (override));
MOCK_METHOD(ScopedAStatus, closeSessionChannel, (), (override));
MOCK_METHOD(SpAIBinder, asBinder, (), (override));
MOCK_METHOD(bool, isRemote, (), (override));
@@ -92,10 +100,12 @@
}
APerformanceHintManager* createManager() {
+ APerformanceHint_setUseFMQForTesting(mUsingFMQ);
ON_CALL(*mMockIHintManager, getHintSessionPreferredRate(_))
.WillByDefault(DoAll(SetArgPointee<0>(123L), [] { return ScopedAStatus::ok(); }));
return APerformanceHint_getManager();
}
+
APerformanceHintSession* createSession(APerformanceHintManager* manager,
int64_t targetDuration = 56789L, bool isHwui = false) {
mMockSession = ndk::SharedRefBase::make<NiceMock<MockIHintSession>>();
@@ -106,8 +116,7 @@
ON_CALL(*mMockIHintManager,
createHintSessionWithConfig(_, Eq(tids), Eq(targetDuration), _, _, _))
- .WillByDefault(DoAll(SetArgPointee<4>(std::make_optional<hal::SessionConfig>(
- {.id = sessionId})),
+ .WillByDefault(DoAll(SetArgPointee<4>(hal::SessionConfig({.id = sessionId})),
SetArgPointee<5>(std::shared_ptr<IHintSession>(mMockSession)),
[] { return ScopedAStatus::ok(); }));
@@ -133,8 +142,47 @@
return APerformanceHint_createSession(manager, tids.data(), tids.size(), targetDuration);
}
+ void setFMQEnabled(bool enabled) {
+ mUsingFMQ = enabled;
+ if (enabled) {
+ mMockFMQ = std::make_shared<
+ AidlMessageQueue<hal::ChannelMessage, SynchronizedReadWrite>>(kMockQueueSize,
+ true);
+ mMockFlagQueue =
+ std::make_shared<AidlMessageQueue<int8_t, SynchronizedReadWrite>>(1, true);
+ hardware::EventFlag::createEventFlag(mMockFlagQueue->getEventFlagWord(), &mEventFlag);
+
+ ON_CALL(*mMockIHintManager, getSessionChannel(_, _))
+ .WillByDefault([&](ndk::SpAIBinder, std::optional<hal::ChannelConfig>* config) {
+ config->emplace(
+ hal::ChannelConfig{.channelDescriptor = mMockFMQ->dupeDesc(),
+ .eventFlagDescriptor =
+ mMockFlagQueue->dupeDesc(),
+ .readFlagBitmask =
+ static_cast<int32_t>(mReadBits),
+ .writeFlagBitmask =
+ static_cast<int32_t>(mWriteBits)});
+ return ::ndk::ScopedAStatus::ok();
+ });
+ }
+ }
+ uint32_t mReadBits = 0x00000001;
+ uint32_t mWriteBits = 0x00000002;
std::shared_ptr<NiceMock<MockIHintManager>> mMockIHintManager = nullptr;
std::shared_ptr<NiceMock<MockIHintSession>> mMockSession = nullptr;
+ std::shared_ptr<AidlMessageQueue<hal::ChannelMessage, SynchronizedReadWrite>> mMockFMQ;
+ std::shared_ptr<AidlMessageQueue<int8_t, SynchronizedReadWrite>> mMockFlagQueue;
+ hardware::EventFlag* mEventFlag;
+ int kMockQueueSize = 20;
+ bool mUsingFMQ = false;
+
+ template <HalChannelMessageContents::Tag T, class C = HalChannelMessageContents::_at<T>>
+ void expectToReadFromFmq(C expected) {
+ hal::ChannelMessage readData;
+ mMockFMQ->readBlocking(&readData, 1, mReadBits, mWriteBits, 1000000000, mEventFlag);
+ C got = static_cast<C>(readData.data.get<T>());
+ ASSERT_EQ(got, expected);
+ }
};
bool equalsWithoutTimestamp(hal::WorkDuration lhs, hal::WorkDuration rhs) {
@@ -306,7 +354,7 @@
actualWorkDurations.push_back(pair.duration);
EXPECT_CALL(*mMockSession, reportActualWorkDuration2(WorkDurationEq(actualWorkDurations)))
- .Times(Exactly(1));
+ .Times(Exactly(pair.expectedResult == OK));
result = APerformanceHint_reportActualWorkDuration2(session,
reinterpret_cast<AWorkDuration*>(
&pair.duration));
@@ -327,3 +375,48 @@
AWorkDuration_setActualGpuDurationNanos(aWorkDuration, 8);
AWorkDuration_release(aWorkDuration);
}
+
+TEST_F(PerformanceHintTest, TestCreateUsingFMQ) {
+ setFMQEnabled(true);
+ APerformanceHintManager* manager = createManager();
+ APerformanceHintSession* session = createSession(manager);
+ ASSERT_TRUE(session);
+}
+
+TEST_F(PerformanceHintTest, TestUpdateTargetWorkDurationUsingFMQ) {
+ setFMQEnabled(true);
+ APerformanceHintManager* manager = createManager();
+ APerformanceHintSession* session = createSession(manager);
+ APerformanceHint_updateTargetWorkDuration(session, 456);
+ expectToReadFromFmq<HalChannelMessageContents::Tag::targetDuration>(456);
+}
+
+TEST_F(PerformanceHintTest, TestSendHintUsingFMQ) {
+ setFMQEnabled(true);
+ APerformanceHintManager* manager = createManager();
+ APerformanceHintSession* session = createSession(manager);
+ APerformanceHint_sendHint(session, SessionHint::CPU_LOAD_UP);
+ expectToReadFromFmq<HalChannelMessageContents::Tag::hint>(hal::SessionHint::CPU_LOAD_UP);
+}
+
+TEST_F(PerformanceHintTest, TestReportActualUsingFMQ) {
+ setFMQEnabled(true);
+ APerformanceHintManager* manager = createManager();
+ APerformanceHintSession* session = createSession(manager);
+ hal::WorkDuration duration{.timeStampNanos = 3,
+ .durationNanos = 999999,
+ .workPeriodStartTimestampNanos = 1,
+ .cpuDurationNanos = 999999,
+ .gpuDurationNanos = 999999};
+
+ hal::WorkDurationFixedV1 durationExpected{
+ .durationNanos = duration.durationNanos,
+ .workPeriodStartTimestampNanos = duration.workPeriodStartTimestampNanos,
+ .cpuDurationNanos = duration.cpuDurationNanos,
+ .gpuDurationNanos = duration.gpuDurationNanos,
+ };
+
+ APerformanceHint_reportActualWorkDuration2(session,
+ reinterpret_cast<AWorkDuration*>(&duration));
+ expectToReadFromFmq<HalChannelMessageContents::Tag::workDuration>(durationExpected);
+}
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index f6c3d8e..1346a29 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -160,6 +160,8 @@
private static final String PROPERTY_SF_ENABLE_CPU_HINT = "debug.sf.enable_adpf_cpu_hint";
private static final String PROPERTY_HWUI_ENABLE_HINT_MANAGER = "debug.hwui.use_hint_manager";
+ private Boolean mFMQUsesIntegratedEventFlag = false;
+
@VisibleForTesting final IHintManager.Stub mService = new BinderService();
public HintManagerService(Context context) {
@@ -1032,7 +1034,7 @@
@Override
public IHintSession createHintSessionWithConfig(@NonNull IBinder token,
@NonNull int[] tids, long durationNanos, @SessionTag int tag,
- @Nullable SessionConfig config) {
+ SessionConfig config) {
if (!isHalSupported()) {
throw new UnsupportedOperationException("PowerHAL is not supported!");
}
@@ -1070,7 +1072,7 @@
default -> tag = SessionTag.APP;
}
}
-
+ config.id = -1;
Long halSessionPtr = null;
if (mConfigCreationSupport.get()) {
try {
@@ -1109,7 +1111,7 @@
}
}
- final long sessionId = config != null ? config.id : halSessionPtr;
+ final long sessionId = config.id != -1 ? config.id : halSessionPtr;
logPerformanceHintSessionAtom(
callingUid, sessionId, durationNanos, tids, tag);
@@ -1144,14 +1146,23 @@
}
@Override
- public ChannelConfig getSessionChannel(IBinder token) {
- if (mPowerHalVersion < 5 || !adpfUseFmqChannel()) {
+ public @Nullable ChannelConfig getSessionChannel(IBinder token) {
+ if (mPowerHalVersion < 5 || !adpfUseFmqChannel()
+ || mFMQUsesIntegratedEventFlag) {
return null;
}
java.util.Objects.requireNonNull(token);
final int callingTgid = Process.getThreadGroupLeader(Binder.getCallingPid());
final int callingUid = Binder.getCallingUid();
ChannelItem item = getOrCreateMappedChannelItem(callingTgid, callingUid, token);
+ // FMQ V1 requires a separate event flag to be passed, and the default no-op
+ // implmenentation in PowerHAL does not return such a shared flag. This helps
+ // avoid using the FMQ on a default impl that does not support it.
+ if (item.getConfig().eventFlagDescriptor == null) {
+ mFMQUsesIntegratedEventFlag = true;
+ closeSessionChannel();
+ return null;
+ }
return item.getConfig();
};
@@ -1270,8 +1281,14 @@
@VisibleForTesting
boolean updateHintAllowedByProcState(boolean allowed) {
synchronized (this) {
- if (allowed && !mUpdateAllowedByProcState && !mShouldForcePause) resume();
- if (!allowed && mUpdateAllowedByProcState) pause();
+ if (allowed && !mUpdateAllowedByProcState && !mShouldForcePause) {
+ Slogf.e(TAG, "ADPF IS GETTING RESUMED? UID: " + mUid + " TAG: " + mTag);
+ resume();
+ }
+ if (!allowed && mUpdateAllowedByProcState) {
+ Slogf.e(TAG, "ADPF IS GETTING PAUSED? UID: " + mUid + " TAG: " + mTag);
+ pause();
+ }
mUpdateAllowedByProcState = allowed;
return mUpdateAllowedByProcState;
}
diff --git a/services/core/jni/com_android_server_hint_HintManagerService.cpp b/services/core/jni/com_android_server_hint_HintManagerService.cpp
index 2307ace..febfb9f 100644
--- a/services/core/jni/com_android_server_hint_HintManagerService.cpp
+++ b/services/core/jni/com_android_server_hint_HintManagerService.cpp
@@ -109,7 +109,7 @@
return session_ptr;
} else if (result.isUnsupported()) {
throwUnsupported(env, result.errorMessage());
- return -1;
+ return 0;
}
throwFailed(env, result.errorMessage());
return 0;
@@ -190,7 +190,7 @@
hal::SessionConfig config;
jlong out = createHintSessionWithConfig(env, tgid, uid, std::move(threadIds), durationNanos,
sessionTag, config);
- if (out <= 0) {
+ if (out == 0) {
return out;
}
static jclass configClass = env->FindClass("android/hardware/power/SessionConfig");
diff --git a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
index 7d04470..7224e3c 100644
--- a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -48,7 +48,9 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.hardware.common.fmq.MQDescriptor;
import android.hardware.power.ChannelConfig;
+import android.hardware.power.ChannelMessage;
import android.hardware.power.IPower;
import android.hardware.power.SessionConfig;
import android.hardware.power.SessionTag;
@@ -167,6 +169,8 @@
mConfig = new ChannelConfig();
mConfig.readFlagBitmask = 1;
mConfig.writeFlagBitmask = 2;
+ mConfig.channelDescriptor = new MQDescriptor<ChannelMessage, Byte>();
+ mConfig.eventFlagDescriptor = new MQDescriptor<Byte, Byte>();
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.category = ApplicationInfo.CATEGORY_GAME;
when(mContext.getPackageManager()).thenReturn(mMockPackageManager);