Skip to content
This repository was archived by the owner on Oct 8, 2023. It is now read-only.

Commit

Permalink
hardware: Implement our own charging HAL
Browse files Browse the repository at this point in the history
This commit is a charging control HAL for Sony devices that controls
charging by making use of interfaces exposed by Sony's kernel.

This HAL provides higher level interfaces for disabling/enabling
charging by writing to specific kernel interface. Furthermore, it
references pixel's powerstat implementation, uses uevent to monitor
power_supply uevents, and provides an interface to allow users to set
custom charging limit.

Change-Id: Iea037e400326352443b9810a98401cfa15fa6ac5
  • Loading branch information
hellobbn authored and spectredev-007 committed Nov 30, 2022
1 parent fb2ebe5 commit c05f531
Show file tree
Hide file tree
Showing 23 changed files with 562 additions and 0 deletions.
4 changes: 4 additions & 0 deletions interfaces/sony/Android.bp
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
hidl_package_root {
name: "vendor.sony",
path: "hardware/sony/interfaces/sony",
}
1 change: 1 addition & 0 deletions interfaces/sony/Charger/.clang-format
26 changes: 26 additions & 0 deletions interfaces/sony/Charger/Android.bp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (C) 2022 The LineageOS Project
*
* SPDX-License-Identifier: Apache-2.0
*/

cc_binary {
name: "vendor.sony.charger-service",
relative_install_path: "hw",
init_rc: ["vendor.sony.charger-service.rc"],
vintf_fragments: ["vendor.sony.charger-service.xml"],
vendor: true,
shared_libs: [
"libbase",
"libcutils",
"liblog",
"libutils",
"libbinder_ndk",
"vendor.sony.charger-V1-ndk",
],
srcs: [
"service.cpp",
"Charger.cpp",
"UeventListener.cpp",
]
}
166 changes: 166 additions & 0 deletions interfaces/sony/Charger/Charger.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/*
* Copyright (C) 2022 The LineageOS Project
*
* SPDX-License-Identifier: Apache-2.0
*/

#include "Charger.h"

#include <android-base/logging.h>
#include <android/binder_status.h>
#include <cutils/properties.h>
#include <cutils/uevent.h>
#include <string.h>
#include <fstream>

namespace aidl {
namespace vendor {
namespace sony {
namespace charger {

#define LOG_TAG "vendor.sony.charger"

enum SUPPORTED_BOARD {
kona = 0,
lahaina = 1,
};

static constexpr const char* kChgActivationPath[] = {
"/sys/class/power_supply/battery_ext/smart_charging_activation",
"/sys/class/battchg_ext/smart_charging_activation",
};

static constexpr const char* kChgInterruptionPath[] = {
"/sys/class/power_supply/battery_ext/smart_charging_interruption",
"/sys/class/battchg_ext/smart_charging_interruption",
};

static constexpr const char* kBatCapacityPath[] = {
"/sys/class/power_supply/battery/capacity",
"/sys/class/power_supply/battery/capacity",
};

Charger::Charger() {
LOG(INFO) << "Charger HAL Starting...";
char board_name[PROPERTY_VALUE_MAX];

property_get("ro.product.board", board_name, "null");

if (strncmp(board_name, "kona", strlen("kona")) == 0) {
chargerBoard = kona;
} else if (strncmp(board_name, "lahaina", strlen("lahaina")) == 0) {
chargerBoard = lahaina;
} else {
LOG(ERROR) << "Failed to get board number, default to kona";
chargerBoard = kona;
}
chgLimit = 100;
chgStopReason = 0;
}

bool Charger::updateChargingStatus() {
// If chgStopReason is not 0, we need to stop charging
bool enable = chgStopReason == 0;

return setChargingEnableInternal(enable);
}

bool Charger::setChargingEnableInternal(bool enabled) {
LOG(INFO) << (enabled ? "Enable" : "Disable") << " charging";
std::fstream activationFile(kChgActivationPath[chargerBoard]);
std::fstream interruptionFile(kChgInterruptionPath[chargerBoard]);
int activationResult = -1;
int interruptionResult = -1;

if (!activationFile.is_open() || !interruptionFile.is_open()) goto error;

activationFile >> activationResult;
interruptionFile >> interruptionResult;

if (activationFile.fail() || interruptionFile.fail()) goto error;

if (activationResult == 0) {
activationFile << "1";
if (activationFile.fail()) goto error;
}

if (interruptionResult == enabled) {
interruptionFile << (enabled ? "0" : "1");
if (interruptionFile.fail()) goto error;
}

interruptionFile.close();
activationFile.close();

return true;

error:
LOG(ERROR) << "Failed to read or write charging status: " << strerror(errno);
return false;
}

ndk::ScopedAStatus Charger::isChargingEnabled(bool* _aidl_return) {
*_aidl_return = chgStopReason & CHG_STOP_REASON_FORCE;
return ndk::ScopedAStatus::ok();
}

ndk::ScopedAStatus Charger::setChargingEnable(bool enabled) {
if (enabled)
chgStopReason &= ~CHG_STOP_REASON_FORCE; // clear bit, enable charging
else
chgStopReason |= CHG_STOP_REASON_FORCE; // Set bit, disable charging

if (updateChargingStatus())
return ndk::ScopedAStatus::ok();
else
return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
}

ndk::ScopedAStatus Charger::setChargingLimit(int32_t limit) {
LOG(INFO) << "Setting charging limit to " << limit;
if (limit > 100 || limit < 50) {
LOG(ERROR) << "The charging limit " << limit << " is not supported!";
LOG(ERROR) << " Please select between 50 and 100";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}

chgLimit = limit;

return checkBatCapacityAndApplyLimit();
}

ndk::ScopedAStatus Charger::checkBatCapacityAndApplyLimit() {
std::ifstream batCapFile(kBatCapacityPath[chargerBoard]);
int batCapNow = -1;

batCapFile >> batCapNow;
LOG(INFO) << "Capacity: " << batCapNow << ". Limit: " << chgLimit;

if (chgLimit == 100) {
// We don't limit battery if the limit is 100
chgStopReason &= ~CHG_STOP_REASON_LIMIT;
} else {
if (!batCapFile.is_open()) goto error;

if (batCapNow >= chgLimit) {
// Set limit
chgStopReason |= CHG_STOP_REASON_LIMIT;
} else {
chgStopReason &= ~CHG_STOP_REASON_LIMIT;
}
}

if (updateChargingStatus())
return ndk::ScopedAStatus::ok();
else
return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);

error:
LOG(ERROR) << "Failed to read charging status: " << strerror(errno);
return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
}

} // namespace charger
} // namespace sony
} // namespace vendor
} // namespace aidl
39 changes: 39 additions & 0 deletions interfaces/sony/Charger/Charger.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (C) 2022 The LineageOS Project
*
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <aidl/vendor/sony/charger/BnCharger.h>
#include <android-base/file.h>
#include "android/binder_status.h"

