summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/os/IHintManager.aidl4
-rw-r--r--core/java/android/os/flags.aconfig8
-rw-r--r--native/android/libandroid.map.txt1
-rw-r--r--native/android/performance_hint.cpp334
-rw-r--r--native/android/tests/performance_hint/Android.bp7
-rw-r--r--native/android/tests/performance_hint/PerformanceHintNativeTest.cpp103
-rw-r--r--services/core/java/com/android/server/power/hint/HintManagerService.java31
-rw-r--r--services/core/jni/com_android_server_hint_HintManagerService.cpp4
-rw-r--r--services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java4
9 files changed, 424 insertions, 72 deletions
diff --git a/core/java/android/os/IHintManager.aidl b/core/java/android/os/IHintManager.aidl
index 360b2ac4f3ca..73cdd5682f31 100644
--- a/core/java/android/os/IHintManager.aidl
+++ b/core/java/android/os/IHintManager.aidl
@@ -33,7 +33,7 @@ interface IHintManager {
* 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 @@ interface IHintManager {
*
* 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 f026997bcc57..115ee1b1456e 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -115,14 +115,6 @@ flag {
}
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 346c87daec87..25c063d6ccd8 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -367,6 +367,7 @@ LIBANDROID_PLATFORM {
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 e91c7a9ecda8..095d7d1145ae 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 @@ using namespace std::chrono_literals;
// 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 @@ struct AWorkDuration : public hal::WorkDuration {};
// 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 @@ private:
std::shared_ptr<IHintManager> mHintManager;
ndk::SpAIBinder mToken;
const int64_t mPreferredRateNanos;
+ FMQWrapper mFMQWrapper;
};
struct APerformanceHintSession {
@@ -121,40 +174,57 @@ private:
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 @@ APerformanceHintManager* APerformanceHintManager::create(std::shared_ptr<IHintMa
if (preferredRateNanos <= 0) {
preferredRateNanos = -1L;
}
- return new APerformanceHintManager(std::move(manager), preferredRateNanos);
+ return new APerformanceHintManager(manager, preferredRateNanos);
}
APerformanceHintSession* APerformanceHintManager::createSession(
@@ -187,15 +257,20 @@ APerformanceHintSession* APerformanceHintManager::createSession(
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 @@ int64_t APerformanceHintManager::getPreferredRateNanos() const {
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 @@ APerformanceHintSession::APerformanceHintSession(std::shared_ptr<IHintManager> h
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 @@ int APerformanceHintSession::updateTargetWorkDuration(int64_t targetDurationNano
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;
}
- ndk::ScopedAStatus ret = mHintSession->updateTargetWorkDuration(targetDurationNanos);
- if (!ret.isOk()) {
- ALOGE("%s: HintSession updateTargetWorkDuration failed: %s", __FUNCTION__,
- ret.getMessage());
- return EPIPE;
+ 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;
+ }
}
- 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 @@ int APerformanceHintSession::sendHint(SessionHint hint) {
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::reportActualWorkDuration(AWorkDuration* workDuratio
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 @@ int APerformanceHintSession::reportActualWorkDurationInternal(AWorkDuration* wor
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 AWorkDuration_setActualGpuDurationNanos(AWorkDuration* aWorkDuration,
}
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 608d5d8a923a..f6f1da1eed79 100644
--- a/native/android/tests/performance_hint/Android.bp
+++ b/native/android/tests/performance_hint/Android.bp
@@ -36,10 +36,13 @@ cc_test {
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 @@ cc_test {
],
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 d19fa98f1171..9de3a6f525e6 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 @@ class MockIHintManager : public IHintManager {
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 @@ public:
(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 @@ public:
}
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 @@ public:
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 @@ public:
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 @@ TEST_F(PerformanceHintTest, TestAPerformanceHint_reportActualWorkDuration2) {
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 @@ TEST_F(PerformanceHintTest, TestAWorkDuration) {
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 f6c3d8ef1249..1346a294b7d8 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 @@ public final class HintManagerService extends SystemService {
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 @@ public final class HintManagerService extends SystemService {
@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 @@ public final class HintManagerService extends SystemService {
default -> tag = SessionTag.APP;
}
}
-
+ config.id = -1;
Long halSessionPtr = null;
if (mConfigCreationSupport.get()) {
try {
@@ -1109,7 +1111,7 @@ public final class HintManagerService extends SystemService {
}
}
- 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 @@ public final class HintManagerService extends SystemService {
}
@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 @@ public final class HintManagerService extends SystemService {
@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 2307aced6141..febfb9ff1def 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 @@ static jlong createHintSessionWithConfig(JNIEnv* env, int32_t tgid, int32_t uid,
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 @@ static jlong nativeCreateHintSessionWithConfig(JNIEnv* env, jclass /* clazz */,
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 7d0447097375..7224e3c21f02 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.app.ActivityManagerInternal;
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 @@ public class HintManagerServiceTest {
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);