Refactor initial support check to use SupportInfo

This change makes the intial support check when
APerformanceHintManager starts pass the SupportInfo
object instead of just relying on the preferred rate

Bug: 367803904
Test: atest PerformanceHintNativeTestCases
Test: atest HintManagerServiceTest
Flag: EXEMPT refactor

Change-Id: I0a803706d4ef8d9cb05d9cdb066af5a9721a951e
diff --git a/core/java/android/os/IHintManager.aidl b/core/java/android/os/IHintManager.aidl
index 4a14a8d..56a089a 100644
--- a/core/java/android/os/IHintManager.aidl
+++ b/core/java/android/os/IHintManager.aidl
@@ -21,11 +21,13 @@
 import android.os.GpuHeadroomParamsInternal;
 import android.os.IHintSession;
 import android.os.SessionCreationConfig;
-import android.hardware.power.CpuHeadroomResult;
+
 import android.hardware.power.ChannelConfig;
+import android.hardware.power.CpuHeadroomResult;
 import android.hardware.power.GpuHeadroomResult;
 import android.hardware.power.SessionConfig;
 import android.hardware.power.SessionTag;
+import android.hardware.power.SupportInfo;
 
 /** {@hide} */
 interface IHintManager {
@@ -40,11 +42,6 @@
     IHintSession createHintSessionWithConfig(in IBinder token, in SessionTag tag,
             in SessionCreationConfig creationConfig, out SessionConfig config);
 
-    /**
-     * Get preferred rate limit in nanoseconds.
-     */
-    long getHintSessionPreferredRate();
-
     void setHintSessionThreads(in IHintSession hintSession, in int[] tids);
     int[] getHintSessionThreadIds(in IHintSession hintSession);
 
@@ -61,13 +58,28 @@
     long getGpuHeadroomMinIntervalMillis();
 
     /**
-     * Get Maximum number of graphics pipeline threads allowed per-app.
-     */
-    int getMaxGraphicsPipelineThreadsCount();
-
-    /**
      * Used by the JNI to pass an interface to the SessionManager;
      * for internal use only.
      */
     oneway void passSessionManagerBinder(in IBinder sessionManager);
+
+    parcelable HintManagerClientData {
+        int powerHalVersion;
+        int maxGraphicsPipelineThreads;
+        long preferredRateNanos;
+        SupportInfo supportInfo;
+    }
+
+    interface IHintManagerClient {
+        /**
+        * Returns FMQ channel information for the caller, which it associates to the callback binder lifespan.
+        */
+        oneway void receiveChannelConfig(in ChannelConfig config);
+    }
+
+    /**
+     * Set up an ADPF client, receiving a remote client binder interface and
+     * passing back a bundle of support and configuration information.
+     */
+    HintManagerClientData registerClient(in IHintManagerClient client);
 }
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 608c01c..a25fa62 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -22,6 +22,7 @@
 #include <aidl/android/hardware/power/SessionHint.h>
 #include <aidl/android/hardware/power/SessionMode.h>
 #include <aidl/android/hardware/power/SessionTag.h>
+#include <aidl/android/hardware/power/SupportInfo.h>
 #include <aidl/android/hardware/power/WorkDuration.h>
 #include <aidl/android/hardware/power/WorkDurationFixedV1.h>
 #include <aidl/android/os/IHintManager.h>
@@ -148,10 +149,36 @@
     std::future<bool> mChannelCreationFinished;
 };
 