namespace aidl {
namespace vendor {
namespace sony {
namespace charger {

#define CHG_STOP_REASON_FORCE (1 << 0)
#define CHG_STOP_REASON_LIMIT (1 << 1)

struct Charger : public BnCharger {
Charger();
ndk::ScopedAStatus setChargingEnable(bool enabled) override;
ndk::ScopedAStatus isChargingEnabled(bool* _aidl_return) override;
ndk::ScopedAStatus setChargingLimit(int32_t limit) override;
ndk::ScopedAStatus checkBatCapacityAndApplyLimit() override;

private:
bool updateChargingStatus();
bool setChargingEnableInternal(bool enabled);
int chargerBoard;
int chgLimit;
int chgStopReason;
};

} // namespace charger
} // namespace sony
} // namespace vendor
} // namespace aidl
112 changes: 112 additions & 0 deletions interfaces/sony/Charger/UeventListener.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright (C) 2018 The Android Open Source Project
* Copyright (C) 2022 The LineageOS 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.
*/

#define LOG_TAG "sonycharger-uevent"

#include "UeventListener.h"
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <cutils/uevent.h>

namespace aidl {
namespace vendor {
namespace sony {
namespace charger {

UeventListener::UeventListener() : uevent_fd_(-1) {}

std::shared_ptr<ICharger> getChargerService() {
const std::string instance = std::string() + ICharger::descriptor + "/default";
static bool isChargerDeclared = false;
if (!isChargerDeclared) {
// It is good to cache the result - it would not be changed
isChargerDeclared = AServiceManager_isDeclared(instance.c_str());
if (!isChargerDeclared) {
LOG(ERROR) << "Charger service is not registered";
return nullptr;
}
}

return ICharger::fromBinder(ndk::SpAIBinder(AServiceManager_waitForService(instance.c_str())));
}

void UeventListener::handlePowerSupplyChange() {
LOG(INFO) << "handlePowerSupplyChange HERE";
const std::shared_ptr<ICharger> charger_client = getChargerService();
charger_client->checkBatCapacityAndApplyLimit();
}

#define UEVENT_MSG_LEN 2048
bool UeventListener::ProcessUevent() {
char msg[UEVENT_MSG_LEN + 2];
char* cp;
int n;

if (uevent_fd_ < 0) {
uevent_fd_ = uevent_open_socket(64 * 1024, true);
if (uevent_fd_ < 0) {
LOG(ERROR) << "uevent_init: uevent_open_socket failed\n";
return false;
}
}

n = uevent_kernel_multicast_recv(uevent_fd_, msg, UEVENT_MSG_LEN);
if (n <= 0 || n >= UEVENT_MSG_LEN) return false;

// Ensure double-null termination of msg
msg[n] = '\0';
msg[n + 1] = '\0';

/**
* msg is a sequence of null-terminated strings.
* Iterate through and record positions of string/value pairs of interest.
* Double null indicates end of the message. (enforced above)
*/
cp = msg;
while (*cp) {
if (!strncmp(cp, "SUBSYSTEM=power_supply", strlen("SUBSYSTEM=power_supply"))) {
handlePowerSupplyChange();
break;
}

while (*cp++)
;
}

return true;
};

void UeventListener::ListenForever() {
constexpr int kMaxConsecutiveErrors = 10;
int consecutive_errors = 0;

while (1) {
if (ProcessUevent()) {
consecutive_errors = 0;
} else {
if (++consecutive_errors == kMaxConsecutiveErrors) {
LOG(ERROR) << "Too many ProcessUevent errors; exiting UeventListener";
return;
}
}
}
}

} // namespace charger
} // namespace sony
} // namespace vendor
} // namespace aidl
Loading

0 comments on commit c05f531

Please sign in to comment.