1 整体架构

Android的电池架构包括从顶层用户使用App需要调用的电池相关的Android sdk api到最底层的硬件驱动,分别涉及到Android FrameworkAndroid HALLinux Kernel Driver这几个部分。

本文基于基于Android 8.1系统,Rockchip平台进行分析,其中,对于硬件平台的电源驱动移植或者电源驱动编写,其主要关心的部分还是Linux Kernel Driver,这个部分,通常需要实现charger单元电量计这两个部分的功能。整体的架构简单整理了一下如下图所示;

2 设计构架

这节大致介绍一下,电池的各种状态以及充电的状态信息如何从内核空间上报到用户空间。通俗的讲就是如何从内核驱动将各种状态数据上报到Android层Android系统与传统Linux不同,对于内核空间和用户空间的数据交互,并不再使用udev的机制,而是使用了uevent的方式 ,当电池状态发生变化,可以实时更新到用户空间,具体的数据架构如下图所示;

2.1 driver

内核需要注册充电IC驱动电量计驱动,分别为Charger.koBattery.ko,当然,驱动的名字可以自定义命名,并不是不固定为Charger.koBattery.ko。这两个驱动主要完成设备的电源插入拔出检测,电池充放电,电池电量检测,然后通过注册power supply class将各个数据通过uevent的方式上传到用户空间。下面简单介绍一下驱动需要完成的工作,详细内容可以参考具体的驱动源码分析。

2.1.1 Charger.ko

Charger 驱动需要完成以下几个工作,并上报给用户空间:

  • Charger IC的初始化;

  • 设备电源的插入和拔出检测;

  • 设备电源类型的检测:ac(dcp),dc,usb(cdp,sdp)

  • 充放电电流的设置;

    • 充电四个阶段:涓流,恒流,恒压,截止
    • 放电:过放检测,NTC补偿等等;
  • 当前充电状态的查询,包括未充电,充电,充满等等,这个需要预留接口,电量计驱动会使用到;

  • 注册acusb这两个power supply类用于数据上报;

    在这里,Charger在实际工作中还需要完成的任务还有很多,这里暂时不一一列举了,详细参考Charger 驱动源码解析

2.1.2 Battery.ko

电量计驱动需要完成很大一部分的工作,电量计实现比较复杂,这里简单列举一下:

  • 充电曲线平滑算法;
  • 放电曲线平滑算法;
  • 电池基本信息上报,包括电池容量,充放电状态,电池百分比等等;

2.2 power supply

power supply frameworkkernel/drivers/power/下。内核抽象出来power supply子系统为驱动提供了统一的框架。功能包括:

  • 抽象PSY设备的共性,向用户空间提供统一的API;
  • 为底层PSY驱动的编写,提供简单、统一的方式。同事封装并实现公共逻辑

2.2.1 基础架构

power supply class位于drivers/power/目录中,主要由3部分组成(可参考下图的软件架构):

  1. power supply core,用于抽象核心数据结构、实现公共逻辑。位于drivers/power/power_supply_core.c中。
  2. power supply sysfs,实现sysfs以及uevent功能。位于drivers/power/power_supply_sysfs.c中。
  3. power supply leds,基于linux led class,提供PSY设备状态指示的通用实现。位于drivers/power/power_suppply_leds.c中。

根据以上的结构并在内核成功注册之后,会在sysfs系统中注册以下节点ac,battery,usb,如下图所示:



进入battery路径下可以发现很多文件,如下图所示:



这里是系统为了方便调试,可以读取这些文件的内容,这些大多数内容可以在Linux内核源码/kernel/include/power_supply.h中查阅到,下面做个简单介绍:

  1. capacity:当前电池容量
  2. charge_full:充满电的电池容量
  3. temp:电池温度
  4. uevent:上报uevent需要用到的数据
  5. charge_counter:充电次数
  6. current_now:当前充电电流
  7. health status:电池健康状态,包括coldhot ,Good
  8. technology:电池类型,包括锂电池,镍铬电池等等
  9. voltage_now:当前电池电压

