Merge "Log event information when event is dropped" into tm-dev
diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h
index 4c83a14..ee392fc 100644
--- a/include/android/multinetwork.h
+++ b/include/android/multinetwork.h
@@ -235,7 +235,7 @@
*
* Available since API level 33.
*/
-int android_tag_socket_with_uid(int sockfd, int tag, uid_t uid) __INTRODUCED_IN(33);
+int android_tag_socket_with_uid(int sockfd, uint32_t tag, uid_t uid) __INTRODUCED_IN(33);
/*
* Set the socket tag for traffic statistics on the specified socket.
@@ -245,14 +245,26 @@
* opened by another UID or was previously tagged by another UID. Subsequent
* calls always replace any existing parameters. The socket tag is kept when the
* socket is sent to another process using binder IPCs or other mechanisms such
- * as UNIX socket fd passing.
+ * as UNIX socket fd passing. The tag is a value defined by the caller and used
+ * together with uid for data traffic accounting, so that the function callers
+ * can account different types of data usage for a uid.
*
* Returns 0 on success, or a negative POSIX error code (see errno.h) on
* failure.
*
+ * Some possible error codes:
+ * -EBADF Bad socketfd.
+ * -EPERM No permission.
+ * -EAFNOSUPPORT Socket family is neither AF_INET nor AF_INET6.
+ * -EPROTONOSUPPORT Socket protocol is neither IPPROTO_UDP nor IPPROTO_TCP.
+ * -EMFILE Too many stats entries.
+ * There are still other error codes that may provided by -errno of
+ * [getsockopt()](https://man7.org/linux/man-pages/man2/getsockopt.2.html) or by
+ * BPF maps read/write sys calls, which are set appropriately.
+ *
* Available since API level 33.
*/
-int android_tag_socket(int sockfd, int tag) __INTRODUCED_IN(33);
+int android_tag_socket(int sockfd, uint32_t tag) __INTRODUCED_IN(33);
/*
* Untag a network socket.
@@ -267,6 +279,12 @@
* Returns 0 on success, or a negative POSIX error code (see errno.h) on
* failure.
*
+ * One of possible error code:
+ * -EBADF Bad socketfd.
+ * Other error codes are either provided by -errno of
+ * [getsockopt()](https://man7.org/linux/man-pages/man2/getsockopt.2.html) or by
+ * BPF map element deletion sys call, which are set appropriately.
+ *
* Available since API level 33.
*/
int android_untag_socket(int sockfd) __INTRODUCED_IN(33);
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index be50a75..58b0b35 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -1584,6 +1584,7 @@
template<class T>
status_t Parcel::readAligned(T *pArg) const {
static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
+ static_assert(std::is_trivially_copyable_v<T>);
if ((mDataPos+sizeof(T)) <= mDataSize) {
if (mObjectsSize > 0) {
@@ -1595,9 +1596,8 @@
}
}
- const void* data = mData+mDataPos;
+ memcpy(pArg, mData + mDataPos, sizeof(T));
mDataPos += sizeof(T);
- *pArg = *reinterpret_cast<const T*>(data);
return NO_ERROR;
} else {
return NOT_ENOUGH_DATA;
@@ -1617,10 +1617,11 @@
template<class T>
status_t Parcel::writeAligned(T val) {
static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
+ static_assert(std::is_trivially_copyable_v<T>);
if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
- *reinterpret_cast<T*>(mData+mDataPos) = val;
+ memcpy(mData + mDataPos, &val, sizeof(val));
return finishWrite(sizeof(val));
}
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 2e7084e..6d89064 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -313,7 +313,8 @@
const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs,
const std::function<status_t()>& altPoll) {
for (int i = 0; i < niovs; i++) {
- LOG_RPC_DETAIL("Sending %s on RpcTransport %p: %s", what, connection->rpcTransport.get(),
+ LOG_RPC_DETAIL("Sending %s (part %d of %d) on RpcTransport %p: %s",
+ what, i + 1, niovs, connection->rpcTransport.get(),
android::base::HexString(iovs[i].iov_base, iovs[i].iov_len).c_str());
}
@@ -343,7 +344,8 @@
}
for (int i = 0; i < niovs; i++) {
- LOG_RPC_DETAIL("Received %s on RpcTransport %p: %s", what, connection->rpcTransport.get(),
+ LOG_RPC_DETAIL("Received %s (part %d of %d) on RpcTransport %p: %s",
+ what, i + 1, niovs, connection->rpcTransport.get(),
android::base::HexString(iovs[i].iov_base, iovs[i].iov_len).c_str());
}
return OK;
@@ -660,8 +662,14 @@
status_t RpcState::drainCommands(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, CommandType type) {
uint8_t buf;
- while (connection->rpcTransport->peek(&buf, sizeof(buf)).value_or(0) > 0) {
- status_t status = getAndExecuteCommand(connection, session, type);
+ while (true) {
+ size_t num_bytes;
+ status_t status = connection->rpcTransport->peek(&buf, sizeof(buf), &num_bytes);
+ if (status == WOULD_BLOCK) break;
+ if (status != OK) return status;
+ if (!num_bytes) break;
+
+ status = getAndExecuteCommand(connection, session, type);
if (status != OK) return status;
}
return OK;
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index 636e5d0..7cfc780 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -24,9 +24,6 @@
#include "FdTrigger.h"
#include "RpcState.h"
-using android::base::ErrnoError;
-using android::base::Result;
-
namespace android {
namespace {
@@ -35,12 +32,20 @@
class RpcTransportRaw : public RpcTransport {
public:
explicit RpcTransportRaw(android::base::unique_fd socket) : mSocket(std::move(socket)) {}
- Result<size_t> peek(void *buf, size_t size) override {
+ status_t peek(void* buf, size_t size, size_t* out_size) override {
ssize_t ret = TEMP_FAILURE_RETRY(::recv(mSocket.get(), buf, size, MSG_PEEK));
if (ret < 0) {
- return ErrnoError() << "recv(MSG_PEEK)";
+ int savedErrno = errno;
+ if (savedErrno == EAGAIN || savedErrno == EWOULDBLOCK) {
+ return WOULD_BLOCK;
+ }
+
+ LOG_RPC_DETAIL("RpcTransport peek(): %s", strerror(savedErrno));
+ return -savedErrno;
}
- return ret;
+
+ *out_size = static_cast<size_t>(ret);
+ return OK;
}
template <typename SendOrReceive>
diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp
index 3936204..bc68c37 100644
--- a/libs/binder/RpcTransportTls.cpp
+++ b/libs/binder/RpcTransportTls.cpp
@@ -37,10 +37,6 @@
#define LOG_TLS_DETAIL(...) ALOGV(__VA_ARGS__) // for type checking
#endif
-using android::base::ErrnoError;
-using android::base::Error;
-using android::base::Result;
-
namespace android {
namespace {
@@ -165,17 +161,8 @@
return ret;
}
- // |sslError| should be from Ssl::getError().
- // If |sslError| is WANT_READ / WANT_WRITE, poll for POLLIN / POLLOUT respectively. Otherwise
- // return error. Also return error if |fdTrigger| is triggered before or during poll().
- status_t pollForSslError(android::base::borrowed_fd fd, int sslError, FdTrigger* fdTrigger,
- const char* fnString, int additionalEvent,
- const std::function<status_t()>& altPoll) {
+ status_t toStatus(int sslError, const char* fnString) {
switch (sslError) {
- case SSL_ERROR_WANT_READ:
- return handlePoll(POLLIN | additionalEvent, fd, fdTrigger, fnString, altPoll);
- case SSL_ERROR_WANT_WRITE:
- return handlePoll(POLLOUT | additionalEvent, fd, fdTrigger, fnString, altPoll);
case SSL_ERROR_SYSCALL: {
auto queue = toString();
LOG_TLS_DETAIL("%s(): %s. Treating as DEAD_OBJECT. Error queue: %s", fnString,
@@ -191,6 +178,22 @@
}
}
+ // |sslError| should be from Ssl::getError().
+ // If |sslError| is WANT_READ / WANT_WRITE, poll for POLLIN / POLLOUT respectively. Otherwise
+ // return error. Also return error if |fdTrigger| is triggered before or during poll().
+ status_t pollForSslError(android::base::borrowed_fd fd, int sslError, FdTrigger* fdTrigger,
+ const char* fnString, int additionalEvent,
+ const std::function<status_t()>& altPoll) {
+ switch (sslError) {
+ case SSL_ERROR_WANT_READ:
+ return handlePoll(POLLIN | additionalEvent, fd, fdTrigger, fnString, altPoll);
+ case SSL_ERROR_WANT_WRITE:
+ return handlePoll(POLLOUT | additionalEvent, fd, fdTrigger, fnString, altPoll);
+ default:
+ return toStatus(sslError, fnString);
+ }
+ }
+
private:
bool mHandled = false;
@@ -274,7 +277,7 @@
public:
RpcTransportTls(android::base::unique_fd socket, Ssl ssl)
: mSocket(std::move(socket)), mSsl(std::move(ssl)) {}
- Result<size_t> peek(void* buf, size_t size) override;
+ status_t peek(void* buf, size_t size, size_t* out_size) override;
status_t interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, int niovs,
const std::function<status_t()>& altPoll) override;
status_t interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, int niovs,
@@ -286,7 +289,7 @@
};
// Error code is errno.
-Result<size_t> RpcTransportTls::peek(void* buf, size_t size) {
+status_t RpcTransportTls::peek(void* buf, size_t size, size_t* out_size) {
size_t todo = std::min<size_t>(size, std::numeric_limits<int>::max());
auto [ret, errorQueue] = mSsl.call(SSL_peek, buf, static_cast<int>(todo));
if (ret < 0) {
@@ -294,13 +297,15 @@
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
// Seen EAGAIN / EWOULDBLOCK on recv(2) / send(2).
// Like RpcTransportRaw::peek(), don't handle it here.
- return Error(EWOULDBLOCK) << "SSL_peek(): " << errorQueue.toString();
+ errorQueue.clear();
+ return WOULD_BLOCK;
}
- return Error() << "SSL_peek(): " << errorQueue.toString();
+ return errorQueue.toStatus(err, "SSL_peek");
}
errorQueue.clear();
LOG_TLS_DETAIL("TLS: Peeked %d bytes!", ret);
- return ret;
+ *out_size = static_cast<size_t>(ret);
+ return OK;
}
status_t RpcTransportTls::interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, int niovs,
diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h
index ade2d94..751c4f9 100644
--- a/libs/binder/include/binder/RpcTransport.h
+++ b/libs/binder/include/binder/RpcTransport.h
@@ -22,7 +22,6 @@
#include <memory>
#include <string>
-#include <android-base/result.h>
#include <android-base/unique_fd.h>
#include <utils/Errors.h>
@@ -41,7 +40,7 @@
virtual ~RpcTransport() = default;
// replacement of ::recv(MSG_PEEK). Error code may not be set if TLS is enabled.
- [[nodiscard]] virtual android::base::Result<size_t> peek(void *buf, size_t size) = 0;
+ [[nodiscard]] virtual status_t peek(void *buf, size_t size, size_t *out_size) = 0;
/**
* Read (or write), but allow to be interrupted by a trigger.
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index b3bc7f4..c8e78fc 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -324,4 +324,42 @@
} // namespace ndk
+// Once minSdkVersion is 30, we are guaranteed to be building with the
+// Android 11 AIDL compiler which supports the SharedRefBase::make API.
+#if !defined(__ANDROID_API__) || __ANDROID_API__ >= 30 || defined(__ANDROID_APEX__)
+namespace ndk::internal {
+template <typename T, typename = void>
+struct is_complete_type : std::false_type {};
+
+template <typename T>
+struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};
+} // namespace ndk::internal
+
+namespace std {
+
+// Define `SharedRefBase` specific versions of `std::make_shared` and
+// `std::make_unique` to block people from using them. Using them to allocate
+// `ndk::SharedRefBase` objects results in double ownership. Use
+// `ndk::SharedRefBase::make<T>(...)` instead.
+//
+// Note: We exclude incomplete types because `std::is_base_of` is undefined in
+// that case.
+
+template <typename T, typename... Args,
+ std::enable_if_t<ndk::internal::is_complete_type<T>::value, bool> = true,
+ std::enable_if_t<std::is_base_of<ndk::SharedRefBase, T>::value, bool> = true>
+shared_ptr<T> make_shared(Args...) { // SEE COMMENT ABOVE.
+ static_assert(!std::is_base_of<ndk::SharedRefBase, T>::value);
+}
+
+template <typename T, typename... Args,
+ std::enable_if_t<ndk::internal::is_complete_type<T>::value, bool> = true,
+ std::enable_if_t<std::is_base_of<ndk::SharedRefBase, T>::value, bool> = true>
+unique_ptr<T> make_unique(Args...) { // SEE COMMENT ABOVE.
+ static_assert(!std::is_base_of<ndk::SharedRefBase, T>::value);
+}
+
+} // namespace std
+#endif
+
/** @} */
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 357b454..1b136dc 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -231,8 +231,7 @@
}
TEST(NdkBinder, DetectNoSharedRefBaseCreated) {
- EXPECT_DEATH(std::make_shared<MyBinderNdkUnitTest>(),
- "SharedRefBase: no ref created during lifetime");
+ EXPECT_DEATH(MyBinderNdkUnitTest(), "SharedRefBase: no ref created during lifetime");
}
TEST(NdkBinder, GetServiceThatDoesntExist) {
diff --git a/libs/binder/tests/include_tls_test_utils/binder/RpcTlsTestUtils.h b/libs/binder/tests/include_tls_test_utils/binder/RpcTlsTestUtils.h
index 094addd..50d12c4 100644
--- a/libs/binder/tests/include_tls_test_utils/binder/RpcTlsTestUtils.h
+++ b/libs/binder/tests/include_tls_test_utils/binder/RpcTlsTestUtils.h
@@ -18,6 +18,7 @@
#include <memory>
#include <mutex>
+#include <vector>
#include <binder/RpcAuth.h>
#include <binder/RpcCertificateFormat.h>
diff --git a/services/sensorservice/SensorServiceUtils.cpp b/services/sensorservice/SensorServiceUtils.cpp
index 7dd2331..6bad962 100644
--- a/services/sensorservice/SensorServiceUtils.cpp
+++ b/services/sensorservice/SensorServiceUtils.cpp
@@ -39,6 +39,7 @@
return 5;
case SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED:
+ case SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED:
case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED:
case SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES:
case SENSOR_TYPE_GYROSCOPE_LIMITED_AXES:
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index aefc014..516c3ef 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -23,6 +23,7 @@
"android.hardware.graphics.composer3-V1-ndk",
"android.hardware.power@1.0",
"android.hardware.power@1.3",
+ "android.hardware.power-V2-cpp",
"libbase",
"libcutils",
"libgui",
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 44c086d..05f488b 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -32,7 +32,6 @@
#include <utils/Trace.h>
#include <android/hardware/power/1.3/IPower.h>
-#include <android/hardware/power/IPower.h>
#include <android/hardware/power/IPowerHintSession.h>
#include <android/hardware/power/WorkDuration.h>
@@ -62,8 +61,6 @@
using scheduler::OneShotTimer;
-class AidlPowerHalWrapper;
-
PowerAdvisor::~PowerAdvisor() = default;
namespace {
@@ -294,259 +291,232 @@
const sp<V1_3::IPower> mPowerHal = nullptr;
};
-class AidlPowerHalWrapper : public PowerAdvisor::HalWrapper {
-public:
- AidlPowerHalWrapper(sp<IPower> powerHal) : mPowerHal(std::move(powerHal)) {
- auto ret = mPowerHal->isModeSupported(Mode::EXPENSIVE_RENDERING, &mHasExpensiveRendering);
- if (!ret.isOk()) {
- mHasExpensiveRendering = false;
- }
-
- ret = mPowerHal->isBoostSupported(Boost::DISPLAY_UPDATE_IMMINENT,
- &mHasDisplayUpdateImminent);
- if (!ret.isOk()) {
- mHasDisplayUpdateImminent = false;
- }
-
- mSupportsPowerHint = checkPowerHintSessionSupported();
+AidlPowerHalWrapper::AidlPowerHalWrapper(sp<IPower> powerHal) : mPowerHal(std::move(powerHal)) {
+ auto ret = mPowerHal->isModeSupported(Mode::EXPENSIVE_RENDERING, &mHasExpensiveRendering);
+ if (!ret.isOk()) {
+ mHasExpensiveRendering = false;
}
- ~AidlPowerHalWrapper() override {
- if (mPowerHintSession != nullptr) {
- mPowerHintSession->close();
- mPowerHintSession = nullptr;
- }
- };
-
- static std::unique_ptr<HalWrapper> connect() {
- // This only waits if the service is actually declared
- sp<IPower> powerHal = waitForVintfService<IPower>();
- if (powerHal == nullptr) {
- return nullptr;
- }
- ALOGI("Loaded AIDL Power HAL service");
-
- return std::make_unique<AidlPowerHalWrapper>(std::move(powerHal));
+ ret = mPowerHal->isBoostSupported(Boost::DISPLAY_UPDATE_IMMINENT, &mHasDisplayUpdateImminent);
+ if (!ret.isOk()) {
+ mHasDisplayUpdateImminent = false;
}
- bool setExpensiveRendering(bool enabled) override {
- ALOGV("AIDL setExpensiveRendering %s", enabled ? "T" : "F");
- if (!mHasExpensiveRendering) {
- ALOGV("Skipped sending EXPENSIVE_RENDERING because HAL doesn't support it");
- return true;
- }
+ mSupportsPowerHint = checkPowerHintSessionSupported();
+}
- auto ret = mPowerHal->setMode(Mode::EXPENSIVE_RENDERING, enabled);
- if (ret.isOk()) {
- traceExpensiveRendering(enabled);
- }
- return ret.isOk();
+AidlPowerHalWrapper::~AidlPowerHalWrapper() {
+ if (mPowerHintSession != nullptr) {
+ mPowerHintSession->close();
+ mPowerHintSession = nullptr;
}
-
- bool notifyDisplayUpdateImminent() override {
- ALOGV("AIDL notifyDisplayUpdateImminent");
- if (!mHasDisplayUpdateImminent) {
- ALOGV("Skipped sending DISPLAY_UPDATE_IMMINENT because HAL doesn't support it");
- return true;
- }
-
- auto ret = mPowerHal->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 0);
- return ret.isOk();
- }
-
- // only version 2+ of the aidl supports power hint sessions, hidl has no support
- bool supportsPowerHintSession() override { return mSupportsPowerHint; }
-
- bool checkPowerHintSessionSupported() {
- int64_t unused;
- // Try to get preferred rate to determine if hint sessions are supported
- // We check for isOk not EX_UNSUPPORTED_OPERATION to lump other errors
- return mPowerHal->getHintSessionPreferredRate(&unused).isOk();
- }
-
- bool isPowerHintSessionRunning() override { return mPowerHintSession != nullptr; }
-
- void closePowerHintSession() {
- if (mPowerHintSession != nullptr) {
- mPowerHintSession->close();
- mPowerHintSession = nullptr;
- }
- }
-
- void restartPowerHintSession() {
- closePowerHintSession();
- startPowerHintSession();
- }
-
- void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) override {
- if (threadIds != mPowerHintThreadIds) {
- mPowerHintThreadIds = threadIds;
- if (isPowerHintSessionRunning()) {
- restartPowerHintSession();
- }
- }
- }
-
- bool startPowerHintSession() override {
- if (mPowerHintSession != nullptr || mPowerHintThreadIds.empty()) {
- ALOGV("Cannot start power hint session, skipping");
- return false;
- }
- auto ret = mPowerHal->createHintSession(getpid(), static_cast<int32_t>(getuid()),
- mPowerHintThreadIds, mTargetDuration,
- &mPowerHintSession);
- if (!ret.isOk()) {
- ALOGW("Failed to start power hint session with error: %s",
- ret.exceptionToString(ret.exceptionCode()).c_str());
- } else {
- mLastTargetDurationSent = mTargetDuration;
- }
- return isPowerHintSessionRunning();
- }
-
- bool shouldSetTargetDuration(int64_t targetDurationNanos) {
- // report if the change in target from our last submission to now exceeds the threshold
- return abs(1.0 -
- static_cast<double>(mLastTargetDurationSent) /
- static_cast<double>(targetDurationNanos)) >=
- kAllowedTargetDeviationPercent;
- }
-
- void setTargetWorkDuration(int64_t targetDurationNanos) override {
- ATRACE_CALL();
- mTargetDuration = targetDurationNanos;
- if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDurationNanos);
- if (!sNormalizeTarget && shouldSetTargetDuration(targetDurationNanos) &&
- isPowerHintSessionRunning()) {
- if (mLastActualDurationSent.has_value()) {
- // update the error term here since we are actually sending an update to powerhal
- if (sTraceHintSessionData)
- ATRACE_INT64("Target error term",
- targetDurationNanos - *mLastActualDurationSent);
- }
- ALOGV("Sending target time: %lld ns", static_cast<long long>(targetDurationNanos));
- mLastTargetDurationSent = targetDurationNanos;
- auto ret = mPowerHintSession->updateTargetWorkDuration(targetDurationNanos);
- if (!ret.isOk()) {
- ALOGW("Failed to set power hint target work duration with error: %s",
- ret.exceptionMessage().c_str());
- mShouldReconnectHal = true;
- }
- }
- }
-
- bool shouldReportActualDurationsNow() {
- // report if we have never reported before or are approaching a stale session
- if (!mLastActualDurationSent.has_value() ||
- (systemTime() - mLastActualReportTimestamp) > kStaleTimeout.count()) {
- return true;
- }
-
- if (!mActualDuration.has_value()) {
- return false;
- }
-
- // duration of most recent timing
- const double mostRecentActualDuration = static_cast<double>(*mActualDuration);
- // duration of the last timing actually reported to the powerhal
- const double lastReportedActualDuration = static_cast<double>(*mLastActualDurationSent);
-
- // report if the change in duration from then to now exceeds the threshold
- return abs(1.0 - mostRecentActualDuration / lastReportedActualDuration) >=
- kAllowedActualDeviationPercent;
- }
-
- void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) override {
- ATRACE_CALL();
-
- if (actualDurationNanos < 0 || !isPowerHintSessionRunning()) {
- ALOGV("Failed to send actual work duration, skipping");
- return;
- }
-
- WorkDuration duration;
- duration.durationNanos = actualDurationNanos;
- mActualDuration = actualDurationNanos;
-
- // normalize the sent values to a pre-set target
- if (sNormalizeTarget) {
- duration.durationNanos += mLastTargetDurationSent - mTargetDuration;
- }
- duration.timeStampNanos = timeStampNanos;
- mPowerHintQueue.push_back(duration);
-
- long long targetNsec = mTargetDuration;
- long long durationNsec = actualDurationNanos;
-
- if (sTraceHintSessionData) {
- ATRACE_INT64("Measured duration", durationNsec);
- ATRACE_INT64("Target error term", targetNsec - durationNsec);
- }
-
- ALOGV("Sending actual work duration of: %lld on target: %lld with error: %lld",
- durationNsec, targetNsec, targetNsec - durationNsec);
-
- // This rate limiter queues similar duration reports to the powerhal into
- // batches to avoid excessive binder calls. The criteria to send a given batch
- // are outlined in shouldReportActualDurationsNow()
- if (shouldReportActualDurationsNow()) {
- ALOGV("Sending hint update batch");
- mLastActualReportTimestamp = systemTime();
- auto ret = mPowerHintSession->reportActualWorkDuration(mPowerHintQueue);
- if (!ret.isOk()) {
- ALOGW("Failed to report actual work durations with error: %s",
- ret.exceptionMessage().c_str());
- mShouldReconnectHal = true;
- }
- mPowerHintQueue.clear();
- // we save the non-normalized value here to detect % changes
- mLastActualDurationSent = actualDurationNanos;
- }
- }
-
- bool shouldReconnectHAL() override { return mShouldReconnectHal; }
-
- std::vector<int32_t> getPowerHintSessionThreadIds() override { return mPowerHintThreadIds; }
-
- std::optional<int64_t> getTargetWorkDuration() override { return mTargetDuration; }
-
-private:
- const sp<IPower> mPowerHal = nullptr;
- bool mHasExpensiveRendering = false;
- bool mHasDisplayUpdateImminent = false;
- // Used to indicate an error state and need for reconstruction
- bool mShouldReconnectHal = false;
- // This is not thread safe, but is currently protected by mPowerHalMutex so it needs no lock
- sp<IPowerHintSession> mPowerHintSession = nullptr;
- // Queue of actual durations saved to report
- std::vector<WorkDuration> mPowerHintQueue;
- // The latest un-normalized values we have received for target and actual
- int64_t mTargetDuration = kDefaultTarget.count();
- std::optional<int64_t> mActualDuration;
- // The list of thread ids, stored so we can restart the session from this class if needed
- std::vector<int32_t> mPowerHintThreadIds;
- bool mSupportsPowerHint;
- // Keep track of the last messages sent for rate limiter change detection
- std::optional<int64_t> mLastActualDurationSent;
- // timestamp of the last report we sent, used to avoid stale sessions
- int64_t mLastActualReportTimestamp = 0;
- int64_t mLastTargetDurationSent = kDefaultTarget.count();
- // Whether to normalize all the actual values as error terms relative to a constant target
- // This saves a binder call by not setting the target, and should not affect the pid values
- static const bool sNormalizeTarget;
- // Whether we should emit ATRACE_INT data for hint sessions
- static const bool sTraceHintSessionData;
- // Max percent the actual duration can vary without causing a report (eg: 0.1 = 10%)
- static constexpr double kAllowedActualDeviationPercent = 0.1;
- // Max percent the target duration can vary without causing a report (eg: 0.05 = 5%)
- static constexpr double kAllowedTargetDeviationPercent = 0.05;
- // Target used for init and normalization, the actual value does not really matter
- static constexpr const std::chrono::nanoseconds kDefaultTarget = 50ms;
- // Amount of time after the last message was sent before the session goes stale
- // actually 100ms but we use 80 here to ideally avoid going stale
- static constexpr const std::chrono::nanoseconds kStaleTimeout = 80ms;
};
+std::unique_ptr<PowerAdvisor::HalWrapper> AidlPowerHalWrapper::connect() {
+ // This only waits if the service is actually declared
+ sp<IPower> powerHal = waitForVintfService<IPower>();
+ if (powerHal == nullptr) {
+ return nullptr;
+ }
+ ALOGI("Loaded AIDL Power HAL service");
+
+ return std::make_unique<AidlPowerHalWrapper>(std::move(powerHal));
+}
+
+bool AidlPowerHalWrapper::setExpensiveRendering(bool enabled) {
+ ALOGV("AIDL setExpensiveRendering %s", enabled ? "T" : "F");
+ if (!mHasExpensiveRendering) {
+ ALOGV("Skipped sending EXPENSIVE_RENDERING because HAL doesn't support it");
+ return true;
+ }
+
+ auto ret = mPowerHal->setMode(Mode::EXPENSIVE_RENDERING, enabled);
+ if (ret.isOk()) {
+ traceExpensiveRendering(enabled);
+ }
+ return ret.isOk();
+}
+
+bool AidlPowerHalWrapper::notifyDisplayUpdateImminent() {
+ ALOGV("AIDL notifyDisplayUpdateImminent");
+ if (!mHasDisplayUpdateImminent) {
+ ALOGV("Skipped sending DISPLAY_UPDATE_IMMINENT because HAL doesn't support it");
+ return true;
+ }
+
+ auto ret = mPowerHal->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 0);
+ return ret.isOk();
+}
+
+// only version 2+ of the aidl supports power hint sessions, hidl has no support
+bool AidlPowerHalWrapper::supportsPowerHintSession() {
+ return mSupportsPowerHint;
+}
+
+bool AidlPowerHalWrapper::checkPowerHintSessionSupported() {
+ int64_t unused;
+ // Try to get preferred rate to determine if hint sessions are supported
+ // We check for isOk not EX_UNSUPPORTED_OPERATION to lump together errors
+ return mPowerHal->getHintSessionPreferredRate(&unused).isOk();
+}
+
+bool AidlPowerHalWrapper::isPowerHintSessionRunning() {
+ return mPowerHintSession != nullptr;
+}
+
+void AidlPowerHalWrapper::closePowerHintSession() {
+ if (mPowerHintSession != nullptr) {
+ mPowerHintSession->close();
+ mPowerHintSession = nullptr;
+ }
+}
+
+void AidlPowerHalWrapper::restartPowerHintSession() {
+ closePowerHintSession();
+ startPowerHintSession();
+}
+
+void AidlPowerHalWrapper::setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) {
+ if (threadIds != mPowerHintThreadIds) {
+ mPowerHintThreadIds = threadIds;
+ if (isPowerHintSessionRunning()) {
+ restartPowerHintSession();
+ }
+ }
+}
+
+bool AidlPowerHalWrapper::startPowerHintSession() {
+ if (mPowerHintSession != nullptr || mPowerHintThreadIds.empty()) {
+ ALOGV("Cannot start power hint session, skipping");
+ return false;
+ }
+ auto ret =
+ mPowerHal->createHintSession(getpid(), static_cast<int32_t>(getuid()),
+ mPowerHintThreadIds, mTargetDuration, &mPowerHintSession);
+ if (!ret.isOk()) {
+ ALOGW("Failed to start power hint session with error: %s",
+ ret.exceptionToString(ret.exceptionCode()).c_str());
+ } else {
+ mLastTargetDurationSent = mTargetDuration;
+ }
+ return isPowerHintSessionRunning();
+}
+
+bool AidlPowerHalWrapper::shouldSetTargetDuration(int64_t targetDurationNanos) {
+ if (targetDurationNanos <= 0) {
+ return false;
+ }
+ // report if the change in target from our last submission to now exceeds the threshold
+ return abs(1.0 -
+ static_cast<double>(mLastTargetDurationSent) /
+ static_cast<double>(targetDurationNanos)) >= kAllowedTargetDeviationPercent;
+}
+
+void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDurationNanos) {
+ ATRACE_CALL();
+ mTargetDuration = targetDurationNanos;
+ if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDurationNanos);
+ if (!sNormalizeTarget && isPowerHintSessionRunning() &&
+ shouldSetTargetDuration(targetDurationNanos)) {
+ if (mLastActualDurationSent.has_value()) {
+ // update the error term here since we are actually sending an update to powerhal
+ if (sTraceHintSessionData)
+ ATRACE_INT64("Target error term", targetDurationNanos - *mLastActualDurationSent);
+ }
+ ALOGV("Sending target time: %" PRId64 "ns", targetDurationNanos);
+ mLastTargetDurationSent = targetDurationNanos;
+ auto ret = mPowerHintSession->updateTargetWorkDuration(targetDurationNanos);
+ if (!ret.isOk()) {
+ ALOGW("Failed to set power hint target work duration with error: %s",
+ ret.exceptionMessage().c_str());
+ mShouldReconnectHal = true;
+ }
+ }
+}
+
+bool AidlPowerHalWrapper::shouldReportActualDurationsNow() {
+ // report if we have never reported before or are approaching a stale session
+ if (!mLastActualDurationSent.has_value() ||
+ (systemTime() - mLastActualReportTimestamp) > kStaleTimeout.count()) {
+ return true;
+ }
+
+ if (!mActualDuration.has_value()) {
+ return false;
+ }
+
+ // duration of most recent timing
+ const double mostRecentActualDuration = static_cast<double>(*mActualDuration);
+ // duration of the last timing actually reported to the powerhal
+ const double lastReportedActualDuration = static_cast<double>(*mLastActualDurationSent);
+
+ // report if the change in duration from then to now exceeds the threshold
+ return abs(1.0 - mostRecentActualDuration / lastReportedActualDuration) >=
+ kAllowedActualDeviationPercent;
+}
+
+void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDurationNanos,
+ nsecs_t timeStampNanos) {
+ ATRACE_CALL();
+
+ if (actualDurationNanos < 0 || !isPowerHintSessionRunning()) {
+ ALOGV("Failed to send actual work duration, skipping");
+ return;
+ }
+
+ WorkDuration duration;
+ duration.durationNanos = actualDurationNanos;
+ mActualDuration = actualDurationNanos;
+
+ // normalize the sent values to a pre-set target
+ if (sNormalizeTarget) {
+ duration.durationNanos += mLastTargetDurationSent - mTargetDuration;
+ }
+ duration.timeStampNanos = timeStampNanos;
+ mPowerHintQueue.push_back(duration);
+
+ nsecs_t targetNsec = mTargetDuration;
+ nsecs_t durationNsec = actualDurationNanos;
+
+ if (sTraceHintSessionData) {
+ ATRACE_INT64("Measured duration", durationNsec);
+ ATRACE_INT64("Target error term", targetNsec - durationNsec);
+ }
+
+ ALOGV("Sending actual work duration of: %" PRId64 " on target: %" PRId64
+ " with error: %" PRId64,
+ durationNsec, targetNsec, targetNsec - durationNsec);
+
+ // This rate limiter queues similar duration reports to the powerhal into
+ // batches to avoid excessive binder calls. The criteria to send a given batch
+ // are outlined in shouldReportActualDurationsNow()
+ if (shouldReportActualDurationsNow()) {
+ ALOGV("Sending hint update batch");
+ mLastActualReportTimestamp = systemTime();
+ auto ret = mPowerHintSession->reportActualWorkDuration(mPowerHintQueue);
+ if (!ret.isOk()) {
+ ALOGW("Failed to report actual work durations with error: %s",
+ ret.exceptionMessage().c_str());
+ mShouldReconnectHal = true;
+ }
+ mPowerHintQueue.clear();
+ // we save the non-normalized value here to detect % changes
+ mLastActualDurationSent = actualDurationNanos;
+ }
+}
+
+bool AidlPowerHalWrapper::shouldReconnectHAL() {
+ return mShouldReconnectHal;
+}
+
+std::vector<int32_t> AidlPowerHalWrapper::getPowerHintSessionThreadIds() {
+ return mPowerHintThreadIds;
+}
+
+std::optional<int64_t> AidlPowerHalWrapper::getTargetWorkDuration() {
+ return mTargetDuration;
+}
+
const bool AidlPowerHalWrapper::sTraceHintSessionData =
base::GetBoolProperty(std::string("debug.sf.trace_hint_sessions"), false);
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 0db56aa..3f47ffd 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -22,6 +22,7 @@
#include <utils/Mutex.h>
+#include <android/hardware/power/IPower.h>
#include <ui/DisplayIdentification.h>
#include "../Scheduler/OneShotTimer.h"
@@ -118,6 +119,69 @@
scheduler::OneShotTimer mScreenUpdateTimer;
};
+class AidlPowerHalWrapper : public PowerAdvisor::HalWrapper {
+public:
+ explicit AidlPowerHalWrapper(sp<hardware::power::IPower> powerHal);
+ ~AidlPowerHalWrapper() override;
+
+ static std::unique_ptr<HalWrapper> connect();
+
+ bool setExpensiveRendering(bool enabled) override;
+ bool notifyDisplayUpdateImminent() override;
+ bool supportsPowerHintSession() override;
+ bool isPowerHintSessionRunning() override;
+ void restartPowerHintSession() override;
+ void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) override;
+ bool startPowerHintSession() override;
+ void setTargetWorkDuration(int64_t targetDurationNanos) override;
+ void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) override;
+ bool shouldReconnectHAL() override;
+ std::vector<int32_t> getPowerHintSessionThreadIds() override;
+ std::optional<int64_t> getTargetWorkDuration() override;
+
+private:
+ bool checkPowerHintSessionSupported();
+ void closePowerHintSession();
+ bool shouldReportActualDurationsNow();
+ bool shouldSetTargetDuration(int64_t targetDurationNanos);
+
+ const sp<hardware::power::IPower> mPowerHal = nullptr;
+ bool mHasExpensiveRendering = false;
+ bool mHasDisplayUpdateImminent = false;
+ // Used to indicate an error state and need for reconstruction
+ bool mShouldReconnectHal = false;
+ // This is not thread safe, but is currently protected by mPowerHalMutex so it needs no lock
+ sp<hardware::power::IPowerHintSession> mPowerHintSession = nullptr;
+ // Queue of actual durations saved to report
+ std::vector<hardware::power::WorkDuration> mPowerHintQueue;
+ // The latest un-normalized values we have received for target and actual
+ int64_t mTargetDuration = kDefaultTarget.count();
+ std::optional<int64_t> mActualDuration;
+ // The list of thread ids, stored so we can restart the session from this class if needed
+ std::vector<int32_t> mPowerHintThreadIds;
+ bool mSupportsPowerHint;
+ // Keep track of the last messages sent for rate limiter change detection
+ std::optional<int64_t> mLastActualDurationSent;
+ // timestamp of the last report we sent, used to avoid stale sessions
+ int64_t mLastActualReportTimestamp = 0;
+ int64_t mLastTargetDurationSent = kDefaultTarget.count();
+ // Whether to normalize all the actual values as error terms relative to a constant target
+ // This saves a binder call by not setting the target, and should not affect the pid values
+ static const bool sNormalizeTarget;
+ // Whether we should emit ATRACE_INT data for hint sessions
+ static const bool sTraceHintSessionData;
+
+ // Max percent the actual duration can vary without causing a report (eg: 0.1 = 10%)
+ static constexpr double kAllowedActualDeviationPercent = 0.1;
+ // Max percent the target duration can vary without causing a report (eg: 0.05 = 5%)
+ static constexpr double kAllowedTargetDeviationPercent = 0.05;
+ // Target used for init and normalization, the actual value does not really matter
+ static constexpr const std::chrono::nanoseconds kDefaultTarget = 50ms;
+ // Amount of time after the last message was sent before the session goes stale
+ // actually 100ms but we use 80 here to ideally avoid going stale
+ static constexpr const std::chrono::nanoseconds kStaleTimeout = 80ms;
+};
+
} // namespace impl
} // namespace Hwc2
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 4fc9d27..768ae65 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -681,7 +681,9 @@
if (renderEngineTid.has_value()) {
tidList.emplace_back(*renderEngineTid);
}
- mPowerAdvisor.startPowerHintSession(tidList);
+ if (!mPowerAdvisor.startPowerHintSession(tidList)) {
+ ALOGW("Cannot start power hint session");
+ }
}
mBootStage = BootStage::FINISHED;
@@ -993,13 +995,8 @@
outMode.resolution = ui::Size(width, height);
- if (mEmulatedDisplayDensity) {
- outMode.xDpi = mEmulatedDisplayDensity;
- outMode.yDpi = mEmulatedDisplayDensity;
- } else {
- outMode.xDpi = xDpi;
- outMode.yDpi = yDpi;
- }
+ outMode.xDpi = xDpi;
+ outMode.yDpi = yDpi;
const nsecs_t period = mode->getVsyncPeriod();
outMode.refreshRate = Fps::fromPeriodNsecs(period).getValue();
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 168b576..704815d 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -28,6 +28,7 @@
"android.hardware.graphics.mapper@3.0",
"android.hardware.graphics.mapper@4.0",
"android.hardware.power@1.3",
+ "android.hardware.power-V2-cpp",
"libbase",
"libbinder",
"libbinder_ndk",
diff --git a/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp b/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp
new file mode 100644
index 0000000..e25a0ae
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "AidlPowerHalWrapperTest"
+
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/IPowerHintSession.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <algorithm>
+#include <chrono>
+#include <memory>
+#include "DisplayHardware/PowerAdvisor.h"
+#include "android/hardware/power/WorkDuration.h"
+#include "binder/Status.h"
+#include "log/log_main.h"
+#include "mock/DisplayHardware/MockIPower.h"
+#include "mock/DisplayHardware/MockIPowerHintSession.h"
+#include "utils/Timers.h"
+
+using namespace android;
+using namespace android::Hwc2::mock;
+using namespace android::hardware::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+namespace android::Hwc2::impl {
+
+class AidlPowerHalWrapperTest : public testing::Test {
+public:
+ void SetUp() override;
+
+protected:
+ std::unique_ptr<AidlPowerHalWrapper> mWrapper = nullptr;
+ sp<NiceMock<MockIPower>> mMockHal = nullptr;
+ sp<NiceMock<MockIPowerHintSession>> mMockSession = nullptr;
+ void verifyAndClearExpectations();
+ void sendActualWorkDurationGroup(std::vector<WorkDuration> durations,
+ std::chrono::nanoseconds sleepBeforeLastSend);
+};
+
+void AidlPowerHalWrapperTest::SetUp() {
+ mMockHal = new NiceMock<MockIPower>();
+ mMockSession = new NiceMock<MockIPowerHintSession>();
+ ON_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)).WillByDefault(Return(Status::ok()));
+ mWrapper = std::make_unique<AidlPowerHalWrapper>(mMockHal);
+}
+
+void AidlPowerHalWrapperTest::verifyAndClearExpectations() {
+ Mock::VerifyAndClearExpectations(mMockHal.get());
+ Mock::VerifyAndClearExpectations(mMockSession.get());
+}
+
+void AidlPowerHalWrapperTest::sendActualWorkDurationGroup(
+ std::vector<WorkDuration> durations, std::chrono::nanoseconds sleepBeforeLastSend) {
+ for (size_t i = 0; i < durations.size(); i++) {
+ if (i == durations.size() - 1) {
+ std::this_thread::sleep_for(sleepBeforeLastSend);
+ }
+ auto duration = durations[i];
+ mWrapper->sendActualWorkDuration(duration.durationNanos, duration.timeStampNanos);
+ }
+}
+WorkDuration toWorkDuration(std::chrono::nanoseconds durationNanos, int64_t timeStampNanos) {
+ WorkDuration duration;
+ duration.durationNanos = durationNanos.count();
+ duration.timeStampNanos = timeStampNanos;
+ return duration;
+}
+
+namespace {
+TEST_F(AidlPowerHalWrapperTest, supportsPowerHintSession) {
+ ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+ Mock::VerifyAndClearExpectations(mMockHal.get());
+ ON_CALL(*mMockHal.get(), getHintSessionPreferredRate(_))
+ .WillByDefault(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE)));
+ auto newWrapper = AidlPowerHalWrapper(mMockHal);
+ EXPECT_FALSE(newWrapper.supportsPowerHintSession());
+}
+
+TEST_F(AidlPowerHalWrapperTest, startPowerHintSession) {
+ ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+ std::vector<int32_t> threadIds = {1, 2};
+ mWrapper->setPowerHintSessionThreadIds(threadIds);
+ EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+ EXPECT_TRUE(mWrapper->startPowerHintSession());
+ EXPECT_FALSE(mWrapper->startPowerHintSession());
+}
+
+TEST_F(AidlPowerHalWrapperTest, restartNewPoserHintSessionWithNewThreadIds) {
+ ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+
+ std::vector<int32_t> threadIds = {1, 2};
+ EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+ mWrapper->setPowerHintSessionThreadIds(threadIds);
+ EXPECT_EQ(mWrapper->getPowerHintSessionThreadIds(), threadIds);
+ ASSERT_TRUE(mWrapper->startPowerHintSession());
+ verifyAndClearExpectations();
+
+ threadIds = {2, 3};
+ EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+ EXPECT_CALL(*mMockSession.get(), close()).Times(1);
+ mWrapper->setPowerHintSessionThreadIds(threadIds);
+ EXPECT_EQ(mWrapper->getPowerHintSessionThreadIds(), threadIds);
+ verifyAndClearExpectations();
+
+ EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)).Times(0);
+ EXPECT_CALL(*mMockSession.get(), close()).Times(0);
+ mWrapper->setPowerHintSessionThreadIds(threadIds);
+ verifyAndClearExpectations();
+}
+
+TEST_F(AidlPowerHalWrapperTest, setTargetWorkDuration) {
+ ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+
+ std::vector<int32_t> threadIds = {1, 2};
+ mWrapper->setPowerHintSessionThreadIds(threadIds);
+ EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+ ASSERT_TRUE(mWrapper->startPowerHintSession());
+ verifyAndClearExpectations();
+
+ std::chrono::nanoseconds base = 100ms;
+ // test cases with target work duration and whether it should update hint against baseline 100ms
+ const std::vector<std::pair<std::chrono::nanoseconds, bool>> testCases = {{0ms, false},
+ {-1ms, false},
+ {200ms, true},
+ {2ms, true},
+ {96ms, false},
+ {104ms, false}};
+
+ for (const auto& test : testCases) {
+ // reset to 100ms baseline
+ mWrapper->setTargetWorkDuration(1);
+ mWrapper->setTargetWorkDuration(base.count());
+
+ auto target = test.first;
+ EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(target.count()))
+ .Times(test.second ? 1 : 0);
+ mWrapper->setTargetWorkDuration(target.count());
+ verifyAndClearExpectations();
+ }
+}
+
+TEST_F(AidlPowerHalWrapperTest, setTargetWorkDuration_shouldReconnectOnError) {
+ ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+
+ std::vector<int32_t> threadIds = {1, 2};
+ mWrapper->setPowerHintSessionThreadIds(threadIds);
+ EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+ ASSERT_TRUE(mWrapper->startPowerHintSession());
+ verifyAndClearExpectations();
+
+ EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(1))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE)));
+ mWrapper->setTargetWorkDuration(1);
+ EXPECT_TRUE(mWrapper->shouldReconnectHAL());
+}
+
+TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration) {
+ ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+
+ std::vector<int32_t> threadIds = {1, 2};
+ mWrapper->setPowerHintSessionThreadIds(threadIds);
+ EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+ ASSERT_TRUE(mWrapper->startPowerHintSession());
+ verifyAndClearExpectations();
+
+ auto base = toWorkDuration(100ms, 0);
+ // test cases with actual work durations and whether it should update hint against baseline
+ // 100ms
+ const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>>
+ testCases = {{{{-1ms, 100}}, false},
+ {{{91ms, 100}}, false},
+ {{{109ms, 100}}, false},
+ {{{100ms, 100}, {200ms, 200}}, true},
+ {{{100ms, 500}, {100ms, 600}, {3ms, 600}}, true}};
+
+ for (const auto& test : testCases) {
+ // reset actual duration
+ sendActualWorkDurationGroup({base}, 80ms);
+
+ auto raw = test.first;
+ std::vector<WorkDuration> durations(raw.size());
+ std::transform(raw.begin(), raw.end(), durations.begin(),
+ [](std::pair<std::chrono::nanoseconds, nsecs_t> d) {
+ return toWorkDuration(d.first, d.second);
+ });
+ EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations))
+ .Times(test.second ? 1 : 0);
+ sendActualWorkDurationGroup(durations, 0ms);
+ verifyAndClearExpectations();
+ }
+}
+
+TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_exceedsStaleTime) {
+ ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+
+ std::vector<int32_t> threadIds = {1, 2};
+ mWrapper->setPowerHintSessionThreadIds(threadIds);
+ EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+ ASSERT_TRUE(mWrapper->startPowerHintSession());
+ verifyAndClearExpectations();
+
+ auto base = toWorkDuration(100ms, 0);
+ // test cases with actual work durations and whether it should update hint against baseline
+ // 100ms
+ const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>>
+ testCases = {{{{91ms, 100}}, true}, {{{109ms, 100}}, true}};
+
+ for (const auto& test : testCases) {
+ // reset actual duration
+ sendActualWorkDurationGroup({base}, 80ms);
+
+ auto raw = test.first;
+ std::vector<WorkDuration> durations(raw.size());
+ std::transform(raw.begin(), raw.end(), durations.begin(),
+ [](std::pair<std::chrono::nanoseconds, nsecs_t> d) {
+ return toWorkDuration(d.first, d.second);
+ });
+ EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations))
+ .Times(test.second ? 1 : 0);
+ sendActualWorkDurationGroup(durations, 80ms);
+ verifyAndClearExpectations();
+ }
+}
+
+TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_shouldReconnectOnError) {
+ ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+
+ std::vector<int32_t> threadIds = {1, 2};
+ mWrapper->setPowerHintSessionThreadIds(threadIds);
+ EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+ ASSERT_TRUE(mWrapper->startPowerHintSession());
+ verifyAndClearExpectations();
+ WorkDuration duration;
+ duration.durationNanos = 1;
+ EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(_))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE)));
+ sendActualWorkDurationGroup({duration}, 0ms);
+ EXPECT_TRUE(mWrapper->shouldReconnectHAL());
+}
+
+} // namespace
+} // namespace android::Hwc2::impl
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 1eea023..cc9d48c 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -26,6 +26,8 @@
srcs: [
"mock/DisplayHardware/MockComposer.cpp",
"mock/DisplayHardware/MockHWC2.cpp",
+ "mock/DisplayHardware/MockIPower.cpp",
+ "mock/DisplayHardware/MockIPowerHintSession.cpp",
"mock/DisplayHardware/MockPowerAdvisor.cpp",
"mock/MockEventThread.cpp",
"mock/MockFrameTimeline.cpp",
@@ -67,6 +69,7 @@
":libsurfaceflinger_mock_sources",
":libsurfaceflinger_sources",
"libsurfaceflinger_unittest_main.cpp",
+ "AidlPowerHalWrapperTest.cpp",
"CachingTest.cpp",
"CompositionTest.cpp",
"DispSyncSourceTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp
new file mode 100644
index 0000000..2323ebb
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#include "mock/DisplayHardware/MockIPower.h"
+
+namespace android::Hwc2::mock {
+
+// Explicit default instantiation is recommended.
+MockIPower::MockIPower() = default;
+
+} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
new file mode 100644
index 0000000..0ddc90d
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#pragma once
+
+#include "binder/Status.h"
+
+#include <android/hardware/power/IPower.h>
+#include <gmock/gmock.h>
+
+using android::binder::Status;
+using android::hardware::power::Boost;
+using android::hardware::power::IPower;
+using android::hardware::power::IPowerHintSession;
+using android::hardware::power::Mode;
+
+namespace android::Hwc2::mock {
+
+class MockIPower : public IPower {
+public:
+ MockIPower();
+
+ MOCK_METHOD(Status, isBoostSupported, (Boost boost, bool* ret), (override));
+ MOCK_METHOD(Status, setBoost, (Boost boost, int32_t durationMs), (override));
+ MOCK_METHOD(Status, isModeSupported, (Mode mode, bool* ret), (override));
+ MOCK_METHOD(Status, setMode, (Mode mode, bool enabled), (override));
+ MOCK_METHOD(Status, createHintSession,
+ (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos, sp<IPowerHintSession>* session),
+ (override));
+ MOCK_METHOD(Status, getHintSessionPreferredRate, (int64_t * rate), (override));
+ MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
+ MOCK_METHOD(std::string, getInterfaceHash, (), (override));
+ MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+};
+
+} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.cpp
new file mode 100644
index 0000000..770bc15
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#include "mock/DisplayHardware/MockIPowerHintSession.h"
+
+namespace android::Hwc2::mock {
+
+// Explicit default instantiation is recommended.
+MockIPowerHintSession::MockIPowerHintSession() = default;
+
+} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
new file mode 100644
index 0000000..439f6f4
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#pragma once
+
+#include "binder/Status.h"
+
+#include <android/hardware/power/IPower.h>
+#include <gmock/gmock.h>
+
+using android::binder::Status;
+using android::hardware::power::IPowerHintSession;
+
+using namespace android::hardware::power;
+
+namespace android::Hwc2::mock {
+
+class MockIPowerHintSession : public IPowerHintSession {
+public:
+ MockIPowerHintSession();
+
+ MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+ MOCK_METHOD(Status, pause, (), (override));
+ MOCK_METHOD(Status, resume, (), (override));
+ MOCK_METHOD(Status, close, (), (override));
+ MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
+ MOCK_METHOD(std::string, getInterfaceHash, (), (override));
+ MOCK_METHOD(Status, updateTargetWorkDuration, (int64_t), (override));
+ MOCK_METHOD(Status, reportActualWorkDuration, (const ::std::vector<WorkDuration>&), (override));
+};
+
+} // namespace android::Hwc2::mock