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

Commit 236fa890 authored by Yifan Hong's avatar Yifan Hong Committed by Gerrit Code Review
Browse files

Merge "health: Add default impl of health AIDL HAL"

parents b311ffdb 830cdb11
Loading
Loading
Loading
Loading
+76 −0
Original line number Diff line number Diff line
// Copyright (C) 2021 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

cc_defaults {
    name: "libhealth_aidl_common_defaults",
    vendor: true,
    shared_libs: [
        "libbase",
        "libbinder_ndk",
        "libcutils",
        "liblog",
        "libutils",
        "android.hardware.health-V1-ndk",

        // TODO(b/177269435): remove when BatteryMonitor works with AIDL HealthInfo.
        "libhidlbase",
    ],
    static_libs: [
        "libbatterymonitor",
        "libhealthloop",

        // TODO(b/177269435): remove when BatteryMonitor works with AIDL HealthInfo.
        "android.hardware.health-translate-ndk",
    ],
}

// AIDL version of libhealth2impl.
// A helper library for health HAL implementation.
// HAL implementations can link to this library and extend the Health class.
cc_library_static {
    name: "libhealth_aidl_impl",
    defaults: [
        "libhealth_aidl_common_defaults",
    ],
    export_include_dirs: ["include"],
    export_static_lib_headers: [
        "libbatterymonitor",
    ],
    srcs: [
        "health-convert.cpp",
        "HalHealthLoop.cpp",
        "Health.cpp",
        "LinkedCallback.cpp",
    ],
    visibility: [
        ":__subpackages__",
        "//hardware/interfaces/tests/extension/health:__subpackages__",
    ],
}

// AIDL version of android.hardware.health@2.1-service.
// Default binder service of the health HAL.
cc_binary {
    name: "android.hardware.health-service.example",
    relative_install_path: "hw",
    init_rc: ["android.hardware.health-service.example.rc"],
    vintf_fragments: ["android.hardware.health-service.example.xml"],
    defaults: [
        "libhealth_aidl_common_defaults",
    ],
    static_libs: [
        "libhealth_aidl_impl",
    ],
    srcs: ["main.cpp"],
}
+67 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <health-impl/HalHealthLoop.h>

#include <android-base/logging.h>

#include <health-impl/Health.h>
#include "health-convert.h"

namespace aidl::android::hardware::health {

// Unlike the HIDL version android::hardware::health::V2_1::implementation::HalHealthLoop,
// do not define HalHealthLoop::Init because we no longer have Health::getHealthConfig.
// Let the Health class handle Init.
void HalHealthLoop::Init(struct healthd_config* config) {
    callback_->OnInit(this, config);
}

void HalHealthLoop::Heartbeat() {
    callback_->OnHeartbeat();
}

void HalHealthLoop::ScheduleBatteryUpdate() {
    // ignore errors. impl may not be able to handle any callbacks, so
    // update() may return errors.
    if (auto res = service_->update(); !res.isOk()) {
        LOG(WARNING) << "update() on the health HAL implementation failed with "
                     << res.getDescription();
    }

    HealthInfo health_info;
    auto res = service_->getHealthInfo(&health_info);
    CHECK(res.isOk()) << "getHealthInfo() on the health HAL implementation failed with "
                      << res.getDescription();
    OnHealthInfoChanged(health_info);
}

int HalHealthLoop::PrepareToWait() {
    return callback_->OnPrepareToWait();
}

void HalHealthLoop::OnHealthInfoChanged(const HealthInfo& health_info) {
    callback_->OnHealthInfoChanged(health_info);
    set_charger_online(health_info);
    AdjustWakealarmPeriods(charger_online());
}

void HalHealthLoop::set_charger_online(const HealthInfo& health_info) {
    charger_online_ = health_info.chargerAcOnline || health_info.chargerUsbOnline ||
                      health_info.chargerWirelessOnline;
}

}  // namespace aidl::android::hardware::health
+341 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "health-impl/Health.h"

#include <android-base/file.h>
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <android/hardware/health/translate-ndk.h>
#include <health/utils.h>

#include "LinkedCallback.h"
#include "health-convert.h"

using std::string_literals::operator""s;