2.2.2 代码分析

这里拿ac作为例子来解释一下如何进行power_supply的注册,总共分为四个部分:

  1. 根据硬件特定的情况,确定psy设备具备哪些特性,哪些数据需要上报,并把他们和enum power_supply_property对应;
  2. 根据实际的情况,去实现这些propertiesget/set的接口,具体参考bq24296_charge_usb_get_property函数的实现;
  3. 定义一个struct power_supply_desc变量,并初始化必要成员后,调用devm_power_supply_register将其注册到kernel中;
  4. 根据实际情况,启动设备属性变化的监控函数,可以使用中断,轮询等,并在发生改变时,调用power_supply_changed,通知power suopply core,具体可以参考bq24296_charge_set_chrg_param

下面是相应的代码实现;

static enum power_supply_property bq24296_usb_props[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_CURRENT_MAX,
}; static int bq24296_charge_usb_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val){
struct bq24296_device_info *charge = power_supply_get_drvdata(psy);
int ret = 0; switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
val->intval = charge->usb_in;
break;
case POWER_SUPPLY_PROP_STATUS:
val->intval = charge->prop_status;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
val->intval = charge->max_chrg_voltage;
break;
case POWER_SUPPLY_PROP_CURRENT_MAX:
val->intval = charge->max_chrg_current;
break;
default:
ret = -EINVAL;
break;
}
return ret;
} static const struct power_supply_desc bq24296_usb_desc = {
.name = "usb",
.type = POWER_SUPPLY_TYPE_USB,
.properties = bq24296_usb_props,
.num_properties = ARRAY_SIZE(bq24296_usb_props),
.get_property = bq24296_charge_usb_get_property,
}; static int bq24296_charge_init_power_supply(struct bq24296_device_info *charge){ struct power_supply_config psy_cfg = { .drv_data = charge, };
charge->usb_psy = devm_power_supply_register(charge->dev,
&bq24296_usb_desc,
&psy_cfg);
if (IS_ERR(charge->usb_psy)) {
dev_err(charge->dev, "register usb power supply fail\n");
return PTR_ERR(charge->usb_psy);
}
} static void bq24296_charge_set_chrg_param(struct bq24296_device_info *charge, enum charger_t charger, int det_pin)
{
switch (charger) {
case USB_TYPE_NONE_CHARGER:
charge->usb_in = 0;
charge->ac_in = 0;
if (charge->dc_in == 0) {
charge->prop_status = POWER_SUPPLY_STATUS_DISCHARGING;// 2
}
power_supply_changed(charge->usb_psy);
power_supply_changed(charge->ac_psy);
break;
case USB_TYPE_USB_CHARGER:
case USB_TYPE_CDP_CHARGER:
charge->usb_in = 1;
charge->ac_in = 0;
if (det_pin)
bq24296_set_prop_status(charge);
else
charge->prop_status = POWER_SUPPLY_STATUS_CHARGING;
power_supply_changed(charge->usb_psy);
power_supply_changed(charge->ac_psy);
break;
case USB_TYPE_AC_CHARGER:
charge->ac_in = 1;
charge->usb_in = 0;
if (det_pin)
bq24296_set_prop_status(charge);
else
charge->prop_status = POWER_SUPPLY_STATUS_CHARGING;
power_supply_changed(charge->usb_psy);
power_supply_changed(charge->ac_psy);
break;
case DC_TYPE_DC_CHARGER:
charge->dc_in = 1;
if (det_pin)
bq24296_set_prop_status(charge);
else
charge->prop_status = POWER_SUPPLY_STATUS_CHARGING;
power_supply_changed(charge->usb_psy);
power_supply_changed(charge->ac_psy);
break;
case DC_TYPE_NONE_CHARGER:
charge->dc_in = 0;
charge->ac_in = 0;
charge->usb_in = 0;
charge->prop_status = POWER_SUPPLY_STATUS_DISCHARGING;
power_supply_changed(charge->usb_psy);
power_supply_changed(charge->ac_psy);
break;
default:
charge->prop_status = POWER_SUPPLY_STATUS_DISCHARGING;
break;
}
}