+class SupportInfoWrapper {
+public:
+    SupportInfoWrapper(hal::SupportInfo& info);
+    bool isSessionModeSupported(hal::SessionMode mode);
+    bool isSessionHintSupported(hal::SessionHint hint);
+
+private:
+    template <class T>
+    bool getEnumSupportFromBitfield(T& enumValue, int64_t& supportBitfield) {
+        // extract the bit corresponding to the enum by shifting the bitfield
+        // over that much and cutting off any extra values
+        return (supportBitfield >> static_cast<int>(enumValue)) % 2;
+    }
+    hal::SupportInfo mSupportInfo;
+};
+
+class HintManagerClient : public IHintManager::BnHintManagerClient {
+public:
+    // Currently a no-op that exists for FMQ init to call in the future
+    ndk::ScopedAStatus receiveChannelConfig(const hal::ChannelConfig&) {
+        return ndk::ScopedAStatus::ok();
+    }
+};
+
 struct APerformanceHintManager {
 public:
     static APerformanceHintManager* getInstance();
-    APerformanceHintManager(std::shared_ptr<IHintManager>& service, int64_t preferredRateNanos);
+    APerformanceHintManager(std::shared_ptr<IHintManager>& service,
+                            IHintManager::HintManagerClientData&& clientData,
+                            std::shared_ptr<HintManagerClient> callbackClient);
     APerformanceHintManager() = delete;
     ~APerformanceHintManager();
 
@@ -169,29 +196,21 @@
     FMQWrapper& getFMQWrapper();
     bool canSendLoadHints(std::vector<hal::SessionHint>& hints, int64_t now) REQUIRES(sHintMutex);
     void initJava(JNIEnv* _Nonnull env);
-    ndk::ScopedAIBinder_Weak x;
     template <class T>
     static void layersFromNativeSurfaces(ANativeWindow** windows, int numWindows,
                                          ASurfaceControl** controls, int numSurfaceControls,
                                          std::vector<T>& out);
+    ndk::SpAIBinder& getToken();
+    SupportInfoWrapper& getSupportInfo();
 
 private:
-    // Necessary to create an empty binder object
-    static void* tokenStubOnCreate(void*) {
-        return nullptr;
-    }
-    static void tokenStubOnDestroy(void*) {}
-    static binder_status_t tokenStubOnTransact(AIBinder*, transaction_code_t, const AParcel*,
-                                               AParcel*) {
-        return STATUS_OK;
-    }
-
     static APerformanceHintManager* create(std::shared_ptr<IHintManager> iHintManager);
 
     std::shared_ptr<IHintManager> mHintManager;
+    std::shared_ptr<HintManagerClient> mCallbackClient;
+    IHintManager::HintManagerClientData mClientData;
+    SupportInfoWrapper mSupportInfoWrapper;
     ndk::SpAIBinder mToken;
-    const int64_t mPreferredRateNanos;
-    std::optional<int32_t> mMaxGraphicsPipelineThreadsCount;
     FMQWrapper mFMQWrapper;
     double mHintBudget = kMaxLoadHintsPerInterval;
     int64_t mLastBudgetReplenish = 0;
@@ -272,14 +291,27 @@
     return APerformanceHintManager::getInstance()->getFMQWrapper();
 }
 