namespace aidl::android::hardware::health {

namespace {
// Wrap LinkedCallback::OnCallbackDied() into a void(void*).
void OnCallbackDiedWrapped(void* cookie) {
    LinkedCallback* linked = reinterpret_cast<LinkedCallback*>(cookie);
    linked->OnCallbackDied();
}
}  // namespace

/*
// If you need to call healthd_board_init, construct the Health instance with
// the healthd_config after calling healthd_board_init:
class MyHealth : public Health {
  protected:
    MyHealth() : Health(CreateConfig()) {}
  private:
    static std::unique_ptr<healthd_config> CreateConfig() {
      auto config = std::make_unique<healthd_config>();
      ::android::hardware::health::InitHealthdConfig(config.get());
      healthd_board_init(config.get());
      return std::move(config);
    }
};
*/
Health::Health(std::string_view instance_name, std::unique_ptr<struct healthd_config>&& config)
    : instance_name_(instance_name),
      healthd_config_(std::move(config)),
      death_recipient_(AIBinder_DeathRecipient_new(&OnCallbackDiedWrapped)) {
    battery_monitor_.init(healthd_config_.get());
}

//
// Getters.
//

template <typename T>
static ndk::ScopedAStatus GetProperty(::android::BatteryMonitor* monitor, int id, T defaultValue,
                                      T* out) {
    *out = defaultValue;
    struct ::android::BatteryProperty prop;
    ::android::status_t err = monitor->getProperty(static_cast<int>(id), &prop);
    if (err == ::android::OK) {
        *out = static_cast<T>(prop.valueInt64);
    } else {
        LOG(DEBUG) << "getProperty(" << id << ")"
                   << " fails: (" << err << ") " << ::android::statusToString(err);
    }

    switch (err) {
        case ::android::OK:
            return ndk::ScopedAStatus::ok();
        case ::android::NAME_NOT_FOUND:
            return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
        default:
            return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
                    IHealth::STATUS_UNKNOWN, ::android::statusToString(err).c_str());
    }
}

ndk::ScopedAStatus Health::getChargeCounterUah(int32_t* out) {
    return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CHARGE_COUNTER, 0, out);
}

ndk::ScopedAStatus Health::getCurrentNowMicroamps(int32_t* out) {
    return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CURRENT_NOW, 0, out);
}

ndk::ScopedAStatus Health::getCurrentAverageMicroamps(int32_t* out) {
    return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CURRENT_AVG, 0, out);
}

ndk::ScopedAStatus Health::getCapacity(int32_t* out) {
    return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CAPACITY, 0, out);
}

ndk::ScopedAStatus Health::getEnergyCounterNwh(int64_t* out) {
    return GetProperty<int64_t>(&battery_monitor_, ::android::BATTERY_PROP_ENERGY_COUNTER, 0, out);
}

ndk::ScopedAStatus Health::getChargeStatus(BatteryStatus* out) {
    return GetProperty(&battery_monitor_, ::android::BATTERY_PROP_BATTERY_STATUS,
                       BatteryStatus::UNKNOWN, out);
}

ndk::ScopedAStatus Health::getDiskStats(std::vector<DiskStats>*) {
    // This implementation does not support DiskStats. An implementation may extend this
    // class and override this function to support disk stats.
    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}

ndk::ScopedAStatus Health::getStorageInfo(std::vector<StorageInfo>*) {
    // This implementation does not support StorageInfo. An implementation may extend this
    // class and override this function to support storage info.
    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}

ndk::ScopedAStatus Health::getHealthInfo(HealthInfo* out) {
    battery_monitor_.updateValues();

    // TODO(b/177269435): BatteryMonitor should store AIDL HealthInfo instead.
    auto health_info_2_1 = battery_monitor_.getHealthInfo_2_1();
    if (!::android::h2a::translate(health_info_2_1, out)) {
        return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
                IHealth::STATUS_UNKNOWN, "Cannot translate HIDL HealthInfo to AIDL");
    }

    // Fill in storage infos; these aren't retrieved by BatteryMonitor.
    if (auto res = getStorageInfo(&out->storageInfos); !res.isOk()) {
        if (res.getServiceSpecificError() == 0 &&
            res.getExceptionCode() != EX_UNSUPPORTED_OPERATION) {
            return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
                    IHealth::STATUS_UNKNOWN,
                    ("getStorageInfo fails: " + res.getDescription()).c_str());
        }
        LOG(DEBUG) << "getHealthInfo: getStorageInfo fails with service-specific error, clearing: "
                   << res.getDescription();
        out->storageInfos = {};
    }
    if (auto res = getDiskStats(&out->diskStats); !res.isOk()) {
        if (res.getServiceSpecificError() == 0 &&
            res.getExceptionCode() != EX_UNSUPPORTED_OPERATION) {
            return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
                    IHealth::STATUS_UNKNOWN,
                    ("getDiskStats fails: " + res.getDescription()).c_str());
        }
        LOG(DEBUG) << "getHealthInfo: getDiskStats fails with service-specific error, clearing: "
                   << res.getDescription();
        out->diskStats = {};
    }

    // A subclass may want to update health info struct before returning it.
    UpdateHealthInfo(out);

    return ndk::ScopedAStatus::ok();
}