2.3 healthd

healthd是android4.4之后提出来的一种中介模型,安卓源码路径下system/core/healthd,有兴趣可以分析一下源码,healthd已经属于Android层面的进程了,参考了资料上和代码中隐约看到了通过binder机制去调用healthd监听底层的上报的信息,篇幅有限,需要单独参考源码进行分析。

2.3.1 基础架构

首先看到安卓源码路径下的health,文件列表如下所示;

/home/AndroidSource/system/core/healthd# tree
.
├── Android.mk
├── animation.h
├── AnimationParser.cpp
├── AnimationParser.h
├── BatteryMonitor.cpp
├── BatteryPropertiesRegistrar.cpp
├── BatteryPropertiesRegistrar.h
├── charger.cpp
├── healthd_common.cpp
├── healthd.cpp
├── healthd_draw.cpp
├── healthd_draw.h
├── healthd_mode_android.cpp
├── healthd_mode_charger.cpp

可以看到文件比较多,主要是通过BatteryMonitor.cpp中的bool BatteryMonitor::update(void)函数上报信息,其中,内核首先会更新数据到/sys/class/power_supply/battery节点下各个属性,这个在上一个小节有做解释,先来看一下整体的架构,后面再来深入到代码中去分析;具体图片(该图片来自互联网,因为被转载较多,已经不知道出处),具体的流程整理的很清楚,如下所示;



这幅图片再一次把整体的数据走向具体化,可以看到主要负责工作的是BatteryMonitor,主要分析一下该文件中的initupdate就可以搞清楚大部分的问题。

2.3.2 init

下面是init()函数的具体实现;