+// ===================================== SupportInfoWrapper implementation
+
+SupportInfoWrapper::SupportInfoWrapper(hal::SupportInfo& info) : mSupportInfo(info) {}
+
+bool SupportInfoWrapper::isSessionHintSupported(hal::SessionHint hint) {
+    return getEnumSupportFromBitfield(hint, mSupportInfo.sessionHints);
+}
+
+bool SupportInfoWrapper::isSessionModeSupported(hal::SessionMode mode) {
+    return getEnumSupportFromBitfield(mode, mSupportInfo.sessionModes);
+}
+
 // ===================================== APerformanceHintManager implementation
 APerformanceHintManager::APerformanceHintManager(std::shared_ptr<IHintManager>& manager,
-                                                 int64_t preferredRateNanos)
-      : mHintManager(std::move(manager)), mPreferredRateNanos(preferredRateNanos) {
-    static AIBinder_Class* tokenBinderClass =
-            AIBinder_Class_define("phm_token", tokenStubOnCreate, tokenStubOnDestroy,
-                                  tokenStubOnTransact);
-    mToken = ndk::SpAIBinder(AIBinder_new(tokenBinderClass, nullptr));
+                                                 IHintManager::HintManagerClientData&& clientData,
+                                                 std::shared_ptr<HintManagerClient> callbackClient)
+      : mHintManager(std::move(manager)),
+        mCallbackClient(callbackClient),
+        mClientData(clientData),
+        mSupportInfoWrapper(clientData.supportInfo),
+        mToken(callbackClient->asBinder()) {
     if (mFMQWrapper.isSupported()) {
         mFMQWrapper.setToken(mToken);
         mFMQWrapper.startChannel(mHintManager.get());
@@ -314,16 +346,17 @@
         ALOGE("%s: PerformanceHint service is not ready ", __FUNCTION__);
         return nullptr;
     }
-    int64_t preferredRateNanos = -1L;
-    ndk::ScopedAStatus ret = manager->getHintSessionPreferredRate(&preferredRateNanos);
+    std::shared_ptr<HintManagerClient> client = ndk::SharedRefBase::make<HintManagerClient>();
+    IHintManager::HintManagerClientData clientData;
+    ndk::ScopedAStatus ret = manager->registerClient(client, &clientData);
     if (!ret.isOk()) {
-        ALOGE("%s: PerformanceHint cannot get preferred rate. %s", __FUNCTION__, ret.getMessage());
+        ALOGE("%s: PerformanceHint is not supported. %s", __FUNCTION__, ret.getMessage());
         return nullptr;
     }
-    if (preferredRateNanos <= 0) {
-        preferredRateNanos = -1L;
+    if (clientData.preferredRateNanos <= 0) {
+        clientData.preferredRateNanos = -1L;
     }
-    return new APerformanceHintManager(manager, preferredRateNanos);
+    return new APerformanceHintManager(manager, std::move(clientData), client);
 }
 
 bool APerformanceHintManager::canSendLoadHints(std::vector<hal::SessionHint>& hints, int64_t now) {
@@ -388,7 +421,9 @@
         ALOGE("%s: PerformanceHint cannot create session. %s", __FUNCTION__, ret.getMessage());
         return nullptr;
     }
-    auto out = new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos,
+
+    auto out = new APerformanceHintSession(mHintManager, std::move(session),
+                                           mClientData.preferredRateNanos,
                                            sessionCreationConfig->targetWorkDurationNanos, isJava,
                                            sessionConfig.id == -1
                                                    ? std::nullopt
@@ -415,24 +450,11 @@
 }
 
 int64_t APerformanceHintManager::getPreferredRateNanos() const {
-    return mPreferredRateNanos;
+    return mClientData.preferredRateNanos;
 }
 
 int32_t APerformanceHintManager::getMaxGraphicsPipelineThreadsCount() {
-    if (!mMaxGraphicsPipelineThreadsCount.has_value()) {
-        int32_t threadsCount = -1;
-        ndk::ScopedAStatus ret = mHintManager->getMaxGraphicsPipelineThreadsCount(&threadsCount);
-        if (!ret.isOk()) {
-            ALOGE("%s: PerformanceHint cannot get max graphics pipeline threads count. %s",
-                  __FUNCTION__, ret.getMessage());
-            return -1;
-        }
-        if (threadsCount <= 0) {
-            threadsCount = -1;
-        }
-        mMaxGraphicsPipelineThreadsCount.emplace(threadsCount);
-    }
-    return mMaxGraphicsPipelineThreadsCount.value();
+    return mClientData.maxGraphicsPipelineThreads;
 }
 
 FMQWrapper& APerformanceHintManager::getFMQWrapper() {
@@ -449,6 +471,14 @@
     mJavaInitialized = true;
 }
 
+ndk::SpAIBinder& APerformanceHintManager::getToken() {
+    return mToken;
+}
+
+SupportInfoWrapper& APerformanceHintManager::getSupportInfo() {
+    return mSupportInfoWrapper;
+}
+
 // ===================================== APerformanceHintSession implementation
 
 constexpr int kNumEnums = enum_size<hal::SessionHint>();
diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
index b8f574f..47d7b87 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -56,9 +56,6 @@
                  const SessionCreationConfig& creationConfig, hal::SessionConfig* config,
                  std::shared_ptr<IHintSession>* _aidl_return),
                 (override));
