關鍵詞:android電池系統電池系統架構 uevent power_supply驅動
平台資訊:
核心:linux2.6/linux3.0
系統:android/android4.0
平台:S5PV310(samsungexynos4210) 、samsung exynos4412
作者:xubin341719(歡迎轉載,請註明作者)
歡迎指正錯誤,共同學習、共同進步!!
android 電池(一):鋰電池基本原理篇
android 電池(二):android關機充電流程、充電畫面顯示
android 電池(三):android電池系統
android電池(四):電池 電量計(MAX17040)驅動分析篇
android電池(五):電池 充電IC(PM2301)驅動分析篇
一、電池系統結構
Android中的電池使用方式主要有三種:AC、USB、Battery 等不同的模式。在應用程式層次,通常包括了電池狀態顯示的功能。因此從 Android 系統的軟體方面(包括驅動程式和使用者空間內容)需要在一定程度上獲得電池的狀態,電池系統主要負責電池資訊統計、顯示。電池系統的架構如下所示:
自下而上, Android 的電池系統分成以下幾個部分:
1、驅動程式:
特定硬體平台電池的驅動程式,用 Linux的Power Supply 驅動程式,實現向使用者空間提供資訊。Battery 驅動程式需要通過sys檔案系 統向使用者空間提供介面, sys檔案系統的路徑是由上層的程式指定的。Linux標準的 Power Supply驅動程式 所使用的檔案系統路徑為:/sys/class/power_supply ,其中的每個子目錄表示一種能源供應裝置的名稱。
Power Supply 驅動程式的標頭檔在 include/linux/power_supply.h中定義,註冊和登出驅動程式的函數如下所示:
int power_supply_register(struct device *parent,struct power_supply *psy); void power_supply_unregister(struct power_supply *psy); struct power_supply { const char *name; /* 裝置名稱 */ enum power_supply_type type; /* 類型 */ enum power_supply_property *properties; /* 屬性指標 */ size_t num_properties; /* 屬性的數目 */ char **supplied_to; size_t num_supplicants; int (*get_property)(struct power_supply *psy, /* 獲得屬性 */ enum power_supply_property psp, union power_supply_propval *val); void (*external_power_changed)(struct power_supply *psy); /* ...... 省略部分內容 */ };
Linux中驅動程式:power_supply
2、本地代碼 - JNI
代碼路徑: frameworks/base/services/jni/com_android_server_BatteryService.cpp 這個類調用sys檔案系統訪問驅動程式,也同時提供了JNI的介面。
這個檔案提供的方法列表如下所示:
static JNINativeMethod sMethods[] = { {"native_update", "()V", (void*)android_server_BatteryService_update}, };
處理的流程為根據裝置類型判定裝置後, 得到各個裝置的相關屬性,則需要得到更多得 資訊。例如:果是交流或者 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
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 應用程式層可以使用的常量。
電池系統在驅動程式層以上的部分都是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觸發,做出相應的數更新。
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"); private UEventObserver mUEventObserver = new UEventObserver() { @Override public void onUEvent(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)) < 0) { SLOGE("Unable to create uevent socket: %s", strerror(errno)); return -1; } if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) { SLOGE("Unable to set uevent socket options: %s", strerror(errno)); return -1; } if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) { SLOGE("Unable to bind uevent socket: %s", strerror(errno)); return -1; }
(2)、然後在system/vold/NetlinkHandler.cpp的NetlinkHandler::onEvent中處理
void NetlinkHandler::onEvent(NetlinkEvent *evt) { VolumeManager *vm = VolumeManager::Instance(); const char *subsys = evt->getSubsystem(); if (!subsys) { SLOGW("No subsystem found in netlink event"); return; } if (!strcmp(subsys, "block")) { vm->handleBlockEvent(evt); } else if (!strcmp(subsys, "switch")) { vm->handleSwitchEvent(evt); } else if (!strcmp(subsys, "battery")) { } else if (!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 = new BatteryService(context); ServiceManager.addService(“battery”, battery);
(2)、資料來源
BatteryService通過JNI(com_android_server_BatteryService.cpp)讀取資料。
BatteryService通過JNI註冊的不僅有函數,還有變數。 如下:BatteryService是跑在system_process當中,在系統初始化的時候啟動,如下在BatteryService.java中:
//##############在BatteryService.java中聲明的變數################ private boolean mAcOnline; private boolean mUsbOnline; private int mBatteryStatus; private int mBatteryHealth; private boolean mBatteryPresent; private int mBatteryLevel; private int mBatteryVoltage; private int mBatteryTemperature; private String 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 = new Intent(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 = new IntentFilter(); mIntentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); registerReceiver(mIntentReceiver, mIntentFilter); private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub String action = intent.getAction(); if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { int nVoltage = 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”); private UEventObserver mUEventObserver = new UEventObserver() { @Override public void onUEvent(UEventObserver.UEvent event) { update(); } };
在UEvent thread中會不停調用 update()方法,來更新電池的資訊資料。