鍍金池/ 教程/ Android/ 第5章? 深入理解 PowerManagerService
第2章? 深入理解 Java Binder 和 MessageQueue
第7章? 深入理解 ContentProvider
第5章? 深入理解 PowerManagerService
第3章? 深入理解 SystemServer
第8章? 深入理解ContentService 和 AccountManagerService
第1章?開(kāi)發(fā)環(huán)境部署
第4章? 深入理解 PackageManagerService
第6章?深入理解ActivityManagerService

第5章? 深入理解 PowerManagerService

本章主要內(nèi)容:

·??深入分析PowerManagerService

·??深入分析BatteryService和BatteryStatsService

本章所涉及的源代碼文件名及位置:

·??PowerManagerService.java

frameworks/base/services/java/com/android/server/PowerManagerService.java

·??com_android_server_PowerManagerService.cpp

frameworks/base/services/jni/com_android_server_PowerManagerService.cpp

·??PowerManager.java

frameworks/base/core/java/android/os/PowerManager.java

·??WorkSoure.java

frameworks/base/core/java/android/os/WorkSoure.java

·??Power.java

frameworks/base/core/java/android/os/Power.java

·??android_os_Power.cpp

frameworks/base/core/jni/android_os_Power.cpp

·??com_android_server_InputManager.cpp

frameworks/base/services/jni/com_android_server_InputManager.cpp

·??LightService.java

frameworks/base/services/java/com/android/server/LightService.java

·??com_android_server_LightService.cpp

frameworks/base/services/jni/com_android_server_LightService.cpp

·??BatteryService.java

frameworks/base/services/java/com/android/server/BatteryService.java

·??com_android_server_BatteryService.cpp

frameworks/base/services/jni/com_android_server_BatteryService.cpp

·??ActivityManagerService.java

frameworks/base/services/java/com/android/server/am/ActivityManagerService.java

·??BatteryStatsService.java

frameworks/base/services/java/com/android/server/am/BatteryStatsService.java

·??BatteryStatsImpl.java

frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java

·??LocalPowerManager.java

frameworks/base/core/java/android/os/LocalPowerManager.java

5.1 ?概述

PowerManagerService負(fù)責(zé)Andorid系統(tǒng)中電源管理方面的工作。作為系統(tǒng)核心服務(wù)之一,PowerManagerService與其他服務(wù)及HAL層等都有交互關(guān)系,所以PowerManagerService相對(duì)PackageManager來(lái)說(shuō),其社會(huì)關(guān)系更復(fù)雜,分析難度也會(huì)更大一些。

先來(lái)看直接與PowerManagerService有關(guān)的類家族成員,如圖5-1所示

http://wiki.jikexueyuan.com/project/deep-android-v2/images/chapter5/image001.png" alt="image" />

圖5-1? PowerManagerService及相關(guān)類家族

由圖5-1可知:

·??PowerManagerService從IPowerManager.Stub類派生,并實(shí)現(xiàn)了Watchdog.Monitor及LocalPowerManager接口。PowerManagerService內(nèi)部定義了較多的成員變量,在后續(xù)分析中,我們會(huì)對(duì)其中比較重要的成員逐一進(jìn)行介紹。

·??根據(jù)第4章介紹的知識(shí),IPowerManager.Stub及內(nèi)部類Proxy均由aidl工具處理PowerManager.aidl后得到。

·??客戶端使用PowerManager類,其內(nèi)部通過(guò)代表BinderProxy端的mService成員變量與PowerManagerService進(jìn)行跨Binder通信。

現(xiàn)在開(kāi)始PowerManagerService(以后簡(jiǎn)寫為PMS)的分析之旅,先從它的調(diào)用流程入手。

提示PMS和BatteryService、BatteryStatsService均有交互關(guān)系,這些內(nèi)容放在后面分析。

5.2? 初識(shí)PowerManagerService

PMS由SystemServer在ServerThread線程中創(chuàng)建。這里從中提取了4個(gè)關(guān)鍵調(diào)用點(diǎn),如下所示:

[-->SystemServer.java]

??? ......//ServerThread的run函數(shù)

??? power =new PowerManagerService();//①創(chuàng)建PMS對(duì)象

???ServiceManager.addService(Context.POWER_SERVICE, power);//注冊(cè)到SM中

?? ......

?? //②調(diào)用PMS的init函數(shù)

?? power.init(context,lights, ActivityManagerService.self(), battery);

?? ......//其他服務(wù)

?? power.systemReady();//③調(diào)用PMS的systemReady

?? ......//系統(tǒng)啟動(dòng)完畢,會(huì)收到ACTION_BOOT_COMPLETED廣播

?? //④PMS處理ACTION_BOOT_COMPLETED廣播

先從第一個(gè)關(guān)鍵點(diǎn)即PMS的構(gòu)造函數(shù)開(kāi)始分析。

5.2.1? PMS構(gòu)造函數(shù)分析

PMS構(gòu)造函數(shù)的代碼如下:

[-->PowerManagerService.java::構(gòu)造函數(shù)]