-    MOCK_METHOD(ScopedAStatus, getHintSessionPreferredRate, (int64_t * _aidl_return), (override));
-    MOCK_METHOD(ScopedAStatus, getMaxGraphicsPipelineThreadsCount, (int32_t* _aidl_return),
-                (override));
     MOCK_METHOD(ScopedAStatus, setHintSessionThreads,
                 (const std::shared_ptr<IHintSession>& hintSession,
                  const ::std::vector<int32_t>& tids),
@@ -84,6 +81,11 @@
     MOCK_METHOD(ScopedAStatus, getGpuHeadroomMinIntervalMillis, (int64_t* _aidl_return),
                 (override));
     MOCK_METHOD(ScopedAStatus, passSessionManagerBinder, (const SpAIBinder& sessionManager));
+    MOCK_METHOD(ScopedAStatus, registerClient,
+                (const std::shared_ptr<::aidl::android::os::IHintManager::IHintManagerClient>&
+                         clientDataIn,
+                 ::aidl::android::os::IHintManager::HintManagerClientData* _aidl_return),
+                (override));
     MOCK_METHOD(SpAIBinder, asBinder, (), (override));
     MOCK_METHOD(bool, isRemote, (), (override));
 };
@@ -125,10 +127,9 @@
 
     APerformanceHintManager* createManager() {
         APerformanceHint_setUseFMQForTesting(mUsingFMQ);
-        ON_CALL(*mMockIHintManager, getHintSessionPreferredRate(_))
-                .WillByDefault(DoAll(SetArgPointee<0>(123L), [] { return ScopedAStatus::ok(); }));
-        ON_CALL(*mMockIHintManager, getMaxGraphicsPipelineThreadsCount(_))
-                .WillByDefault(DoAll(SetArgPointee<0>(5), [] { return ScopedAStatus::ok(); }));
+        ON_CALL(*mMockIHintManager, registerClient(_, _))
+                .WillByDefault(
+                        DoAll(SetArgPointee<1>(mClientData), [] { return ScopedAStatus::ok(); }));
         return APerformanceHint_getManager();
     }
 
@@ -238,6 +239,20 @@
     int kMockQueueSize = 20;
     bool mUsingFMQ = false;
 
+    IHintManager::HintManagerClientData mClientData{
+            .powerHalVersion = 6,
+            .maxGraphicsPipelineThreads = 5,
+            .preferredRateNanos = 123L,
+            .supportInfo{
+                    .usesSessions = true,
+                    .boosts = 0,
+                    .modes = 0,
+                    .sessionHints = -1,
+                    .sessionModes = -1,
+                    .sessionTags = -1,
+            },
+    };
+
     int32_t mMaxLoadHintsPerInterval;
     int64_t mLoadHintInterval;
 
@@ -256,12 +271,6 @@
             lhs.gpuDurationNanos == rhs.gpuDurationNanos && lhs.durationNanos == rhs.durationNanos;
 }
 
-TEST_F(PerformanceHintTest, TestGetPreferredUpdateRateNanos) {
-    APerformanceHintManager* manager = createManager();
-    int64_t preferredUpdateRateNanos = APerformanceHint_getPreferredUpdateRateNanos(manager);
-    EXPECT_EQ(123L, preferredUpdateRateNanos);
-}
-
 TEST_F(PerformanceHintTest, TestSession) {
     APerformanceHintManager* manager = createManager();
     APerformanceHintSession* session = createSession(manager);
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index 987a849..137ea06 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -296,7 +296,11 @@
         mPowerHalVersion = 0;
         mUsesFmq = false;
         if (mPowerHal != null) {
-            mSupportInfo = getSupportInfo();
+            try {
+                mSupportInfo = getSupportInfo();
+            } catch (RemoteException e) {
+                throw new IllegalStateException("Could not contact PowerHAL!", e);
+            }
         }
         mDefaultCpuHeadroomCalculationWindowMillis =
                 new CpuHeadroomParamsInternal().calculationWindowMillis;
@@ -314,7 +318,7 @@
         }
     }
 