binder_status_t Health::dump(int fd, const char**, uint32_t) {
    battery_monitor_.dumpState(fd);

    ::android::base::WriteStringToFd("\ngetHealthInfo -> ", fd);
    HealthInfo health_info;
    auto res = getHealthInfo(&health_info);
    if (res.isOk()) {
        ::android::base::WriteStringToFd(health_info.toString(), fd);
    } else {
        ::android::base::WriteStringToFd(res.getDescription(), fd);
    }

    fsync(fd);
    return STATUS_OK;
}

std::optional<bool> Health::ShouldKeepScreenOn() {
    if (!healthd_config_->screen_on) {
        return std::nullopt;
    }

    HealthInfo health_info;
    auto res = getHealthInfo(&health_info);
    if (!res.isOk()) {
        return std::nullopt;
    }

    ::android::BatteryProperties props = {};
    convert(health_info, &props);
    return healthd_config_->screen_on(&props);
}

namespace {
bool IsDeadObjectLogged(const ndk::ScopedAStatus& ret) {
    if (ret.isOk()) return false;
    if (ret.getStatus() == ::STATUS_DEAD_OBJECT) return true;
    LOG(ERROR) << "Cannot call healthInfoChanged on callback: " << ret.getDescription();
    return false;
}
}  // namespace

//
// Subclass helpers / overrides
//

void Health::UpdateHealthInfo(HealthInfo* /* health_info */) {
    /*
        // Sample code for a subclass to implement this:
        // If you need to modify values (e.g. batteryChargeTimeToFullNowSeconds), do it here.
        health_info->batteryChargeTimeToFullNowSeconds = calculate_charge_time_seconds();

        // If you need to call healthd_board_battery_update, modify its signature
        // and implementation to operate on HealthInfo directly, then call:
        healthd_board_battery_update(health_info);
    */
}

//
// Methods that handle callbacks.
//

ndk::ScopedAStatus Health::registerCallback(const std::shared_ptr<IHealthInfoCallback>& callback) {
    if (callback == nullptr) {
        // For now, this shouldn't happen because argument is not nullable.
        return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
    }

    {
        std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
        callbacks_.emplace_back(LinkedCallback::Make(ref<Health>(), callback));
        // unlock
    }

    HealthInfo health_info;
    if (auto res = getHealthInfo(&health_info); !res.isOk()) {
        LOG(WARNING) << "Cannot call getHealthInfo: " << res.getDescription();
        // No health info to send, so return early.
        return ndk::ScopedAStatus::ok();
    }

    if (auto res = callback->healthInfoChanged(health_info); IsDeadObjectLogged(res)) {
        (void)unregisterCallback(callback);
    }
    return ndk::ScopedAStatus::ok();
}

ndk::ScopedAStatus Health::unregisterCallback(
        const std::shared_ptr<IHealthInfoCallback>& callback) {
    if (callback == nullptr) {
        // For now, this shouldn't happen because argument is not nullable.
        return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
    }

    std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);

    auto matches = [callback](const auto& linked) {
        return linked->callback() == callback;  // compares shared_ptr
    };
    auto it = std::remove_if(callbacks_.begin(), callbacks_.end(), matches);
    bool removed = (it != callbacks_.end());
    callbacks_.erase(it, callbacks_.end());  // calls unlinkToDeath on deleted callbacks.
    return removed ? ndk::ScopedAStatus::ok()
                   : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}

// A combination of the HIDL version
//   android::hardware::health::V2_1::implementation::Health::update() and
//   android::hardware::health::V2_1::implementation::BinderHealth::update()
ndk::ScopedAStatus Health::update() {
    HealthInfo health_info;
    if (auto res = getHealthInfo(&health_info); !res.isOk()) {
        LOG(DEBUG) << "Cannot call getHealthInfo for update(): " << res.getDescription();
        // Propagate service specific errors. If there's none, report unknown error.
        if (res.getServiceSpecificError() != 0 ||
            res.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
            return res;
        }
        return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
                IHealth::STATUS_UNKNOWN, res.getDescription().c_str());
    }
    battery_monitor_.logValues();
    OnHealthInfoChanged(health_info);
    return ndk::ScopedAStatus::ok();
}

void Health::OnHealthInfoChanged(const HealthInfo& health_info) {
    // Notify all callbacks
    std::unique_lock<decltype(callbacks_lock_)> lock(callbacks_lock_);
    // is_dead notifies a callback and return true if it is dead.
    auto is_dead = [&](const auto& linked) {
        auto res = linked->callback()->healthInfoChanged(health_info);
        return IsDeadObjectLogged(res);
    };
    auto it = std::remove_if(callbacks_.begin(), callbacks_.end(), is_dead);
    callbacks_.erase(it, callbacks_.end());  // calls unlinkToDeath on deleted callbacks.
    lock.unlock();

    // Let HalHealthLoop::OnHealthInfoChanged() adjusts uevent / wakealarm periods
}

void Health::BinderEvent(uint32_t /*epevents*/) {
    if (binder_fd_ >= 0) {
        ABinderProcess_handlePolledCommands();
    }
}