PowerManagerService() {

??? longtoken = Binder.clearCallingIdentity();

??? MY_UID =Process.myUid();//取本進(jìn)程(即SystemServer)的uid及pid

??? MY_PID =Process.myPid();

???Binder.restoreCallingIdentity(token);

??? //設(shè)置超時(shí)時(shí)間為1周。Power類封裝了同Linux內(nèi)核交互的接口。本章最后再來(lái)分析它

???Power.setLastUserActivityTimeout(7*24*3600*1000);

??? //初始化兩個(gè)狀態(tài)變量,它們非常有意義。其具體作用后續(xù)再分析

??? mUserState= mPowerState = 0;

??? //將自己添加到看門狗的監(jiān)控管理隊(duì)列中

?? Watchdog.getInstance().addMonitor(this);

?}

PMS的構(gòu)造函數(shù)比較簡(jiǎn)單。值得注意的是mUserState和mPowerState兩個(gè)成員,至于它們的具體作用,后續(xù)分析時(shí)自會(huì)知曉。

下面分析第二個(gè)關(guān)鍵點(diǎn)。

5.2.2? init分析

第二個(gè)關(guān)鍵點(diǎn)是init函數(shù),該函數(shù)將初始化PMS內(nèi)部的一些重要成員變量,由于此函數(shù)代碼較長(zhǎng),此處將分段討論。

從流程角度看,init大體可分為三段。

1.? init分析之一

[-->PowerManagerService.java::init函數(shù)]

void init(Context context, LightsService lights,IActivityManager activity,

????????? ??????????????????BatteryService battery) {

?? //①保存幾個(gè)成員變量

??mLightsService = lights;//保存LightService

?? mContext= context;

??mActivityService = activity;//保存ActivityManagerService

?? //保存BatteryStatsService

??mBatteryStats = BatteryStatsService.getService();//

??mBatteryService = battery;//保存BatteryService

?? //從LightService中獲取代表不同硬件Light的Light對(duì)象

?? mLcdLight= lights.getLight(LightsService.LIGHT_ID_BACKLIGHT);

??mButtonLight = lights.getLight(LightsService.LIGHT_ID_BUTTONS);

??mKeyboardLight = lights.getLight(LightsService.LIGHT_ID_KEYBOARD);

??mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);

?? //②調(diào)用nativeInit函數(shù)

??nativeInit();

??synchronized (mLocks) {

?????updateNativePowerStateLocked();//③更新Native層的電源狀態(tài)

? }

第一階段工作可分為三步:

·??對(duì)一些成員變量進(jìn)行賦值。

·??調(diào)用nativeInit函數(shù)初始化Native層相關(guān)資源。

·??調(diào)用updateNativePowerStateLocked更新Native層的電源狀態(tài)。這個(gè)函數(shù)的調(diào)用次數(shù)較為頻繁,以后續(xù)分析時(shí)討論。

先來(lái)看第一階段出現(xiàn)的各類成員變量,如表5-1所示。

表5-1? 成員變量說(shuō)明

成員變量名

數(shù)據(jù)類型

作用

mLightsService

LightsService

和LightsService交互用

mActivityService

IActivityManager

和ActivityManagerService交互

mBatteryStats

IBatteryStats

和BatteryStatsService交互,用于系統(tǒng)耗電量統(tǒng)計(jì)方面的工作

mBatteryService

BatteryService

用于獲取電源狀態(tài),例如是否為低電狀態(tài)、查詢電池電量等

mLcdLight、mButtonLight、

mKeyboardLight、mAttentionLight

LightsService.Light

由PMS控制,在不同狀態(tài)下點(diǎn)亮或熄滅它們

下面來(lái)看nativeInit函數(shù),其JNI層實(shí)現(xiàn)代碼如下:

[-->com_android_server_PowerManagerService.cpp]

static void android_server_PowerManagerService_nativeInit(JNIEnv*env,

???????????????????????????? jobject obj) {

??? //非常簡(jiǎn)單,就是創(chuàng)建一個(gè)全局引用對(duì)象gPowerManagerServiceObj

???gPowerManagerServiceObj = env->NewGlobalRef(obj);

}

init第一階段工作比較簡(jiǎn)單,下面進(jìn)入第二階段的分析。

2.? init分析之二

init第二階段工作將創(chuàng)建兩個(gè)HandlerThread對(duì)象,即創(chuàng)建兩個(gè)帶消息循環(huán)的工作線程。PMS本身由ServerThread線程創(chuàng)建,并且將自己的工作委托給這兩個(gè)線程,它們分別是:

·??mScreenOffThread:按Power鍵關(guān)閉屏幕時(shí),屏幕不是突然變黑的,而是一個(gè)漸暗的過(guò)程。mScreenOffThread線程就用于控制關(guān)屏過(guò)程中的亮度調(diào)節(jié)。

·??mHandlerThread:該線程是PMS的主要工作線程。

先來(lái)看這兩個(gè)線程的創(chuàng)建。

(1)?mScreenOffThread和mHandlerThread分析

[-->PowerManagerService.java::init函數(shù)]

......

?mScreenOffThread= new HandlerThread("PowerManagerService.mScreenOffThread") {

?? protected void onLooperPrepared() {

??mScreenOffHandler = new Handler();//向這個(gè)handler發(fā)送的消息,將由此線程處理

?? synchronized (mScreenOffThread) {

?? ???mInitComplete = true;

?? ???mScreenOffThread.notifyAll();

??? ??}

?? ?}

?? };

?mScreenOffThread.start();//創(chuàng)建對(duì)應(yīng)的工作線程

?synchronized (mScreenOffThread) {

??? while(!mInitComplete) {

?????? try {//等待mScreenOffThread線程創(chuàng)建完成

?????????????mScreenOffThread.wait();

??????? } ......

?????? }

??? }