-    SupportInfo getSupportInfo() {
+    SupportInfo getSupportInfo() throws RemoteException {
         try {
             mPowerHalVersion = mPowerHal.getInterfaceVersion();
             if (mPowerHalVersion >= 6) {
@@ -325,9 +329,40 @@
         }
 
         SupportInfo supportInfo = new SupportInfo();
+        supportInfo.usesSessions = isHintSessionSupported();
+        // Global boosts & modes aren't currently relevant for HMS clients
+        supportInfo.boosts = 0;
+        supportInfo.modes = 0;
+        supportInfo.sessionHints = 0;
+        supportInfo.sessionModes = 0;
+        supportInfo.sessionTags = 0;
+
         supportInfo.headroom = new SupportInfo.HeadroomSupportInfo();
         supportInfo.headroom.isCpuSupported = false;
         supportInfo.headroom.isGpuSupported = false;
+        if (isHintSessionSupported()) {
+            if (mPowerHalVersion == 4) {
+                // Assume we support the V4 hints & modes unless specified
+                // otherwise; this is to avoid breaking backwards compat
+                // since we historically just assumed they were.
+                supportInfo.sessionHints = 31; // first 5 bits are ones
+            }
+            if (mPowerHalVersion == 5) {
+                // Assume we support the V5 hints & modes unless specified
+                // otherwise; this is to avoid breaking backwards compat
+                // since we historically just assumed they were.
+
+                // Hal V5 has 8 modes, all of which it assumes are supported,
+                // so we represent that by having the first 8 bits set
+                supportInfo.sessionHints = 255; // first 8 bits are ones
+                // Hal V5 has 1 mode which it assumes is supported, so we
+                // represent that by having the first bit set
+                supportInfo.sessionModes = 1;
+                // Hal V5 has 5 tags, all of which it assumes are supported,
+                // so we represent that by having the first 5 bits set
+                supportInfo.sessionTags = 31;
+            }
+        }
         return supportInfo;
     }
 
@@ -1228,7 +1263,7 @@
                     @SessionTag int tag, SessionCreationConfig creationConfig,
                     SessionConfig config) {
             if (!isHintSessionSupported()) {
-                throw new UnsupportedOperationException("PowerHAL is not supported!");
+                throw new UnsupportedOperationException("PowerHintSessions are not supported!");
             }
 
             java.util.Objects.requireNonNull(token);
@@ -1424,12 +1459,6 @@
             removeChannelItem(callingTgid, callingUid);
         };
 
-        @Override
-        public long getHintSessionPreferredRate() {
-            return mHintSessionPreferredRate;
-        }
-
-        @Override
         public int getMaxGraphicsPipelineThreadsCount() {
             return MAX_GRAPHICS_PIPELINE_THREADS_COUNT;
         }
@@ -1561,6 +1590,16 @@
             mSessionManager = ISessionManager.Stub.asInterface(sessionManager);
         }
 
+        public IHintManager.HintManagerClientData
+                registerClient(@NonNull IHintManager.IHintManagerClient clientBinder) {
+            IHintManager.HintManagerClientData out = new IHintManager.HintManagerClientData();
+            out.preferredRateNanos = mHintSessionPreferredRate;
+            out.maxGraphicsPipelineThreads = getMaxGraphicsPipelineThreadsCount();
+            out.powerHalVersion = mPowerHalVersion;
+            out.supportInfo = mSupportInfo;
+            return out;
+        }
+
         @Override
         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
