From 095de769a6a821dffb5b6a5f59b4600671fe7c3f Mon Sep 17 00:00:00 2001 From: Peiyong Lin Date: Fri, 11 Nov 2022 18:28:12 +0000 Subject: [PATCH] Implement setThreads APIs for PerformanceHintManager.Session. Previously the list of threads of a Session was only determined when the Session was created. This means newly forked threads from existing threads of the Session will not get the benefit and the clients have to create new Session for that. This patch adds a new method to allow clients to update the threads of the Session. Bug:b/244216750 Test: atest PerformanceHintManagerTest Test: atest PerformanceHintNativeTest Test: atest HintManagerServiceTest Change-Id: Iae8cbb4ce86a44a7cd9d6e68673c48800bed3a4e --- core/api/current.txt | 1 + core/api/test-current.txt | 1 + core/java/android/os/IHintManager.aidl | 7 +- .../android/os/PerformanceHintManager.java | 34 +++++++++ .../jni/android_os_PerformanceHintManager.cpp | 58 +++++++++++++++ .../os/PerformanceHintManagerTest.java | 9 +++ native/android/libandroid.map.txt | 2 + native/android/performance_hint.cpp | 73 +++++++++++++++++-- .../PerformanceHintNativeTest.cpp | 42 ++++++++++- .../server/power/hint/HintManagerService.java | 59 ++++++++++++++- ...android_server_hint_HintManagerService.cpp | 16 ++++ .../power/hint/HintManagerServiceTest.java | 29 ++++++++ 12 files changed, 321 insertions(+), 10 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index 8b3a234e6acc8..9a969933794de 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -32484,6 +32484,7 @@ package android.os { public static class PerformanceHintManager.Session implements java.io.Closeable { method public void close(); method public void reportActualWorkDuration(long); + method public void setThreads(@NonNull int[]); method public void updateTargetWorkDuration(long); } diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 64e2760110e00..191354dd87629 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1861,6 +1861,7 @@ package android.os { } public static class PerformanceHintManager.Session implements java.io.Closeable { + method @Nullable public int[] getThreadIds(); method public void sendHint(int); field public static final int CPU_LOAD_DOWN = 1; // 0x1 field public static final int CPU_LOAD_RESET = 2; // 0x2 diff --git a/core/java/android/os/IHintManager.aidl b/core/java/android/os/IHintManager.aidl index 661b95affaf3d..d97ea541f73d4 100644 --- a/core/java/android/os/IHintManager.aidl +++ b/core/java/android/os/IHintManager.aidl @@ -24,10 +24,13 @@ interface IHintManager { /** * Creates a {@link Session} for the given set of threads and associates to a binder token. */ - IHintSession createHintSession(in IBinder token, in int[] tids, long durationNanos); + IHintSession createHintSession(in IBinder token, in int[] tids, long durationNanos); /** * Get preferred rate limit in nano second. */ - long getHintSessionPreferredRate(); + long getHintSessionPreferredRate(); + + void setHintSessionThreads(in IHintSession hintSession, in int[] tids); + int[] getHintSessionThreadIds(in IHintSession hintSession); } diff --git a/core/java/android/os/PerformanceHintManager.java b/core/java/android/os/PerformanceHintManager.java index 85d6d83ef9e9d..f79d6e6da1f8b 100644 --- a/core/java/android/os/PerformanceHintManager.java +++ b/core/java/android/os/PerformanceHintManager.java @@ -222,16 +222,50 @@ public final class PerformanceHintManager { Reference.reachabilityFence(this); } } + + /** + * Set a list of threads to the performance hint session. This operation will replace + * the current list of threads with the given list of threads. + * Note that this is not an oneway method. + * + * @param tids The list of threads to be associated with this session. They must be + * part of this app's thread group. + * + * @throws IllegalStateException if the hint session is not in the foreground. + * @throws IllegalArgumentException if the thread id list is empty. + * @throws SecurityException if any thread id doesn't belong to the application. + */ + public void setThreads(@NonNull int[] tids) { + if (mNativeSessionPtr == 0) { + return; + } + if (tids.length == 0) { + throw new IllegalArgumentException("Thread id list can't be empty."); + } + nativeSetThreads(mNativeSessionPtr, tids); + } + + /** + * Returns the list of thread ids. + * + * @hide + */ + @TestApi + public @Nullable int[] getThreadIds() { + return nativeGetThreadIds(mNativeSessionPtr); + } } private static native long nativeAcquireManager(); private static native long nativeGetPreferredUpdateRateNanos(long nativeManagerPtr); private static native long nativeCreateSession(long nativeManagerPtr, int[] tids, long initialTargetWorkDurationNanos); + private static native int[] nativeGetThreadIds(long nativeSessionPtr); private static native void nativeUpdateTargetWorkDuration(long nativeSessionPtr, long targetDurationNanos); private static native void nativeReportActualWorkDuration(long nativeSessionPtr, long actualDurationNanos); private static native void nativeCloseSession(long nativeSessionPtr); private static native void nativeSendHint(long nativeSessionPtr, int hint); + private static native void nativeSetThreads(long nativeSessionPtr, int[] tids); } diff --git a/core/jni/android_os_PerformanceHintManager.cpp b/core/jni/android_os_PerformanceHintManager.cpp index ac1401dbb16dd..0223b9669013c 100644 --- a/core/jni/android_os_PerformanceHintManager.cpp +++ b/core/jni/android_os_PerformanceHintManager.cpp @@ -41,6 +41,8 @@ typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t); typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t); typedef void (*APH_closeSession)(APerformanceHintSession* session); typedef void (*APH_sendHint)(APerformanceHintSession*, int32_t); +typedef void (*APH_setThreads)(APerformanceHintSession*, const int32_t*, size_t); +typedef void (*APH_getThreadIds)(APerformanceHintSession*, int32_t* const, size_t* const); bool gAPerformanceHintBindingInitialized = false; APH_getManager gAPH_getManagerFn = nullptr; @@ -50,6 +52,8 @@ APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr; APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr; APH_closeSession gAPH_closeSessionFn = nullptr; APH_sendHint gAPH_sendHintFn = nullptr; +APH_setThreads gAPH_setThreadsFn = nullptr; +APH_getThreadIds gAPH_getThreadIdsFn = nullptr; void ensureAPerformanceHintBindingInitialized() { if (gAPerformanceHintBindingInitialized) return; @@ -95,6 +99,14 @@ void ensureAPerformanceHintBindingInitialized() { "Failed to find required symbol " "APerformanceHint_sendHint!"); + gAPH_setThreadsFn = (APH_setThreads)dlsym(handle_, "APerformanceHint_setThreads"); + LOG_ALWAYS_FATAL_IF(gAPH_setThreadsFn == nullptr, + "Failed to find required symbol APerformanceHint_setThreads!"); + + gAPH_getThreadIdsFn = (APH_getThreadIds)dlsym(handle_, "APerformanceHint_getThreadIds"); + LOG_ALWAYS_FATAL_IF(gAPH_getThreadIdsFn == nullptr, + "Failed to find required symbol APerformanceHint_getThreadIds!"); + gAPerformanceHintBindingInitialized = true; } @@ -150,6 +162,50 @@ static void nativeSendHint(JNIEnv* env, jclass clazz, jlong nativeSessionPtr, ji gAPH_sendHintFn(reinterpret_cast(nativeSessionPtr), hint); } +static void nativeSetThreads(JNIEnv* env, jclass clazz, jlong nativeSessionPtr, jintArray tids) { + ensureAPerformanceHintBindingInitialized(); + + if (tids == nullptr) { + return; + } + ScopedIntArrayRO tidsArray(env, tids); + std::vector tidsVector; + tidsVector.reserve(tidsArray.size()); + for (size_t i = 0; i < tidsArray.size(); ++i) { + tidsVector.push_back(static_cast(tidsArray[i])); + } + gAPH_setThreadsFn(reinterpret_cast(nativeSessionPtr), + tidsVector.data(), tidsVector.size()); +} + +// This call should only be used for validation in tests only. This call will initiate two IPC +// calls, the first one is used to determined the size of the thread ids list, the second one +// is used to return the actual list. +static jintArray nativeGetThreadIds(JNIEnv* env, jclass clazz, jlong nativeSessionPtr) { + ensureAPerformanceHintBindingInitialized(); + size_t size = 0; + gAPH_getThreadIdsFn(reinterpret_cast(nativeSessionPtr), nullptr, + &size); + if (size == 0) { + jintArray jintArr = env->NewIntArray(0); + return jintArr; + } + std::vector tidsVector(size); + gAPH_getThreadIdsFn(reinterpret_cast(nativeSessionPtr), + tidsVector.data(), &size); + jintArray jintArr = env->NewIntArray(size); + if (jintArr == nullptr) { + jniThrowException(env, "java/lang/OutOfMemoryError", nullptr); + return nullptr; + } + jint* threadIds = env->GetIntArrayElements(jintArr, 0); + for (int i = 0; i < size; ++i) { + threadIds[i] = tidsVector[i]; + } + env->ReleaseIntArrayElements(jintArr, threadIds, 0); + return jintArr; +} + static const JNINativeMethod gPerformanceHintMethods[] = { {"nativeAcquireManager", "()J", (void*)nativeAcquireManager}, {"nativeGetPreferredUpdateRateNanos", "(J)J", (void*)nativeGetPreferredUpdateRateNanos}, @@ -158,6 +214,8 @@ static const JNINativeMethod gPerformanceHintMethods[] = { {"nativeReportActualWorkDuration", "(JJ)V", (void*)nativeReportActualWorkDuration}, {"nativeCloseSession", "(J)V", (void*)nativeCloseSession}, {"nativeSendHint", "(JI)V", (void*)nativeSendHint}, + {"nativeSetThreads", "(J[I)V", (void*)nativeSetThreads}, + {"nativeGetThreadIds", "(J)[I", (void*)nativeGetThreadIds}, }; int register_android_os_PerformanceHintManager(JNIEnv* env) { diff --git a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java index 44923b60fbb5d..7eefbbca2d015 100644 --- a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java +++ b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java @@ -137,4 +137,13 @@ public class PerformanceHintManagerTest { assumeNotNull(s); s.close(); } + + @Test + public void testSetThreadsWithIllegalArgument() { + Session session = createSession(); + assumeNotNull(session); + assertThrows(IllegalArgumentException.class, () -> { + session.setThreads(new int[] { }); + }); + } } diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index 4e6a0c540f497..e89c8c9aa5839 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -330,6 +330,7 @@ LIBANDROID { APerformanceHint_updateTargetWorkDuration; # introduced=Tiramisu APerformanceHint_reportActualWorkDuration; # introduced=Tiramisu APerformanceHint_closeSession; # introduced=Tiramisu + APerformanceHint_setThreads; # introduced=UpsideDownCake local: *; }; @@ -338,6 +339,7 @@ LIBANDROID_PLATFORM { global: APerformanceHint_setIHintManagerForTesting; APerformanceHint_sendHint; + APerformanceHint_getThreadIds; extern "C++" { ASurfaceControl_registerSurfaceStatsListener*; ASurfaceControl_unregisterSurfaceStatsListener*; diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp index 43b3d2e94aac5..dfbd7b55a1f0c 100644 --- a/native/android/performance_hint.cpp +++ b/native/android/performance_hint.cpp @@ -62,18 +62,21 @@ private: struct APerformanceHintSession { public: - APerformanceHintSession(sp session, int64_t preferredRateNanos, - int64_t targetDurationNanos); + APerformanceHintSession(sp hintManager, sp session, + int64_t preferredRateNanos, int64_t targetDurationNanos); APerformanceHintSession() = delete; ~APerformanceHintSession(); int updateTargetWorkDuration(int64_t targetDurationNanos); int reportActualWorkDuration(int64_t actualDurationNanos); int sendHint(int32_t hint); + int setThreads(const int32_t* threadIds, size_t size); + int getThreadIds(int32_t* const threadIds, size_t* size); private: friend struct APerformanceHintManager; + sp mHintManager; sp mHintSession; // HAL preferred update rate const int64_t mPreferredRateNanos; @@ -140,7 +143,7 @@ APerformanceHintSession* APerformanceHintManager::createSession( if (!ret.isOk() || !session) { return nullptr; } - return new APerformanceHintSession(std::move(session), mPreferredRateNanos, + return new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos, initialTargetWorkDurationNanos); } @@ -150,10 +153,12 @@ int64_t APerformanceHintManager::getPreferredRateNanos() const { // ===================================== APerformanceHintSession implementation -APerformanceHintSession::APerformanceHintSession(sp session, +APerformanceHintSession::APerformanceHintSession(sp hintManager, + sp session, int64_t preferredRateNanos, int64_t targetDurationNanos) - : mHintSession(std::move(session)), + : mHintManager(hintManager), + mHintSession(std::move(session)), mPreferredRateNanos(preferredRateNanos), mTargetDurationNanos(targetDurationNanos), mFirstTargetMetTimestamp(0), @@ -260,6 +265,47 @@ int APerformanceHintSession::sendHint(int32_t hint) { return 0; } +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__); + return EINVAL; + } + std::vector tids(threadIds, threadIds + size); + binder::Status ret = mHintManager->setHintSessionThreads(mHintSession, tids); + if (!ret.isOk()) { + ALOGE("%s: failed: %s", __FUNCTION__, ret.exceptionMessage().c_str()); + if (ret.exceptionCode() == binder::Status::Exception::EX_SECURITY || + ret.exceptionCode() == binder::Status::Exception::EX_ILLEGAL_ARGUMENT) { + return EINVAL; + } + return EPIPE; + } + return 0; +} + +int APerformanceHintSession::getThreadIds(int32_t* const threadIds, size_t* size) { + std::vector tids; + binder::Status ret = mHintManager->getHintSessionThreadIds(mHintSession, &tids); + if (!ret.isOk()) { + ALOGE("%s: failed: %s", __FUNCTION__, ret.exceptionMessage().c_str()); + return EPIPE; + } + + // When threadIds is nullptr, this is the first call to determine the size + // of the thread ids list. + if (threadIds == nullptr) { + *size = tids.size(); + return 0; + } + + // Second call to return the actual list of thread ids. + *size = tids.size(); + for (size_t i = 0; i < *size; ++i) { + threadIds[i] = tids[i]; + } + return 0; +} + // ===================================== C API APerformanceHintManager* APerformanceHint_getManager() { return APerformanceHintManager::getInstance(); @@ -293,6 +339,23 @@ int APerformanceHint_sendHint(void* session, int32_t hint) { return reinterpret_cast(session)->sendHint(hint); } +int APerformanceHint_setThreads(APerformanceHintSession* session, const int32_t* threadIds, + size_t size) { + if (session == nullptr) { + return EINVAL; + } + return session->setThreads(threadIds, size); +} + +int APerformanceHint_getThreadIds(void* aPerformanceHintSession, int32_t* const threadIds, + size_t* const size) { + if (aPerformanceHintSession == nullptr) { + return EINVAL; + } + return static_cast(aPerformanceHintSession) + ->getThreadIds(threadIds, size); +} + void APerformanceHint_setIHintManagerForTesting(void* iManager) { delete gHintManagerForTesting; gHintManagerForTesting = nullptr; diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp index 0c2d3b6cd201d..321a7dddb1443 100644 --- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp +++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp @@ -37,10 +37,15 @@ using namespace testing; class MockIHintManager : public IHintManager { public: MOCK_METHOD(Status, createHintSession, - (const ::android::sp<::android::IBinder>& token, const ::std::vector& tids, - int64_t durationNanos, ::android::sp<::android::os::IHintSession>* _aidl_return), + (const sp& token, const ::std::vector& tids, + int64_t durationNanos, ::android::sp* _aidl_return), (override)); MOCK_METHOD(Status, getHintSessionPreferredRate, (int64_t * _aidl_return), (override)); + MOCK_METHOD(Status, setHintSessionThreads, + (const sp& hintSession, const ::std::vector& tids), + (override)); + MOCK_METHOD(Status, getHintSessionThreadIds, + (const sp& hintSession, ::std::vector* tids), (override)); MOCK_METHOD(IBinder*, onAsBinder, (), (override)); }; @@ -141,3 +146,36 @@ TEST_F(PerformanceHintTest, TestSession) { EXPECT_CALL(*iSession, close()).Times(Exactly(1)); APerformanceHint_closeSession(session); } + +TEST_F(PerformanceHintTest, SetThreads) { + APerformanceHintManager* manager = createManager(); + + std::vector tids; + tids.push_back(1); + tids.push_back(2); + int64_t targetDuration = 56789L; + + StrictMock* iSession = new StrictMock(); + sp session_sp(iSession); + + EXPECT_CALL(*mMockIHintManager, createHintSession(_, Eq(tids), Eq(targetDuration), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<3>(std::move(session_sp)), Return(Status()))); + + APerformanceHintSession* session = + APerformanceHint_createSession(manager, tids.data(), tids.size(), targetDuration); + ASSERT_TRUE(session); + + std::vector emptyTids; + int result = APerformanceHint_setThreads(session, emptyTids.data(), emptyTids.size()); + EXPECT_EQ(EINVAL, result); + + std::vector newTids; + newTids.push_back(1); + newTids.push_back(3); + EXPECT_CALL(*mMockIHintManager, setHintSessionThreads(_, Eq(newTids))) + .Times(Exactly(1)) + .WillOnce(Return(Status())); + result = APerformanceHint_setThreads(session, newTids.data(), newTids.size()); + EXPECT_EQ(0, result); +} 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 952fcdce7e502..a9a1d5e9102c9 100644 --- a/services/core/java/com/android/server/power/hint/HintManagerService.java +++ b/services/core/java/com/android/server/power/hint/HintManagerService.java @@ -189,6 +189,8 @@ public final class HintManagerService extends SystemService { private static native void nativeSendHint(long halPtr, int hint); + private static native void nativeSetThreads(long halPtr, int[] tids); + private static native long nativeGetHintSessionPreferredRate(); /** Wrapper for HintManager.nativeInit */ @@ -237,6 +239,11 @@ public final class HintManagerService extends SystemService { public long halGetHintSessionPreferredRate() { return nativeGetHintSessionPreferredRate(); } + + /** Wrapper for HintManager.nativeSetThreads */ + public void halSetThreads(long halPtr, int[] tids) { + nativeSetThreads(halPtr, tids); + } } @VisibleForTesting @@ -399,6 +406,18 @@ public final class HintManagerService extends SystemService { return mHintSessionPreferredRate; } + @Override + public void setHintSessionThreads(@NonNull IHintSession hintSession, @NonNull int[] tids) { + AppHintSession appHintSession = (AppHintSession) hintSession; + appHintSession.setThreads(tids); + } + + @Override + public int[] getHintSessionThreadIds(@NonNull IHintSession hintSession) { + AppHintSession appHintSession = (AppHintSession) hintSession; + return appHintSession.getThreadIds(); + } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) { @@ -434,11 +453,12 @@ public final class HintManagerService extends SystemService { final class AppHintSession extends IHintSession.Stub implements IBinder.DeathRecipient { protected final int mUid; protected final int mPid; - protected final int[] mThreadIds; + protected int[] mThreadIds; protected final IBinder mToken; protected long mHalSessionPtr; protected long mTargetDurationNanos; protected boolean mUpdateAllowed; + protected int[] mNewThreadIds; protected AppHintSession( int uid, int pid, int[] threadIds, IBinder token, @@ -541,6 +561,38 @@ public final class HintManagerService extends SystemService { } } + public void setThreads(@NonNull int[] tids) { + synchronized (mLock) { + if (mHalSessionPtr == 0) { + return; + } + if (tids.length == 0) { + throw new IllegalArgumentException("Thread id list can't be empty."); + } + final int callingUid = Binder.getCallingUid(); + final int callingTgid = Process.getThreadGroupLeader(Binder.getCallingPid()); + final long identity = Binder.clearCallingIdentity(); + try { + if (!checkTidValid(callingUid, callingTgid, tids)) { + throw new SecurityException("Some tid doesn't belong to the application."); + } + } finally { + Binder.restoreCallingIdentity(identity); + } + if (!updateHintAllowed()) { + Slogf.v(TAG, "update hint not allowed, storing tids."); + mNewThreadIds = tids; + return; + } + mNativeWrapper.halSetThreads(mHalSessionPtr, tids); + mThreadIds = tids; + } + } + + public int[] getThreadIds() { + return mThreadIds; + } + private void onProcStateChanged() { updateHintAllowed(); } @@ -556,6 +608,11 @@ public final class HintManagerService extends SystemService { synchronized (mLock) { if (mHalSessionPtr == 0) return; mNativeWrapper.halResumeHintSession(mHalSessionPtr); + if (mNewThreadIds != null) { + mNativeWrapper.halSetThreads(mHalSessionPtr, mNewThreadIds); + mThreadIds = mNewThreadIds; + mNewThreadIds = null; + } } } diff --git a/services/core/jni/com_android_server_hint_HintManagerService.cpp b/services/core/jni/com_android_server_hint_HintManagerService.cpp index d975760cbfc23..e322fa23e5b48 100644 --- a/services/core/jni/com_android_server_hint_HintManagerService.cpp +++ b/services/core/jni/com_android_server_hint_HintManagerService.cpp @@ -87,6 +87,11 @@ static void sendHint(int64_t session_ptr, SessionHint hint) { appSession->sendHint(hint); } +static void setThreads(int64_t session_ptr, const std::vector& threadIds) { + sp appSession = reinterpret_cast(session_ptr); + appSession->setThreads(threadIds); +} + static int64_t getHintSessionPreferredRate() { int64_t rate = -1; auto result = gPowerHalController.getHintSessionPreferredRate(); @@ -149,6 +154,16 @@ static void nativeSendHint(JNIEnv* env, jclass /* clazz */, jlong session_ptr, j sendHint(session_ptr, static_cast(hint)); } +static void nativeSetThreads(JNIEnv* env, jclass /* clazz */, jlong session_ptr, jintArray tids) { + ScopedIntArrayRO arrayThreadIds(env, tids); + + std::vector threadIds(arrayThreadIds.size()); + for (size_t i = 0; i < arrayThreadIds.size(); i++) { + threadIds[i] = arrayThreadIds[i]; + } + setThreads(session_ptr, threadIds); +} + static jlong nativeGetHintSessionPreferredRate(JNIEnv* /* env */, jclass /* clazz */) { return static_cast(getHintSessionPreferredRate()); } @@ -164,6 +179,7 @@ static const JNINativeMethod sHintManagerServiceMethods[] = { {"nativeUpdateTargetWorkDuration", "(JJ)V", (void*)nativeUpdateTargetWorkDuration}, {"nativeReportActualWorkDuration", "(J[J[J)V", (void*)nativeReportActualWorkDuration}, {"nativeSendHint", "(JI)V", (void*)nativeSendHint}, + {"nativeSetThreads", "(J[I)V", (void*)nativeSetThreads}, {"nativeGetHintSessionPreferredRate", "()J", (void*)nativeGetHintSessionPreferredRate}, }; diff --git a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java index dcbdcdc980170..136507d429fc6 100644 --- a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java @@ -19,6 +19,7 @@ package com.android.server.power.hint; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; @@ -310,4 +311,32 @@ public class HintManagerServiceTest { a.mUid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0); assertTrue(a.updateHintAllowed()); } + + @Test + public void testSetThreads() throws Exception { + HintManagerService service = createService(); + IBinder token = new Binder(); + + AppHintSession a = (AppHintSession) service.getBinderServiceInstance() + .createHintSession(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION); + + a.updateTargetWorkDuration(100L); + + assertThrows(IllegalArgumentException.class, () -> { + a.setThreads(new int[]{}); + }); + + a.setThreads(SESSION_TIDS_B); + verify(mNativeWrapperMock, times(1)).halSetThreads(anyLong(), eq(SESSION_TIDS_B)); + assertArrayEquals(SESSION_TIDS_B, a.getThreadIds()); + + reset(mNativeWrapperMock); + // Set session to background, then the duration would not be updated. + service.mUidObserver.onUidStateChanged( + a.mUid, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0); + FgThread.getHandler().runWithScissors(() -> { }, 500); + assertFalse(a.updateHintAllowed()); + a.setThreads(SESSION_TIDS_A); + verify(mNativeWrapperMock, never()).halSetThreads(anyLong(), any()); + } }