void BatteryMonitor::init(struct healthd_config *hc) {
String8 path;
char pval[PROPERTY_VALUE_MAX]; mHealthdConfig = hc;
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(POWER_SUPPLY_SYSFS_PATH), closedir);
if (dir == NULL) {
KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH);
} else {
struct dirent* entry; while ((entry = readdir(dir.get()))) {
const char* name = entry->d_name; if (!strcmp(name, ".") || !strcmp(name, ".."))
continue; // Look for "type" file in each subdirectory
path.clear();
path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name);
switch(readPowerSupplyType(path)) {
case ANDROID_POWER_SUPPLY_TYPE_AC:
case ANDROID_POWER_SUPPLY_TYPE_USB:
case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
path.clear();
path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
if (access(path.string(), R_OK) == 0)
mChargerNames.add(String8(name));
break; case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
mBatteryDevicePresent = true; if (mHealthdConfig->batteryStatusPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryStatusPath = path;
} if (mHealthdConfig->batteryHealthPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryHealthPath = path;
} if (mHealthdConfig->batteryPresentPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,
name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryPresentPath = path;
} if (mHealthdConfig->batteryCapacityPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,
name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryCapacityPath = path;
} if (mHealthdConfig->batteryVoltagePath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/voltage_now",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0) {
mHealthdConfig->batteryVoltagePath = path;
} else {
path.clear();
path.appendFormat("%s/%s/batt_vol",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryVoltagePath = path;
}
} if (mHealthdConfig->batteryFullChargePath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/charge_full",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryFullChargePath = path;
} if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/current_now",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryCurrentNowPath = path;
} if (mHealthdConfig->batteryCycleCountPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/cycle_count",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryCycleCountPath = path;
} if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/current_avg",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryCurrentAvgPath = path;
} if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/charge_counter",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryChargeCounterPath = path;
} if (mHealthdConfig->batteryTemperaturePath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH,
name);
if (access(path, R_OK) == 0) {
mHealthdConfig->batteryTemperaturePath = path;
} else {
path.clear();
path.appendFormat("%s/%s/batt_temp",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryTemperaturePath = path;
}
} if (mHealthdConfig->batteryTechnologyPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/technology",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryTechnologyPath = path;
} break; case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
break;
}
}
} // Typically the case for devices which do not have a battery and
// and are always plugged into AC mains.
if (!mBatteryDevicePresent) {
KLOG_WARNING(LOG_TAG, "No battery devices found\n");
hc->periodic_chores_interval_fast = -1;
hc->periodic_chores_interval_slow = -1;
mBatteryFixedCapacity = ALWAYS_PLUGGED_CAPACITY;
mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
mAlwaysPluggedDevice = true;
} else {
if (mHealthdConfig->batteryStatusPath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
if (mHealthdConfig->batteryHealthPath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n");
if (mHealthdConfig->batteryPresentPath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n");
if (mHealthdConfig->batteryCapacityPath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");
if (mHealthdConfig->batteryVoltagePath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");
if (mHealthdConfig->batteryTemperaturePath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
if (mHealthdConfig->batteryTechnologyPath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
if (mHealthdConfig->batteryCurrentNowPath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryCurrentNowPath not found\n");
if (mHealthdConfig->batteryFullChargePath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryFullChargePath not found\n");
if (mHealthdConfig->batteryCycleCountPath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryCycleCountPath not found\n");
} if (property_get("ro.boot.fake_battery", pval, NULL) > 0
&& strtol(pval, NULL, 10) != 0) {
mBatteryFixedCapacity = FAKE_BATTERY_CAPACITY;
mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
}
}

在这个函数中,我们可以清楚地看到,对于battery下每一个属性,在init函数中都会提前获取到相应的路径,然后再去读取属性值去做对应的更新,这里通过update函数进行上报。

2.3.4 update

update函数具体实现如下;

bool BatteryMonitor::update(void) {
bool logthis; initBatteryProperties(&props); if (!mHealthdConfig->batteryPresentPath.isEmpty())
props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
else
props.batteryPresent = mBatteryDevicePresent; props.batteryLevel = mBatteryFixedCapacity ?
mBatteryFixedCapacity :
getIntField(mHealthdConfig->batteryCapacityPath);
props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000; if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
props.batteryCurrent = getIntField(mHealthdConfig->batteryCurrentNowPath) / 1000; if (!mHealthdConfig->batteryFullChargePath.isEmpty())
props.batteryFullCharge = getIntField(mHealthdConfig->batteryFullChargePath); if (!mHealthdConfig->batteryCycleCountPath.isEmpty())
props.batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath); if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
props.batteryChargeCounter = getIntField(mHealthdConfig->batteryChargeCounterPath); props.batteryTemperature = mBatteryFixedTemperature ?
mBatteryFixedTemperature :
getIntField(mHealthdConfig->batteryTemperaturePath); // For devices which do not have battery and are always plugged
// into power souce.
if (mAlwaysPluggedDevice) {
props.chargerAcOnline = true;
props.batteryPresent = true;
props.batteryStatus = BATTERY_STATUS_CHARGING;
props.batteryHealth = BATTERY_HEALTH_GOOD;
} std::string buf; if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
props.batteryStatus = getBatteryStatus(buf.c_str()); if (readFromFile(mHealthdConfig->batteryHealthPath, &buf) > 0)
props.batteryHealth = getBatteryHealth(buf.c_str()); if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
props.batteryTechnology = String8(buf.c_str()); unsigned int i;
double MaxPower = 0; for (i = 0; i < mChargerNames.size(); i++) {
String8 path;
path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
mChargerNames[i].string());
if (getIntField(path)) {
path.clear();
path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
mChargerNames[i].string());
switch(readPowerSupplyType(path)) {
case ANDROID_POWER_SUPPLY_TYPE_AC:
props.chargerAcOnline = true;
break;
case ANDROID_POWER_SUPPLY_TYPE_USB:
props.chargerUsbOnline = true;
break;
case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
props.chargerWirelessOnline = true;
break;
default:
KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
mChargerNames[i].string());
}
path.clear();
path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
mChargerNames[i].string());
int ChargingCurrent =
(access(path.string(), R_OK) == 0) ? getIntField(path) : 0; path.clear();
path.appendFormat("%s/%s/voltage_max", POWER_SUPPLY_SYSFS_PATH,
mChargerNames[i].string()); int ChargingVoltage =
(access(path.string(), R_OK) == 0) ? getIntField(path) :
DEFAULT_VBUS_VOLTAGE; double power = ((double)ChargingCurrent / MILLION) *
((double)ChargingVoltage / MILLION);
if (MaxPower < power) {
props.maxChargingCurrent = ChargingCurrent;
props.maxChargingVoltage = ChargingVoltage;
MaxPower = power;
}
}
} logthis = !healthd_board_battery_update(&props); if (logthis) {
char dmesgline[256];
size_t len;
if (props.batteryPresent) {
snprintf(dmesgline, sizeof(dmesgline),
"battery l=%d v=%d t=%s%d.%d h=%d st=%d",
props.batteryLevel, props.batteryVoltage,
props.batteryTemperature < 0 ? "-" : "",
abs(props.batteryTemperature / 10),
abs(props.batteryTemperature % 10), props.batteryHealth,
props.batteryStatus); len = strlen(dmesgline);
if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
" c=%d", props.batteryCurrent);
} if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
" fc=%d", props.batteryFullCharge);
} if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
" cc=%d", props.batteryCycleCount);
}
} else {
len = snprintf(dmesgline, sizeof(dmesgline),
"battery none");
} snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s",
props.chargerAcOnline ? "a" : "",
props.chargerUsbOnline ? "u" : "",
props.chargerWirelessOnline ? "w" : ""); KLOG_WARNING(LOG_TAG, "%s\n", dmesgline);
} healthd_mode_ops->battery_update(&props);
return props.chargerAcOnline | props.chargerUsbOnline |
props.chargerWirelessOnline;
}

