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

Commit b965ca73 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "hal: report battery status to sound trigger hal"

parents 330de89c c1088ea9
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ AUDIO_FEATURE_ENABLED_SND_MONITOR := true
AUDIO_FEATURE_ENABLED_DLKM := true
AUDIO_FEATURE_ENABLED_USB_BURST_MODE := true
AUDIO_FEATURE_ENABLED_SVA_MULTI_STAGE := true
AUDIO_FEATURE_ENABLED_BATTERY_LISTENER := true
##AUDIO_FEATURE_FLAGS

ifneq ($(strip $(TARGET_USES_QSSI)), true)
+9 −0
Original line number Diff line number Diff line
@@ -405,6 +405,15 @@ ifeq ($(strip $(AUDIO_FEATURE_ENABLED_USB_BURST_MODE)), true)
    LOCAL_CFLAGS += -DUSB_BURST_MODE_ENABLED
endif

ifeq ($(strip $(AUDIO_FEATURE_ENABLED_BATTERY_LISTENER)), true)
    LOCAL_CFLAGS += -DBATTERY_LISTENER_ENABLED
    LOCAL_SRC_FILES += audio_extn/battery_listener.cpp
    LOCAL_SHARED_LIBRARIES += android.hardware.health@1.0 android.hardware.health@2.0 \
                              libhidltransport libbase libhidlbase libhwbinder \
                              libutils android.hardware.power@1.2
    LOCAL_STATIC_LIBRARIES := libhealthhalutils
endif

LOCAL_CFLAGS += -Wall -Werror

LOCAL_COPY_HEADERS_TO   := mm-audio
+3 −0
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@
#include <cutils/str_parms.h>
#include "adsp_hdlr.h"
#include "ip_hdlr_intf.h"
#include "battery_listener.h"

#ifndef AFE_PROXY_ENABLED
#define AUDIO_DEVICE_OUT_PROXY 0x40000
@@ -378,6 +379,7 @@ void audio_extn_listen_set_parameters(struct audio_device *adev,
#define audio_extn_sound_trigger_deinit(adev)                          (0)
#define audio_extn_sound_trigger_update_device_status(snd_dev, event)  (0)
#define audio_extn_sound_trigger_update_stream_status(uc_info, event)  (0)
#define audio_extn_sound_trigger_update_battery_status(charging)       (0)
#define audio_extn_sound_trigger_set_parameters(adev, parms)           (0)
#define audio_extn_sound_trigger_get_parameters(adev, query, reply)    (0)
#define audio_extn_sound_trigger_check_and_get_session(in)             (0)
@@ -399,6 +401,7 @@ void audio_extn_sound_trigger_update_device_status(snd_device_t snd_device,
                                     st_event_type_t event);
void audio_extn_sound_trigger_update_stream_status(struct audio_usecase *uc_info,
                                     st_event_type_t event);
void audio_extn_sound_trigger_update_battery_status(bool charging);
void audio_extn_sound_trigger_set_parameters(struct audio_device *adev,
                                             struct str_parms *parms);
void audio_extn_sound_trigger_check_and_get_session(struct stream_in *in);
+247 −0
Original line number Diff line number Diff line
/*
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above
*       copyright notice, this list of conditions and the following
*       disclaimer in the documentation and/or other materials provided
*       with the distribution.
*     * Neither the name of The Linux Foundation nor the names of its
*       contributors may be used to endorse or promote products derived
*       from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define LOG_TAG "audio_hw::BatteryListener"
#include <log/log.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
#include <android/hardware/health/2.0/IHealth.h>
#include <healthhalutils/HealthHalUtils.h>
#include <hidl/HidlTransportSupport.h>
#include <thread>
#include "battery_listener.h"

using android::hardware::interfacesEqual;
using android::hardware::Return;
using android::hardware::Void;
using android::hardware::health::V1_0::BatteryStatus;
using android::hardware::health::V1_0::toString;
using android::hardware::health::V2_0::get_health_service;
using android::hardware::health::V2_0::HealthInfo;
using android::hardware::health::V2_0::IHealth;
using android::hardware::health::V2_0::Result;
using android::hidl::manager::V1_0::IServiceManager;
using namespace std::literals::chrono_literals;

namespace android {

#define GET_HEALTH_SVC_RETRY_CNT 5
#define GET_HEALTH_SVC_WAIT_TIME_MS 500

struct BatteryListenerImpl : public hardware::health::V2_0::IHealthInfoCallback,
                             public hardware::hidl_death_recipient {
    typedef std::function<void(bool)> cb_fn_t;
    BatteryListenerImpl(cb_fn_t cb);
    virtual ~BatteryListenerImpl ();
    virtual hardware::Return<void> healthInfoChanged(
        const hardware::health::V2_0::HealthInfo& info);
    virtual void serviceDied(uint64_t cookie,
                             const wp<hidl::base::V1_0::IBase>& who);
    bool isCharging() {
        std::lock_guard<std::mutex> _l(mLock);
        return statusToBool(mStatus);
    }
  private:
    sp<hardware::health::V2_0::IHealth> mHealth;
    status_t init();
    BatteryStatus mStatus;
    cb_fn_t mCb;
    std::mutex mLock;
    std::condition_variable mCond;
    std::unique_ptr<std::thread> mThread;
    bool mDone;
    bool statusToBool(const BatteryStatus &s) const {
        return (s == BatteryStatus::CHARGING) ||
               (s ==  BatteryStatus::FULL);
    }
};

status_t BatteryListenerImpl::init()
{
    int tries = 0;

    if (mHealth != NULL)
        return INVALID_OPERATION;

    do {
        mHealth = get_health_service();
        if (mHealth != NULL)
            break;
        usleep(GET_HEALTH_SVC_WAIT_TIME_MS * 1000);
        tries++;
    } while(tries < GET_HEALTH_SVC_RETRY_CNT);

    if (mHealth == NULL) {
        ALOGE("no health service found, retries %d", tries);
        return NO_INIT;
    } else {
        ALOGI("Get health service in %d tries", tries);
    }
    mStatus = BatteryStatus::UNKNOWN;
    auto ret = mHealth->getChargeStatus([&](Result r, BatteryStatus status) {
        if (r != Result::SUCCESS) {
            ALOGE("batterylistener: cannot get battery status");
            return;
        }
        mStatus = status;
    });
    if (!ret.isOk())
        ALOGE("batterylistener: get charge status transaction error");

    if (mStatus == BatteryStatus::UNKNOWN)
        ALOGW("batterylistener: init: invalid battery status");
    mDone = false;
    mThread = std::make_unique<std::thread>([this]() {
            std::unique_lock<std::mutex> l(mLock);
            BatteryStatus local_status = mStatus;
            while (!mDone) {
                if (local_status == mStatus) {
                    mCond.wait(l);
                    continue;
                }
                local_status = mStatus;
                switch (local_status) {
                    // NOT_CHARGING is a special event that indicates, a battery is connected,
                    // but not charging. This is seen for approx a second
                    // after charger is plugged in. A charging event is eventually received.
                    // We must try to avoid an unnecessary cb to HAL
                    // only to call it again shortly.
                    // An option to deal with this transient event would be to ignore this.
                    // Or process this event with a slight delay (i.e cancel this event
                    // if a different event comes in within a timeout
                    case BatteryStatus::NOT_CHARGING : {
                        auto mStatusnot_ncharging =
                                [this, local_status]() { return mStatus != local_status; };
                        mCond.wait_for(l, 3s, mStatusnot_ncharging);
                        if (mStatusnot_ncharging()) // i.e event changed
                            break;
                    }
                    default:
                        bool c = statusToBool(local_status);
                        ALOGI("healthInfo cb thread: cb %s", c ? "CHARGING" : "NOT CHARGING");
                        l.unlock();
                        mCb(c);
                        l.lock();
                        break;
                }
            }
        });
    mHealth->registerCallback(this);
    mHealth->linkToDeath(this, 0 /* cookie */);
    return NO_ERROR;
}