注意,在Android代碼中經(jīng)常出現(xiàn)“線程A創(chuàng)建線程B,然后線程A等待線程B創(chuàng)建完成”的情況,讀者了解它們的作用即可。接著看以下代碼。

[-->PowerManagerService.java::init函數(shù)]

?? mInitComplete= false;

?? //創(chuàng)建 mHandlerThread

??mHandlerThread = new HandlerThread("PowerManagerService") {

?? protectedvoid onLooperPrepared() {

??????super.onLooperPrepared();

??????initInThread();//①初始化另外一些成員變量

???? }

?? };

?mHandlerThread.start();

??????? ......//等待mHandlerThread創(chuàng)建完成

由于mHandlerThread承擔(dān)了PMS的主要工作任務(wù),因此需要先做一些初始化工作,相關(guān)的代碼在initInThread中,擬放在單獨(dú)一節(jié)中進(jìn)行討論。

(2)?initInThread分析

initInThread本身比較簡(jiǎn)單,涉及三個(gè)方面的工作,總結(jié)如下:

·??PMS需要了解外面的世界,所以它會(huì)注冊(cè)一些廣播接收對(duì)象,接收諸如啟動(dòng)完畢、電池狀態(tài)變化等廣播。

·??PMS所從事的電源管理工作需要遵守一定的規(guī)則,而這些規(guī)則在代碼中就是一些配置參數(shù),這些配置參數(shù)的值可以是固定寫死的(編譯完后就無(wú)法改動(dòng)),也可以是經(jīng)由Settings數(shù)據(jù)庫(kù)動(dòng)態(tài)設(shè)定的。

·??PMS需要對(duì)外發(fā)出一些通知,例如屏幕關(guān)閉/屏幕開(kāi)啟。

了解initInThread的概貌后,再來(lái)看如下代碼。

[-->PowerManagerService.java::initInThread]

void initInThread() {

?? mHandler= new Handler();

? ?//PMS內(nèi)部也需要使用WakeLock,此處定義了幾種不同的UnsynchronizedWakeLock。它們的

? ?//作用見(jiàn)后文分析

? ?mBroadcastWakeLock = newUnsynchronizedWakeLock(

??????????????PowerManager.PARTIAL_WAKE_LOCK, "sleep_broadcast", true);

? ?//創(chuàng)建廣播通知的Intent,用于通知SCREEN_ON和SCREEN_OFF消息

??mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);

??mScreenOnIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);

??mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF);

??mScreenOffIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);

?

?? //取配置參數(shù),這些參數(shù)是編譯時(shí)確定的,運(yùn)行過(guò)程中無(wú)法修改

?? Resourcesresources = mContext.getResources();

??mAnimateScreenLights = resources.getBoolean(

???????????????com.android.internal.R.bool.config_animateScreenLights);

??????? ......//見(jiàn)下文的配置參數(shù)匯總

??????? //通過(guò)數(shù)據(jù)庫(kù)設(shè)置的配置參數(shù)

? ?ContentResolver resolver =mContext.getContentResolver();

? ?Cursor settingsCursor =resolver.query(Settings.System.CONTENT_URI, null,

???????????????......//設(shè)置查詢條件和查詢項(xiàng)的名字,見(jiàn)后文的配置參數(shù)匯總

???????????????null);

? ?//ContentQueryMap是一個(gè)常用類,簡(jiǎn)化了數(shù)據(jù)庫(kù)查詢工作。讀者可參考SDK中該類的說(shuō)明文檔

?? mSettings= new ContentQueryMap(settingsCursor, Settings.System.NAME,

???????????????????????????? ??????true, mHandler);

?? //監(jiān)視上邊創(chuàng)建的ContentQueryMap中內(nèi)容的變化

??SettingsObserver settingsObserver = new SettingsObserver();

? ?mSettings.addObserver(settingsObserver);

? ?settingsObserver.update(mSettings, null);

?? //注冊(cè)接收通知的BroadcastReceiver

??IntentFilter filter = new IntentFilter();

??filter.addAction(Intent.ACTION_BATTERY_CHANGED);

??mContext.registerReceiver(new BatteryReceiver(), filter);

?? filter =new IntentFilter();

??filter.addAction(Intent.ACTION_BOOT_COMPLETED);

??mContext.registerReceiver(new BootCompletedReceiver(), filter);

?? filter =new IntentFilter();

??filter.addAction(Intent.ACTION_DOCK_EVENT);

??mContext.registerReceiver(new DockReceiver(), filter);

??? //監(jiān)視Settings數(shù)據(jù)中secure表的變化

??mContext.getContentResolver().registerContentObserver(

??? ????????Settings.Secure.CONTENT_URI, true,

???????????new ContentObserver(new Handler()) {

???????????????public void onChange(boolean selfChange) {

???????????????????updateSettingsValues();

???????????????}

???????????});

? ?updateSettingsValues();

??? ......//通知其他線程

?}

在上述代碼中,很大一部分用于獲取配置參數(shù)。同時(shí),對(duì)于數(shù)據(jù)庫(kù)中的配置值,還需要建立監(jiān)測(cè)機(jī)制,細(xì)節(jié)部分請(qǐng)讀者自己閱讀相關(guān)代碼,這里總結(jié)一下常用的配置參數(shù),如表5-2所示。

表5-2? PMS使用的配置參數(shù)