2.4 framework

对于Framework的分析,暂时还没有深入到代码去做研究,这里网上资料比较多,但是有必要再去debug一下代码,这样才能知己知彼,百战不殆,可以参考这篇文章5,下面顺便贴一下文章中的一张图片,感觉瞬间把思路都理清楚了。

3 总结

Android的电源管理涉及到的东西比较杂乱,单纯进行硬件驱动的开发只需要关心power supply类的注册和具体的驱动实现,本文大概分成两个部分进行展开说明,一,整体架构的实现包括软件的分层;二,设计上的架构,包括驱动的设计以及设备数据如何上报的用户空间,这里还有一个问题没有阐述清楚,就是healthd的数据提交的具体实现过程,后面有时间再更新。由于水平有限,难免存在纰漏,错误之处,希望斧正。

参考

[1]:Android Uevent 分析,从kernel到framework

[2]:linux设备驱动uevent详解,高通平台battery上报电量实例

[3]:Linux uevent机制

[4]:Android 电池(三):android电池系统

[5]:android-power-and-battery-management-a-look-underneath-the-hood

[6]:power_management

Android 电池管理系统架构总结 Android power and battery management architecture summaries的更多相关文章

  1. android电池管理系统从上层的java到底层驱动的调用(转载)

    1.概述 随着移动智能设备的快速发屏,电池的续航能力在很大情况下诱导了大众消费者的购买选择,android系统对电源管理的合理与否直接影响到电池的续航能力,而电池系统作为其中的一部分,主要用于对电池状 ...

  2. android电池管理系统

    原文:http://www.2cto.com/kf/201408/326462.html 1.概述 随着移动智能设备的快速发屏,电池的续航能力在很大情况下诱导了大众消费者的购买选择,android系统 ...

  3. Android面试题3之描写叙述下Android的系统架构

    描写叙述下Android的系统架构: Android系统从下往上分为Linux内核层(linux kerner),执行库(runtime library),应用程序框架层,应用程序层 linuxker ...

  4. 【转】android 电池(三):android电池系统

    关键词:android电池系统电池系统架构 uevent power_supply驱动 平台信息: 内核:linux2.6/linux3.0系统:android/android4.0 平台:S5PV3 ...

  5. Android系统框架构

    写此本文是为了对Android系统框架有一个整体的认识和了解,对于开发和测试人员脑子里要有整体认识以便对工作有所帮助. 进入正题 首先Android系统架构采用了分层架构的思想,共分为四层由上到下分: ...

  6. android 电池(三):android电池系统【转】

    本文转载自:http://blog.csdn.net/xubin341719/article/details/8709838 一.电池系统结构 Android中的电池使用方式主要有三种:AC.USB. ...

  7. Android四层体系架构

    Application应用层 应用是用Java语言编写的运行在虚拟机上的程序,即图中最上层的蓝色部分.手机的上层应用其实,Google最开始时就在Android系统中捆绑了一些核心应用比如e-mail ...

  8. 【转】android电池(四):电池 电量计(MAX17040)驱动分析篇

    关键词:android 电池  电量计  MAX17040 任务初始化宏 power_supply 平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台: ...

  9. 【转】android 电池(二):android关机充电流程、充电画面显示

    关键词:android 电池关机充电 androidboot.mode charger关机充电 充电画面显示 平台信息:内核:linux2.6/linux3.0系统:android/android4. ...

