一、電池系統結構
Android中的電池使用方式主要有三種:AC、USB、Battery
等不同的模式。在應用程式層次,通常包括了電池狀态顯示的功能。是以從 Android
系統的軟體方面(包括驅動程式和使用者空間内容)需要在一定程度上獲得電池的狀态,電池系統主要負責電池資訊統計、顯示。電池系統的架構如下所示:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5iZyQDNzMDZzEWOwcTZiFDM3UDMzUDO4UTZxQmNyMGNh9CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
自下而上, Android 的電池系統分成以下幾個部分:
1、驅動程式:
特定硬體平台電池的驅動程式,用 Linux的Power Supply 驅動程式,實作向使用者空間提供資訊。Battery
驅動程式需要通過sys檔案系 統向使用者空間提供接口, sys檔案系統的路徑是由上層的程式指定的。Linux标準的 Power
Supply驅動程式 所使用的檔案系統路徑為:/sys/class/power_supply
,其中的每個子目錄表示一種能源供應裝置的名稱。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5iZyQDNzMDZzEWOwcTZiFDM3UDMzUDO4UTZxQmNyMGNh9CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
Power Supply 驅動程式的頭檔案在
include/linux/power_supply.h中定義,注冊和登出驅動程式的函數如下所示:
intpower_supply_register(structdevice *parent,structpower_supply *psy);
voidpower_supply_unregister(structpower_supply *psy);
structpower_supply {
constchar*name;
enumpower_supply_type type;
enumpower_supply_property *properties;
size_tnum_properties;
char**supplied_to;
size_tnum_supplicants;
int(*get_property)(structpower_supply *psy,
enumpower_supply_property psp,
unionpower_supply_propval *val);
void(*external_power_changed)(structpower_supply *psy);
};
Linux中驅動程式:power_supply
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5iZyQDNzMDZzEWOwcTZiFDM3UDMzUDO4UTZxQmNyMGNh9CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
2、本地代碼 - JNI
代碼路徑:
frameworks/base/services/jni/com_android_server_BatteryService.cpp
這個類調用sys檔案系統通路驅動程式,也同時提供了JNI的接口。
這個檔案提供的方法清單如下所示:
staticJNINativeMethod sMethods[] = {
{"native_update","()V", (void*)android_server_BatteryService_update},
};
上面的JNINativeMethod[cpp]view plaincopy
typedefstruct{
constchar* name;
constchar* signature;
void* fnPtr;
} JNINativeMethod;
name對應java中的method
name; signature是參數清單,由一串字元串表示,fnPtr是function pointer,
指向jni中的method。
最難了解的就是signature了,裡面包含JAVAC/C++之間的參數的map,即每個java類型,都有一個c/c++類型與之對應。
JNI是JAVA
Native Interface的縮寫,意思是“JAVA 本地接口”。JNI幫助JAVA能和其它程式設計語言(C++ , C,
彙編)和庫進行互動。
“()V”JNI函數的參數和傳回值,()"填的是參數(這裡為空),傳回值也為void空
處理的流程為根據裝置類型判定裝置後, 得到各個裝置的相關屬性,則需要得到更多得 資訊。例如:果是交流或者 USB 裝置,隻需
要得到它們是否線上( onLine );如果是電 池裝置,則需要得到更多的資訊,例如狀态 ( status ),健康程度(
health ),容 量( capacity ),電壓 ( voltage_now )等。
Linux 驅動 driver 維護着儲存電池資訊的一組檔案
sysfs,供應用程式擷取電源相關狀态:
#define AC_ONLINE_PATH "/sys/class/power_supply/ac/online" AC 電源連接配接狀态
#define USB_ONLINE_PATH "/sys/class/power_supply/usb/online" USB電源連接配接狀态
#define BATTERY_STATUS_PATH "/sys/class/power_supply/battery/status"充電狀态
#define BATTERY_HEALTH_PATH "/sys/class/power_supply/battery/health"電池狀态
#define BATTERY_PRESENT_PATH "/sys/class/power_supply/battery/present"使用狀态
#define BATTERY_CAPACITY_PATH "/sys/class/power_supply/battery/capacity"電池 level
#define BATTERY_VOLTAGE_PATH "/sys/class/power_supply/battery/batt_vol"電池電壓
#define BATTERY_TEMPERATURE_PATH "/sys/class/power_supply/battery/batt_temp"電池溫度
#define BATTERY_TECHNOLOGY_PATH "/sys/class/power_supply/battery/technology"電池技術 當電池狀态發生變化時,driver 會更新這些檔案。傳送資訊到java
先看一下/frameworks/base/services/jni/com_android_server_BatteryService.cpp的register_android_server_BatteryService方法。可以看到,每個資訊都是從相應的檔案中讀取到的。
/frameworks/base/services/java/com/android/server/BatteryService.java中注冊一個UEventObserver,每次電池資訊有更新時,會調用update(),update()方法分兩步,先調用native_update()方法通過JNI讀取上面幾個檔案中的資訊,然後通過Broadcast廣播出去。
TAG =
BatteryServices.class.getSimpleName(); //得到類的簡寫名稱BatteryServices
再看一下應用程式層面如何擷取這些資訊的。
/packages/apps/Settings/src/com/android/settings/BatteryInfo.java檔案中,在onResume()方法中通過registerReceiver(mIntentReceiver,
mIntentFilter);注冊一個Receiver,最終在onReceive()方法中擷取資訊。
3 、JAVA 代碼
代碼路徑:
frameworks/base/services/java/com/android/server/BatteryService.java
frameworks/base/core/java/android/os/ : android.os :包中和Battery
相關的部分
frameworks/base/core/java/com/android/internal/os/:和Battery
相關的内部部分 BatteryService.java 通過調用, BatteryService
JNI來實作com.android.server包中的
BatteryService類。BatteryManager.java中定義了一些 JAVA
應用程式層可以使用的常量。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5iZyQDNzMDZzEWOwcTZiFDM3UDMzUDO4UTZxQmNyMGNh9CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
電池系統在驅動程式層以上的部分都是Android
系統中預設的内容。在移植的過程中基本不需要改動。電池系統需要移植的部分僅有Battery驅動程式。Battery 驅動程式用Linux
标準的Power
Supply驅動程式與上層的接口是sys檔案系統,主要用于讀取sys檔案系統中的檔案來擷取電池相關的資訊。整個系統中各部件的聯系:
BatteryService 作為電池及充電相關的服務: 監聽 Uevent、讀取sysfs 裡中的狀态
、廣播Intent.ACTION_BATTERY_CHANGED。
(1)、mUEventObserver
BatteryService實作了一個UevenObserver mUEventObserver。uevent是Linux
核心用來向使用者空間主動上報事件的機制,對于JAVA程式來說,隻實作 UEventObserver的虛函數
onUEvent,然後注冊即可。
BatteryService隻關注 power_supply 的事件,是以在構造函數注冊:
(2)、update()
update讀取sysfs檔案做到同步取得電池資訊, 然後根據讀到的狀态更新 BatteryService
的成員變量,并廣播一個Intent來通知其它關注電源狀态的 元件。
當kernel有power_supply事件上報時, mUEventObserver調用update()函數,然後update
調用native_update從sysfs中讀取相關狀态(com_android_server_BatteryService.cpp):
(3)、sysfs
Linux 驅動 driver 維護着儲存電池資訊的一組檔案 sysfs,供應用程式獲
取電源相關狀态:
二、Uevent部分
Uevent是核心通知android有狀态變化的一種方法,比如USB線插入、拔出,電池電量變化等等。其本質是核心發送(可以通過socket)一個字元串,應用層(android)接收并解釋該字元串,擷取相應資訊。如下圖所示,如果其中有資訊變化,uevent觸發,做出相應的數更新。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5iZyQDNzMDZzEWOwcTZiFDM3UDMzUDO4UTZxQmNyMGNh9CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
Android中的BatteryService及相關元件
1、Androiduevent架構
Android很多事件都是通過uevent跟kernel來異步通信的。其中類UEventObserver是核心。UEventObserver接收kernel的uevent資訊的抽象類。
(1)、server層代碼
battery server:
frameworks/frameworks/base/services/java/com/android/server/SystemServer.java
frameworks/frameworks/base/services/java/com/android/server/BatteryService.java
(2)、java層代碼
frameworks/base/core/java/android/os/UEventObserver.java
(3)、JNI層代碼
frameworks/base/core/jni/android_os_UEventObserver.cpp
(4)、底層代碼
hardware/libhardware_legacy/uevent/uevent.c
讀寫kernel的接口socket(PF_NETLINK,SOCK_DGRAM,
NETLINK_KOBJECT_UEVENT);
2、UEventObserver的使用
類UEventObserver提供了三個接口給子類來調用:
(1)、onUEvent(UEvent event): 子類必須重寫這個onUEvent來處理uevent。
(2)、startObserving(Stringmatch): 啟動程序,要提供一個字元串參數。
(3)、stopObserving(): 停止程序。
例子://在BatteryService.java中
mUEventObserver.startObserving("SUBSYSTEM=power_supply");
privateUEventObserver mUEventObserver =newUEventObserver() {
@Override
publicvoidonUEvent(UEventObserver.UEvent event) {
update();
}
};
在UEvent thread中會不停調用 update()方法,來更新電池的資訊資料。
3、vold
server分析
(1)、在system/vold/NetlinkManager.cpp中:
if((mSock = socket(PF_NETLINK,SOCK_DGRAM,NETLINK_KOBJECT_UEVENT))
SLOGE("Unable to create uevent socket: %s", strerror(errno));
return-1;
}
if(setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz,sizeof(sz))
SLOGE("Unable to set uevent socket options: %s", strerror(errno));
return-1;
}
if(bind(mSock, (structsockaddr *) &nladdr,sizeof(nladdr))
SLOGE("Unable to bind uevent socket: %s", strerror(errno));
return-1;
}
(2)、然後在system/vold/NetlinkHandler.cpp的NetlinkHandler::onEvent中處理
voidNetlinkHandler::onEvent(NetlinkEvent *evt) {
VolumeManager *vm = VolumeManager::Instance();
constchar*subsys = evt->getSubsystem();
if(!subsys) {
SLOGW("No subsystem found in netlink event");
return;
}
if(!strcmp(subsys,"block")) {
vm->handleBlockEvent(evt);
} elseif(!strcmp(subsys,"switch")) {
vm->handleSwitchEvent(evt);
} elseif(!strcmp(subsys,"battery")) {
} elseif(!strcmp(subsys,"power_supply")) {
}
}
(3)、在system/core/libsysutils/src/NetlinkListener.cpp中監聽。
4、batteryserver分析
java代碼:frameworks/frameworks/base/services/java/com/android/server/BatteryService.java
JNI代碼: frameworks/base/services/jni/com_android_server_BatteryService.cpp
(1)、BatteryService是跑在system_process當中,在系統初始化的時候啟動,
如下在BatteryService.java中:
Log.i(TAG, “Starting Battery Service.”);
BatteryService battery = newBatteryService(context);
ServiceManager.addService(“battery”, battery);
(2)、資料來源BatteryService通過JNI(com_android_server_BatteryService.cpp)讀取資料。
BatteryService通過JNI注冊的不僅有函數,還有變量。
如下:BatteryService是跑在system_process當中,在系統初始化的時候啟動,如下在BatteryService.java中:
//##############在BatteryService.java中聲明的變量################
privatebooleanmAcOnline;
privatebooleanmUsbOnline;
privateintmBatteryStatus;
privateintmBatteryHealth;
privatebooleanmBatteryPresent;
privateintmBatteryLevel;
privateintmBatteryVoltage;
privateintmBatteryTemperature;
privateString mBatteryTechnology;
//在BatteryService.java中聲明的變量,在com_android_server_BatteryService.cpp中共用,即在com_android_server_BatteryService.cpp中其實操作的也是BatteryService.java中聲明的變量。
gFieldIds.mAcOnline = env->GetFieldID(clazz, “mAcOnline”, “Z”);
gFieldIds.mUsbOnline = env->GetFieldID(clazz, “mUsbOnline”, “Z”);
gFieldIds.mBatteryStatus = env->GetFieldID(clazz, “mBatteryStatus”, “I”);
gFieldIds.mBatteryHealth = env->GetFieldID(clazz, “mBatteryHealth”, “I”);
gFieldIds.mBatteryPresent = env->GetFieldID(clazz, “mBatteryPresent”, “Z”);
gFieldIds.mBatteryLevel = env->GetFieldID(clazz, “mBatteryLevel”, “I”);
gFieldIds.mBatteryTechnology = env->GetFieldID(clazz, “mBatteryTechnology”, Ljava/lang/String;”);
gFieldIds.mBatteryVoltage = env->GetFieldID(clazz, “mBatteryVoltage”, “I”);
gFieldIds.mBatteryTemperature = env->GetFieldID(clazz, “mBatteryTemperature”, “I”);
//上面這些變量的值,對應是從下面的檔案中讀取的,一隻檔案存儲一個數值。
#define AC_ONLINE_PATH “/sys/class/power_supply/ac/online”
#define USB_ONLINE_PATH “/sys/class/power_supply/usb/online”
#define BATTERY_STATUS_PATH “/sys/class/power_supply/battery/status”
#define BATTERY_HEALTH_PATH “/sys/class/power_supply/battery/health”
#define BATTERY_PRESENT_PATH “/sys/class/power_supply/battery/present”
#define BATTERY_CAPACITY_PATH “/sys/class/power_supply/battery/capacity”
#define BATTERY_VOLTAGE_PATH “/sys/class/power_supply/battery/batt_vol”
#define BATTERY_TEMPERATURE_PATH “/sys/class/power_supply/battery/batt_temp”
#define BATTERY_TECHNOLOGY_PATH “/sys/class/power_supply/battery/technology”
(3)、資料傳送
BatteryService主動把資料傳送給所關心的應用程式,所有的電池的資訊資料是通過Intent傳送出去的。在BatteryService.java中,Code如下:
Intent intent =newIntent(Intent.ACTION_BATTERY_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
intent.putExtra(“status”, mBatteryStatus);
intent.putExtra(“health”, mBatteryHealth);
intent.putExtra(“present”, mBatteryPresent);
intent.putExtra(“level”, mBatteryLevel);
intent.putExtra(“scale”, BATTERY_SCALE);
intent.putExtra(“icon-small”, icon);
intent.putExtra(“plugged”, mPlugType);
intent.putExtra(“voltage”, mBatteryVoltage);
intent.putExtra(“temperature”, mBatteryTemperature);
intent.putExtra(“technology”, mBatteryTechnology);
ActivityManagerNative.broadcastStickyIntent(intent, null);
(4)、資料接收
應用如果想要接收到BatteryService發送出來的電池資訊,則需要注冊一個Intent為Intent.ACTION_BATTERY_CHANGED的BroadcastReceiver。
注冊方法如下:
IntentFilter mIntentFilter =newIntentFilter();
mIntentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
registerReceiver(mIntentReceiver, mIntentFilter);
privateBroadcastReceiver mIntentReceiver =newBroadcastReceiver() {
@Override
publicvoidonReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
String action = intent.getAction();
if(action.equals(Intent.ACTION_BATTERY_CHANGED)) {
intnVoltage = intent.getIntExtra(“voltage”,0);
if(nVoltage!=0){
mVoltage.setText(“V: ” + nVoltage + “mV – Success…”);
}
else{
mVoltage.setText(“V: ” + nVoltage + “mV – fail…”);
}
}
}
};
(5)、資料更新
電池的資訊會随着時間不停變化,自然地,就需要考慮如何實時的更新電池的資料資訊。在BatteryService啟動的時候,會同時通過UEventObserver啟動一個onUEvent
Thread。每一個Process最多隻能有一個onUEvent
Thread,即使這個Process中有多個UEventObserver的執行個體。當在一個Process中,第一次Call
startObserving()方法後,這個UEvent thread就啟動了。而一旦這個UEvent
thread啟動之後,就不會停止。
//在BatteryService.java中
mUEventObserver.startObserving(“SUBSYSTEM=power_supply”);
privateUEventObserver mUEventObserver =newUEventObserver() {
@Override
publicvoidonUEvent(UEventObserver.UEvent event) {
update();
}
};
在UEvent
thread中會不停調用 update()方法,來更新電池的資訊資料。