@@ -1568,7 +1607,7 @@
             }
             pw.println("HintSessionPreferredRate: " + mHintSessionPreferredRate);
             pw.println("MaxGraphicsPipelineThreadsCount: " + MAX_GRAPHICS_PIPELINE_THREADS_COUNT);
-            pw.println("HAL Support: " + isHintSessionSupported());
+            pw.println("Hint Session Support: " + isHintSessionSupported());
             pw.println("Active Sessions:");
             synchronized (mLock) {
                 for (int i = 0; i < mActiveSessions.size(); i++) {
diff --git a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
index 7248833..b166514 100644
--- a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -64,6 +64,7 @@
 import android.os.CpuHeadroomParamsInternal;
 import android.os.GpuHeadroomParamsInternal;
 import android.os.IBinder;
+import android.os.IHintManager;
 import android.os.IHintSession;
 import android.os.PerformanceHintManager;
 import android.os.Process;
@@ -154,6 +155,8 @@
     private ActivityManagerInternal mAmInternalMock;
     @Mock
     private PackageManager mMockPackageManager;
+    @Mock
+    private IHintManager.IHintManagerClient mClientCallback;
     @Rule
     public final CheckFlagsRule mCheckFlagsRule =
             DeviceFlagsValueProvider.createCheckFlagsRule();
@@ -171,6 +174,23 @@
         };
     }
 
+    private SupportInfo makeDefaultSupportInfo() {
+        mSupportInfo = new SupportInfo();
+        mSupportInfo.usesSessions = true;
+        // By default, mark everything as fully supported
+        mSupportInfo.sessionHints = -1;
+        mSupportInfo.sessionModes = -1;
+        mSupportInfo.modes = -1;
+        mSupportInfo.boosts = -1;
+        mSupportInfo.sessionTags = -1;
+        mSupportInfo.headroom = new SupportInfo.HeadroomSupportInfo();
+        mSupportInfo.headroom.isCpuSupported = true;
+        mSupportInfo.headroom.cpuMinIntervalMillis = 2000;
+        mSupportInfo.headroom.isGpuSupported = true;
+        mSupportInfo.headroom.gpuMinIntervalMillis = 2000;
+        return mSupportInfo;
+    }
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -181,12 +201,7 @@
         mConfig.eventFlagDescriptor = new MQDescriptor<Byte, Byte>();
         ApplicationInfo applicationInfo = new ApplicationInfo();
         applicationInfo.category = ApplicationInfo.CATEGORY_GAME;
-        mSupportInfo = new SupportInfo();
-        mSupportInfo.headroom = new SupportInfo.HeadroomSupportInfo();
-        mSupportInfo.headroom.isCpuSupported = true;
-        mSupportInfo.headroom.cpuMinIntervalMillis = 2000;
-        mSupportInfo.headroom.isGpuSupported = true;
-        mSupportInfo.headroom.gpuMinIntervalMillis = 2000;
+        mSupportInfo = makeDefaultSupportInfo();
         when(mContext.getPackageManager()).thenReturn(mMockPackageManager);
         when(mMockPackageManager.getNameForUid(anyInt())).thenReturn(TEST_APP_NAME);
         when(mMockPackageManager.getApplicationInfo(eq(TEST_APP_NAME), anyInt()))
@@ -215,6 +230,7 @@
         when(mIPowerMock.getInterfaceVersion()).thenReturn(6);
         when(mIPowerMock.getSupportInfo()).thenReturn(mSupportInfo);
         when(mIPowerMock.getSessionChannel(anyInt(), anyInt())).thenReturn(mConfig);
+        when(mIPowerMock.getSupportInfo()).thenReturn(mSupportInfo);
         LocalServices.removeServiceForTest(ActivityManagerInternal.class);
         LocalServices.addService(ActivityManagerInternal.class, mAmInternalMock);
     }