參數(shù)名:類型

來(lái)源

備注

mAnimateScreenLights:bool

config.xml[①]

關(guān)屏?xí)r屏幕光是否漸暗,默認(rèn)為true

mUnplugTurnsOnScreen:bool

config.xml

拔掉USB線,是否點(diǎn)亮屏幕

mScreenBrightnessDim:int

config.xml

PMS可設(shè)置的屏幕亮度的最小值,默認(rèn)20(單位lx)

mUseSoftwareAutoBrightness:bool

config.xml

是否啟用Setting中的亮度自動(dòng)調(diào)節(jié),如果硬件不支持該功能,則可由軟件控制。默認(rèn)為false

mAutoBrightnessLevels:int[]

mLcdBacklightValues:int[]

......

config.xml,具體值由硬件廠商定義

當(dāng)使用軟件自動(dòng)亮度調(diào)節(jié)時(shí),需配置不同亮度時(shí)對(duì)應(yīng)的參數(shù)

STAY_ON_WHILE_PLUGGED_IN:int

Settings.db

插入U(xiǎn)SB時(shí)是否保持喚醒狀態(tài)

SCREEN_OFF_TIMEOUT:int

Settings.db

屏幕超時(shí)時(shí)間

DIM_SCREEN:int

Settings.db

是否變暗(dim)屏幕

SCREEN_BRIGHTNESS_MODE:int

Settings.db

屏幕亮度模式(自動(dòng)還是手動(dòng)調(diào)節(jié))

除了獲取配置參數(shù)外,initInThread還創(chuàng)建了好幾個(gè)UnsynchronizedWakeLock對(duì)象,它的作用是:在Android系統(tǒng)中,為了搶占電力資源,客戶端要使用WakeLock對(duì)象。PMS自己也不例外,所以為了保證在工作中不至于突然掉電(當(dāng)其他客戶端都不使用WakeLock的時(shí)候,這種情況理論上是有可能發(fā)生的),PMS需要定義供自己使用的WakeLock。由于線程同步方面的原因,PMS封裝了一個(gè)UnsynchronizedWakeLock結(jié)構(gòu),它的調(diào)用已經(jīng)處于鎖保護(hù)下,所以在內(nèi)部無(wú)需再做同步處理。UnsynchronizedWakeLock比較簡(jiǎn)單,因此不再贅述。

下面來(lái)看init第三階段的工作。

3.? init分析之三

[-->PowerManagerService.java::init函數(shù)]

??nativeInit();//不知道此處為何還要調(diào)用一次nativeInit,筆者懷疑此處為bug

??synchronized (mLocks) {

?? ??updateNativePowerStateLocked();//更新native層power狀態(tài),以后分析

??? ?forceUserActivityLocked();//強(qiáng)制觸發(fā)一次用戶事件

????mInitialized = true;

?}//init函數(shù)完畢

forceUserActivityLocked表示強(qiáng)制觸發(fā)一次用戶事件。這個(gè)解釋是否會(huì)讓讀者丈二和尚摸不著頭?先來(lái)看它的代碼:

[-->PowerManagerService.java:: forceUserActivityLocked]

private void forceUserActivityLocked() {

?? if(isScreenTurningOffLocked()) {

????mScreenBrightness.animating = false;

? }

? ?boolean savedActivityAllowed =mUserActivityAllowed;

??mUserActivityAllowed = true;

? //下面這個(gè)函數(shù)以后會(huì)分析, SDK中有對(duì)應(yīng)的API

??userActivity(SystemClock.uptimeMillis(), false);

?? mUserActivityAllowed= savedActivityAllowed;

?}

forceUserActivityLocked內(nèi)部就是為調(diào)用userActivity掃清一切障礙。對(duì)于SDK中PowerManager.userActivity的說(shuō)明文檔“User activity happened.Turnsthe device from whatever state it's in to full on, and resets the auto-offtimer.”簡(jiǎn)單翻譯過(guò)來(lái)是:調(diào)用此函數(shù)后,手機(jī)將被喚醒。屏幕超時(shí)時(shí)間將重新計(jì)算。

userActivity是PMS中很重要的一個(gè)函數(shù),本章后面將對(duì)其進(jìn)行詳細(xì)分析。

4.? init函數(shù)總結(jié)

PMS的init函數(shù)比較簡(jiǎn)單,但是其眾多的成員變量讓人感到有點(diǎn)頭暈。讀者自行閱讀代碼時(shí),不妨參考表5-1和表5-2。

5.2.3? systemReady分析

下面來(lái)分析PMS第三階段的工作。此時(shí)系統(tǒng)中大部分服務(wù)都已創(chuàng)建好,即將進(jìn)入就緒階段。就緒階段的工作在systemReady中完成,代碼如下:

[-->PowerManagerService.java::systemReady]

void systemReady() {

? /*

? 創(chuàng)建一個(gè)SensorManager,用于和系統(tǒng)中的傳感器系統(tǒng)交互,由于該部分涉及較多的native層

? 代碼,因此將相關(guān)內(nèi)容放到本書后續(xù)章節(jié)進(jìn)行討論

? */

?mSensorManager = new SensorManager(mHandlerThread.getLooper());

?mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);

? if(mUseSoftwareAutoBrightness) {

???? ?mLightSensor =mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);

? }

