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