@@ -409,8 +425,11 @@
         HintManagerService service = createService();
         IBinder token = new Binder();
 
-        final int threadCount =
-                service.getBinderServiceInstance().getMaxGraphicsPipelineThreadsCount();
+        IHintManager.HintManagerClientData data = service.getBinderServiceInstance()
+                .registerClient(mClientCallback);
+
+        final int threadCount = data.maxGraphicsPipelineThreads;
+
         long sessionPtr1 = 1111L;
         long sessionId1 = 11111L;
         CountDownLatch stopLatch1 = new CountDownLatch(1);
@@ -1400,4 +1419,67 @@
         verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams1));
         verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2));
     }
+
+    @Test
+    public void testRegisteringClient() throws Exception {
+        HintManagerService service = createService();
+        IHintManager.HintManagerClientData data = service.getBinderServiceInstance()
+                .registerClient(mClientCallback);
+        assertNotNull(data);
+        assertEquals(data.supportInfo, mSupportInfo);
+    }
+
+    @Test
+    public void testRegisteringClientOnV4() throws Exception {
+        when(mIPowerMock.getInterfaceVersion()).thenReturn(4);
+        HintManagerService service = createService();
+        IHintManager.HintManagerClientData data = service.getBinderServiceInstance()
+                .registerClient(mClientCallback);
+        assertNotNull(data);
+        assertEquals(data.supportInfo.usesSessions, true);
+        assertEquals(data.supportInfo.boosts, 0);
+        assertEquals(data.supportInfo.modes, 0);
+        assertEquals(data.supportInfo.sessionHints, 31);
+        assertEquals(data.supportInfo.sessionModes, 0);
+        assertEquals(data.supportInfo.sessionTags, 0);
+        assertEquals(data.powerHalVersion, 4);
+        assertEquals(data.preferredRateNanos, DEFAULT_HINT_PREFERRED_RATE);
+    }
+
+    @Test
+    public void testRegisteringClientOnV5() throws Exception {
+        when(mIPowerMock.getInterfaceVersion()).thenReturn(5);
+        HintManagerService service = createService();
+        IHintManager.HintManagerClientData data = service.getBinderServiceInstance()
+                .registerClient(mClientCallback);
+        assertNotNull(data);
+        assertEquals(data.supportInfo.usesSessions, true);
+        assertEquals(data.supportInfo.boosts, 0);
+        assertEquals(data.supportInfo.modes, 0);
+        assertEquals(data.supportInfo.sessionHints, 255);
+        assertEquals(data.supportInfo.sessionModes, 1);
+        assertEquals(data.supportInfo.sessionTags, 31);
+        assertEquals(data.powerHalVersion, 5);
+        assertEquals(data.preferredRateNanos, DEFAULT_HINT_PREFERRED_RATE);
+    }
+
+    @Test
+    public void testSettingUpOldClientWhenUnsupported() throws Exception {
+        when(mIPowerMock.getInterfaceVersion()).thenReturn(5);
+        // Mock unsupported to modify the default support behavior
+        when(mNativeWrapperMock.halGetHintSessionPreferredRate())
+                .thenReturn(-1L);
+        HintManagerService service = createService();
+        IHintManager.HintManagerClientData data = service.getBinderServiceInstance()
+                .registerClient(mClientCallback);
+        assertNotNull(data);
+        assertEquals(data.supportInfo.usesSessions, false);
+        assertEquals(data.supportInfo.boosts, 0);
+        assertEquals(data.supportInfo.modes, 0);
+        assertEquals(data.supportInfo.sessionHints, 0);
+        assertEquals(data.supportInfo.sessionModes, 0);
+        assertEquals(data.supportInfo.sessionTags, 0);
+        assertEquals(data.powerHalVersion, 5);
+        assertEquals(data.preferredRateNanos, -1);
+    }
 }