随机推荐

  1. 74HC595芯片的特性及使用方法和点评

    一 它能干什么?   74HC595是一个8位串行输入.平行输出的位移缓存器:平行输出为三态输出.在SCK的上升沿,单行数据由SDL输人到内部的8位位移缓存器,并由Q7‘输出,而平行输出则是在LCK的 ...

  2. 【Jenkins】插件更改国内源

    最近调试脚本,本机安装了Jenkins,但是安装插件时一直失败.更改升级站点也不生效,究其原因是因为default.json中插件下载地址还https://updates.jenkins.io,升级站 ...

  3. The Super Powers UVA - 11752

    题目大意:将范围从1~pow(2,64)-1内的super power输出.super power的定义:一个数x至少存在两种x=pow(i,k),(k!=1). 题解: 注意数据范围2的64次方-1 ...

  4. C - Highways poj1751最小生成树

    The island nation of Flatopia is perfectly flat. Unfortunately, Flatopia has a very poor system of p ...

  5. 原创Pig0.16.0安装搭建

    tar -zxvf pig-0.16.0.tar.gz -C ~   vi ~/.bash_profile export PIG_HOME=/home/hadoop/pig-0.16.0 export ...

  6. 论JDK5/7/8版本都做出了哪些革新

    在Java发展的里程碑上,有三个版本做出的改动,是革命性的 为什么说是革命性的呢? 因为这三个版本所推出的有些新机制,在之后的Java框架开发.新类的产生等等中, 都被广泛使用了. 那么,这三个版本的 ...

  7. 如何在非 sudo 用户下运行 docker 命令?

    当我们在一台 Linux 系统中安装了 Docker 后, 有时候会遇到下面这样的错误, 我们在运行 docker 的命令时必须加上 sudo, 例如: sudo docker ps, 但是我们其实更 ...

  8. iview使用之怎样通过render函数在tabs组件中添加标签

    在实际项目开发中我们通常会遇到一些比较'新颖'的需求,而这时iview库里往往没有现成可用的组件示例,所以我们就需要自己动手翻阅IviewAPI进行自定义一些组件,也可以说是将iview库里的多种组件 ...

  9. 挑战全网最幽默的Vuex系列教程:第五讲 Vuex的小帮手

    先说两句 前面已经讲完了 Vuex 下的 State.Getter.Mutation 及 Action 这四驾马车,不知道大家是否已经理解.当然,要想真正熟练掌握的话,还是需要不断的练习和动手实践才行 ...

  10. .NetCore对接各大财务软件凭证API——金蝶系列(1)

    哈喽,又和大家见面了,虽然看文章的小伙伴不多,但是我相信总有一天,自己写的这些文章或多或少会对其他人有些帮助,让他们在相关的业务开发下能少走些弯路,那我的目的就达到了,好了,今天就正式开始我们的系列了 ...