diff options
Diffstat (limited to 'native')
| -rw-r--r-- | native/android/Android.bp | 6 | ||||
| -rw-r--r-- | native/android/TEST_MAPPING | 19 | ||||
| -rw-r--r-- | native/android/configuration.cpp | 2 | ||||
| -rw-r--r-- | native/android/libandroid.map.txt | 10 | ||||
| -rw-r--r-- | native/android/performance_hint.cpp | 191 | ||||
| -rw-r--r-- | native/android/system_fonts.cpp | 9 | ||||
| -rw-r--r-- | native/android/tests/performance_hint/PerformanceHintNativeTest.cpp | 187 | ||||
| -rw-r--r-- | native/android/tests/thermal/Android.bp | 65 | ||||
| -rw-r--r-- | native/android/tests/thermal/NativeThermalUnitTest.cpp | 158 | ||||
| -rw-r--r-- | native/android/tests/thermal/OWNERS | 1 | ||||
| -rw-r--r-- | native/android/thermal.cpp | 124 | ||||
| -rw-r--r-- | native/webview/TEST_MAPPING | 2 |
12 files changed, 689 insertions, 85 deletions
diff --git a/native/android/Android.bp b/native/android/Android.bp index 254eb4494ed8..7f3792d06795 100644 --- a/native/android/Android.bp +++ b/native/android/Android.bp @@ -46,7 +46,10 @@ cc_defaults { cc_library_shared { name: "libandroid", - defaults: ["libandroid_defaults"], + defaults: [ + "libandroid_defaults", + "android.hardware.power-ndk_shared", + ], srcs: [ "activity_manager.cpp", @@ -95,7 +98,6 @@ cc_library_shared { "libpowermanager", "android.hardware.configstore@1.0", "android.hardware.configstore-utils", - "android.hardware.power-V4-ndk", "libnativedisplay", ], diff --git a/native/android/TEST_MAPPING b/native/android/TEST_MAPPING index 6a5d2c008b1d..7c710982e4f6 100644 --- a/native/android/TEST_MAPPING +++ b/native/android/TEST_MAPPING @@ -12,6 +12,25 @@ } ], "file_patterns": ["permission_manager.cpp"] + }, + { + "name": "CtsOsTestCases", + "options": [ + { + "include-filter": "android.os.cts.PerformanceHintManagerTest" + } + ], + "file_patterns": ["performance_hint.cpp"] + } + ], + "postsubmit": [ + { + "name": "CtsThermalTestCases", + "file_patterns": ["thermal.cpp"] + }, + { + "name": "NativeThermalUnitTestCases", + "file_patterns": ["thermal.cpp"] } ] } diff --git a/native/android/configuration.cpp b/native/android/configuration.cpp index b50514d27bac..283445fc8a9a 100644 --- a/native/android/configuration.cpp +++ b/native/android/configuration.cpp @@ -36,7 +36,7 @@ void AConfiguration_delete(AConfiguration* config) { void AConfiguration_fromAssetManager(AConfiguration* out, AAssetManager* am) { ScopedLock<AssetManager2> locked_mgr(*AssetManagerForNdkAssetManager(am)); - ResTable_config config = locked_mgr->GetConfiguration(); + ResTable_config config = locked_mgr->GetConfigurations()[0]; // AConfiguration is not a virtual subclass, so we can memcpy. memcpy(out, &config, sizeof(config)); diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index 985305f02a82..960510879a4c 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -327,6 +327,7 @@ LIBANDROID { AThermal_registerThermalStatusListener; # introduced=30 AThermal_unregisterThermalStatusListener; # introduced=30 AThermal_getThermalHeadroom; # introduced=31 + AThermal_getThermalHeadroomThresholds; # introduced=VanillaIceCream APerformanceHint_getManager; # introduced=Tiramisu APerformanceHint_createSession; # introduced=Tiramisu APerformanceHint_getPreferredUpdateRateNanos; # introduced=Tiramisu @@ -334,12 +335,21 @@ LIBANDROID { APerformanceHint_reportActualWorkDuration; # introduced=Tiramisu APerformanceHint_closeSession; # introduced=Tiramisu APerformanceHint_setThreads; # introduced=UpsideDownCake + APerformanceHint_setPreferPowerEfficiency; # introduced=VanillaIceCream + APerformanceHint_reportActualWorkDuration2; # introduced=VanillaIceCream + AWorkDuration_create; # introduced=VanillaIceCream + AWorkDuration_release; # introduced=VanillaIceCream + AWorkDuration_setWorkPeriodStartTimestampNanos; # introduced=VanillaIceCream + AWorkDuration_setActualTotalDurationNanos; # introduced=VanillaIceCream + AWorkDuration_setActualCpuDurationNanos; # introduced=VanillaIceCream + AWorkDuration_setActualGpuDurationNanos; # introduced=VanillaIceCream local: *; }; LIBANDROID_PLATFORM { global: + AThermal_setIThermalServiceForTesting; APerformanceHint_setIHintManagerForTesting; APerformanceHint_sendHint; APerformanceHint_getThreadIds; diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp index b3628fa3e5ce..c4c81284780e 100644 --- a/native/android/performance_hint.cpp +++ b/native/android/performance_hint.cpp @@ -17,12 +17,15 @@ #define LOG_TAG "perf_hint" #include <aidl/android/hardware/power/SessionHint.h> +#include <aidl/android/hardware/power/SessionMode.h> +#include <android/WorkDuration.h> #include <android/os/IHintManager.h> #include <android/os/IHintSession.h> #include <android/performance_hint.h> #include <binder/Binder.h> #include <binder/IBinder.h> #include <binder/IServiceManager.h> +#include <inttypes.h> #include <performance_hint_private.h> #include <utils/SystemClock.h> @@ -36,6 +39,7 @@ using namespace android::os; using namespace std::chrono_literals; using AidlSessionHint = aidl::android::hardware::power::SessionHint; +using AidlSessionMode = aidl::android::hardware::power::SessionMode; struct APerformanceHintSession; @@ -72,10 +76,14 @@ public: int sendHint(SessionHint hint); int setThreads(const int32_t* threadIds, size_t size); int getThreadIds(int32_t* const threadIds, size_t* size); + int setPreferPowerEfficiency(bool enabled); + int reportActualWorkDuration(AWorkDuration* workDuration); private: friend struct APerformanceHintManager; + int reportActualWorkDurationInternal(WorkDuration* workDuration); + sp<IHintManager> mHintManager; sp<IHintSession> mHintSession; // HAL preferred update rate @@ -89,8 +97,7 @@ private: // Last hint reported from sendHint indexed by hint value std::vector<int64_t> mLastHintSentTimestamp; // Cached samples - std::vector<int64_t> mActualDurationsNanos; - std::vector<int64_t> mTimestampsNanos; + std::vector<WorkDuration> mActualWorkDurations; }; static IHintManager* gIHintManagerForTesting = nullptr; @@ -192,8 +199,7 @@ int APerformanceHintSession::updateTargetWorkDuration(int64_t targetDurationNano * Most of the workload is target_duration dependent, so now clear the cached samples * as they are most likely obsolete. */ - mActualDurationsNanos.clear(); - mTimestampsNanos.clear(); + mActualWorkDurations.clear(); mFirstTargetMetTimestamp = 0; mLastTargetMetTimestamp = 0; return 0; @@ -204,43 +210,10 @@ int APerformanceHintSession::reportActualWorkDuration(int64_t actualDurationNano ALOGE("%s: actualDurationNanos must be positive", __FUNCTION__); return EINVAL; } - int64_t now = elapsedRealtimeNano(); - mActualDurationsNanos.push_back(actualDurationNanos); - mTimestampsNanos.push_back(now); - - if (actualDurationNanos >= mTargetDurationNanos) { - // Reset timestamps if we are equal or over the target. - mFirstTargetMetTimestamp = 0; - } else { - // Set mFirstTargetMetTimestamp for first time meeting target. - if (!mFirstTargetMetTimestamp || !mLastTargetMetTimestamp || - (now - mLastTargetMetTimestamp > 2 * mPreferredRateNanos)) { - mFirstTargetMetTimestamp = now; - } - /** - * Rate limit the change if the update is over mPreferredRateNanos since first - * meeting target and less than mPreferredRateNanos since last meeting target. - */ - if (now - mFirstTargetMetTimestamp > mPreferredRateNanos && - now - mLastTargetMetTimestamp <= mPreferredRateNanos) { - return 0; - } - mLastTargetMetTimestamp = now; - } - binder::Status ret = - mHintSession->reportActualWorkDuration(mActualDurationsNanos, mTimestampsNanos); - if (!ret.isOk()) { - ALOGE("%s: HintSession reportActualWorkDuration failed: %s", __FUNCTION__, - ret.exceptionMessage().c_str()); - mFirstTargetMetTimestamp = 0; - mLastTargetMetTimestamp = 0; - return EPIPE; - } - mActualDurationsNanos.clear(); - mTimestampsNanos.clear(); + WorkDuration workDuration(0, actualDurationNanos, actualDurationNanos, 0); - return 0; + return reportActualWorkDurationInternal(&workDuration); } int APerformanceHintSession::sendHint(SessionHint hint) { @@ -274,9 +247,10 @@ int APerformanceHintSession::setThreads(const int32_t* threadIds, size_t 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) { + if (ret.exceptionCode() == binder::Status::Exception::EX_ILLEGAL_ARGUMENT) { return EINVAL; + } else if (ret.exceptionCode() == binder::Status::Exception::EX_SECURITY) { + return EPERM; } return EPIPE; } @@ -306,6 +280,79 @@ int APerformanceHintSession::getThreadIds(int32_t* const threadIds, size_t* size return 0; } +int APerformanceHintSession::setPreferPowerEfficiency(bool enabled) { + binder::Status ret = + mHintSession->setMode(static_cast<int32_t>(AidlSessionMode::POWER_EFFICIENCY), enabled); + + if (!ret.isOk()) { + ALOGE("%s: HintSession setPreferPowerEfficiency failed: %s", __FUNCTION__, + ret.exceptionMessage().c_str()); + return EPIPE; + } + return OK; +} + +int APerformanceHintSession::reportActualWorkDuration(AWorkDuration* aWorkDuration) { + WorkDuration* workDuration = static_cast<WorkDuration*>(aWorkDuration); + if (workDuration->workPeriodStartTimestampNanos <= 0) { + ALOGE("%s: workPeriodStartTimestampNanos must be positive", __FUNCTION__); + return EINVAL; + } + if (workDuration->actualTotalDurationNanos <= 0) { + ALOGE("%s: actualDurationNanos must be positive", __FUNCTION__); + return EINVAL; + } + if (workDuration->actualCpuDurationNanos <= 0) { + ALOGE("%s: cpuDurationNanos must be positive", __FUNCTION__); + return EINVAL; + } + if (workDuration->actualGpuDurationNanos < 0) { + ALOGE("%s: gpuDurationNanos must be non negative", __FUNCTION__); + return EINVAL; + } + + return reportActualWorkDurationInternal(workDuration); +} + +int APerformanceHintSession::reportActualWorkDurationInternal(WorkDuration* workDuration) { + int64_t actualTotalDurationNanos = workDuration->actualTotalDurationNanos; + int64_t now = uptimeNanos(); + workDuration->timestampNanos = now; + mActualWorkDurations.push_back(std::move(*workDuration)); + + if (actualTotalDurationNanos >= mTargetDurationNanos) { + // Reset timestamps if we are equal or over the target. + mFirstTargetMetTimestamp = 0; + } else { + // Set mFirstTargetMetTimestamp for first time meeting target. + if (!mFirstTargetMetTimestamp || !mLastTargetMetTimestamp || + (now - mLastTargetMetTimestamp > 2 * mPreferredRateNanos)) { + mFirstTargetMetTimestamp = now; + } + /** + * Rate limit the change if the update is over mPreferredRateNanos since first + * meeting target and less than mPreferredRateNanos since last meeting target. + */ + if (now - mFirstTargetMetTimestamp > mPreferredRateNanos && + now - mLastTargetMetTimestamp <= mPreferredRateNanos) { + return 0; + } + mLastTargetMetTimestamp = now; + } + + binder::Status ret = mHintSession->reportActualWorkDuration2(mActualWorkDurations); + if (!ret.isOk()) { + ALOGE("%s: HintSession reportActualWorkDuration failed: %s", __FUNCTION__, + ret.exceptionMessage().c_str()); + mFirstTargetMetTimestamp = 0; + mLastTargetMetTimestamp = 0; + return ret.exceptionCode() == binder::Status::EX_ILLEGAL_ARGUMENT ? EINVAL : EPIPE; + } + mActualWorkDurations.clear(); + + return 0; +} + // ===================================== C API APerformanceHintManager* APerformanceHint_getManager() { return APerformanceHintManager::getInstance(); @@ -356,6 +403,68 @@ int APerformanceHint_getThreadIds(void* aPerformanceHintSession, int32_t* const ->getThreadIds(threadIds, size); } +int APerformanceHint_setPreferPowerEfficiency(APerformanceHintSession* session, bool enabled) { + return session->setPreferPowerEfficiency(enabled); +} + +int APerformanceHint_reportActualWorkDuration2(APerformanceHintSession* session, + AWorkDuration* workDuration) { + if (session == nullptr || workDuration == nullptr) { + ALOGE("Invalid value: (session %p, workDuration %p)", session, workDuration); + return EINVAL; + } + return session->reportActualWorkDuration(workDuration); +} + +AWorkDuration* AWorkDuration_create() { + WorkDuration* workDuration = new WorkDuration(); + return static_cast<AWorkDuration*>(workDuration); +} + +void AWorkDuration_release(AWorkDuration* aWorkDuration) { + if (aWorkDuration == nullptr) { + ALOGE("%s: aWorkDuration is nullptr", __FUNCTION__); + } + delete aWorkDuration; +} + +void AWorkDuration_setWorkPeriodStartTimestampNanos(AWorkDuration* aWorkDuration, + int64_t workPeriodStartTimestampNanos) { + if (aWorkDuration == nullptr || workPeriodStartTimestampNanos <= 0) { + ALOGE("%s: Invalid value. (AWorkDuration: %p, workPeriodStartTimestampNanos: %" PRIi64 ")", + __FUNCTION__, aWorkDuration, workPeriodStartTimestampNanos); + } + static_cast<WorkDuration*>(aWorkDuration)->workPeriodStartTimestampNanos = + workPeriodStartTimestampNanos; +} + +void AWorkDuration_setActualTotalDurationNanos(AWorkDuration* aWorkDuration, + int64_t actualTotalDurationNanos) { + if (aWorkDuration == nullptr || actualTotalDurationNanos <= 0) { + ALOGE("%s: Invalid value. (AWorkDuration: %p, actualTotalDurationNanos: %" PRIi64 ")", + __FUNCTION__, aWorkDuration, actualTotalDurationNanos); + } + static_cast<WorkDuration*>(aWorkDuration)->actualTotalDurationNanos = actualTotalDurationNanos; +} + +void AWorkDuration_setActualCpuDurationNanos(AWorkDuration* aWorkDuration, + int64_t actualCpuDurationNanos) { + if (aWorkDuration == nullptr || actualCpuDurationNanos <= 0) { + ALOGE("%s: Invalid value. (AWorkDuration: %p, actualCpuDurationNanos: %" PRIi64 ")", + __FUNCTION__, aWorkDuration, actualCpuDurationNanos); + } + static_cast<WorkDuration*>(aWorkDuration)->actualCpuDurationNanos = actualCpuDurationNanos; +} + +void AWorkDuration_setActualGpuDurationNanos(AWorkDuration* aWorkDuration, + int64_t actualGpuDurationNanos) { + if (aWorkDuration == nullptr || actualGpuDurationNanos < 0) { + ALOGE("%s: Invalid value. (AWorkDuration: %p, actualGpuDurationNanos: %" PRIi64 ")", + __FUNCTION__, aWorkDuration, actualGpuDurationNanos); + } + static_cast<WorkDuration*>(aWorkDuration)->actualGpuDurationNanos = actualGpuDurationNanos; +} + void APerformanceHint_setIHintManagerForTesting(void* iManager) { delete gHintManagerForTesting; gHintManagerForTesting = nullptr; diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp index ceab164aa324..91f78ce6f950 100644 --- a/native/android/system_fonts.cpp +++ b/native/android/system_fonts.cpp @@ -249,14 +249,15 @@ ASystemFontIterator* ASystemFontIterator_open() { locale.emplace(minikin::getLocaleString(localeId)); } std::vector<std::pair<uint32_t, float>> axes; - for (const auto& [tag, value] : font->typeface()->GetAxes()) { + for (const auto& [tag, value] : font->baseTypeface()->GetAxes()) { axes.push_back(std::make_pair(tag, value)); } - fonts.insert({font->typeface()->GetFontPath(), std::move(locale), + fonts.insert({font->baseTypeface()->GetFontPath(), std::move(locale), font->style().weight(), font->style().slant() == minikin::FontStyle::Slant::ITALIC, - static_cast<uint32_t>(font->typeface()->GetFontIndex()), axes}); + static_cast<uint32_t>(font->baseTypeface()->GetFontIndex()), + axes}); } }); @@ -321,7 +322,7 @@ AFont* _Nonnull AFontMatcher_match( .font; std::unique_ptr<AFont> result = std::make_unique<AFont>(); const android::MinikinFontSkia* minikinFontSkia = - reinterpret_cast<android::MinikinFontSkia*>(font->typeface().get()); + reinterpret_cast<android::MinikinFontSkia*>(font->baseTypeface().get()); result->mFilePath = minikinFontSkia->getFilePath(); result->mWeight = font->style().weight(); result->mItalic = font->style().slant() == minikin::FontStyle::Slant::ITALIC; diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp index 791adfd33fcd..4553b4919d2d 100644 --- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp +++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp @@ -16,6 +16,7 @@ #define LOG_TAG "PerformanceHintNativeTest" +#include <android/WorkDuration.h> #include <android/os/IHintManager.h> #include <android/os/IHintSession.h> #include <android/performance_hint.h> @@ -56,9 +57,12 @@ public: (const ::std::vector<int64_t>& actualDurationNanos, const ::std::vector<int64_t>& timeStampNanos), (override)); - MOCK_METHOD(Status, sendHint, (int32_t hints), (override)); + MOCK_METHOD(Status, sendHint, (int32_t hint), (override)); + MOCK_METHOD(Status, setMode, (int32_t mode, bool enabled), (override)); MOCK_METHOD(Status, close, (), (override)); MOCK_METHOD(IBinder*, onAsBinder, (), (override)); + MOCK_METHOD(Status, reportActualWorkDuration2, + (const ::std::vector<android::os::WorkDuration>& workDurations), (override)); }; class PerformanceHintTest : public Test { @@ -119,6 +123,7 @@ TEST_F(PerformanceHintTest, TestSession) { std::vector<int64_t> actualDurations; actualDurations.push_back(20); EXPECT_CALL(*iSession, reportActualWorkDuration(Eq(actualDurations), _)).Times(Exactly(1)); + EXPECT_CALL(*iSession, reportActualWorkDuration2(_)).Times(Exactly(1)); result = APerformanceHint_reportActualWorkDuration(session, actualDurationNanos); EXPECT_EQ(0, result); @@ -178,4 +183,184 @@ TEST_F(PerformanceHintTest, SetThreads) { .WillOnce(Return(Status())); result = APerformanceHint_setThreads(session, newTids.data(), newTids.size()); EXPECT_EQ(0, result); + + testing::Mock::VerifyAndClearExpectations(mMockIHintManager); + std::vector<int32_t> invalidTids; + auto status = Status::fromExceptionCode(binder::Status::Exception::EX_SECURITY); + invalidTids.push_back(4); + invalidTids.push_back(6); + EXPECT_CALL(*mMockIHintManager, setHintSessionThreads(_, Eq(invalidTids))) + .Times(Exactly(1)) + .WillOnce(Return(status)); + result = APerformanceHint_setThreads(session, invalidTids.data(), invalidTids.size()); + EXPECT_EQ(EPERM, result); +} + +TEST_F(PerformanceHintTest, SetPowerEfficient) { + APerformanceHintManager* manager = createManager(); + + std::vector<int32_t> tids; + tids.push_back(1); + tids.push_back(2); + int64_t targetDuration = 56789L; + + StrictMock<MockIHintSession>* iSession = new StrictMock<MockIHintSession>(); + sp<IHintSession> 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); + + EXPECT_CALL(*iSession, setMode(_, Eq(true))).Times(Exactly(1)); + int result = APerformanceHint_setPreferPowerEfficiency(session, true); + EXPECT_EQ(0, result); + + EXPECT_CALL(*iSession, setMode(_, Eq(false))).Times(Exactly(1)); + result = APerformanceHint_setPreferPowerEfficiency(session, false); + EXPECT_EQ(0, result); +} + +TEST_F(PerformanceHintTest, CreateZeroTargetDurationSession) { + APerformanceHintManager* manager = createManager(); + + std::vector<int32_t> tids; + tids.push_back(1); + tids.push_back(2); + int64_t targetDuration = 0; + + StrictMock<MockIHintSession>* iSession = new StrictMock<MockIHintSession>(); + sp<IHintSession> 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); +} + +MATCHER_P(WorkDurationEq, expected, "") { + if (arg.size() != expected.size()) { + *result_listener << "WorkDuration vectors are different sizes. Expected: " + << expected.size() << ", Actual: " << arg.size(); + return false; + } + for (int i = 0; i < expected.size(); ++i) { + android::os::WorkDuration expectedWorkDuration = expected[i]; + android::os::WorkDuration actualWorkDuration = arg[i]; + if (!expectedWorkDuration.equalsWithoutTimestamp(actualWorkDuration)) { + *result_listener << "WorkDuration at [" << i << "] is different: " + << "Expected: " << expectedWorkDuration + << ", Actual: " << actualWorkDuration; + return false; + } + } + return true; +} + +TEST_F(PerformanceHintTest, TestAPerformanceHint_reportActualWorkDuration2) { + APerformanceHintManager* manager = createManager(); + + std::vector<int32_t> tids; + tids.push_back(1); + tids.push_back(2); + int64_t targetDuration = 56789L; + + StrictMock<MockIHintSession>* iSession = new StrictMock<MockIHintSession>(); + sp<IHintSession> 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. + { + std::vector<android::os::WorkDuration> actualWorkDurations; + android::os::WorkDuration workDuration(1, 20, 13, 8); + actualWorkDurations.push_back(workDuration); + + EXPECT_CALL(*iSession, reportActualWorkDuration2(WorkDurationEq(actualWorkDurations))) + .Times(Exactly(1)); + result = APerformanceHint_reportActualWorkDuration2(session, + static_cast<AWorkDuration*>( + &workDuration)); + EXPECT_EQ(0, result); + } + + { + std::vector<android::os::WorkDuration> actualWorkDurations; + android::os::WorkDuration workDuration(-1, 20, 13, 8); + actualWorkDurations.push_back(workDuration); + + EXPECT_CALL(*iSession, reportActualWorkDuration2(WorkDurationEq(actualWorkDurations))) + .Times(Exactly(1)); + result = APerformanceHint_reportActualWorkDuration2(session, + static_cast<AWorkDuration*>( + &workDuration)); + EXPECT_EQ(22, result); + } + { + std::vector<android::os::WorkDuration> actualWorkDurations; + android::os::WorkDuration workDuration(1, -20, 13, 8); + actualWorkDurations.push_back(workDuration); + + EXPECT_CALL(*iSession, reportActualWorkDuration2(WorkDurationEq(actualWorkDurations))) + .Times(Exactly(1)); + result = APerformanceHint_reportActualWorkDuration2(session, + static_cast<AWorkDuration*>( + &workDuration)); + EXPECT_EQ(22, result); + } + { + std::vector<android::os::WorkDuration> actualWorkDurations; + android::os::WorkDuration workDuration(1, 20, -13, 8); + actualWorkDurations.push_back(workDuration); + + EXPECT_CALL(*iSession, reportActualWorkDuration2(WorkDurationEq(actualWorkDurations))) + .Times(Exactly(1)); + result = APerformanceHint_reportActualWorkDuration2(session, + static_cast<AWorkDuration*>( + &workDuration)); + EXPECT_EQ(EINVAL, result); + } + { + std::vector<android::os::WorkDuration> actualWorkDurations; + android::os::WorkDuration workDuration(1, 20, 13, -8); + actualWorkDurations.push_back(workDuration); + + EXPECT_CALL(*iSession, reportActualWorkDuration2(WorkDurationEq(actualWorkDurations))) + .Times(Exactly(1)); + result = APerformanceHint_reportActualWorkDuration2(session, + static_cast<AWorkDuration*>( + &workDuration)); + EXPECT_EQ(EINVAL, result); + } + + EXPECT_CALL(*iSession, close()).Times(Exactly(1)); + APerformanceHint_closeSession(session); +} + +TEST_F(PerformanceHintTest, TestAWorkDuration) { + AWorkDuration* aWorkDuration = AWorkDuration_create(); + ASSERT_NE(aWorkDuration, nullptr); + + AWorkDuration_setWorkPeriodStartTimestampNanos(aWorkDuration, 1); + AWorkDuration_setActualTotalDurationNanos(aWorkDuration, 20); + AWorkDuration_setActualCpuDurationNanos(aWorkDuration, 13); + AWorkDuration_setActualGpuDurationNanos(aWorkDuration, 8); + AWorkDuration_release(aWorkDuration); } diff --git a/native/android/tests/thermal/Android.bp b/native/android/tests/thermal/Android.bp new file mode 100644 index 000000000000..8540d8327ada --- /dev/null +++ b/native/android/tests/thermal/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: "NativeThermalUnitTestCases", + + multilib: { + lib32: { + suffix: "32", + }, + lib64: { + suffix: "64", + }, + }, + + srcs: ["NativeThermalUnitTest.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/thermal/NativeThermalUnitTest.cpp b/native/android/tests/thermal/NativeThermalUnitTest.cpp new file mode 100644 index 000000000000..6d6861a3026a --- /dev/null +++ b/native/android/tests/thermal/NativeThermalUnitTest.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2023 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 "NativeThermalUnitTest" + +#include <android/os/IThermalService.h> +#include <android/thermal.h> +#include <binder/IBinder.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <thermal_private.h> + +using android::binder::Status; + +using namespace testing; +using namespace android; +using namespace android::os; + +class MockIThermalService : public IThermalService { +public: + MOCK_METHOD(Status, registerThermalEventListener, + (const ::android::sp<::android::os::IThermalEventListener>& listener, + bool* _aidl_return), + (override)); + MOCK_METHOD(Status, registerThermalEventListenerWithType, + (const ::android::sp<::android::os::IThermalEventListener>& listener, int32_t type, + bool* _aidl_return), + (override)); + MOCK_METHOD(Status, unregisterThermalEventListener, + (const ::android::sp<::android::os::IThermalEventListener>& listener, + bool* _aidl_return), + (override)); + MOCK_METHOD(Status, getCurrentTemperatures, + (::std::vector<::android::os::Temperature> * _aidl_return), (override)); + MOCK_METHOD(Status, getCurrentTemperaturesWithType, + (int32_t type, ::std::vector<::android::os::Temperature>* _aidl_return), + (override)); + MOCK_METHOD(Status, registerThermalStatusListener, + (const ::android::sp<::android::os::IThermalStatusListener>& listener, + bool* _aidl_return), + (override)); + MOCK_METHOD(Status, unregisterThermalStatusListener, + (const ::android::sp<::android::os::IThermalStatusListener>& listener, + bool* _aidl_return), + (override)); + MOCK_METHOD(Status, getCurrentThermalStatus, (int32_t * _aidl_return), (override)); + MOCK_METHOD(Status, getCurrentCoolingDevices, + (::std::vector<::android::os::CoolingDevice> * _aidl_return), (override)); + MOCK_METHOD(Status, getCurrentCoolingDevicesWithType, + (int32_t type, ::std::vector<::android::os::CoolingDevice>* _aidl_return), + (override)); + MOCK_METHOD(Status, getThermalHeadroom, (int32_t forecastSeconds, float* _aidl_return), + (override)); + MOCK_METHOD(Status, getThermalHeadroomThresholds, (::std::vector<float> * _aidl_return), + (override)); + MOCK_METHOD(IBinder*, onAsBinder, (), (override)); +}; + +class NativeThermalUnitTest : public Test { +public: + void SetUp() override { + mMockIThermalService = new StrictMock<MockIThermalService>(); + AThermal_setIThermalServiceForTesting(mMockIThermalService); + mThermalManager = AThermal_acquireManager(); + } + + void TearDown() override { + AThermal_setIThermalServiceForTesting(nullptr); + AThermal_releaseManager(mThermalManager); + } + + StrictMock<MockIThermalService>* mMockIThermalService = nullptr; + AThermalManager* mThermalManager = nullptr; +}; + +static void checkThermalHeadroomThresholds(const std::vector<float>& expected, + const AThermalHeadroomThreshold* thresholds, + size_t size) { + if (thresholds == nullptr) { + FAIL() << "Unexpected null thresholds pointer"; + } + for (int i = 0; i < (int)size; i++) { + auto t = thresholds[i]; + ASSERT_EQ(i, t.thermalStatus) << "threshold " << i << " should have status " << i; + ASSERT_EQ(expected[i], t.headroom) + << "threshold " << i << " should have headroom " << expected[i]; + } +} + +TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholds) { + std::vector<float> expected = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + EXPECT_CALL(*mMockIThermalService, getThermalHeadroomThresholds(_)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<0>(expected), Return(Status()))); + const AThermalHeadroomThreshold* thresholds1 = nullptr; + size_t size1; + ASSERT_EQ(OK, AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds1, &size1)); + checkThermalHeadroomThresholds(expected, thresholds1, size1); + // following calls should be cached + EXPECT_CALL(*mMockIThermalService, getThermalHeadroomThresholds(_)).Times(0); + + const AThermalHeadroomThreshold* thresholds2 = nullptr; + size_t size2; + ASSERT_EQ(OK, AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds2, &size2)); + checkThermalHeadroomThresholds(expected, thresholds2, size2); +} + +TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholdsFailedWithServerError) { + const AThermalHeadroomThreshold* thresholds = nullptr; + size_t size; + EXPECT_CALL(*mMockIThermalService, getThermalHeadroomThresholds(_)) + .Times(Exactly(1)) + .WillOnce(Return( + Status::fromExceptionCode(binder::Status::Exception::EX_ILLEGAL_ARGUMENT))); + ASSERT_EQ(EPIPE, AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds, &size)); + ASSERT_EQ(nullptr, thresholds); +} + +TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholdsFailedWithFeatureDisabled) { + const AThermalHeadroomThreshold* thresholds = nullptr; + size_t size; + EXPECT_CALL(*mMockIThermalService, getThermalHeadroomThresholds(_)) + .Times(Exactly(1)) + .WillOnce(Return(Status::fromExceptionCode( + binder::Status::Exception::EX_UNSUPPORTED_OPERATION))); + ASSERT_EQ(ENOSYS, AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds, &size)); + ASSERT_EQ(nullptr, thresholds); +} + +TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholdsFailedWithNullPtr) { + const AThermalHeadroomThreshold* thresholds = nullptr; + size_t size; + size_t* nullSize = nullptr; + ASSERT_EQ(EINVAL, + AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds, nullSize)); + ASSERT_EQ(nullptr, thresholds); + ASSERT_EQ(EINVAL, AThermal_getThermalHeadroomThresholds(mThermalManager, nullptr, &size)); +} + +TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholdsFailedWithNonEmptyPtr) { + const AThermalHeadroomThreshold* initialized = new AThermalHeadroomThreshold[1]; + size_t size; + ASSERT_EQ(EINVAL, AThermal_getThermalHeadroomThresholds(mThermalManager, &initialized, &size)); + delete[] initialized; +} diff --git a/native/android/tests/thermal/OWNERS b/native/android/tests/thermal/OWNERS new file mode 100644 index 000000000000..e3bbee92057d --- /dev/null +++ b/native/android/tests/thermal/OWNERS @@ -0,0 +1 @@ +include /ADPF_OWNERS diff --git a/native/android/thermal.cpp b/native/android/thermal.cpp index 1f6ef4755aff..b43f2f16a7cb 100644 --- a/native/android/thermal.cpp +++ b/native/android/thermal.cpp @@ -16,27 +16,32 @@ #define LOG_TAG "thermal" -#include <cerrno> -#include <thread> -#include <limits> - -#include <android/thermal.h> +#include <android-base/thread_annotations.h> #include <android/os/BnThermalStatusListener.h> #include <android/os/IThermalService.h> +#include <android/thermal.h> #include <binder/IServiceManager.h> +#include <thermal_private.h> #include <utils/Log.h> +#include <cerrno> +#include <limits> +#include <thread> + using android::sp; using namespace android; using namespace android::os; struct ThermalServiceListener : public BnThermalStatusListener { - public: - virtual binder::Status onStatusChange(int32_t status) override; - ThermalServiceListener(AThermalManager *manager) {mMgr = manager;} - private: - AThermalManager *mMgr; +public: + virtual binder::Status onStatusChange(int32_t status) override; + ThermalServiceListener(AThermalManager *manager) { + mMgr = manager; + } + +private: + AThermalManager *mMgr; }; struct ListenerCallback { @@ -44,22 +49,29 @@ struct ListenerCallback { void* data; }; +static IThermalService *gIThermalServiceForTesting = nullptr; + struct AThermalManager { - public: - static AThermalManager* createAThermalManager(); - AThermalManager() = delete; - ~AThermalManager(); - status_t notifyStateChange(int32_t status); - status_t getCurrentThermalStatus(int32_t *status); - status_t addListener(AThermal_StatusCallback, void *data); - status_t removeListener(AThermal_StatusCallback, void *data); - status_t getThermalHeadroom(int32_t forecastSeconds, float *result); - private: - AThermalManager(sp<IThermalService> service); - sp<IThermalService> mThermalSvc; - sp<ThermalServiceListener> mServiceListener; - std::vector<ListenerCallback> mListeners; - std::mutex mMutex; +public: + static AThermalManager *createAThermalManager(); + AThermalManager() = delete; + ~AThermalManager(); + status_t notifyStateChange(int32_t status); + status_t getCurrentThermalStatus(int32_t *status); + status_t addListener(AThermal_StatusCallback, void *data); + status_t removeListener(AThermal_StatusCallback, void *data); + status_t getThermalHeadroom(int32_t forecastSeconds, float *result); + status_t getThermalHeadroomThresholds(const AThermalHeadroomThreshold **, size_t *size); + +private: + AThermalManager(sp<IThermalService> service); + sp<IThermalService> mThermalSvc; + std::mutex mListenerMutex; + sp<ThermalServiceListener> mServiceListener GUARDED_BY(mListenerMutex); + std::vector<ListenerCallback> mListeners GUARDED_BY(mListenerMutex); + std::mutex mThresholdsMutex; + const AThermalHeadroomThreshold *mThresholds = nullptr; // GUARDED_BY(mThresholdsMutex) + size_t mThresholdsCount GUARDED_BY(mThresholdsMutex); }; binder::Status ThermalServiceListener::onStatusChange(int32_t status) { @@ -70,6 +82,9 @@ binder::Status ThermalServiceListener::onStatusChange(int32_t status) { } AThermalManager* AThermalManager::createAThermalManager() { + if (gIThermalServiceForTesting) { + return new AThermalManager(gIThermalServiceForTesting); + } sp<IBinder> binder = defaultServiceManager()->checkService(String16("thermalservice")); @@ -81,12 +96,10 @@ AThermalManager* AThermalManager::createAThermalManager() { } AThermalManager::AThermalManager(sp<IThermalService> service) - : mThermalSvc(service), - mServiceListener(nullptr) { -} + : mThermalSvc(std::move(service)), mServiceListener(nullptr) {} AThermalManager::~AThermalManager() { - std::unique_lock<std::mutex> lock(mMutex); + std::unique_lock<std::mutex> listenerLock(mListenerMutex); mListeners.clear(); if (mServiceListener != nullptr) { @@ -94,10 +107,13 @@ AThermalManager::~AThermalManager() { mThermalSvc->unregisterThermalStatusListener(mServiceListener, &success); mServiceListener = nullptr; } + listenerLock.unlock(); + std::unique_lock<std::mutex> lock(mThresholdsMutex); + delete[] mThresholds; } status_t AThermalManager::notifyStateChange(int32_t status) { - std::unique_lock<std::mutex> lock(mMutex); + std::unique_lock<std::mutex> lock(mListenerMutex); AThermalStatus thermalStatus = static_cast<AThermalStatus>(status); for (auto listener : mListeners) { @@ -107,7 +123,7 @@ status_t AThermalManager::notifyStateChange(int32_t status) { } status_t AThermalManager::addListener(AThermal_StatusCallback callback, void *data) { - std::unique_lock<std::mutex> lock(mMutex); + std::unique_lock<std::mutex> lock(mListenerMutex); if (callback == nullptr) { // Callback can not be nullptr @@ -141,7 +157,7 @@ status_t AThermalManager::addListener(AThermal_StatusCallback callback, void *da } status_t AThermalManager::removeListener(AThermal_StatusCallback callback, void *data) { - std::unique_lock<std::mutex> lock(mMutex); + std::unique_lock<std::mutex> lock(mListenerMutex); auto it = std::remove_if(mListeners.begin(), mListeners.end(), @@ -198,6 +214,32 @@ status_t AThermalManager::getThermalHeadroom(int32_t forecastSeconds, float *res return OK; } +status_t AThermalManager::getThermalHeadroomThresholds(const AThermalHeadroomThreshold **result, + size_t *size) { + std::unique_lock<std::mutex> lock(mThresholdsMutex); + if (mThresholds == nullptr) { + auto thresholds = std::make_unique<std::vector<float>>(); + binder::Status ret = mThermalSvc->getThermalHeadroomThresholds(thresholds.get()); + if (!ret.isOk()) { + if (ret.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { + // feature is not enabled + return ENOSYS; + } + return EPIPE; + } + mThresholdsCount = thresholds->size(); + auto t = new AThermalHeadroomThreshold[mThresholdsCount]; + for (int i = 0; i < (int)mThresholdsCount; i++) { + t[i].headroom = (*thresholds)[i]; + t[i].thermalStatus = static_cast<AThermalStatus>(i); + } + mThresholds = t; + } + *size = mThresholdsCount; + *result = mThresholds; + return OK; +} + /** * Acquire an instance of the thermal manager. This must be freed using * {@link AThermal_releaseManager}. @@ -291,14 +333,24 @@ int AThermal_unregisterThermalStatusListener(AThermalManager *manager, * threshold. Returns NaN if the device does not support this functionality or if * this function is called significantly faster than once per second. */ -float AThermal_getThermalHeadroom(AThermalManager *manager, - int forecastSeconds) { +float AThermal_getThermalHeadroom(AThermalManager *manager, int forecastSeconds) { float result = 0.0f; status_t ret = manager->getThermalHeadroom(forecastSeconds, &result); - if (ret != OK) { result = std::numeric_limits<float>::quiet_NaN(); } - return result; } + +int AThermal_getThermalHeadroomThresholds(AThermalManager *manager, + const AThermalHeadroomThreshold **outThresholds, + size_t *size) { + if (outThresholds == nullptr || *outThresholds != nullptr || size == nullptr) { + return EINVAL; + } + return manager->getThermalHeadroomThresholds(outThresholds, size); +} + +void AThermal_setIThermalServiceForTesting(void *iThermalService) { + gIThermalServiceForTesting = static_cast<IThermalService *>(iThermalService); +} diff --git a/native/webview/TEST_MAPPING b/native/webview/TEST_MAPPING index c1bc6d720ece..07f438329e43 100644 --- a/native/webview/TEST_MAPPING +++ b/native/webview/TEST_MAPPING @@ -26,6 +26,7 @@ }, { "name": "GtsWebViewTestCases", + "keywords": ["internal"], "options": [ { "exclude-annotation": "android.test.FlakyTest" @@ -34,6 +35,7 @@ }, { "name": "GtsWebViewHostTestCases", + "keywords": ["internal"], "options": [ { "exclude-annotation": "android.test.FlakyTest" |