? if(mUseSoftwareAutoBrightness) {

?????setPowerState(SCREEN_BRIGHT);

??? } else {//不考慮軟件自動(dòng)亮度調(diào)節(jié),所以執(zhí)行下面這個(gè)分支

???setPowerState(ALL_BRIGHT);//設(shè)置手機(jī)電源狀態(tài)為ALL_BRIGHT,即屏幕、按鍵燈都打開(kāi)

?}

?synchronized (mLocks) {

?mDoneBooting = true;

? //根據(jù)情況啟用LightSensor

?enableLightSensorLocked(mUseSoftwareAutoBrightness&&mAutoBrightessEnabled);

? longidentity = Binder.clearCallingIdentity();

? try {//通知BatteryStatsService,它將統(tǒng)計(jì)相關(guān)的電量使用情況,后續(xù)再分析它

? ??mBatteryStats.noteScreenBrightness(getPreferredBrightness());

?? ?mBatteryStats.noteScreenOn();

? }......

}

systemReady主要工作為:

·??PMS創(chuàng)建SensorManager,通過(guò)它可與對(duì)應(yīng)的傳感器交互。關(guān)于Android傳感器系統(tǒng),將放到本書后續(xù)章節(jié)討論。PMS僅僅啟用或禁止特定的傳感器,而來(lái)自傳感器的數(shù)據(jù)將通過(guò)回調(diào)的方式通知PMS,PMS根據(jù)接收到的傳感器事件做相應(yīng)處理。

·??通過(guò)setPowerState函數(shù)設(shè)置電源狀態(tài)為ALL_BRIGHT(不考慮UseSoftwareAutoBrightness的情況)。此時(shí)屏幕及鍵盤燈都會(huì)點(diǎn)亮。關(guān)于setPowrState函數(shù),后文再做詳細(xì)分析。

·??調(diào)用BatteryStatsService提供的函數(shù),以通知屏幕打開(kāi)事件,在BatteryStatsService內(nèi)部將處理該事件。稍后,本章將詳細(xì)討論BatteryStatsService的功能。

當(dāng)系統(tǒng)中的服務(wù)都在systemReady中進(jìn)行處理后,系統(tǒng)會(huì)廣播一次ACTION_BOOT_COMPLETED消息,而PMS也將處理該廣播,下面來(lái)分析。

5.2.4? BootComplete處理

[-->PowerManagerService.java::BootCompletedReceiver]

private final class BootCompletedReceiver extendsBroadcastReceiver {

? publicvoid onReceive(Context context, Intent intent) {

??bootCompleted();//調(diào)用PMS的bootCompleted函數(shù)

? }

}

[-->PowerManagerService.java::bootCompleted函數(shù)]

void bootCompleted() {

?

?synchronized (mLocks) {

??mBootCompleted = true;

?? //再次碰見(jiàn)userActivity,根據(jù)前面的描述,此時(shí)將重新計(jì)算屏幕超時(shí)時(shí)間

??userActivity(SystemClock.uptimeMillis(), false, BUTTON_EVENT, true);

??updateWakeLockLocked();//此處先分析這個(gè)函數(shù)

??mLocks.notifyAll();

?? }

?}

在以上代碼中,再一次遇見(jiàn)了userActivity,暫且對(duì)其置之不理。先分析updateWakeLockLocked函數(shù),其代碼如下:

private void updateWakeLockLocked() {

? /*

? ??mStayOnConditions用于控制當(dāng)插上USB時(shí),手機(jī)是否保持喚醒狀態(tài)。

? ??mBatteryService的isPowered用于判斷當(dāng)前是否處于USB充電狀態(tài)。

? ??如果滿足下面的if條件滿,則PMS需要使用wakeLock來(lái)確保系統(tǒng)不會(huì)掉電

? */

? if(mStayOnConditions != 0 &&mBatteryService.isPowered(mStayOnConditions)) {

?????mStayOnWhilePluggedInScreenDimLock.acquire();

?????mStayOnWhilePluggedInPartialLock.acquire();

? } else {

????? //如果不滿足if條件,則釋放對(duì)應(yīng)的wakeLock,這樣系統(tǒng)就可以進(jìn)入休眠狀態(tài)

?????mStayOnWhilePluggedInScreenDimLock.release();

?????mStayOnWhilePluggedInPartialLock.release();

? }

}

mStayOnWhilePluggedInScreenDimLock和mStayOnWhilePluggedInPartialLock都為UnsynchronizedWakeLock類型,它們封裝了WakeLock,可幫助PMS在使用它們時(shí)免遭線程同步之苦。

5.2.5? 初識(shí)PowerManagerService總結(jié)

這一節(jié)向讀者展示了PMS的大體面貌,包括:

·??主要的成員變量及它們的作用和來(lái)歷。如有需要,可查閱表5-1和5-2。

·??見(jiàn)識(shí)了PMS中幾個(gè)主要的函數(shù),其中有一些將留到后文進(jìn)行深入分析,現(xiàn)在只需要了解其大概作用即可。

5.3? PMS WakeLock分析

WakeLock是Android提供給應(yīng)用程序獲取電力資源的唯一方法。只要還有地方在使用WakeLock,系統(tǒng)就不會(huì)進(jìn)入休眠狀態(tài)。

WakeLock的一般使用方法如下:

PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);

?//①創(chuàng)建一個(gè)WakeLock,注意它的參數(shù)

?PowerManager.WakeLock wl =pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK,

????????????????????????????????????????????? "MyTag");

?wl.acquire();//②獲取該鎖