BatteryListenerImpl::BatteryListenerImpl(cb_fn_t cb) :
        mCb(cb)
{
    init();
}

BatteryListenerImpl::~BatteryListenerImpl()
{
    {
        std::lock_guard<std::mutex> _l(mLock);
        if (mHealth != NULL)
            mHealth->unlinkToDeath(this);
    }
    mDone = true;
    mThread->join();
}

void BatteryListenerImpl::serviceDied(uint64_t cookie __unused,
                                     const wp<hidl::base::V1_0::IBase>& who)
{
    {
        std::lock_guard<std::mutex> _l(mLock);
        if (mHealth == NULL || !interfacesEqual(mHealth, who.promote())) {
            ALOGE("health not initialized or unknown interface died");
            return;
        }
        ALOGI("health service died, reinit");
        mDone = true;
    }
    mThread->join();
    std::lock_guard<std::mutex> _l(mLock);
    init();
}

// this callback seems to be a SYNC callback and so
// waits for return before next event is issued.
// therefore we need not have a queue to process
// NOT_CHARGING and CHARGING concurrencies.
// Replace single var by a list if this assumption is broken
Return<void> BatteryListenerImpl::healthInfoChanged(
        const hardware::health::V2_0::HealthInfo& info)
{
    ALOGV("healthInfoChanged: %d", info.legacy.batteryStatus);
    std::unique_lock<std::mutex> l(mLock);
    if (info.legacy.batteryStatus != mStatus) {
        mStatus = info.legacy.batteryStatus;
        mCond.notify_one();
    }
    return Void();
}

static sp<BatteryListenerImpl> batteryListener;
status_t batteryPropertiesListenerInit(BatteryListenerImpl::cb_fn_t cb)
{
    batteryListener = new BatteryListenerImpl(cb);
    return NO_ERROR;
}

status_t batteryPropertiesListenerDeinit()
{
    batteryListener.clear();
    return OK;
}

bool batteryPropertiesListenerIsCharging()
{
    return batteryListener->isCharging();
}

} // namespace android

extern "C" {
void audio_extn_battery_properties_listener_init(battery_status_change_fn_t fn)
{
    android::batteryPropertiesListenerInit([=](bool charging) {
                                               fn(charging);
                                          });
}

void audio_extn_battery_properties_listener_deinit()
{
    android::batteryPropertiesListenerDeinit();
}

bool audio_extn_battery_properties_is_charging()
{
    return android::batteryPropertiesListenerIsCharging();
}

} // extern C
+44 −0
Original line number Diff line number Diff line
/*
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above
*       copyright notice, this list of conditions and the following
*       disclaimer in the documentation and/or other materials provided
*       with the distribution.
*     * Neither the name of The Linux Foundation nor the names of its
*       contributors may be used to endorse or promote products derived
*       from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef BATTERY_LISTENER_ENABLED
#ifdef __cplusplus
extern "C" {
#endif
typedef void (* battery_status_change_fn_t)(bool);
void audio_extn_battery_properties_listener_init(battery_status_change_fn_t fn);
void audio_extn_battery_properties_listener_deinit();
bool audio_extn_battery_properties_is_charging();
#ifdef __cplusplus
}
#endif
#else
#define audio_extn_battery_properties_listener_init(fn) do { } while(0)
#define audio_extn_battery_properties_listener_deinit() do { } while(0)
#define audio_extn_battery_properties_is_charging() (false)
#endif
Loading