Update performance_hint to follow NDK API guidance

This patch updates the performance_hint NDK in response to API guidance,
and updates the tests accordingly.

Additionally, this patch updates the documentation and ergonomics of a
few related methods to better accomodate these changes, and to downgrade
going over the graphics pipeline limit from a breakage to a very severe
warning.

Flag: EXEMPT NDK
Test: atest
cts/tests/tests/os/src/android/os/cts/PerformanceHintManagerTest.java
Test: atest PerformanceHintNativeTestCases
Test: atest HintManagerServiceTest
Bug: 380299912
Bug: 381269529

Change-Id: I31f1c1db4ee4846a8aca63243f61ac1672aac309
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 1ccadf9..f629c88 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -396,6 +396,7 @@
     APerformanceHint_notifyWorkloadSpike; # introduced=36
     APerformanceHint_borrowSessionFromJava; # introduced=36
     APerformanceHint_setNativeSurfaces; # introduced=36
+    APerformanceHint_isFeatureSupported; # introduced=36
     AWorkDuration_create; # introduced=VanillaIceCream
     AWorkDuration_release; # introduced=VanillaIceCream
     AWorkDuration_setWorkPeriodStartTimestampNanos; # introduced=VanillaIceCream
@@ -419,7 +420,6 @@
     AThermal_setIThermalServiceForTesting;
     APerformanceHint_setIHintManagerForTesting;
     APerformanceHint_sendHint;
-    APerformanceHint_setUseGraphicsPipelineForTesting;
     APerformanceHint_getThreadIds;
     APerformanceHint_createSessionInternal;
     APerformanceHint_createSessionUsingConfigInternal;
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 9257901..7e65523 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -71,26 +71,31 @@
 
 struct APerformanceHintSession;
 
-constexpr int64_t SEND_HINT_TIMEOUT = std::chrono::nanoseconds(100ms).count();
 struct AWorkDuration : public hal::WorkDuration {};
 struct ASessionCreationConfig : public SessionCreationConfig {
     std::vector<wp<IBinder>> layers{};
-    bool hasMode(hal::SessionMode&& mode) {
+    bool hasMode(hal::SessionMode mode) {
         return std::find(modesToEnable.begin(), modesToEnable.end(), mode) != modesToEnable.end();
     }
+    void setMode(hal::SessionMode mode, bool enabled) {
+        if (hasMode(mode)) {
+            if (!enabled) {
+                std::erase(modesToEnable, mode);
+            }
+        } else if (enabled) {
+            modesToEnable.push_back(mode);
+        }
+    }
 };
 
-bool kForceGraphicsPipeline = false;
-
-bool useGraphicsPipeline() {
-    return android::os::adpf_graphics_pipeline() || kForceGraphicsPipeline;
-}
-
 // A pair of values that determine the behavior of the
 // load hint rate limiter, to only allow "X hints every Y seconds"
-constexpr double kLoadHintInterval = std::chrono::nanoseconds(2s).count();
+constexpr int64_t kLoadHintInterval = std::chrono::nanoseconds(2s).count();
 constexpr double kMaxLoadHintsPerInterval = 20;
-constexpr double kReplenishRate = kMaxLoadHintsPerInterval / kLoadHintInterval;
+// Replenish rate is used for new rate limiting behavior, it currently replenishes at a rate of
+// 20 / 2s = 1 per 100us, which is the same limit as before, just enforced differently
+constexpr double kReplenishRate = kMaxLoadHintsPerInterval / static_cast<double>(kLoadHintInterval);
+constexpr int64_t kSendHintTimeout = kLoadHintInterval / kMaxLoadHintsPerInterval;
 bool kForceNewHintBehavior = false;
 
 template <class T>
@@ -149,9 +154,7 @@
     std::future<bool> mChannelCreationFinished;
 };
 
-class SupportInfoWrapper {
-public:
-    SupportInfoWrapper(hal::SupportInfo& info);
+struct SupportInfoWrapper : public hal::SupportInfo {
     bool isSessionModeSupported(hal::SessionMode mode);
     bool isSessionHintSupported(hal::SessionHint hint);
 
@@ -162,7 +165,6 @@
         // over that much and cutting off any extra values
         return (supportBitfield >> static_cast<int>(enumValue)) % 2;
     }
-    hal::SupportInfo mSupportInfo;
 };
 
 class HintManagerClient : public IHintManager::BnHintManagerClient {
@@ -188,9 +190,9 @@
                                            bool isJava = false);
     APerformanceHintSession* getSessionFromJava(JNIEnv* _Nonnull env, jobject _Nonnull sessionObj);
 
-    APerformanceHintSession* createSessionUsingConfig(ASessionCreationConfig* sessionCreationConfig,
-                                                      hal::SessionTag tag = hal::SessionTag::APP,
-                                                      bool isJava = false);
+    int createSessionUsingConfig(ASessionCreationConfig* sessionCreationConfig,
+                                 APerformanceHintSession** sessionPtr,
+                                 hal::SessionTag tag = hal::SessionTag::APP, bool isJava = false);
     int64_t getPreferredRateNanos() const;
     int32_t getMaxGraphicsPipelineThreadsCount();
     FMQWrapper& getFMQWrapper();
@@ -202,6 +204,7 @@
                                          std::vector<T>& out);
     ndk::SpAIBinder& getToken();
     SupportInfoWrapper& getSupportInfo();
+    bool isFeatureSupported(APerformanceHintFeature feature);
 
 private:
     static APerformanceHintManager* create(std::shared_ptr<IHintManager> iHintManager);
@@ -239,8 +242,8 @@
     int setPreferPowerEfficiency(bool enabled);
     int reportActualWorkDuration(AWorkDuration* workDuration);
     bool isJava();
-    status_t setNativeSurfaces(ANativeWindow** windows, int numWindows, ASurfaceControl** controls,
-                               int numSurfaceControls);
+    status_t setNativeSurfaces(ANativeWindow** windows, size_t numWindows,
+                               ASurfaceControl** controls, size_t numSurfaceControls);
 
 private:
     friend struct APerformanceHintManager;
@@ -294,14 +297,12 @@
 
 // ===================================== SupportInfoWrapper implementation
 
-SupportInfoWrapper::SupportInfoWrapper(hal::SupportInfo& info) : mSupportInfo(info) {}
-
 bool SupportInfoWrapper::isSessionHintSupported(hal::SessionHint hint) {
-    return getEnumSupportFromBitfield(hint, mSupportInfo.sessionHints);
+    return getEnumSupportFromBitfield(hint, sessionHints);
 }
 
 bool SupportInfoWrapper::isSessionModeSupported(hal::SessionMode mode) {
-    return getEnumSupportFromBitfield(mode, mSupportInfo.sessionModes);
+    return getEnumSupportFromBitfield(mode, sessionModes);
 }
 
 // ===================================== APerformanceHintManager implementation
@@ -386,12 +387,14 @@
             .targetWorkDurationNanos = initialTargetWorkDurationNanos,
     }};
 
-    return APerformanceHintManager::createSessionUsingConfig(&creationConfig, tag, isJava);
+    APerformanceHintSession* sessionOut;
+    APerformanceHintManager::createSessionUsingConfig(&creationConfig, &sessionOut, tag, isJava);
+    return sessionOut;
 }
 
