內核:linux2.6/linux3.0
系統:android/android4.0
平台:S5PV310(samsungexynos4210) 、samsung exynos4412
android 電池(二):android關機充電流程、充電畫面顯示
一、電池系統結構
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觸發,做出相應的數更新。
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的使用
(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注冊的不僅有函數,還有變量。 如下:ont-faVi:T�{ ~Roman";mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:新宋體;mso-fareast-language:ZH-CN'>) 、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如下:so-�-o�/z�{:新宋體;mso-ansi-language:EN-US;mso-fareast-language:#0400;mso-bidi-language:AR-SA'>注冊的不僅有函數,還有變量。 如下:
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启動之後,就不會停止。
mUEventObserver.startObserving(“SUBSYSTEM=power_supply”); private UEventObserver mUEventObserver = new UEventObserver() { @Override public void onUEvent(UEventObserver.UEvent event) { update(); } };
在UEvent thread中會不停調用 update()方法,來更新電池的信息數據。