Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 420db78c authored by Xiang Wang's avatar Xiang Wang
Browse files

Add NDK support for thermal headroom callback API

Fix the bug that thermal status server listener is not reset to null
if the server registration call fails temporarily, which can prevent
the apps from registering thermal callback indefinitely until restart

Fix the bug that app listener is removed even if server
listener fails to unregister, which can confuse the app as it
will return the error status and subsequent unregistration will always
fail with EINVAL due to listener not found

Bug: 360486877
Flag: EXEMPT NDK
Test: atest NativeThermalTest NativeThermalUnitTest
Change-Id: I2d08ad15e80cc5ea9a75f52ac53d4662db42a11c
parent b8debc69
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -362,6 +362,8 @@ LIBANDROID {
    AThermal_unregisterThermalStatusListener; # introduced=30
    AThermal_getThermalHeadroom; # introduced=31
    AThermal_getThermalHeadroomThresholds; # introduced=VanillaIceCream
    AThermal_registerThermalHeadroomListener; # introduced=36
    AThermal_unregisterThermalHeadroomListener; # introduced=36
    APerformanceHint_getManager; # introduced=Tiramisu
    APerformanceHint_createSession; # introduced=Tiramisu
    APerformanceHint_getPreferredUpdateRateNanos; # introduced=Tiramisu
+300 −3
Original line number Diff line number Diff line
@@ -77,12 +77,62 @@ public:
                (override));
};

struct HeadroomCallbackData {
    void* data;
    float headroom;
    float forecast;
    int32_t forecastSeconds;
    const std::vector<float> thresholds;
};

struct StatusCallbackData {
    void* data;
    AThermalStatus status;
};

static std::optional<HeadroomCallbackData> headroomCalled1;
static std::optional<HeadroomCallbackData> headroomCalled2;
static std::optional<StatusCallbackData> statusCalled1;
static std::optional<StatusCallbackData> statusCalled2;

static std::vector<float> convertThresholds(const AThermalHeadroomThreshold* thresholds,
                                            size_t size) {
    std::vector<float> ret;
    for (int i = 0; i < (int)size; i++) {
        ret.emplace_back(thresholds[i].headroom);
    }
    return ret;
};

static void onHeadroomChange1(void* data, float headroom, float forecast, int32_t forecastSeconds,
                              const AThermalHeadroomThreshold* thresholds, size_t size) {
    headroomCalled1.emplace(data, headroom, forecast, forecastSeconds,
                            convertThresholds(thresholds, size));
}

static void onHeadroomChange2(void* data, float headroom, float forecast, int32_t forecastSeconds,
                              const AThermalHeadroomThreshold* thresholds, size_t size) {
    headroomCalled2.emplace(data, headroom, forecast, forecastSeconds,
                            convertThresholds(thresholds, size));
}

static void onStatusChange1(void* data, AThermalStatus status) {
    statusCalled1.emplace(data, status);
}
static void onStatusChange2(void* data, AThermalStatus status) {
    statusCalled2.emplace(data, status);
}

class NativeThermalUnitTest : public Test {
public:
    void SetUp() override {
        mMockIThermalService = new StrictMock<MockIThermalService>();
        AThermal_setIThermalServiceForTesting(mMockIThermalService);
        mThermalManager = AThermal_acquireManager();
        headroomCalled1.reset();
        headroomCalled2.reset();
        statusCalled1.reset();
        statusCalled2.reset();
    }

    void TearDown() override {
@@ -117,9 +167,11 @@ TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholds) {
    size_t size1;
    ASSERT_EQ(OK, AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds1, &size1));
    checkThermalHeadroomThresholds(expected, thresholds1, size1);
    // following calls should be cached
    EXPECT_CALL(*mMockIThermalService, getThermalHeadroomThresholds(_)).Times(0);

    // following calls should not be cached
    expected = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
    EXPECT_CALL(*mMockIThermalService, getThermalHeadroomThresholds(_))
            .Times(Exactly(1))
            .WillRepeatedly(DoAll(SetArgPointee<0>(expected), Return(Status())));
    const AThermalHeadroomThreshold* thresholds2 = nullptr;
    size_t size2;
    ASSERT_EQ(OK, AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds2, &size2));
@@ -164,3 +216,248 @@ TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholdsFailedWithNonEmpty
    ASSERT_EQ(EINVAL, AThermal_getThermalHeadroomThresholds(mThermalManager, &initialized, &size));
    delete[] initialized;
}