?? ......//工作

?wl.release();//③釋放該鎖

以上代碼中共列出三個(gè)關(guān)鍵點(diǎn),本章將分析前兩個(gè)(在此基礎(chǔ)上,讀者可自行分析release函數(shù))。

這3個(gè)函數(shù)都由PMS的Binder客戶端的PowerManager使用,所以將本次分析劃分為客戶端和服務(wù)端兩大部分。

?

5.3.1? WakeLock客戶端分析

1.? newWakeLock分析

通過(guò)PowerManager(以后簡(jiǎn)稱PM)的newWakeLock將創(chuàng)建一個(gè)WakeLock,代碼如下:

public WakeLock newWakeLock(int flags, String tag)

{

??//tag不能為null,否則拋異常

??return new WakeLock(flags, tag);//WakeLock為PM的內(nèi)部類,第一個(gè)參數(shù)flags很關(guān)鍵

?}

WakeLock的第一個(gè)參數(shù)flags很關(guān)鍵,它用于控制CPU/Screen/Keyboard的休眠狀態(tài)。flags的可選值如表5-3所示。

表5-3? WakeLock 的flags參數(shù)說(shuō)明

flags值

CPU

Screen

Keyboard

備注

PARTIAL_WAKE_LOCK

On

Off

Off

不受電源鍵影響

SCREEN_DIM_WAKE_LOCK

On

Dim

Off

按下電源鍵后,系統(tǒng)還是會(huì)進(jìn)入休眠狀態(tài)

SCREEN_BRIGHT_WAKE_LOCK

On

Bright

Off

FULL_WAKE_LOCK

On

Bright

On

ACQUIRE_CAUSES_WAKEUP

說(shuō)明:在正常情況下,獲取WakeLock并不會(huì)喚醒機(jī)器(例如acquire之前機(jī)器處于關(guān)屏狀態(tài),則無(wú)法喚醒)。加上該標(biāo)志后,acquire WakeLock同時(shí)也能喚醒機(jī)器(即點(diǎn)亮屏幕等)。該標(biāo)志常用于提示框、來(lái)電提醒等應(yīng)用場(chǎng)景

ON_AFTER_RELEASE

說(shuō)明:和用戶體驗(yàn)有關(guān),當(dāng)WakeLock釋放后,如沒(méi)有該標(biāo)志,系統(tǒng)會(huì)立即黑屏。有了該標(biāo)志,系統(tǒng)會(huì)延時(shí)一段時(shí)間再黑屏

由表5-3可知:

·??WakeLock只控制CPU、屏幕和鍵盤三大部分。

·??表中最后兩項(xiàng)是附加標(biāo)志,和前面的其他WAKE_LOCK標(biāo)志組合使用。注意, PARTIAL_WAKE_LOCK比較特殊,附加標(biāo)志不能影響它。

·??PARTIAL_WAKE_LOCK不受電源鍵控制,即按電源鍵不能使PARTIAL_WAKE_LOCK系統(tǒng)進(jìn)入休眠狀態(tài)(屏幕可以關(guān)閉,但CPU不會(huì)休眠)。

了解了上述知識(shí)后,再來(lái)看如下代碼:

[-->PowerManager.java::WakeLock]

WakeLock(int flags, String tag)

{

???? //檢查flags參數(shù)是否非法

??? mFlags =flags;

? ??mTag =tag;

?? ?//創(chuàng)建一個(gè)Binder對(duì)象,除了做Token外,PMS需要監(jiān)視客戶端的生死狀況,否則有可能導(dǎo)致

?? ?//WakeLock不能被釋放

?? ??mToken= new Binder();

}

客戶端創(chuàng)建WakeLock后,需要調(diào)用acquire以確保電力資源供應(yīng)正常。下面對(duì)acquire代碼進(jìn)行分析。

2.? acquire分析

[-->PowerManager.java::WakeLock.acquire函數(shù)]

public void acquire()

?{

?synchronized (mToken) {

??acquireLocked();//調(diào)用acquireLocked函數(shù)

? }

?}

//acquireLoced函數(shù)

private void acquireLocked() {

? if(!mRefCounted || mCount++ == 0) {

?????mHandler.removeCallbacks(mReleaser);//引用計(jì)數(shù)控制

? try {

????? //調(diào)用PMS的acquirewakeLock,注意這里傳遞的參數(shù),其中mWorkSource為空

?????mService.acquireWakeLock(mFlags, mToken, mTag, mWorkSource);

? }......

??? mHeld =true;

?? }

}

上邊代碼中調(diào)用PMS的acquireWakeLock函數(shù)與PMS交互,該函數(shù)最后一個(gè)參數(shù)為WorkSource類。這個(gè)類從Android 2.2開(kāi)始就存在,但一直沒(méi)有明確的作用,下面是關(guān)于它的一段說(shuō)明。

/**??? 見(jiàn)WorkSoure.java

?* Describesthe source of some work that may be done by someone else.

?* Currentlythe public representation of what a work source is is not

?* defined;this is an opaque container.

?*/

由以上注釋可知,WorkSource本意用來(lái)描述某些任務(wù)的Source。傳遞此Source給其他人,這些人就可以執(zhí)行該Source對(duì)應(yīng)的工作。目前使用WorkSource的地方僅是ContentService中的SynManager。讀者暫時(shí)可不理會(huì)WorkSource。

