Add new surface binding and auto ADPF methods for ADPF Timeline API
Add new APIs to allow sessions to bind to ANativeWindows and
ASurfaceControls for the ADPF timeline API, and expose a new way
for sessions to run automatically in certain circumstances.
Flag: EXEMPT NDK_API
Bug: 360908317
Bug: 367803904
Test: atest HintManagerServiceTest
Test: atest PerformanceHintManagerTest
Test: atest PerformanceHintNativeTestCases
Change-Id: I473a0806c9796a0994bd7451698248c0cdcf56ed
diff --git a/core/java/android/os/IHintManager.aidl b/core/java/android/os/IHintManager.aidl
index f1936b5..4a14a8d 100644
--- a/core/java/android/os/IHintManager.aidl
+++ b/core/java/android/os/IHintManager.aidl
@@ -64,4 +64,10 @@
* Get Maximum number of graphics pipeline threads allowed per-app.
*/
int getMaxGraphicsPipelineThreadsCount();
+
+ /**
+ * Used by the JNI to pass an interface to the SessionManager;
+ * for internal use only.
+ */
+ oneway void passSessionManagerBinder(in IBinder sessionManager);
}
diff --git a/core/java/android/os/IHintSession.aidl b/core/java/android/os/IHintSession.aidl
index 6fd4f3c..e3f899d 100644
--- a/core/java/android/os/IHintSession.aidl
+++ b/core/java/android/os/IHintSession.aidl
@@ -27,4 +27,9 @@
void sendHint(int hint);
void setMode(int mode, boolean enabled);
void reportActualWorkDuration2(in WorkDuration[] workDurations);
+
+ /**
+ * Used by apps to associate a session to a given set of layers
+ */
+ oneway void associateToLayers(in IBinder[] layerTokens);
}
diff --git a/core/java/android/os/SessionCreationConfig.aidl b/core/java/android/os/SessionCreationConfig.aidl
index cdc0ef4..17147e4 100644
--- a/core/java/android/os/SessionCreationConfig.aidl
+++ b/core/java/android/os/SessionCreationConfig.aidl
@@ -36,4 +36,12 @@
* List of the modes to be enabled upon session creation.
*/
SessionMode[] modesToEnable;
+
+ /**
+ * List of layers to attach this session to.
+ *
+ * Note: DO NOT STORE THESE IN HintSessionManager, as
+ * it will break the layer lifecycle.
+ */
+ IBinder[] layerTokens;
}
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 7f555a8..077d7d3 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -376,6 +376,7 @@
APerformanceHint_notifyWorkloadIncrease; # introduced=36
APerformanceHint_notifyWorkloadReset; # introduced=36
APerformanceHint_borrowSessionFromJava; # introduced=36
+ APerformanceHint_setNativeSurfaces; # introduced=36
AWorkDuration_create; # introduced=VanillaIceCream
AWorkDuration_release; # introduced=VanillaIceCream
AWorkDuration_setWorkPeriodStartTimestampNanos; # introduced=VanillaIceCream
@@ -388,6 +389,8 @@
ASessionCreationConfig_setTargetWorkDurationNanos; # introduced=36
ASessionCreationConfig_setPreferPowerEfficiency; # introduced=36
ASessionCreationConfig_setGraphicsPipeline; # introduced=36
+ ASessionCreationConfig_setNativeSurfaces; # introduced=36
+ ASessionCreationConfig_setUseAutoTiming; # introduced=36
local:
*;
};
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 883e139..608c01c 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -29,13 +29,19 @@
#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>
@@ -66,7 +72,12 @@
constexpr int64_t SEND_HINT_TIMEOUT = std::chrono::nanoseconds(100ms).count();
struct AWorkDuration : public hal::WorkDuration {};
-struct ASessionCreationConfig : public SessionCreationConfig {};
+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;
@@ -158,6 +169,11 @@
FMQWrapper& getFMQWrapper();
bool canSendLoadHints(std::vector<hal::SessionHint>& hints, int64_t now) REQUIRES(sHintMutex);
void initJava(JNIEnv* _Nonnull env);
+ ndk::ScopedAIBinder_Weak x;
+ template <class T>
+ static void layersFromNativeSurfaces(ANativeWindow** windows, int numWindows,
+ ASurfaceControl** controls, int numSurfaceControls,
+ std::vector<T>& out);
private:
// Necessary to create an empty binder object
@@ -203,6 +219,8 @@
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;
@@ -231,7 +249,7 @@
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(const std::vector<int32_t>& tids) REQUIRES(sHintMutex);
void tracePowerEfficient(bool powerEfficient);
@@ -329,14 +347,12 @@
ndk::ScopedAStatus ret;
hal::SessionConfig sessionConfig{.id = -1};
- SessionCreationConfig creationConfig{
+ ASessionCreationConfig creationConfig{{
.tids = std::vector<int32_t>(threadIds, threadIds + size),
.targetWorkDurationNanos = initialTargetWorkDurationNanos,
- };
+ }};
- return APerformanceHintManager::createSessionUsingConfig(static_cast<ASessionCreationConfig*>(
- &creationConfig),
- tag, isJava);
+ return APerformanceHintManager::createSessionUsingConfig(&creationConfig, tag, isJava);
}
APerformanceHintSession* APerformanceHintManager::createSessionUsingConfig(
@@ -345,11 +361,29 @@
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;
@@ -679,6 +713,57 @@
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() {
@@ -963,8 +1048,7 @@
hal::SessionTag::APP, true);
}
-APerformanceHintSession* APerformanceHint_borrowSessionFromJava(JNIEnv* env,
- jobject sessionObj) {
+APerformanceHintSession* APerformanceHint_borrowSessionFromJava(JNIEnv* env, jobject sessionObj) {
VALIDATE_PTR(env)
VALIDATE_PTR(sessionObj)
return APerformanceHintManager::getInstance()->getSessionFromJava(env, sessionObj);
@@ -1065,6 +1149,14 @@
return session->notifyWorkloadReset(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();
}
@@ -1180,6 +1272,11 @@
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;
}
@@ -1197,3 +1294,48 @@
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/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
index b006580..b8f574f 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -83,6 +83,7 @@
(override));
MOCK_METHOD(ScopedAStatus, getGpuHeadroomMinIntervalMillis, (int64_t* _aidl_return),
(override));
+ MOCK_METHOD(ScopedAStatus, passSessionManagerBinder, (const SpAIBinder& sessionManager));
MOCK_METHOD(SpAIBinder, asBinder, (), (override));
MOCK_METHOD(bool, isRemote, (), (override));
};
@@ -99,6 +100,8 @@
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));
};
diff --git a/services/core/Android.bp b/services/core/Android.bp
index aea16b0..77c88a0 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -243,6 +243,7 @@
"powerstats_flags_lib",
"locksettings_flags_lib",
"profiling_flags_lib",
+ "android.adpf.sessionmanager_aidl-java",
],
javac_shard_size: 50,
javacflags: [
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 aba15c8..0f6688f 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -23,6 +23,7 @@
import static com.android.server.power.hint.Flags.powerhintThreadCleanup;
import static com.android.server.power.hint.Flags.resetOnForkEnabled;
+import android.adpf.ISessionManager;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -193,6 +194,7 @@
private final Object mCpuHeadroomLock = new Object();
+ private ISessionManager mSessionManager;
// this cache tracks the expiration time of the items and performs cleanup on lookup
private static class HeadroomCache<K, V> {
@@ -818,6 +820,23 @@
for (int i = tokenMap.size() - 1; i >= 0; i--) {
// Will remove the session from tokenMap
ArraySet<AppHintSession> sessionSet = tokenMap.valueAt(i);
+ IntArray closedSessionsForSf = new IntArray();
+ // Batch the closure call to SF for all the sessions that die
+ for (int j = sessionSet.size() - 1; j >= 0; j--) {
+ AppHintSession session = sessionSet.valueAt(j);
+ if (session.isTrackedBySf()) {
+ // Mark it as untracked so we don't untrack again on close
+ session.setTrackedBySf(false);
+ closedSessionsForSf.add(session.getSessionId());
+ }
+ }
+ if (mSessionManager != null) {
+ try {
+ mSessionManager.trackedSessionsDied(closedSessionsForSf.toArray());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to communicate with SessionManager");
+ }
+ }
for (int j = sessionSet.size() - 1; j >= 0; j--) {
sessionSet.valueAt(j).close();
}
@@ -1350,9 +1369,9 @@
}
}
- final long sessionId = config.id != -1 ? config.id : halSessionPtr;
+ final long sessionIdForTracing = config.id != -1 ? config.id : halSessionPtr;
logPerformanceHintSessionAtom(
- callingUid, sessionId, durationNanos, tids, tag);
+ callingUid, sessionIdForTracing, durationNanos, tids, tag);
synchronized (mSessionSnapshotMapLock) {
// Update session snapshot upon session creation
@@ -1362,8 +1381,12 @@
}
AppHintSession hs = null;
synchronized (mLock) {
+ Integer configId = null;
+ if (config.id != -1) {
+ configId = new Integer((int) config.id);
+ }
hs = new AppHintSession(callingUid, callingTgid, tag, tids,
- token, halSessionPtr, durationNanos);
+ token, halSessionPtr, durationNanos, configId);
ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap =
mActiveSessions.get(callingUid);
if (tokenMap == null) {
@@ -1390,6 +1413,11 @@
}
}
+ if (creationConfig.layerTokens != null
+ && creationConfig.layerTokens.length > 0) {
+ hs.associateToLayers(creationConfig.layerTokens);
+ }
+
synchronized (mThreadsUsageObject) {
mThreadsUsageMap.computeIfAbsent(callingUid, k -> new ArraySet<>());
ArraySet<ThreadUsageTracker> threadsSet = mThreadsUsageMap.get(callingUid);
@@ -1566,6 +1594,15 @@
}
@Override
+ public void passSessionManagerBinder(IBinder sessionManager) {
+ // Ensure caller is internal
+ if (Process.myUid() != Binder.getCallingUid()) {
+ return;
+ }
+ mSessionManager = ISessionManager.Stub.asInterface(sessionManager);
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
return;
@@ -1688,6 +1725,8 @@
protected boolean mHasBeenPowerEfficient;
protected boolean mHasBeenGraphicsPipeline;
protected boolean mShouldForcePause;
+ protected Integer mSessionId;
+ protected boolean mTrackedBySF;
enum SessionModes {
POWER_EFFICIENCY,
@@ -1696,7 +1735,7 @@
protected AppHintSession(
int uid, int pid, int sessionTag, int[] threadIds, IBinder token,
- long halSessionPtr, long durationNanos) {
+ long halSessionPtr, long durationNanos, Integer sessionId) {
mUid = uid;
mPid = pid;
mTag = sessionTag;
@@ -1710,6 +1749,8 @@
mHasBeenPowerEfficient = false;
mHasBeenGraphicsPipeline = false;
mShouldForcePause = false;
+ mSessionId = sessionId;
+ mTrackedBySF = false;
final boolean allowed = mUidObserver.isUidForeground(mUid);
updateHintAllowedByProcState(allowed);
try {
@@ -1799,6 +1840,19 @@
} catch (NoSuchElementException ignored) {
Slogf.d(TAG, "Death link does not exist for session with UID " + mUid);
}
+ if (mTrackedBySF) {
+ if (mSessionManager != null) {
+ try {
+ mSessionManager.trackedSessionsDied(new int[]{mSessionId});
+ } catch (RemoteException e) {
+ throw new IllegalStateException(
+ "Could not communicate with SessionManager", e);
+ }
+ mTrackedBySF = false;
+ } else {
+ Slog.e(TAG, "SessionManager is null but there are tracked sessions");
+ }
+ }
}
synchronized (mLock) {
ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap = mActiveSessions.get(mUid);
@@ -1875,6 +1929,24 @@
}
}
+ @Override
+ public void associateToLayers(IBinder[] layerTokens) {
+ synchronized (this) {
+ if (mSessionManager != null && mSessionId != null && layerTokens != null) {
+ // Sf only untracks a session when it dies
+ if (layerTokens.length > 0) {
+ mTrackedBySF = true;
+ }
+ try {
+ mSessionManager.associateSessionToLayers(mSessionId, mUid, layerTokens);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(
+ "Could not communicate with SessionManager", e);
+ }
+ }
+ }
+ }
+
public void setThreads(@NonNull int[] tids) {
setThreadsInternal(tids, true);
}
@@ -2124,10 +2196,27 @@
return mUid;
}
+ public boolean isTrackedBySf() {
+ synchronized (this) {
+ return mTrackedBySF;
+ }
+ }
+
+ public void setTrackedBySf(boolean tracked) {
+ synchronized (this) {
+ mTrackedBySF = tracked;
+ }
+ }
+
+
public int getTag() {
return mTag;
}
+ public Integer getSessionId() {
+ return mSessionId;
+ }
+
public long getTargetDurationNs() {
synchronized (this) {
return mTargetDurationNanos;