void Health::OnInit(HalHealthLoop* hal_health_loop, struct healthd_config* config) {
    LOG(INFO) << instance_name_ << " instance initializing with healthd_config...";

    // Similar to HIDL's android::hardware::health::V2_1::implementation::HalHealthLoop::Init,
    // copy configuration parameters to |config| for HealthLoop (e.g. uevent / wake alarm periods)
    *config = *healthd_config_.get();

    binder_status_t status = ABinderProcess_setupPolling(&binder_fd_);

    if (status == ::STATUS_OK && binder_fd_ >= 0) {
        std::shared_ptr<Health> thiz = ref<Health>();
        auto binder_event = [thiz](auto*, uint32_t epevents) { thiz->BinderEvent(epevents); };
        if (hal_health_loop->RegisterEvent(binder_fd_, binder_event, EVENT_NO_WAKEUP_FD) != 0) {
            PLOG(ERROR) << instance_name_ << " instance: Register for binder events failed";
        }
    }

    std::string health_name = IHealth::descriptor + "/"s + instance_name_;
    CHECK_EQ(STATUS_OK, AServiceManager_addService(this->asBinder().get(), health_name.c_str()))
            << instance_name_ << ": Failed to register HAL";

    LOG(INFO) << instance_name_ << ": Hal init done";
}

// Unlike hwbinder, for binder, there's no need to explicitly call flushCommands()
// in PrepareToWait(). See b/139697085.

}  // namespace aidl::android::hardware::health
+66 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <android-base/logging.h>
#include <android/binder_ibinder.h>

#include <health-impl/Health.h>
#include <utils/Errors.h>

#include "LinkedCallback.h"

namespace aidl::android::hardware::health {

std::unique_ptr<LinkedCallback> LinkedCallback::Make(
        std::shared_ptr<Health> service, std::shared_ptr<IHealthInfoCallback> callback) {
    std::unique_ptr<LinkedCallback> ret(new LinkedCallback());
    binder_status_t linkRet =
            AIBinder_linkToDeath(callback->asBinder().get(), service->death_recipient_.get(),
                                 reinterpret_cast<void*>(ret.get()));
    if (linkRet != ::STATUS_OK) {
        LOG(WARNING) << __func__ << "Cannot link to death: " << linkRet;
        return nullptr;
    }
    ret->service_ = service;
    ret->callback_ = std::move(callback);
    return ret;
}

LinkedCallback::LinkedCallback() = default;

LinkedCallback::~LinkedCallback() {
    if (callback_ == nullptr) {
        return;
    }
    auto status =
            AIBinder_unlinkToDeath(callback_->asBinder().get(), service()->death_recipient_.get(),
                                   reinterpret_cast<void*>(this));
    if (status != STATUS_OK && status != STATUS_DEAD_OBJECT) {
        LOG(WARNING) << __func__ << "Cannot unlink to death: " << ::android::statusToString(status);
    }
}

std::shared_ptr<Health> LinkedCallback::service() {
    auto service_sp = service_.lock();
    CHECK_NE(nullptr, service_sp);
    return service_sp;
}

void LinkedCallback::OnCallbackDied() {
    service()->unregisterCallback(callback_);
}

}  // namespace aidl::android::hardware::health
+60 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <memory>

#include <aidl/android/hardware/health/IHealthInfoCallback.h>
#include <android-base/macros.h>
#include <android/binder_auto_utils.h>

#include <health-impl/Health.h>

namespace aidl::android::hardware::health {

// Type of the cookie pointer in linkToDeath.
// A (Health, IHealthInfoCallback) tuple.
class LinkedCallback {
  public:
    // Automatically linkToDeath upon construction with the returned object as the cookie.
    // service->death_reciepient() should be from CreateDeathRecipient().
    // Not using a strong reference to |service| to avoid circular reference. The lifetime
    // of |service| must be longer than this LinkedCallback object.
    static std::unique_ptr<LinkedCallback> Make(std::shared_ptr<Health> service,
                                                std::shared_ptr<IHealthInfoCallback> callback);

    // Automatically unlinkToDeath upon destruction. So, it is always safe to reinterpret_cast
    // the cookie back to the LinkedCallback object.
    ~LinkedCallback();

    // The wrapped IHealthInfoCallback object.
    const std::shared_ptr<IHealthInfoCallback>& callback() const { return callback_; }

    // On callback died, unreigster it from the service.
    void OnCallbackDied();

  private:
    LinkedCallback();
    DISALLOW_COPY_AND_ASSIGN(LinkedCallback);

    std::shared_ptr<Health> service();

    std::weak_ptr<Health> service_;
    std::shared_ptr<IHealthInfoCallback> callback_;
};

}  // namespace aidl::android::hardware::health
Loading