客戶端的功能比較簡(jiǎn)單,和PMS僅通過(guò)acquireWakeLock函數(shù)交互。下面來(lái)分析服務(wù)端的工作。

5.3.2? PMSacquireWakeLock分析

[-->PowerManagerService.java::acquireWakeLock]

public void acquireWakeLock(int flags, IBinderlock, String tag, WorkSource ws) {

??????? intuid = Binder.getCallingUid();

??????? intpid = Binder.getCallingPid();

??????? if(uid != Process.myUid()) {

???????????mContext.enforceCallingOrSelfPermission(//檢查WAKE_LOCK權(quán)限

??????? ??????????????????android.Manifest.permission.WAKE_LOCK,null);

??????? }

??????? if(ws != null) {

??????????? //如果ws不為空,需要檢查調(diào)用進(jìn)程是否有UPDATE_DEVICE_STATS的權(quán)限

???????????enforceWakeSourcePermission(uid, pid);

??????? }

??????? longident = Binder.clearCallingIdentity();

??????? try{

???????????synchronized (mLocks) {調(diào)用acquireWakeLockLocked函數(shù)

???????????????acquireWakeLockLocked(flags, lock, uid, pid, tag, ws);

???????????}

??????? } ......

??? }

接下來(lái)分析acquireWakeLockLocked函數(shù)。由于此段代碼較長(zhǎng),宜分段來(lái)看。

1.? acquireWakeLockLocked分析之一

開(kāi)始分析之前,有必要先介紹另外一個(gè)數(shù)據(jù)結(jié)構(gòu),它為PowerManagerService的內(nèi)部類,名字也為WakeLock。其定義如下:

[-->PowerManagerService.java]

class WakeLock implements IBinder.DeathRecipient

PMS的WakeLock實(shí)現(xiàn)了DeathRecipient接口。根據(jù)前面Binder系統(tǒng)的知識(shí)可知,當(dāng)Binder服務(wù)端死亡后,Binder系統(tǒng)會(huì)向注冊(cè)了訃告接收的Binder客戶端發(fā)送訃告通知,因此客戶端可以做一些資源清理工作。在本例中,PM.WakeLock是Binder服務(wù)端,而PMS.WakeLock是Binder客戶端。假如PM.WakeLock所在進(jìn)程在release喚醒鎖(即WakeLock)之前死亡,PMS.WakeLock的binderDied函數(shù)則會(huì)被調(diào)用,這樣,PMS也能及時(shí)進(jìn)行釋放(release)工作。對(duì)于系統(tǒng)的重要資源來(lái)說(shuō),采用這種安全保護(hù)措施尤其必要。

回到acquireWakeLockLocked函數(shù),先看第一段代碼:

[-->PowerManagerService.java::acquireWakeLockLocked]