-APerformanceHintSession* APerformanceHintManager::createSessionUsingConfig(
-        ASessionCreationConfig* sessionCreationConfig, hal::SessionTag tag, bool isJava) {
-    std::shared_ptr<IHintSession> session;
+int APerformanceHintManager::createSessionUsingConfig(ASessionCreationConfig* sessionCreationConfig,
+                                                      APerformanceHintSession** sessionOut,
+                                                      hal::SessionTag tag, bool isJava) {
     hal::SessionConfig sessionConfig{.id = -1};
     ndk::ScopedAStatus ret;
 
@@ -411,31 +414,65 @@
         }
     }
 
+    bool autoCpu = sessionCreationConfig->hasMode(hal::SessionMode::AUTO_CPU);
+    bool autoGpu = sessionCreationConfig->hasMode(hal::SessionMode::AUTO_GPU);
+
+    if (autoCpu || autoGpu) {
+        LOG_ALWAYS_FATAL_IF(!sessionCreationConfig->hasMode(hal::SessionMode::GRAPHICS_PIPELINE),
+                            "Automatic session timing enabled without graphics pipeline mode");
+    }
+
+    if (autoCpu && !mSupportInfoWrapper.isSessionModeSupported(hal::SessionMode::AUTO_CPU)) {
+        ALOGE("Automatic CPU timing enabled but not supported");
+        return ENOTSUP;
+    }
+
+    if (autoGpu && !mSupportInfoWrapper.isSessionModeSupported(hal::SessionMode::AUTO_GPU)) {
+        ALOGE("Automatic GPU timing enabled but not supported");
+        return ENOTSUP;
+    }
+
+    IHintManager::SessionCreationReturn returnValue;
     ret = mHintManager->createHintSessionWithConfig(mToken, tag,
                                                     *static_cast<SessionCreationConfig*>(
                                                             sessionCreationConfig),
-                                                    &sessionConfig, &session);
+                                                    &sessionConfig, &returnValue);
 
     sessionCreationConfig->layerTokens.clear();
 
-    if (!ret.isOk() || !session) {
+    if (!ret.isOk() || !returnValue.session) {
         ALOGE("%s: PerformanceHint cannot create session. %s", __FUNCTION__, ret.getMessage());
-        return nullptr;
+        switch (ret.getExceptionCode()) {
+            case binder::Status::EX_UNSUPPORTED_OPERATION:
+                return ENOTSUP;
+            case binder::Status::EX_ILLEGAL_ARGUMENT:
+                return EINVAL;
+            default:
+                return EPIPE;
+        }
     }
 
-    auto out = new APerformanceHintSession(mHintManager, std::move(session),
+    auto out = new APerformanceHintSession(mHintManager, std::move(returnValue.session),
                                            mClientData.preferredRateNanos,
                                            sessionCreationConfig->targetWorkDurationNanos, isJava,
                                            sessionConfig.id == -1
                                                    ? std::nullopt
                                                    : std::make_optional<hal::SessionConfig>(
                                                              std::move(sessionConfig)));
+
+    *sessionOut = out;
+
     std::scoped_lock lock(sHintMutex);
     out->traceThreads(sessionCreationConfig->tids);
     out->traceTargetDuration(sessionCreationConfig->targetWorkDurationNanos);
     out->traceModes(sessionCreationConfig->modesToEnable);
 
-    return out;
+    if (returnValue.pipelineThreadLimitExceeded) {
+        ALOGE("Graphics pipeline session thread limit exceeded!");
+        return EBUSY;
+    }
+
+    return 0;
 }
 
 APerformanceHintSession* APerformanceHintManager::getSessionFromJava(JNIEnv* env,
@@ -480,6 +517,25 @@
     return mSupportInfoWrapper;
 }
 
+bool APerformanceHintManager::isFeatureSupported(APerformanceHintFeature feature) {
+    switch (feature) {
+        case (APERF_HINT_SESSIONS):
+            return mSupportInfoWrapper.usesSessions;
+        case (APERF_HINT_POWER_EFFICIENCY):
+            return mSupportInfoWrapper.isSessionModeSupported(hal::SessionMode::POWER_EFFICIENCY);
+        case (APERF_HINT_SURFACE_BINDING):
+            return mSupportInfoWrapper.compositionData.isSupported;
+        case (APERF_HINT_GRAPHICS_PIPELINE):
+            return mSupportInfoWrapper.isSessionModeSupported(hal::SessionMode::GRAPHICS_PIPELINE);
+        case (APERF_HINT_AUTO_CPU):
+            return mSupportInfoWrapper.isSessionModeSupported(hal::SessionMode::AUTO_CPU);
+        case (APERF_HINT_AUTO_GPU):
+            return mSupportInfoWrapper.isSessionModeSupported(hal::SessionMode::AUTO_GPU);
+        default:
+            return false;
+    }
+}
+
 // ===================================== APerformanceHintSession implementation
 
 constexpr int kNumEnums = enum_size<hal::SessionHint>();
@@ -512,10 +568,6 @@
 }
 
 int APerformanceHintSession::updateTargetWorkDuration(int64_t targetDurationNanos) {
-    if (targetDurationNanos <= 0) {
-        ALOGE("%s: targetDurationNanos must be positive", __FUNCTION__);
-        return EINVAL;
-    }
     std::scoped_lock lock(sHintMutex);
     if (mTargetDurationNanos == targetDurationNanos) {
         return 0;
@@ -546,7 +598,6 @@
                                    .workPeriodStartTimestampNanos = 0,
                                    .cpuDurationNanos = actualDurationNanos,
                                    .gpuDurationNanos = 0};
-
     return reportActualWorkDurationInternal(static_cast<AWorkDuration*>(&workDuration));
 }
 
