diff options
Diffstat (limited to 'gps/android/utils')
-rw-r--r-- | gps/android/utils/Android.bp | 37 | ||||
-rw-r--r-- | gps/android/utils/battery_listener.cpp | 280 | ||||
-rw-r--r-- | gps/android/utils/battery_listener.h | 32 |
3 files changed, 349 insertions, 0 deletions
diff --git a/gps/android/utils/Android.bp b/gps/android/utils/Android.bp new file mode 100644 index 0000000..c3dc17a --- /dev/null +++ b/gps/android/utils/Android.bp @@ -0,0 +1,37 @@ +cc_library_static { + + name: "liblocbatterylistener", + vendor: true, + + sanitize: GNSS_SANITIZE, + + cflags: GNSS_CFLAGS + ["-DBATTERY_LISTENER_ENABLED"], + local_include_dirs: ["."], + + srcs: ["battery_listener.cpp"], + + shared_libs: [ + "liblog", + "libhidlbase", + "libcutils", + "libutils", + "android.hardware.health@1.0", + "android.hardware.health@2.0", + "android.hardware.health@2.1", + "android.hardware.power@1.2", + "libbase", + ], + + static_libs: ["libhealthhalutils"], + + header_libs: [ + "libgps.utils_headers", + "libloc_pla_headers", + ], +} + +cc_library_headers { + + name: "liblocbatterylistener_headers", + export_include_dirs: ["."], +} diff --git a/gps/android/utils/battery_listener.cpp b/gps/android/utils/battery_listener.cpp new file mode 100644 index 0000000..9cbfabd --- /dev/null +++ b/gps/android/utils/battery_listener.cpp @@ -0,0 +1,280 @@ +/* +* Copyright (c) 2019-2020, 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. +*/ +#include "battery_listener.h" +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "LocSvc_BatteryListener" +#define LOG_NDEBUG 0 + +#include <android/hidl/manager/1.0/IServiceManager.h> +#include <android/hardware/health/2.1/IHealth.h> +#include <android/hardware/health/2.1/IHealthInfoCallback.h> +#include <healthhalutils/HealthHalUtils.h> +#include <hidl/HidlTransportSupport.h> +#include <thread> +#include <log_util.h> + +using android::hardware::interfacesEqual; +using android::hardware::Return; +using android::hardware::Void; +using android::hardware::health::V1_0::BatteryStatus; +using android::hardware::health::V2_1::HealthInfo; +using android::hardware::health::V2_1::IHealthInfoCallback; +using android::hardware::health::V2_1::IHealth; +using android::hardware::health::V2_0::Result; +using android::hidl::manager::V1_0::IServiceManager; +using namespace std::literals::chrono_literals; + +static bool sIsBatteryListened = false; +namespace android { + +#define GET_HEALTH_SVC_RETRY_CNT 5 +#define GET_HEALTH_SVC_WAIT_TIME_MS 500 + +struct BatteryListenerImpl : public hardware::health::V2_1::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 hardware::Return<void> healthInfoChanged_2_1( + const hardware::health::V2_1::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_1::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 = IHealth::getService(); + if (mHealth != NULL) + break; + usleep(GET_HEALTH_SVC_WAIT_TIME_MS * 1000); + tries++; + } while(tries < GET_HEALTH_SVC_RETRY_CNT); + + if (mHealth == NULL) { + LOC_LOGe("no health service found, retries %d", tries); + return NO_INIT; + } else { + LOC_LOGi("Get health service in %d tries", tries); + } + mStatus = BatteryStatus::UNKNOWN; + auto ret = mHealth->getChargeStatus([&](Result r, BatteryStatus status) { + if (r != Result::SUCCESS) { + LOC_LOGe("batterylistener: cannot get battery status"); + return; + } + mStatus = status; + }); + if (!ret.isOk()) { + LOC_LOGe("batterylistener: get charge status transaction error"); + } + if (mStatus == BatteryStatus::UNKNOWN) { + LOC_LOGw("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; + [[clang::fallthrough]]; //explicit fall-through between switch labels + } + default: + bool c = statusToBool(local_status); + LOC_LOGi("healthInfo cb thread: cb %s", c ? "CHARGING" : "NOT CHARGING"); + l.unlock(); + mCb(c); + l.lock(); + break; + } + } + }); + auto reg = mHealth->registerCallback(this); + if (!reg.isOk()) { + LOC_LOGe("Transaction error in registeringCb to HealthHAL death: %s", + reg.description().c_str()); + } + + auto linked = mHealth->linkToDeath(this, 0 /* cookie */); + if (!linked.isOk() || linked == false) { + LOC_LOGe("Transaction error in linking to HealthHAL death: %s", + linked.description().c_str()); + } + 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->unregisterCallback(this); + auto r = mHealth->unlinkToDeath(this); + if (!r.isOk() || r == false) { + LOC_LOGe("Transaction error in unregister to HealthHAL death: %s", + r.description().c_str()); + } + } + 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())) { + LOC_LOGe("health not initialized or unknown interface died"); + return; + } + LOC_LOGi("health service died, reinit"); + mDone = true; + } + mHealth = NULL; + mCond.notify_one(); + 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) { + LOC_LOGv("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(); +} + +Return<void> BatteryListenerImpl::healthInfoChanged_2_1( + const hardware::health::V2_1::HealthInfo& info) { + LOC_LOGv("healthInfoChanged_2_1: %d", info.legacy.legacy.batteryStatus); + healthInfoChanged(info.legacy); + return Void(); +} + +static sp<BatteryListenerImpl> batteryListener; + +bool batteryPropertiesListenerIsCharging() { + return batteryListener->isCharging(); +} + +status_t batteryPropertiesListenerInit(BatteryListenerImpl::cb_fn_t cb) { + batteryListener = new BatteryListenerImpl(cb); + bool isCharging = batteryPropertiesListenerIsCharging(); + LOC_LOGv("charging status: %s charging", isCharging ? "" : "not");; + if (isCharging) { + cb(isCharging); + } + return NO_ERROR; +} + +status_t batteryPropertiesListenerDeinit() { + batteryListener.clear(); + return OK; +} + +} // namespace android + +void loc_extn_battery_properties_listener_init(battery_status_change_fn_t fn) { + LOC_LOGv("loc_extn_battery_properties_listener_init entry"); + if (!sIsBatteryListened) { + std::thread t1(android::batteryPropertiesListenerInit, + [=](bool charging) { fn(charging); }); + t1.detach(); + sIsBatteryListened = true; + } +} + +void loc_extn_battery_properties_listener_deinit() { + android::batteryPropertiesListenerDeinit(); +} + +bool loc_extn_battery_properties_is_charging() { + return android::batteryPropertiesListenerIsCharging(); +} diff --git a/gps/android/utils/battery_listener.h b/gps/android/utils/battery_listener.h new file mode 100644 index 0000000..bb6b715 --- /dev/null +++ b/gps/android/utils/battery_listener.h @@ -0,0 +1,32 @@ +/* +* Copyright (c) 2019, 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. +*/ +typedef void (* battery_status_change_fn_t)(bool); +void loc_extn_battery_properties_listener_init(battery_status_change_fn_t fn); +void loc_extn_battery_properties_listener_deinit(); +bool loc_extn_battery_properties_is_charging(); |