public void acquireWakeLockLocked(int flags,IBinder lock, int uid,

??????????????????????? int pid, Stringtag,WorkSource ws) {

? ......

? //mLocks是一個(gè)ArrayList,保存PMS.WakeLock對(duì)象

? int index= mLocks.getIndex(lock);

? WakeLockwl;

? booleannewlock;

? booleandiffsource;

? WorkSourceoldsource;

? if (index< 0) {

???? //創(chuàng)建一個(gè)PMS.WakeLock對(duì)象,保存客戶端acquire傳來(lái)的參數(shù)

?? ?wl = new WakeLock(flags, lock, tag, uid, pid);

??? switch(wl.flags & LOCK_MASK)

??? {??? //將flags轉(zhuǎn)換成對(duì)應(yīng)的minState

????? casePowerManager.FULL_WAKE_LOCK:

?????? if(mUseSoftwareAutoBrightness) {

????????wl.minState = SCREEN_BRIGHT;

?????? }else {

??? ?????wl.minState = (mKeyboardVisible ? ALL_BRIGHT: SCREEN_BUTTON_BRIGHT);

??????? }

???????break;

????? casePowerManager.SCREEN_BRIGHT_WAKE_LOCK:

????????wl.minState = SCREEN_BRIGHT;

???????? break;

?????? casePowerManager.SCREEN_DIM_WAKE_LOCK:

????????wl.minState = SCREEN_DIM;

????????break;

????? ?case PowerManager.PARTIAL_WAKE_LOCK:

?????? //PROXIMITY_SCREEN_OFF_WAKE_LOCK在SDK中并未輸出,原因是有部分手機(jī)并沒(méi)有接近

?????? //傳感器

?????? casePowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:

????????break;

??????default:

?????? ??return;

???? ?}

???mLocks.addLock(wl);//將PMS.WakeLock對(duì)象保存到mLocks中

??? if (ws!= null) {

???????wl.ws = new WorkSource(ws);

???? }

???? newlock= true;? //設(shè)置幾個(gè)參數(shù)信息,newlock表示新創(chuàng)建了一個(gè)PMS.WakeLock對(duì)象

????diffsource = false;

????oldsource = null;

?}else{

?? //如果之前保存有PMS.WakeLock,則要判斷新傳入的WorkSource和之前保存的WorkSource

?? //是否一樣。此處不討論這種情況

?? ......

}

在上面代碼中,很重要一部分是將前面flags信息轉(zhuǎn)成PMS.WakeLock的成員變量minState,下面是對(duì)轉(zhuǎn)換關(guān)系的總結(jié)。

·??FULL_WAKE_LOCK:當(dāng)啟用mUseSoftwareAutoBrightness時(shí),minState為SCREEN_BRIGHT(表示屏幕全亮),否則為ALL_BRIGHT(屏幕、鍵盤、按鍵全亮。注意,只有在打開(kāi)鍵盤時(shí)才能選擇此項(xiàng))或SCREEN_BUTTON_BRIGHT(屏幕、按鍵全亮)。

·??SCREEN_BRIGHT_WAKE_LOCK:minState為SCREEN_BRIGHT,表示屏幕全亮。

·??SCREEN_DIM_WAKE_LOCK:minState為SCREEN_DIM,表示屏幕Dim。

·??對(duì)PARTIAL_WAKE_LOCK和PROXIMITY_SCREEN_OFF_WAKE_LOCK情況不做處理。

該做的準(zhǔn)備工作都做了,下面來(lái)看第二階段的工作是什么。

2.? acquireWakeLockLocked分析之二

代碼如下:

? //isScreenLock用于判斷flags是否和屏幕有關(guān),除PARTIAL_WAKE_LOCK外,其他WAKE_LOCK

? //都和屏幕有關(guān)

if (isScreenLock(flags)) {

? if ((flags& LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) {

??????mProximityWakeLockCount++;//引用計(jì)數(shù)控制

?????? if(mProximityWakeLockCount == 1) {

????????enableProximityLockLocked();//使能Proximity傳感器

??????? }

?? } else {

?? if((wl.flags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) {

??? ?......//ACQUIRE_CAUSES_WAKEUP標(biāo)志處理

? } else {

? ?//①gatherState返回一個(gè)狀態(tài),稍后分析該函數(shù)

??mWakeLockState = (mUserState | mWakeLockState) &mLocks.gatherState();

? }

? ?//②設(shè)置電源狀態(tài),

? ?setPowerState(mWakeLockState | mUserState);

?? }

?}

以上代碼列出了兩個(gè)關(guān)鍵函數(shù),一個(gè)是gatherState,另外一個(gè)是setPowerState,下面來(lái)分析它們。

(1)?gatherState分析

gatherState函數(shù)的代碼如下:

[-->PowerManagerService.java::gatherState]

int gatherState()

{

??? intresult = 0;

??? int N =this.size();

??? for (inti=0; i<N; i++) {

???? WakeLock wl = this.get(i);

???? if(wl.activated)

??????? if(isScreenLock(wl.flags))

??????????result |= wl.minState;//對(duì)系統(tǒng)中所有活躍PMS.WakeLock的狀態(tài)進(jìn)行或操作

? }

?? returnresult;

?}

由以上代碼可知,gatherState將統(tǒng)計(jì)當(dāng)前系統(tǒng)內(nèi)部活躍WakeLock的minState。這里為什么要“使用”或“操作”呢?舉個(gè)例子,假如WakeLock A的minState為SCREEN_DIM,而WakeLock B的minState為SCREEN_BRIGHT,二者共同作用,最終的屏幕狀態(tài)顯然應(yīng)該是SCREEN_BRIGHT。

提示讀者也可參考PowerManagerService中SCREEN_DIM等變量的定義。

下面來(lái)看setPowerState,本章前面曾兩次對(duì)該函數(shù)避而不談,現(xiàn)在該見(jiàn)識(shí)見(jiàn)識(shí)它了。

(2)?setPowerState分析

setPowerState用于設(shè)置電源狀態(tài),先來(lái)看其在代碼中的調(diào)用:

setPowerState(mWakeLockState | mUserState);

在以上代碼中除了mWakeLockState外,還有一個(gè)mUserState。根據(jù)前面對(duì)gatherState函數(shù)的介紹可知,mWakeLockState的值來(lái)源于系統(tǒng)當(dāng)前活躍WakeLock的minState。那么mUserState代表什么呢?

mUserState代表用戶觸發(fā)事件導(dǎo)致的電源狀態(tài)。例如,按Home鍵后,將該值設(shè)置為SCREEN_BUTTON_BRIGHT(假設(shè)手機(jī)沒(méi)有鍵盤)。很顯然,此時(shí)系統(tǒng)的電源狀態(tài)應(yīng)該是mUserState和mWakeLockState的組合。

提示 “一個(gè)小小的變量背后代表了一個(gè)很重要的case”,讀者能體會(huì)到嗎?

下面來(lái)看setPowerState的代碼,這段代碼較長(zhǎng),也適合分段來(lái)看。第一段代碼如下:

[-->PowerManagerService.java::setPowerState]

private void setPowerState(int state)

{//調(diào)用另外一個(gè)同名函數(shù)

?setPowerState(state, false,WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT);

}

//setPowerState

private void setPowerState(int newState, booleannoChangeLights, int reason)

{

?synchronized (mLocks) {

? int err;

??if (noChangeLights)//在這種情況中,noChangeLights為false

????newState = (newState & ~LIGHTS_MASK) | (mPowerState &LIGHTS_MASK);

??

? if(mProximitySensorActive)//如果打開(kāi)了接近感應(yīng)器,就不需要在這里點(diǎn)亮屏幕了

????newState = (newState & ~SCREEN_BRIGHT);

?

? if(batteryIsLow())//判斷是否處于低電狀態(tài)

?????newState |= BATTERY_LOW_BIT;

?? else

?????newState &= ~BATTERY_LOW_BIT;

?......

? //如果還沒(méi)啟動(dòng)完成,則需要將newState置為ALL_