@@ -556,17 +607,24 @@
 
 int APerformanceHintSession::sendHints(std::vector<hal::SessionHint>& hints, int64_t now,
                                        const char*) {
-    std::scoped_lock lock(sHintMutex);
+    auto& supportInfo = APerformanceHintManager::getInstance()->getSupportInfo();
+
+    // Drop all unsupported hints, there's not much point reporting errors or warnings for this
+    std::erase_if(hints,
+                  [&](hal::SessionHint hint) { return !supportInfo.isSessionHintSupported(hint); });
+
     if (hints.empty()) {
-        return EINVAL;
-    }
-    for (auto&& hint : hints) {
-        if (static_cast<int32_t>(hint) < 0 || static_cast<int32_t>(hint) >= kNumEnums) {
-            ALOGE("%s: invalid session hint %d", __FUNCTION__, hint);
-            return EINVAL;
-        }
+        // We successfully sent all hints we were able to, technically
+        return 0;
     }
 
+    for (auto&& hint : hints) {
+        LOG_ALWAYS_FATAL_IF(static_cast<int32_t>(hint) < 0 ||
+                                    static_cast<int32_t>(hint) >= kNumEnums,
+                            "%s: invalid session hint %d", __FUNCTION__, hint);
+    }
+
+    std::scoped_lock lock(sHintMutex);
     if (useNewLoadHintBehavior()) {
         if (!APerformanceHintManager::getInstance()->canSendLoadHints(hints, now)) {
             return EBUSY;
@@ -575,7 +633,7 @@
     // keep old rate limiter behavior for legacy flag
     else {
         for (auto&& hint : hints) {
-            if (now < (mLastHintSentTimestamp[static_cast<int32_t>(hint)] + SEND_HINT_TIMEOUT)) {
+            if (now < (mLastHintSentTimestamp[static_cast<int32_t>(hint)] + kSendHintTimeout)) {
                 return EBUSY;
             }
         }
@@ -651,7 +709,9 @@
     }
     std::vector<int32_t> tids(threadIds, threadIds + size);
     ndk::ScopedAStatus ret = mHintManager->setHintSessionThreads(mHintSession, tids);
-    if (!ret.isOk()) {
+
+    // Illegal state means there were too many graphics pipeline threads
+    if (!ret.isOk() && ret.getExceptionCode() != EX_SERVICE_SPECIFIC) {
         ALOGE("%s: failed: %s", __FUNCTION__, ret.getMessage());
         if (ret.getExceptionCode() == EX_ILLEGAL_ARGUMENT) {
             return EINVAL;
@@ -663,8 +723,10 @@
 
     std::scoped_lock lock(sHintMutex);
     traceThreads(tids);
+    bool tooManyThreads =
+            ret.getExceptionCode() == EX_SERVICE_SPECIFIC && ret.getServiceSpecificError() == 5;
 
-    return 0;
+    return tooManyThreads ? EBUSY : 0;
 }
 
 int APerformanceHintSession::getThreadIds(int32_t* const threadIds, size_t* size) {
@@ -711,10 +773,16 @@
 
 int APerformanceHintSession::reportActualWorkDurationInternal(AWorkDuration* workDuration) {
     int64_t actualTotalDurationNanos = workDuration->durationNanos;
-    traceActualDuration(workDuration->durationNanos);
     int64_t now = uptimeNanos();
     workDuration->timeStampNanos = now;
     std::scoped_lock lock(sHintMutex);
+
+    if (mTargetDurationNanos <= 0) {
+        ALOGE("Cannot report work durations if the target duration is not positive.");
+        return EINVAL;
+    }
+
+    traceActualDuration(actualTotalDurationNanos);
     mActualWorkDurations.push_back(std::move(*workDuration));
 
     if (actualTotalDurationNanos >= mTargetDurationNanos) {
@@ -757,9 +825,9 @@
     return 0;
 }
 
-status_t APerformanceHintSession::setNativeSurfaces(ANativeWindow** windows, int numWindows,
+status_t APerformanceHintSession::setNativeSurfaces(ANativeWindow** windows, size_t numWindows,
                                                     ASurfaceControl** controls,
-                                                    int numSurfaceControls) {
+                                                    size_t numSurfaceControls) {
     if (!mSessionConfig.has_value()) {
         return ENOTSUP;
     }
@@ -774,7 +842,10 @@
         ndkLayerHandles.emplace_back(ndk::SpAIBinder(AIBinder_fromPlatformBinder(handle)));
     }
 
-    mHintSession->associateToLayers(ndkLayerHandles);
+    auto ret = mHintSession->associateToLayers(ndkLayerHandles);
+    if (!ret.isOk()) {
+        return EPIPE;
+    }
     return 0;
 }
 
@@ -857,6 +928,11 @@
             }
             return true;
         });
+
+        // If we're unit testing the FMQ, we should block for it to finish completing
+        if (gForceFMQEnabled.has_value()) {
+            mChannelCreationFinished.wait();
+        }
     }
     return isActive();
 }
@@ -1029,7 +1105,8 @@
     ATrace_setCounter((mSessionName + " target duration").c_str(), targetDuration);
 }
 
-// ===================================== C API
+// ===================================== Start of C API
+
 APerformanceHintManager* APerformanceHint_getManager() {
     return APerformanceHintManager::getInstance();
 }
@@ -1037,10 +1114,16 @@
 #define VALIDATE_PTR(ptr) \
     LOG_ALWAYS_FATAL_IF(ptr == nullptr, "%s: " #ptr " is nullptr", __FUNCTION__);
 
+#define HARD_VALIDATE_INT(value, cmp)                                        \
+    LOG_ALWAYS_FATAL_IF(!(value cmp),                                        \
+                        "%s: Invalid value. Check failed: (" #value " " #cmp \
+                        ") with value: %" PRIi64,                            \
+                        __FUNCTION__, static_cast<int64_t>(value));
+
 #define VALIDATE_INT(value, cmp)                                                             \
     if (!(value cmp)) {                                                                      \
         ALOGE("%s: Invalid value. Check failed: (" #value " " #cmp ") with value: %" PRIi64, \
-              __FUNCTION__, value);                                                          \
+              __FUNCTION__, static_cast<int64_t>(value));                                    \
         return EINVAL;                                                                       \
     }
 
@@ -1058,19 +1141,27 @@
     return manager->createSession(threadIds, size, initialTargetWorkDurationNanos);
 }
 
-APerformanceHintSession* APerformanceHint_createSessionUsingConfig(
-        APerformanceHintManager* manager, ASessionCreationConfig* sessionCreationConfig) {
+int APerformanceHint_createSessionUsingConfig(APerformanceHintManager* manager,
+                                              ASessionCreationConfig* sessionCreationConfig,
+                                              APerformanceHintSession** sessionOut) {
     VALIDATE_PTR(manager);
     VALIDATE_PTR(sessionCreationConfig);
-    return manager->createSessionUsingConfig(sessionCreationConfig);
+    VALIDATE_PTR(sessionOut);
+    *sessionOut = nullptr;
+
+    return manager->createSessionUsingConfig(sessionCreationConfig, sessionOut);
 }
 
-APerformanceHintSession* APerformanceHint_createSessionUsingConfigInternal(
-        APerformanceHintManager* manager, ASessionCreationConfig* sessionCreationConfig,
-        SessionTag tag) {
+int APerformanceHint_createSessionUsingConfigInternal(APerformanceHintManager* manager,
+                                                      ASessionCreationConfig* sessionCreationConfig,
+                                                      APerformanceHintSession** sessionOut,
+                                                      SessionTag tag) {
     VALIDATE_PTR(manager);
     VALIDATE_PTR(sessionCreationConfig);
-    return manager->createSessionUsingConfig(sessionCreationConfig,
+    VALIDATE_PTR(sessionOut);
+    *sessionOut = nullptr;
+
+    return manager->createSessionUsingConfig(sessionCreationConfig, sessionOut,
                                              static_cast<hal::SessionTag>(tag));
 }
 
@@ -1111,6 +1202,7 @@
 int APerformanceHint_updateTargetWorkDuration(APerformanceHintSession* session,
                                               int64_t targetDurationNanos) {
     VALIDATE_PTR(session)
+    VALIDATE_INT(targetDurationNanos, >= 0)
     return session->updateTargetWorkDuration(targetDurationNanos);
 }
 
@@ -1204,13 +1296,23 @@
 }
 
 int APerformanceHint_setNativeSurfaces(APerformanceHintSession* session,
-                                       ANativeWindow** nativeWindows, int nativeWindowsSize,
-                                       ASurfaceControl** surfaceControls, int surfaceControlsSize) {
+                                       ANativeWindow** nativeWindows, size_t nativeWindowsSize,
+                                       ASurfaceControl** surfaceControls,
+                                       size_t surfaceControlsSize) {
     VALIDATE_PTR(session)
     return session->setNativeSurfaces(nativeWindows, nativeWindowsSize, surfaceControls,
                                       surfaceControlsSize);
 }
 
+bool APerformanceHint_isFeatureSupported(APerformanceHintFeature feature) {
+    APerformanceHintManager* manager = APerformanceHintManager::getInstance();
+    if (manager == nullptr) {
+        // Clearly whatever it is isn't supported in this case
+        return false;
+    }
+    return manager->isFeatureSupported(feature);
+}
+
 AWorkDuration* AWorkDuration_create() {
     return new AWorkDuration();
 }
@@ -1265,78 +1367,32 @@
 
 void ASessionCreationConfig_release(ASessionCreationConfig* config) {
     VALIDATE_PTR(config)
-
     delete config;
 }
 
-int ASessionCreationConfig_setTids(ASessionCreationConfig* config, const pid_t* tids, size_t size) {
+void ASessionCreationConfig_setTids(ASessionCreationConfig* config, const pid_t* tids,
+                                    size_t size) {
     VALIDATE_PTR(config)
     VALIDATE_PTR(tids)
+    HARD_VALIDATE_INT(size, > 0)
 
-    if (!useGraphicsPipeline()) {
-        return ENOTSUP;
-    }
-
-    if (size <= 0) {
-        LOG_ALWAYS_FATAL_IF(size <= 0,
-                            "%s: Invalid value. Thread id list size should be greater than zero.",
-                            __FUNCTION__);
-        return EINVAL;
-    }
     config->tids = std::vector<int32_t>(tids, tids + size);
-    return 0;
 }
 
-int ASessionCreationConfig_setTargetWorkDurationNanos(ASessionCreationConfig* config,
-                                                      int64_t targetWorkDurationNanos) {
+void ASessionCreationConfig_setTargetWorkDurationNanos(ASessionCreationConfig* config,
+                                                       int64_t targetWorkDurationNanos) {
     VALIDATE_PTR(config)
-    VALIDATE_INT(targetWorkDurationNanos, >= 0)
-
-    if (!useGraphicsPipeline()) {
-        return ENOTSUP;
-    }
-
     config->targetWorkDurationNanos = targetWorkDurationNanos;
-    return 0;
 }
 
-int ASessionCreationConfig_setPreferPowerEfficiency(ASessionCreationConfig* config, bool enabled) {
+void ASessionCreationConfig_setPreferPowerEfficiency(ASessionCreationConfig* config, bool enabled) {
     VALIDATE_PTR(config)
-
-    if (!useGraphicsPipeline()) {
-        return ENOTSUP;
-    }
-
-    if (enabled) {
-        config->modesToEnable.push_back(hal::SessionMode::POWER_EFFICIENCY);
-    } else {
-        std::erase(config->modesToEnable, hal::SessionMode::POWER_EFFICIENCY);
-    }
-    return 0;
+    config->setMode(hal::SessionMode::POWER_EFFICIENCY, enabled);
 }
 
-int ASessionCreationConfig_setGraphicsPipeline(ASessionCreationConfig* config, bool enabled) {
+void ASessionCreationConfig_setGraphicsPipeline(ASessionCreationConfig* config, bool enabled) {
     VALIDATE_PTR(config)
-
-    if (!useGraphicsPipeline()) {
-        return ENOTSUP;
-    }
-
-    if (enabled) {
-        config->modesToEnable.push_back(hal::SessionMode::GRAPHICS_PIPELINE);
-    } else {
-        std::erase(config->modesToEnable, hal::SessionMode::GRAPHICS_PIPELINE);
-
-        // Remove automatic timing modes if we turn off GRAPHICS_PIPELINE,
-        // as it is a strict pre-requisite for these to run
-        std::erase(config->modesToEnable, hal::SessionMode::AUTO_CPU);
-        std::erase(config->modesToEnable, hal::SessionMode::AUTO_GPU);
-    }
-    return 0;
-}
-
-void APerformanceHint_setUseGraphicsPipelineForTesting(bool enabled) {
-    kForceGraphicsPipeline = enabled;
+    config->setMode(hal::SessionMode::GRAPHICS_PIPELINE, enabled);
 }
 
 void APerformanceHint_getRateLimiterPropertiesForTesting(int32_t* maxLoadHintsPerInterval,
@@ -1349,47 +1405,21 @@
     kForceNewHintBehavior = newBehavior;
 }
 
-int ASessionCreationConfig_setNativeSurfaces(ASessionCreationConfig* config,
-                                             ANativeWindow** nativeWindows, int nativeWindowsSize,
-                                             ASurfaceControl** surfaceControls,
-                                             int surfaceControlsSize) {
+void ASessionCreationConfig_setNativeSurfaces(ASessionCreationConfig* config,
+                                              ANativeWindow** nativeWindows,
+                                              size_t nativeWindowsSize,
+                                              ASurfaceControl** surfaceControls,
+                                              size_t surfaceControlsSize) {
     VALIDATE_PTR(config)
-
     APerformanceHintManager::layersFromNativeSurfaces<wp<IBinder>>(nativeWindows, nativeWindowsSize,
                                                                    surfaceControls,
                                                                    surfaceControlsSize,
                                                                    config->layers);
-
-    if (config->layers.empty()) {
-        return EINVAL;
-    }
-
-    return 0;
 }
 
-int ASessionCreationConfig_setUseAutoTiming(ASessionCreationConfig* _Nonnull config, bool cpu,
-                                            bool gpu) {
+void ASessionCreationConfig_setUseAutoTiming(ASessionCreationConfig* _Nonnull config, bool cpu,
+                                             bool gpu) {
     VALIDATE_PTR(config)
-    if ((cpu || gpu) && !config->hasMode(hal::SessionMode::GRAPHICS_PIPELINE)) {
-        ALOGE("Automatic timing is not supported unless graphics pipeline mode is enabled first");
-        return ENOTSUP;
-    }
-
-    if (config->hasMode(hal::SessionMode::AUTO_CPU)) {
-        if (!cpu) {
-            std::erase(config->modesToEnable, hal::SessionMode::AUTO_CPU);
-        }
-    } else if (cpu) {
-        config->modesToEnable.push_back(static_cast<hal::SessionMode>(hal::SessionMode::AUTO_CPU));
-    }
-
-    if (config->hasMode(hal::SessionMode::AUTO_GPU)) {
-        if (!gpu) {
-            std::erase(config->modesToEnable, hal::SessionMode::AUTO_GPU);
-        }
-    } else if (gpu) {
-        config->modesToEnable.push_back(static_cast<hal::SessionMode>(hal::SessionMode::AUTO_GPU));
-    }
-
-    return 0;
+    config->setMode(hal::SessionMode::AUTO_CPU, cpu);
+    config->setMode(hal::SessionMode::AUTO_GPU, gpu);
 }
diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
index e3c10f6..f68fa1a 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -49,12 +49,87 @@
 using namespace android;
 using namespace testing;
 
+constexpr int64_t DEFAULT_TARGET_NS = 16666666L;
+
+template <class T, void (*D)(T*)>
+std::shared_ptr<T> wrapSP(T* incoming) {
+    return incoming == nullptr ? nullptr : std::shared_ptr<T>(incoming, [](T* ptr) { D(ptr); });
+}
+constexpr auto&& wrapSession = wrapSP<APerformanceHintSession, APerformanceHint_closeSession>;
+constexpr auto&& wrapConfig = wrapSP<ASessionCreationConfig, ASessionCreationConfig_release>;
+constexpr auto&& wrapWorkDuration = wrapSP<AWorkDuration, AWorkDuration_release>;
+
+std::shared_ptr<ASessionCreationConfig> createConfig() {
+    return wrapConfig(ASessionCreationConfig_create());
+}
+
+struct ConfigCreator {
+    std::vector<int32_t> tids{1, 2};
+    int64_t targetDuration = DEFAULT_TARGET_NS;
+    bool powerEfficient = false;
+    bool graphicsPipeline = false;
+    std::vector<ANativeWindow*> nativeWindows{};
+    std::vector<ASurfaceControl*> surfaceControls{};
+    bool autoCpu = false;
+    bool autoGpu = false;
+};
+
+struct SupportHelper {
+    bool hintSessions : 1;
+    bool powerEfficiency : 1;
+    bool bindToSurface : 1;
+    bool graphicsPipeline : 1;
+    bool autoCpu : 1;
+    bool autoGpu : 1;
+};
+
+SupportHelper getSupportHelper() {
+    return {
+            .hintSessions = APerformanceHint_isFeatureSupported(APERF_HINT_SESSIONS),
+            .powerEfficiency = APerformanceHint_isFeatureSupported(APERF_HINT_POWER_EFFICIENCY),
+            .bindToSurface = APerformanceHint_isFeatureSupported(APERF_HINT_SURFACE_BINDING),
+            .graphicsPipeline = APerformanceHint_isFeatureSupported(APERF_HINT_GRAPHICS_PIPELINE),
+            .autoCpu = APerformanceHint_isFeatureSupported(APERF_HINT_AUTO_CPU),
+            .autoGpu = APerformanceHint_isFeatureSupported(APERF_HINT_AUTO_GPU),
+    };
+}
+
+SupportHelper getFullySupportedSupportHelper() {
+    return {
+            .hintSessions = true,
+            .powerEfficiency = true,
+            .graphicsPipeline = true,
+            .autoCpu = true,
+            .autoGpu = true,
+    };
+}
+
+std::shared_ptr<ASessionCreationConfig> configFromCreator(ConfigCreator&& creator) {
+    auto config = createConfig();
+
+    ASessionCreationConfig_setTids(config.get(), creator.tids.data(), creator.tids.size());
+    ASessionCreationConfig_setTargetWorkDurationNanos(config.get(), creator.targetDuration);
+    ASessionCreationConfig_setPreferPowerEfficiency(config.get(), creator.powerEfficient);
+    ASessionCreationConfig_setGraphicsPipeline(config.get(), creator.graphicsPipeline);
+    ASessionCreationConfig_setNativeSurfaces(config.get(),
+                                             creator.nativeWindows.size() > 0
+                                                     ? creator.nativeWindows.data()
+                                                     : nullptr,
+                                             creator.nativeWindows.size(),
+                                             creator.surfaceControls.size() > 0
+                                                     ? creator.surfaceControls.data()
+                                                     : nullptr,
+                                             creator.surfaceControls.size());
+    ASessionCreationConfig_setUseAutoTiming(config.get(), creator.autoCpu, creator.autoGpu);
+    return config;
+}
+
 class MockIHintManager : public IHintManager {
 public:
     MOCK_METHOD(ScopedAStatus, createHintSessionWithConfig,
                 (const SpAIBinder& token, hal::SessionTag tag,
                  const SessionCreationConfig& creationConfig, hal::SessionConfig* config,
-                 std::shared_ptr<IHintSession>* _aidl_return),
+                 IHintManager::SessionCreationReturn* _aidl_return),
                 (override));
     MOCK_METHOD(ScopedAStatus, setHintSessionThreads,
                 (const std::shared_ptr<IHintSession>& hintSession,
@@ -115,8 +190,9 @@
         APerformanceHint_getRateLimiterPropertiesForTesting(&mMaxLoadHintsPerInterval,
                                                             &mLoadHintInterval);
         APerformanceHint_setIHintManagerForTesting(&mMockIHintManager);
-        APerformanceHint_setUseGraphicsPipelineForTesting(true);
         APerformanceHint_setUseNewLoadHintBehaviorForTesting(true);
+        mTids.push_back(1);
+        mTids.push_back(2);
     }
 
     void TearDown() override {
@@ -130,20 +206,22 @@
         ON_CALL(*mMockIHintManager, registerClient(_, _))
                 .WillByDefault(
                         DoAll(SetArgPointee<1>(mClientData), [] { return ScopedAStatus::ok(); }));
+        ON_CALL(*mMockIHintManager, isRemote()).WillByDefault(Return(true));
         return APerformanceHint_getManager();
     }
 
-    APerformanceHintSession* createSession(APerformanceHintManager* manager,
-                                           int64_t targetDuration = 56789L, bool isHwui = false) {
+    void prepareSessionMock() {
         mMockSession = ndk::SharedRefBase::make<NiceMock<MockIHintSession>>();
         const int64_t sessionId = 123;
-        std::vector<int32_t> tids;
-        tids.push_back(1);
-        tids.push_back(2);
+
+        mSessionCreationReturn = IHintManager::SessionCreationReturn{
+                .session = mMockSession,
+                .pipelineThreadLimitExceeded = false,
+        };
 
         ON_CALL(*mMockIHintManager, createHintSessionWithConfig(_, _, _, _, _))
                 .WillByDefault(DoAll(SetArgPointee<3>(hal::SessionConfig({.id = sessionId})),
-                                     SetArgPointee<4>(std::shared_ptr<IHintSession>(mMockSession)),
+                                     SetArgPointee<4>(mSessionCreationReturn),
                                      [] { return ScopedAStatus::ok(); }));
 
         ON_CALL(*mMockIHintManager, setHintSessionThreads(_, _)).WillByDefault([] {
@@ -161,48 +239,36 @@
         ON_CALL(*mMockSession, reportActualWorkDuration2(_)).WillByDefault([] {
             return ScopedAStatus::ok();
         });
-        if (isHwui) {
-            return APerformanceHint_createSessionInternal(manager, tids.data(), tids.size(),
-                                                          targetDuration, SessionTag::HWUI);
-        }
-        return APerformanceHint_createSession(manager, tids.data(), tids.size(), targetDuration);
     }
 
-    APerformanceHintSession* createSessionUsingConfig(APerformanceHintManager* manager,
-                                                      SessionCreationConfig config,
-                                                      bool isHwui = false) {
-        mMockSession = ndk::SharedRefBase::make<NiceMock<MockIHintSession>>();
-        const int64_t sessionId = 123;
-
-        ON_CALL(*mMockIHintManager, createHintSessionWithConfig(_, _, _, _, _))
-                .WillByDefault(DoAll(SetArgPointee<3>(hal::SessionConfig({.id = sessionId})),
-                                     SetArgPointee<4>(std::shared_ptr<IHintSession>(mMockSession)),
-                                     [] { return ScopedAStatus::ok(); }));
-
-        ON_CALL(*mMockIHintManager, setHintSessionThreads(_, _)).WillByDefault([] {
-            return ScopedAStatus::ok();
-        });
-        ON_CALL(*mMockSession, sendHint(_)).WillByDefault([] { return ScopedAStatus::ok(); });
-        ON_CALL(*mMockSession, setMode(_, true)).WillByDefault([] { return ScopedAStatus::ok(); });
-        ON_CALL(*mMockSession, close()).WillByDefault([] { return ScopedAStatus::ok(); });
-        ON_CALL(*mMockSession, updateTargetWorkDuration(_)).WillByDefault([] {
-            return ScopedAStatus::ok();
-        });
-        ON_CALL(*mMockSession, reportActualWorkDuration(_, _)).WillByDefault([] {
-            return ScopedAStatus::ok();
-        });
-        ON_CALL(*mMockSession, reportActualWorkDuration2(_)).WillByDefault([] {
-            return ScopedAStatus::ok();
-        });
-
+    std::shared_ptr<APerformanceHintSession> createSession(APerformanceHintManager* manager,
+                                                           int64_t targetDuration = 56789L,
+                                                           bool isHwui = false) {
+        prepareSessionMock();
         if (isHwui) {
-            return APerformanceHint_createSessionUsingConfigInternal(
-                    manager, reinterpret_cast<ASessionCreationConfig*>(&config), SessionTag::HWUI);
+            return wrapSession(APerformanceHint_createSessionInternal(manager, mTids.data(),
+                                                                      mTids.size(), targetDuration,
+                                                                      SessionTag::HWUI));
+        }
+        return wrapSession(APerformanceHint_createSession(manager, mTids.data(), mTids.size(),
+                                                          targetDuration));
+    }
+
+    std::shared_ptr<APerformanceHintSession> createSessionUsingConfig(
+            APerformanceHintManager* manager, std::shared_ptr<ASessionCreationConfig>& config,
+            bool isHwui = false) {
+        prepareSessionMock();
+        APerformanceHintSession* session;
+        int out = 0;
+        if (isHwui) {
+            out = APerformanceHint_createSessionUsingConfigInternal(manager, config.get(), &session,
+                                                                    SessionTag::HWUI);
         }
 
-        return APerformanceHint_createSessionUsingConfig(manager,
-                                                         reinterpret_cast<ASessionCreationConfig*>(
-                                                                 &config));
+        out = APerformanceHint_createSessionUsingConfig(manager, config.get(), &session);
+        EXPECT_EQ(out, 0);
+
+        return wrapSession(session);
     }
 
     void setFMQEnabled(bool enabled) {
@@ -233,11 +299,13 @@
     uint32_t mWriteBits = 0x00000002;
     std::shared_ptr<NiceMock<MockIHintManager>> mMockIHintManager = nullptr;
     std::shared_ptr<NiceMock<MockIHintSession>> mMockSession = nullptr;
+    IHintManager::SessionCreationReturn mSessionCreationReturn;
     std::shared_ptr<AidlMessageQueue<hal::ChannelMessage, SynchronizedReadWrite>> mMockFMQ;
     std::shared_ptr<AidlMessageQueue<int8_t, SynchronizedReadWrite>> mMockFlagQueue;
     hardware::EventFlag* mEventFlag;
     int kMockQueueSize = 20;
     bool mUsingFMQ = false;
+    std::vector<int> mTids;
 
     IHintManager::HintManagerClientData mClientData{
             .powerHalVersion = 6,
@@ -273,107 +341,109 @@
 
 TEST_F(PerformanceHintTest, TestSession) {
     APerformanceHintManager* manager = createManager();
-    APerformanceHintSession* session = createSession(manager);
+    auto&& session = createSession(manager);
     ASSERT_TRUE(session);
 
     int64_t targetDurationNanos = 10;
     EXPECT_CALL(*mMockSession, updateTargetWorkDuration(Eq(targetDurationNanos))).Times(Exactly(1));
-    int result = APerformanceHint_updateTargetWorkDuration(session, targetDurationNanos);
+    int result = APerformanceHint_updateTargetWorkDuration(session.get(), targetDurationNanos);
     EXPECT_EQ(0, result);
 
     // subsequent call with same target should be ignored but return no error
-    result = APerformanceHint_updateTargetWorkDuration(session, targetDurationNanos);
+    result = APerformanceHint_updateTargetWorkDuration(session.get(), targetDurationNanos);
     EXPECT_EQ(0, result);
 
+    Mock::VerifyAndClearExpectations(mMockSession.get());
+
     usleep(2); // Sleep for longer than preferredUpdateRateNanos.
     int64_t actualDurationNanos = 20;
     std::vector<int64_t> actualDurations;
     actualDurations.push_back(20);
     EXPECT_CALL(*mMockSession, reportActualWorkDuration2(_)).Times(Exactly(1));
-    result = APerformanceHint_reportActualWorkDuration(session, actualDurationNanos);
+    EXPECT_CALL(*mMockSession, updateTargetWorkDuration(_)).Times(Exactly(1));
+    result = APerformanceHint_reportActualWorkDuration(session.get(), actualDurationNanos);
     EXPECT_EQ(0, result);
-    result = APerformanceHint_updateTargetWorkDuration(session, -1L);
+    result = APerformanceHint_reportActualWorkDuration(session.get(), -1L);
     EXPECT_EQ(EINVAL, result);
-    result = APerformanceHint_reportActualWorkDuration(session, -1L);
+    result = APerformanceHint_updateTargetWorkDuration(session.get(), 0);
+    EXPECT_EQ(0, result);
+    result = APerformanceHint_updateTargetWorkDuration(session.get(), -2);
+    EXPECT_EQ(EINVAL, result);
+    result = APerformanceHint_reportActualWorkDuration(session.get(), 12L);
     EXPECT_EQ(EINVAL, result);
 
     SessionHint hintId = SessionHint::CPU_LOAD_RESET;
     EXPECT_CALL(*mMockSession, sendHint(Eq(hintId))).Times(Exactly(1));
-    result = APerformanceHint_sendHint(session, hintId);
+    result = APerformanceHint_sendHint(session.get(), hintId);
     EXPECT_EQ(0, result);
     EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::CPU_LOAD_UP))).Times(Exactly(1));
-    result = APerformanceHint_notifyWorkloadIncrease(session, true, false, "Test hint");
+    result = APerformanceHint_notifyWorkloadIncrease(session.get(), true, false, "Test hint");
     EXPECT_EQ(0, result);
     EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::CPU_LOAD_RESET))).Times(Exactly(1));
     EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::GPU_LOAD_RESET))).Times(Exactly(1));
-    result = APerformanceHint_notifyWorkloadReset(session, true, true, "Test hint");
+    result = APerformanceHint_notifyWorkloadReset(session.get(), true, true, "Test hint");
     EXPECT_EQ(0, result);
     EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::CPU_LOAD_SPIKE))).Times(Exactly(1));
     EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::GPU_LOAD_SPIKE))).Times(Exactly(1));
-    result = APerformanceHint_notifyWorkloadSpike(session, true, true, "Test hint");
+    result = APerformanceHint_notifyWorkloadSpike(session.get(), true, true, "Test hint");
     EXPECT_EQ(0, result);
 
-    result = APerformanceHint_sendHint(session, static_cast<SessionHint>(-1));
-    EXPECT_EQ(EINVAL, result);
+    EXPECT_DEATH(
+            { APerformanceHint_sendHint(session.get(), static_cast<SessionHint>(-1)); },
+            "invalid session hint");
 
     Mock::VerifyAndClearExpectations(mMockSession.get());
     for (int i = 0; i < mMaxLoadHintsPerInterval; ++i) {
-        APerformanceHint_sendHint(session, hintId);
+        APerformanceHint_sendHint(session.get(), hintId);
     }
 
     // Expect to get rate limited if we try to send faster than the limiter allows
     EXPECT_CALL(*mMockSession, sendHint(_)).Times(Exactly(0));
-    result = APerformanceHint_notifyWorkloadIncrease(session, true, true, "Test hint");
+    result = APerformanceHint_notifyWorkloadIncrease(session.get(), true, true, "Test hint");
     EXPECT_EQ(result, EBUSY);
     EXPECT_CALL(*mMockSession, sendHint(_)).Times(Exactly(0));
-    result = APerformanceHint_notifyWorkloadReset(session, true, true, "Test hint");
+    result = APerformanceHint_notifyWorkloadReset(session.get(), true, true, "Test hint");
     EXPECT_CALL(*mMockSession, close()).Times(Exactly(1));
-    APerformanceHint_closeSession(session);
 }
 
 TEST_F(PerformanceHintTest, TestUpdatedSessionCreation) {
     EXPECT_CALL(*mMockIHintManager, createHintSessionWithConfig(_, _, _, _, _)).Times(1);
     APerformanceHintManager* manager = createManager();
-    APerformanceHintSession* session = createSession(manager);
+    auto&& session = createSession(manager);
     ASSERT_TRUE(session);
-    APerformanceHint_closeSession(session);
 }
 
 TEST_F(PerformanceHintTest, TestSessionCreationUsingConfig) {
     EXPECT_CALL(*mMockIHintManager, createHintSessionWithConfig(_, _, _, _, _)).Times(1);
-    SessionCreationConfig config{.tids = std::vector<int32_t>(1, 2),
-                                 .targetWorkDurationNanos = 5678,
-                                 .modesToEnable = std::vector<hal::SessionMode>(0)};
+    auto&& config = configFromCreator({.tids = mTids});
     APerformanceHintManager* manager = createManager();
-    APerformanceHintSession* session = createSessionUsingConfig(manager, config);
+    auto&& session = createSessionUsingConfig(manager, config);
     ASSERT_TRUE(session);
-    APerformanceHint_closeSession(session);
 }
 
 TEST_F(PerformanceHintTest, TestHwuiSessionCreation) {
     EXPECT_CALL(*mMockIHintManager, createHintSessionWithConfig(_, hal::SessionTag::HWUI, _, _, _))
             .Times(1);
     APerformanceHintManager* manager = createManager();
-    APerformanceHintSession* session = createSession(manager, 56789L, true);
+    auto&& session = createSession(manager, 56789L, true);
     ASSERT_TRUE(session);
-    APerformanceHint_closeSession(session);
 }
 
 TEST_F(PerformanceHintTest, SetThreads) {
     APerformanceHintManager* manager = createManager();
 
-    APerformanceHintSession* session = createSession(manager);
+    auto&& session = createSession(manager);
     ASSERT_TRUE(session);
 
     int32_t emptyTids[2];
-    int result = APerformanceHint_setThreads(session, emptyTids, 0);
+    int result = APerformanceHint_setThreads(session.get(), emptyTids, 0);
     EXPECT_EQ(EINVAL, result);
 
     std::vector<int32_t> newTids;
     newTids.push_back(1);
     newTids.push_back(3);
     EXPECT_CALL(*mMockIHintManager, setHintSessionThreads(_, Eq(newTids))).Times(Exactly(1));
-    result = APerformanceHint_setThreads(session, newTids.data(), newTids.size());
+    result = APerformanceHint_setThreads(session.get(), newTids.data(), newTids.size());
     EXPECT_EQ(0, result);
 
     testing::Mock::VerifyAndClearExpectations(mMockIHintManager.get());
@@ -383,27 +453,27 @@
     EXPECT_CALL(*mMockIHintManager, setHintSessionThreads(_, Eq(invalidTids)))
             .Times(Exactly(1))
             .WillOnce(Return(ByMove(ScopedAStatus::fromExceptionCode(EX_SECURITY))));
-    result = APerformanceHint_setThreads(session, invalidTids.data(), invalidTids.size());
+    result = APerformanceHint_setThreads(session.get(), invalidTids.data(), invalidTids.size());
     EXPECT_EQ(EPERM, result);
 }
 
 TEST_F(PerformanceHintTest, SetPowerEfficient) {
     APerformanceHintManager* manager = createManager();
-    APerformanceHintSession* session = createSession(manager);
+    auto&& session = createSession(manager);
     ASSERT_TRUE(session);
 
     EXPECT_CALL(*mMockSession, setMode(_, Eq(true))).Times(Exactly(1));
-    int result = APerformanceHint_setPreferPowerEfficiency(session, true);
+    int result = APerformanceHint_setPreferPowerEfficiency(session.get(), true);
     EXPECT_EQ(0, result);
 
     EXPECT_CALL(*mMockSession, setMode(_, Eq(false))).Times(Exactly(1));
-    result = APerformanceHint_setPreferPowerEfficiency(session, false);
+    result = APerformanceHint_setPreferPowerEfficiency(session.get(), false);
     EXPECT_EQ(0, result);
 }
 
 TEST_F(PerformanceHintTest, CreateZeroTargetDurationSession) {
     APerformanceHintManager* manager = createManager();
-    APerformanceHintSession* session = createSession(manager, 0);
+    auto&& session = createSession(manager, 0);
     ASSERT_TRUE(session);
 }
 
@@ -428,12 +498,12 @@
 
 TEST_F(PerformanceHintTest, TestAPerformanceHint_reportActualWorkDuration2) {
     APerformanceHintManager* manager = createManager();
-    APerformanceHintSession* session = createSession(manager);
+    auto&& session = createSession(manager);
     ASSERT_TRUE(session);
 
     int64_t targetDurationNanos = 10;
     EXPECT_CALL(*mMockSession, updateTargetWorkDuration(Eq(targetDurationNanos))).Times(Exactly(1));
-    int result = APerformanceHint_updateTargetWorkDuration(session, targetDurationNanos);
+    int result = APerformanceHint_updateTargetWorkDuration(session.get(), targetDurationNanos);
     EXPECT_EQ(0, result);
 
     usleep(2); // Sleep for longer than preferredUpdateRateNanos.
@@ -452,54 +522,53 @@
 
         EXPECT_CALL(*mMockSession, reportActualWorkDuration2(WorkDurationEq(actualWorkDurations)))
                 .Times(Exactly(pair.expectedResult == OK));
-        result = APerformanceHint_reportActualWorkDuration2(session,
+        result = APerformanceHint_reportActualWorkDuration2(session.get(),
                                                             reinterpret_cast<AWorkDuration*>(
                                                                     &pair.duration));
         EXPECT_EQ(pair.expectedResult, result);
     }
 
     EXPECT_CALL(*mMockSession, close()).Times(Exactly(1));
-    APerformanceHint_closeSession(session);
 }
 
 TEST_F(PerformanceHintTest, TestAWorkDuration) {
-    AWorkDuration* aWorkDuration = AWorkDuration_create();
+    // AWorkDuration* aWorkDuration = AWorkDuration_create();
+    auto&& aWorkDuration = wrapWorkDuration(AWorkDuration_create());
     ASSERT_NE(aWorkDuration, nullptr);
 
-    AWorkDuration_setWorkPeriodStartTimestampNanos(aWorkDuration, 1);
-    AWorkDuration_setActualTotalDurationNanos(aWorkDuration, 20);
-    AWorkDuration_setActualCpuDurationNanos(aWorkDuration, 13);
-    AWorkDuration_setActualGpuDurationNanos(aWorkDuration, 8);
-    AWorkDuration_release(aWorkDuration);
+    AWorkDuration_setWorkPeriodStartTimestampNanos(aWorkDuration.get(), 1);
+    AWorkDuration_setActualTotalDurationNanos(aWorkDuration.get(), 20);
+    AWorkDuration_setActualCpuDurationNanos(aWorkDuration.get(), 13);
+    AWorkDuration_setActualGpuDurationNanos(aWorkDuration.get(), 8);
 }
 
 TEST_F(PerformanceHintTest, TestCreateUsingFMQ) {
     setFMQEnabled(true);
     APerformanceHintManager* manager = createManager();
-    APerformanceHintSession* session = createSession(manager);
+    auto&& session = createSession(manager);
     ASSERT_TRUE(session);
 }
 
 TEST_F(PerformanceHintTest, TestUpdateTargetWorkDurationUsingFMQ) {
     setFMQEnabled(true);
     APerformanceHintManager* manager = createManager();
-    APerformanceHintSession* session = createSession(manager);
-    APerformanceHint_updateTargetWorkDuration(session, 456);
+    auto&& session = createSession(manager);
+    APerformanceHint_updateTargetWorkDuration(session.get(), 456);
     expectToReadFromFmq<HalChannelMessageContents::Tag::targetDuration>(456);
 }
 
 TEST_F(PerformanceHintTest, TestSendHintUsingFMQ) {
     setFMQEnabled(true);
     APerformanceHintManager* manager = createManager();
-    APerformanceHintSession* session = createSession(manager);
-    APerformanceHint_sendHint(session, SessionHint::CPU_LOAD_UP);
+    auto&& session = createSession(manager);
+    APerformanceHint_sendHint(session.get(), SessionHint::CPU_LOAD_UP);
     expectToReadFromFmq<HalChannelMessageContents::Tag::hint>(hal::SessionHint::CPU_LOAD_UP);
 }
 
 TEST_F(PerformanceHintTest, TestReportActualUsingFMQ) {
     setFMQEnabled(true);
     APerformanceHintManager* manager = createManager();
-    APerformanceHintSession* session = createSession(manager);
+    auto&& session = createSession(manager);
     hal::WorkDuration duration{.timeStampNanos = 3,
                                .durationNanos = 999999,
                                .workPeriodStartTimestampNanos = 1,
@@ -513,20 +582,91 @@
             .gpuDurationNanos = duration.gpuDurationNanos,
     };
 
-    APerformanceHint_reportActualWorkDuration2(session,
+    APerformanceHint_reportActualWorkDuration2(session.get(),
                                                reinterpret_cast<AWorkDuration*>(&duration));
     expectToReadFromFmq<HalChannelMessageContents::Tag::workDuration>(durationExpected);
 }
 
 TEST_F(PerformanceHintTest, TestASessionCreationConfig) {
-    ASessionCreationConfig* config = ASessionCreationConfig_create();
-    ASSERT_NE(config, nullptr);
+    auto&& config = configFromCreator({
+            .tids = mTids,
+            .targetDuration = 20,
+            .powerEfficient = true,
+            .graphicsPipeline = true,
+    });
 
-    const int32_t testTids[2] = {1, 2};
-    const size_t size = 2;
-    EXPECT_EQ(ASessionCreationConfig_setTids(config, testTids, size), 0);
-    EXPECT_EQ(ASessionCreationConfig_setTargetWorkDurationNanos(config, 20), 0);
-    EXPECT_EQ(ASessionCreationConfig_setPreferPowerEfficiency(config, true), 0);
-    EXPECT_EQ(ASessionCreationConfig_setGraphicsPipeline(config, true), 0);
-    ASessionCreationConfig_release(config);
+    APerformanceHintManager* manager = createManager();
+    auto&& session = createSessionUsingConfig(manager, config);
+
+    ASSERT_NE(session, nullptr);
+    ASSERT_NE(config, nullptr);
+}
+
+TEST_F(PerformanceHintTest, TestSupportObject) {
+    // Disable GPU and Power Efficiency support to test partial enabling
+    mClientData.supportInfo.sessionModes &= ~(1 << (int)hal::SessionMode::AUTO_GPU);
+    mClientData.supportInfo.sessionHints &= ~(1 << (int)hal::SessionHint::GPU_LOAD_UP);
+    mClientData.supportInfo.sessionHints &= ~(1 << (int)hal::SessionHint::POWER_EFFICIENCY);
+
+    APerformanceHintManager* manager = createManager();
+
+    union {
+        int expectedSupportInt;
+        SupportHelper expectedSupport;
+    };
+
+    union {
+        int actualSupportInt;
+        SupportHelper actualSupport;
+    };
+
+    expectedSupport = getFullySupportedSupportHelper();
+    actualSupport = getSupportHelper();
+
+    expectedSupport.autoGpu = false;
+
+    EXPECT_EQ(expectedSupportInt, actualSupportInt);
+}
+
+TEST_F(PerformanceHintTest, TestCreatingAutoSession) {
+    // Disable GPU capability for testing
+    mClientData.supportInfo.sessionModes &= ~(1 << (int)hal::SessionMode::AUTO_GPU);
+    APerformanceHintManager* manager = createManager();
+
+    auto&& invalidConfig = configFromCreator({
+            .tids = mTids,
+            .targetDuration = 20,
+            .graphicsPipeline = false,
+            .autoCpu = true,
+            .autoGpu = true,
+    });
+
+    EXPECT_DEATH({ createSessionUsingConfig(manager, invalidConfig); }, "");
+
+    auto&& unsupportedConfig = configFromCreator({
+            .tids = mTids,
+            .targetDuration = 20,
+            .graphicsPipeline = true,
+            .autoCpu = true,
+            .autoGpu = true,
+    });
+
+    APerformanceHintSession* unsupportedSession = nullptr;
+
+    // Creating a session with auto timing but no graphics pipeline should fail
+    int out = APerformanceHint_createSessionUsingConfig(manager, unsupportedConfig.get(),
+                                                        &unsupportedSession);
+    EXPECT_EQ(out, ENOTSUP);
+    EXPECT_EQ(wrapSession(unsupportedSession), nullptr);
+
+    auto&& validConfig = configFromCreator({
+            .tids = mTids,
+            .targetDuration = 20,
+            .graphicsPipeline = true,
+            .autoCpu = true,
+            .autoGpu = false,
+    });
+
+    auto&& validSession = createSessionUsingConfig(manager, validConfig);
+    EXPECT_NE(validSession, nullptr);
 }