TEST_F(NativeThermalUnitTest, TestRegisterThermalHeadroomListener) {
    EXPECT_CALL(*mMockIThermalService, registerThermalHeadroomListener(_, _))
            .Times(Exactly(2))
            .WillOnce(Return(
                    Status::fromExceptionCode(binder::Status::Exception::EX_TRANSACTION_FAILED)));
    float data1 = 1.0f;
    float data2 = 2.0f;
    ASSERT_EQ(EPIPE,
              AThermal_registerThermalHeadroomListener(mThermalManager, onHeadroomChange1, &data1));
    ASSERT_EQ(EPIPE,
              AThermal_registerThermalHeadroomListener(mThermalManager, onHeadroomChange2, &data2));

    // verify only 1 service call to register a global listener
    sp<IThermalHeadroomListener> capturedServiceListener;
    Mock::VerifyAndClearExpectations(mMockIThermalService);
    EXPECT_CALL(*mMockIThermalService, registerThermalHeadroomListener(_, _))
            .Times(Exactly(1))
            .WillOnce(DoAll(testing::SaveArg<0>(&capturedServiceListener),
                            testing::Invoke([](const sp<IThermalHeadroomListener>&,
                                               bool* aidl_return) { *aidl_return = true; }),
                            Return(Status::ok())));
    ASSERT_EQ(0,
              AThermal_registerThermalHeadroomListener(mThermalManager, onHeadroomChange1, &data1));
    ASSERT_EQ(EINVAL,
              AThermal_registerThermalHeadroomListener(mThermalManager, onHeadroomChange1, &data1));
    ASSERT_EQ(0,
              AThermal_registerThermalHeadroomListener(mThermalManager, onHeadroomChange2, &data2));
    const ::std::vector<float> thresholds = {0.1f, 0.2f};
    capturedServiceListener->onHeadroomChange(0.1f, 0.3f, 20, thresholds);
    ASSERT_TRUE(headroomCalled1.has_value());
    EXPECT_EQ(headroomCalled1->data, &data1);
    EXPECT_EQ(headroomCalled1->headroom, 0.1f);
    EXPECT_EQ(headroomCalled1->forecast, 0.3f);
    EXPECT_EQ(headroomCalled1->forecastSeconds, 20);
    EXPECT_EQ(headroomCalled1->thresholds, thresholds);
    ASSERT_TRUE(headroomCalled2.has_value());
    EXPECT_EQ(headroomCalled2->data, &data2);
    EXPECT_EQ(headroomCalled2->headroom, 0.1f);
    EXPECT_EQ(headroomCalled2->forecast, 0.3f);
    EXPECT_EQ(headroomCalled2->forecastSeconds, 20);
    EXPECT_EQ(headroomCalled2->thresholds, thresholds);

    // after test finished the global service listener should be unregistered
    EXPECT_CALL(*mMockIThermalService, unregisterThermalHeadroomListener(_, _))
            .Times(Exactly(1))
            .WillOnce(Return(binder::Status::ok()));
}

