From 4426772eabda18a5391715ef1a762c2fcfe13eb0 Mon Sep 17 00:00:00 2001 From: Bo Liu Date: Fri, 16 Jul 2021 17:03:20 -0400 Subject: [PATCH] Implement native PerformanceHint API Test: atest PerformanceHintNativeTestCases Bug: 194204196 Change-Id: Ie26e25e9ecf87046df92346dff54174934a8c73e --- core/java/Android.bp | 2 + native/android/Android.bp | 1 + native/android/libandroid.map.txt | 7 + native/android/performance_hint.cpp | 241 ++++++++++++++++++ .../android/tests/performance_hint/Android.bp | 65 +++++ .../PerformanceHintNativeTest.cpp | 124 +++++++++ 6 files changed, 440 insertions(+) create mode 100644 native/android/performance_hint.cpp create mode 100644 native/android/tests/performance_hint/Android.bp create mode 100644 native/android/tests/performance_hint/PerformanceHintNativeTest.cpp diff --git a/core/java/Android.bp b/core/java/Android.bp index 6c001f305ce77..26c83eeca508d 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -112,6 +112,8 @@ filegroup { srcs: [ "android/os/Temperature.aidl", "android/os/CoolingDevice.aidl", + "android/os/IHintManager.aidl", + "android/os/IHintSession.aidl", "android/os/IThermalEventListener.aidl", "android/os/IThermalStatusListener.aidl", "android/os/IThermalService.aidl", diff --git a/native/android/Android.bp b/native/android/Android.bp index 3ee2c18a0eab4..32b7a0780e632 100644 --- a/native/android/Android.bp +++ b/native/android/Android.bp @@ -57,6 +57,7 @@ cc_library_shared { "net.c", "obb.cpp", "permission_manager.cpp", + "performance_hint.cpp", "sensor.cpp", "sharedmem.cpp", "storage_manager.cpp", diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index de6db1ae7d237..f33e11817730b 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -312,6 +312,13 @@ LIBANDROID { LIBANDROID_PLATFORM { global: + APerformanceHint_getManager; + APerformanceHint_createSession; + APerformanceHint_getPreferredUpdateRateNanos; + APerformanceHint_updateTargetWorkDuration; + APerformanceHint_reportActualWorkDuration; + APerformanceHint_closeSession; + APerformanceHint_setIHintManagerForTesting; extern "C++" { ASurfaceControl_registerSurfaceStatsListener*; ASurfaceControl_unregisterSurfaceStatsListener*; diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp new file mode 100644 index 0000000000000..95a2da9226d96 --- /dev/null +++ b/native/android/performance_hint.cpp @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "perf_hint" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace android; +using namespace android::os; + +struct APerformanceHintSession; + +struct APerformanceHintManager { +public: + static APerformanceHintManager* getInstance(); + APerformanceHintManager(sp service, int64_t preferredRateNanos); + APerformanceHintManager() = delete; + ~APerformanceHintManager() = default; + + APerformanceHintSession* createSession(const int32_t* threadIds, size_t size, + int64_t initialTargetWorkDurationNanos); + int64_t getPreferredRateNanos() const; + +private: + static APerformanceHintManager* create(sp iHintManager); + + sp mHintManager; + const int64_t mPreferredRateNanos; +}; + +struct APerformanceHintSession { +public: + APerformanceHintSession(sp session, int64_t preferredRateNanos, + int64_t targetDurationNanos); + APerformanceHintSession() = delete; + ~APerformanceHintSession(); + + int updateTargetWorkDuration(int64_t targetDurationNanos); + int reportActualWorkDuration(int64_t actualDurationNanos); + +private: + friend struct APerformanceHintManager; + + sp mHintSession; + // HAL preferred update rate + const int64_t mPreferredRateNanos; + // Target duration for choosing update rate + int64_t mTargetDurationNanos; + // Last update timestamp + int64_t mLastUpdateTimestamp; + // Cached samples + std::vector mActualDurationsNanos; + std::vector mTimestampsNanos; +}; + +static IHintManager* gIHintManagerForTesting = nullptr; +static APerformanceHintManager* gHintManagerForTesting = nullptr; + +// ===================================== APerformanceHintManager implementation +APerformanceHintManager::APerformanceHintManager(sp manager, + int64_t preferredRateNanos) + : mHintManager(std::move(manager)), mPreferredRateNanos(preferredRateNanos) {} + +APerformanceHintManager* APerformanceHintManager::getInstance() { + if (gHintManagerForTesting) return gHintManagerForTesting; + if (gIHintManagerForTesting) { + APerformanceHintManager* manager = create(gIHintManagerForTesting); + gIHintManagerForTesting = nullptr; + return manager; + } + static APerformanceHintManager* instance = create(nullptr); + return instance; +} + +APerformanceHintManager* APerformanceHintManager::create(sp manager) { + if (!manager) { + manager = interface_cast( + defaultServiceManager()->checkService(String16("performance_hint"))); + } + if (manager == nullptr) { + ALOGE("%s: PerformanceHint service is not ready ", __FUNCTION__); + return nullptr; + } + int64_t preferredRateNanos = -1L; + binder::Status ret = manager->getHintSessionPreferredRate(&preferredRateNanos); + if (!ret.isOk()) { + ALOGE("%s: PerformanceHint cannot get preferred rate. %s", __FUNCTION__, + ret.exceptionMessage().c_str()); + return nullptr; + } + if (preferredRateNanos <= 0) { + ALOGE("%s: PerformanceHint invalid preferred rate.", __FUNCTION__); + return nullptr; + } + return new APerformanceHintManager(std::move(manager), preferredRateNanos); +} + +APerformanceHintSession* APerformanceHintManager::createSession( + const int32_t* threadIds, size_t size, int64_t initialTargetWorkDurationNanos) { + sp token = sp::make(); + std::vector tids(threadIds, threadIds + size); + sp session; + binder::Status ret = + mHintManager->createHintSession(token, tids, initialTargetWorkDurationNanos, &session); + if (!ret.isOk() || !session) { + return nullptr; + } + return new APerformanceHintSession(std::move(session), mPreferredRateNanos, + initialTargetWorkDurationNanos); +} + +int64_t APerformanceHintManager::getPreferredRateNanos() const { + return mPreferredRateNanos; +} + +// ===================================== APerformanceHintSession implementation + +APerformanceHintSession::APerformanceHintSession(sp session, + int64_t preferredRateNanos, + int64_t targetDurationNanos) + : mHintSession(std::move(session)), + mPreferredRateNanos(preferredRateNanos), + mTargetDurationNanos(targetDurationNanos), + mLastUpdateTimestamp(elapsedRealtimeNano()) {} + +APerformanceHintSession::~APerformanceHintSession() { + binder::Status ret = mHintSession->close(); + if (!ret.isOk()) { + ALOGE("%s: HintSession close failed: %s", __FUNCTION__, ret.exceptionMessage().c_str()); + } +} + +int APerformanceHintSession::updateTargetWorkDuration(int64_t targetDurationNanos) { + if (targetDurationNanos <= 0) { + ALOGE("%s: targetDurationNanos must be positive", __FUNCTION__); + return EINVAL; + } + binder::Status ret = mHintSession->updateTargetWorkDuration(targetDurationNanos); + if (!ret.isOk()) { + ALOGE("%s: HintSessionn updateTargetWorkDuration failed: %s", __FUNCTION__, + ret.exceptionMessage().c_str()); + return EPIPE; + } + mTargetDurationNanos = targetDurationNanos; + /** + * Most of the workload is target_duration dependent, so now clear the cached samples + * as they are most likely obsolete. + */ + mActualDurationsNanos.clear(); + mTimestampsNanos.clear(); + mLastUpdateTimestamp = elapsedRealtimeNano(); + return 0; +} + +int APerformanceHintSession::reportActualWorkDuration(int64_t actualDurationNanos) { + if (actualDurationNanos <= 0) { + ALOGE("%s: actualDurationNanos must be positive", __FUNCTION__); + return EINVAL; + } + int64_t now = elapsedRealtimeNano(); + mActualDurationsNanos.push_back(actualDurationNanos); + mTimestampsNanos.push_back(now); + + /** + * Use current sample to determine the rate limit. We can pick a shorter rate limit + * if any sample underperformed, however, it could be the lower level system is slow + * to react. So here we explicitly choose the rate limit with the latest sample. + */ + int64_t rateLimit = actualDurationNanos > mTargetDurationNanos ? mPreferredRateNanos + : 10 * mPreferredRateNanos; + if (now - mLastUpdateTimestamp <= rateLimit) return 0; + + binder::Status ret = + mHintSession->reportActualWorkDuration(mActualDurationsNanos, mTimestampsNanos); + mActualDurationsNanos.clear(); + mTimestampsNanos.clear(); + if (!ret.isOk()) { + ALOGE("%s: HintSession reportActualWorkDuration failed: %s", __FUNCTION__, + ret.exceptionMessage().c_str()); + return EPIPE; + } + mLastUpdateTimestamp = now; + return 0; +} + +// ===================================== C API +APerformanceHintManager* APerformanceHint_getManager() { + return APerformanceHintManager::getInstance(); +} + +APerformanceHintSession* APerformanceHint_createSession(APerformanceHintManager* manager, + const int32_t* threadIds, size_t size, + int64_t initialTargetWorkDurationNanos) { + return manager->createSession(threadIds, size, initialTargetWorkDurationNanos); +} + +int64_t APerformanceHint_getPreferredUpdateRateNanos(APerformanceHintManager* manager) { + return manager->getPreferredRateNanos(); +} + +int APerformanceHint_updateTargetWorkDuration(APerformanceHintSession* session, + int64_t targetDurationNanos) { + return session->updateTargetWorkDuration(targetDurationNanos); +} + +int APerformanceHint_reportActualWorkDuration(APerformanceHintSession* session, + int64_t actualDurationNanos) { + return session->reportActualWorkDuration(actualDurationNanos); +} + +void APerformanceHint_closeSession(APerformanceHintSession* session) { + delete session; +} + +void APerformanceHint_setIHintManagerForTesting(void* iManager) { + delete gHintManagerForTesting; + gHintManagerForTesting = nullptr; + gIHintManagerForTesting = static_cast(iManager); +} diff --git a/native/android/tests/performance_hint/Android.bp b/native/android/tests/performance_hint/Android.bp new file mode 100644 index 0000000000000..fdc1bc6a20d8e --- /dev/null +++ b/native/android/tests/performance_hint/Android.bp @@ -0,0 +1,65 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +cc_test { + name: "PerformanceHintNativeTestCases", + + multilib: { + lib32: { + suffix: "32", + }, + lib64: { + suffix: "64", + }, + }, + + srcs: ["PerformanceHintNativeTest.cpp"], + + shared_libs: [ + "libandroid", + "liblog", + "libbinder", + "libpowermanager", + "libutils", + ], + + static_libs: [ + "libbase", + "libgmock", + "libgtest", + ], + stl: "c++_shared", + + test_suites: [ + "device-tests", + ], + + cflags: [ + "-Werror", + "-Wall", + ], + + header_libs: [ + "libandroid_headers_private", + ], +} diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp new file mode 100644 index 0000000000000..284e9ee909ee8 --- /dev/null +++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PerformanceHintNativeTest" + +#include +#include +#include +#include +#include +#include +#include +#include + +using android::binder::Status; +using android::os::IHintManager; +using android::os::IHintSession; + +using namespace android; +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), + (override)); + MOCK_METHOD(Status, getHintSessionPreferredRate, (int64_t * _aidl_return), (override)); + MOCK_METHOD(IBinder*, onAsBinder, (), (override)); +}; + +class MockIHintSession : public IHintSession { +public: + MOCK_METHOD(Status, updateTargetWorkDuration, (int64_t targetDurationNanos), (override)); + MOCK_METHOD(Status, reportActualWorkDuration, + (const ::std::vector& actualDurationNanos, + const ::std::vector& timeStampNanos), + (override)); + MOCK_METHOD(Status, close, (), (override)); + MOCK_METHOD(IBinder*, onAsBinder, (), (override)); +}; + +class PerformanceHintTest : public Test { +public: + void SetUp() override { + mMockIHintManager = new StrictMock(); + APerformanceHint_setIHintManagerForTesting(mMockIHintManager); + } + + void TearDown() override { + mMockIHintManager = nullptr; + // Destroys MockIHintManager. + APerformanceHint_setIHintManagerForTesting(nullptr); + } + + APerformanceHintManager* createManager() { + EXPECT_CALL(*mMockIHintManager, getHintSessionPreferredRate(_)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<0>(123L), Return(Status()))); + return APerformanceHint_getManager(); + } + + StrictMock* mMockIHintManager = nullptr; +}; + +TEST_F(PerformanceHintTest, TestGetPreferredUpdateRateNanos) { + APerformanceHintManager* manager = createManager(); + int64_t preferredUpdateRateNanos = APerformanceHint_getPreferredUpdateRateNanos(manager); + EXPECT_EQ(123L, preferredUpdateRateNanos); +} + +TEST_F(PerformanceHintTest, TestSession) { + 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); + + int64_t targetDurationNanos = 10; + EXPECT_CALL(*iSession, updateTargetWorkDuration(Eq(targetDurationNanos))).Times(Exactly(1)); + int result = APerformanceHint_updateTargetWorkDuration(session, targetDurationNanos); + EXPECT_EQ(0, result); + + usleep(2); // Sleep for longer than preferredUpdateRateNanos. + int64_t actualDurationNanos = 20; + std::vector actualDurations; + actualDurations.push_back(20); + EXPECT_CALL(*iSession, reportActualWorkDuration(Eq(actualDurations), _)).Times(Exactly(1)); + result = APerformanceHint_reportActualWorkDuration(session, actualDurationNanos); + EXPECT_EQ(0, result); + + result = APerformanceHint_updateTargetWorkDuration(session, -1L); + EXPECT_EQ(EINVAL, result); + result = APerformanceHint_reportActualWorkDuration(session, -1L); + EXPECT_EQ(EINVAL, result); + + EXPECT_CALL(*iSession, close()).Times(Exactly(1)); + APerformanceHint_closeSession(session); +}