TEST_F(NativeThermalUnitTest, TestUnregisterThermalHeadroomListener) {
    sp<IThermalHeadroomListener> capturedServiceListener;
    EXPECT_CALL(*mMockIThermalService, registerThermalHeadroomListener(_, _))
            .Times(Exactly(1))
            .WillOnce(DoAll(testing::SaveArg<0>(&capturedServiceListener),
                            testing::Invoke([](const sp<IThermalHeadroomListener>&,
                                               bool* aidl_return) { *aidl_return = true; }),
                            Return(Status::ok())));
    float data1 = 1.0f;
    float data2 = 2.0f;
    ASSERT_EQ(0,
              AThermal_registerThermalHeadroomListener(mThermalManager, onHeadroomChange1, &data1));
    ASSERT_EQ(0,
              AThermal_registerThermalHeadroomListener(mThermalManager, onHeadroomChange2, &data2));
    capturedServiceListener->onHeadroomChange(0.1f, 0.3f, 20, {});
    ASSERT_TRUE(headroomCalled1.has_value());
    ASSERT_TRUE(headroomCalled2.has_value());

    EXPECT_CALL(*mMockIThermalService, unregisterThermalHeadroomListener(_, _))
            .Times(Exactly(1))
            .WillRepeatedly(Return(
                    Status::fromExceptionCode(binder::Status::Exception::EX_TRANSACTION_FAILED)));

    // callback 1 should be unregistered and callback 2 unregistration should fail due to service
    // listener unregistration call failure
    ASSERT_EQ(0,
              AThermal_unregisterThermalHeadroomListener(mThermalManager, onHeadroomChange1,
                                                         &data1));
    ASSERT_EQ(EPIPE,
              AThermal_unregisterThermalHeadroomListener(mThermalManager, onHeadroomChange2,
                                                         &data2));
    // verify only callback 2 is called after callback 1 is unregistered
    std::vector<float> thresholds = {0.1f, 0.2f};
    headroomCalled1.reset();
    headroomCalled2.reset();
    capturedServiceListener->onHeadroomChange(0.1f, 0.3f, 20, thresholds);
    ASSERT_TRUE(!headroomCalled1.has_value());
    ASSERT_TRUE(headroomCalled2.has_value());

    // verify only 1 service call to unregister global service listener
    Mock::VerifyAndClearExpectations(mMockIThermalService);
    EXPECT_CALL(*mMockIThermalService, unregisterThermalHeadroomListener(_, _))
            .Times(Exactly(1))
            .WillOnce(DoAll(testing::Invoke([](const sp<IThermalHeadroomListener>&,
                                               bool* aidl_return) { *aidl_return = true; }),
                            Return(Status::ok())));
    ASSERT_EQ(EINVAL,
              AThermal_unregisterThermalHeadroomListener(mThermalManager, onHeadroomChange1,
                                                         &data1));
    ASSERT_EQ(0,
              AThermal_unregisterThermalHeadroomListener(mThermalManager, onHeadroomChange2,
                                                         &data2));
    // verify neither callback is called after global service listener is unregistered
    headroomCalled1.reset();
    headroomCalled2.reset();
    capturedServiceListener->onHeadroomChange(0.1f, 0.3f, 20, thresholds);
    ASSERT_TRUE(!headroomCalled1.has_value());
    ASSERT_TRUE(!headroomCalled2.has_value());

    // verify adding a new callback will still work
    Mock::VerifyAndClearExpectations(mMockIThermalService);
    EXPECT_CALL(*mMockIThermalService, registerThermalHeadroomListener(_, _))
            .Times(Exactly(1))
            .WillOnce(DoAll(testing::SaveArg<0>(&capturedServiceListener),
                            testing::Invoke([](const sp<IThermalHeadroomListener>&,
                                               bool* aidl_return) { *aidl_return = true; }),
                            Return(Status::ok())));
    ASSERT_EQ(0,
              AThermal_registerThermalHeadroomListener(mThermalManager, onHeadroomChange1, &data1));
    headroomCalled1.reset();
    capturedServiceListener->onHeadroomChange(0.1f, 0.3f, 20, thresholds);
    ASSERT_TRUE(headroomCalled1.has_value());
    EXPECT_EQ(headroomCalled1->data, &data1);
    EXPECT_EQ(headroomCalled1->headroom, 0.1f);
    EXPECT_EQ(headroomCalled1->forecast, 0.3f);
    EXPECT_EQ(headroomCalled1->forecastSeconds, 20);
    EXPECT_EQ(headroomCalled1->thresholds, thresholds);

    // after test finished the global service listener should be unregistered
    EXPECT_CALL(*mMockIThermalService, unregisterThermalHeadroomListener(_, _))
            .Times(Exactly(1))
            .WillOnce(Return(binder::Status::ok()));
}

TEST_F(NativeThermalUnitTest, TestRegisterThermalStatusListener) {
    EXPECT_CALL(*mMockIThermalService, registerThermalStatusListener(_, _))
            .Times(Exactly(2))
            .WillOnce(Return(
                    Status::fromExceptionCode(binder::Status::Exception::EX_TRANSACTION_FAILED)));
    int data1 = 1;
    int data2 = 2;
    ASSERT_EQ(EPIPE,
              AThermal_registerThermalStatusListener(mThermalManager, onStatusChange1, &data1));
    ASSERT_EQ(EPIPE,
              AThermal_registerThermalStatusListener(mThermalManager, onStatusChange2, &data2));

    // verify only 1 service call to register a global listener
    sp<IThermalStatusListener> capturedServiceListener;
    Mock::VerifyAndClearExpectations(mMockIThermalService);
    EXPECT_CALL(*mMockIThermalService, registerThermalStatusListener(_, _))
            .Times(Exactly(1))
            .WillOnce(DoAll(testing::SaveArg<0>(&capturedServiceListener),
                            testing::Invoke([](const sp<IThermalStatusListener>&,
                                               bool* aidl_return) { *aidl_return = true; }),
                            Return(Status::ok())));
    ASSERT_EQ(0, AThermal_registerThermalStatusListener(mThermalManager, onStatusChange1, &data1));
    ASSERT_EQ(EINVAL,
              AThermal_registerThermalStatusListener(mThermalManager, onStatusChange1, &data1));
    ASSERT_EQ(0, AThermal_registerThermalStatusListener(mThermalManager, onStatusChange2, &data2));

    capturedServiceListener->onStatusChange(AThermalStatus::ATHERMAL_STATUS_LIGHT);
    ASSERT_TRUE(statusCalled1.has_value());
    EXPECT_EQ(statusCalled1->data, &data1);
    EXPECT_EQ(statusCalled1->status, AThermalStatus::ATHERMAL_STATUS_LIGHT);
    ASSERT_TRUE(statusCalled2.has_value());
    EXPECT_EQ(statusCalled2->data, &data2);
    EXPECT_EQ(statusCalled2->status, AThermalStatus::ATHERMAL_STATUS_LIGHT);

    // after test finished the callback should be unregistered
    EXPECT_CALL(*mMockIThermalService, unregisterThermalStatusListener(_, _))
            .Times(Exactly(1))
            .WillOnce(Return(binder::Status::ok()));
}

TEST_F(NativeThermalUnitTest, TestUnregisterThermalStatusListener) {
    sp<IThermalStatusListener> capturedServiceListener;
    EXPECT_CALL(*mMockIThermalService, registerThermalStatusListener(_, _))
            .Times(Exactly(1))
            .WillOnce(DoAll(testing::SaveArg<0>(&capturedServiceListener),
                            testing::Invoke([](const sp<IThermalStatusListener>&,
                                               bool* aidl_return) { *aidl_return = true; }),
                            Return(Status::ok())));
    int data1 = 1;
    int data2 = 2;
    ASSERT_EQ(0, AThermal_registerThermalStatusListener(mThermalManager, onStatusChange1, &data1));
    ASSERT_EQ(0, AThermal_registerThermalStatusListener(mThermalManager, onStatusChange2, &data2));
    capturedServiceListener->onStatusChange(AThermalStatus::ATHERMAL_STATUS_LIGHT);
    ASSERT_TRUE(statusCalled1.has_value());
    ASSERT_TRUE(statusCalled2.has_value());

    EXPECT_CALL(*mMockIThermalService, unregisterThermalStatusListener(_, _))
            .Times(Exactly(1))
            .WillOnce(Return(
                    Status::fromExceptionCode(binder::Status::Exception::EX_TRANSACTION_FAILED)));
    // callback 1 should be unregistered and callback 2 unregistration should fail due to service
    // listener unregistration call failure
    ASSERT_EQ(0,
              AThermal_unregisterThermalStatusListener(mThermalManager, onStatusChange1, &data1));
    ASSERT_EQ(EPIPE,
              AThermal_unregisterThermalStatusListener(mThermalManager, onStatusChange2, &data2));

    // verify only callback 2 is called after callback 1 is unregistered
    statusCalled1.reset();
    statusCalled2.reset();
    capturedServiceListener->onStatusChange(AThermalStatus::ATHERMAL_STATUS_LIGHT);
    ASSERT_TRUE(!statusCalled1.has_value());
    ASSERT_TRUE(statusCalled2.has_value());

    // verify only 1 service call to unregister global service listener
    Mock::VerifyAndClearExpectations(mMockIThermalService);
    EXPECT_CALL(*mMockIThermalService, unregisterThermalStatusListener(_, _))
            .Times(Exactly(1))
            .WillOnce(DoAll(testing::Invoke([](const sp<IThermalStatusListener>&,
                                               bool* aidl_return) { *aidl_return = true; }),
                            Return(Status::ok())));
    ASSERT_EQ(EINVAL,
              AThermal_unregisterThermalStatusListener(mThermalManager, onStatusChange1, &data1));
    ASSERT_EQ(0,
              AThermal_unregisterThermalStatusListener(mThermalManager, onStatusChange2, &data2));
    // verify neither callback is called after global service listener is unregistered
    statusCalled1.reset();
    statusCalled2.reset();
    capturedServiceListener->onStatusChange(AThermalStatus::ATHERMAL_STATUS_LIGHT);
    ASSERT_TRUE(!statusCalled1.has_value());
    ASSERT_TRUE(!statusCalled2.has_value());

    // verify adding a new callback will still work
    Mock::VerifyAndClearExpectations(mMockIThermalService);
    EXPECT_CALL(*mMockIThermalService, registerThermalStatusListener(_, _))
            .Times(Exactly(1))
            .WillOnce(DoAll(testing::SaveArg<0>(&capturedServiceListener),
                            testing::Invoke([](const sp<IThermalStatusListener>&,
                                               bool* aidl_return) { *aidl_return = true; }),
                            Return(Status::ok())));
    ASSERT_EQ(0, AThermal_registerThermalStatusListener(mThermalManager, onStatusChange1, &data1));
    statusCalled1.reset();
    capturedServiceListener->onStatusChange(AThermalStatus::ATHERMAL_STATUS_LIGHT);
    ASSERT_TRUE(statusCalled1.has_value());
    EXPECT_EQ(statusCalled1->data, &data1);
    EXPECT_EQ(statusCalled1->status, AThermalStatus::ATHERMAL_STATUS_LIGHT);

    // after test finished the global service listener should be unregistered
    EXPECT_CALL(*mMockIThermalService, unregisterThermalStatusListener(_, _))
            .Times(Exactly(1))
            .WillOnce(Return(binder::Status::ok()));
}
+192 −131

File changed.

Preview size limit exceeded, changes collapsed.