前段时间解了个Bug:Android系统在低电时充电,StatusBar上的充电图标不会变化,始终显示的是同一个图标
当时没有来得及整理,现在补一下
电池电量信息是从BatteryService中通过Intent发送出去的,在上一篇有讲到
frameworks/base/services/java/com/android/server/BatteryService.java
其中函数update负责读具体信息并发送
208
private synchronized final void update() {
1、发送:Intent.ACTION_BATTERY_LOW && Intent.ACTION_BATTERY_OKAY)
首先需要判断是否需要发送低电量信息:Intent.ACTION_BATTERY_LOW
288
/* The ACTION_BATTERY_LOW broadcast is sent in these situations:
289
* - is just un-plugged (previously was plugged) and battery level is
290
* less than or equal to WARNING, or
291
* - is not plugged and battery level falls to WARNING boundary
292
* (becomes <= mLowBatteryWarningLevel).
293
*/
294
final boolean sendBatteryLow = !plugged
295
&& mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
296
&& mBatteryLevel <= mLowBatteryWarningLevel
297
&& (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
可以看到发送低电量信息会有两个条件,
1)当前不在充电状态,上次update时处于充电状态,并且电池电量小于等于mLowBatteryWarningLevel(低电量警告值)
2)当前不在充电状态,电池电量小于等于mLowBatteryWarningLevel(低电量警告值),并且上次update时,电量大于mLowBatteryWarningLevel(低电量警告值)
但是mLowBatteryWarningLevel这个值具体是多少呢?
127
mLowBatteryWarningLevel = mContext.getResources().getInteger(
128
com.android.internal.R.integer.config_lowBatteryWarningLevel);
从以上可以看出,是通过config.xml读取到的
文件位于frameworks/base/core/res/res/values/config.xml
261
262
15
下面代码是具体发送
315
if (sendBatteryLow) {
316
mSentLowBatteryBroadcast = true;
317
statusIntent.setAction(Intent.ACTION_BATTERY_LOW);
318
mContext.sendBroadcast(statusIntent);
319
} else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= mLowBatteryCloseWarningLevel) {
320
mSentLowBatteryBroadcast = false;
321
statusIntent.setAction(Intent.ACTION_BATTERY_OKAY);
322
mContext.sendBroadcast(statusIntent);
323
}
其中if分支是发送Intent.ACTION_BATTERY_LOW,else分析负责发送Intent.ACTION_BATTERY_OKAY);
if 分支只要满足上面两个条件就回发送,而else什么时候发送呢?
根据代码可以这么理解:当发送Intent.ACTION_BATTERY_LOW时,会把mSentLowBatteryBroadcast 置为true,
同时mBatteryLevel 会小于15;由于电池更新较快,也就是此update函数较频繁,可以推断
mLastBatteryLevel 将会略大约else分支用到mBatteryLevel 的值(可能为16),是否会满足else分支的
mLastBatteryLevel >= mLowBatteryCloseWarningLevel条件呢?
此时就需要我们来查看mLowBatteryCloseWarningLevel的值具体是多少,定义方式与mLowBatteryWarningLevel类似,如下
129
mLowBatteryCloseWarningLevel = mContext.getResources().getInteger(
130
com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
也是通过config.xml读取到的
文件位于frameworks/base/core/res/res/values/config.xml
264
265
20
按照如上分析,貌似else分析永远都不会执行,也就是Intent.ACTION_BATTERY_OKAY不会发出
但其实有一种情况会执行的,当电池电量小于mLowBatteryWarningLevel,并且已经成功发送了
Intent.ACTION_BATTERY_LOW信息,此时用户插上USB或充电器充电,当电池电量达到
mLowBatteryCloseWarningLevel,此时才会发送Intent.ACTION_BATTERY_OKAY信息
2、接收:Intent.ACTION_BATTERY_LOW && Intent.ACTION_BATTERY_OKAY)
1)StatusBarPolicy.java
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
这个类在2.3之前的版本中放在frameworks/base/services/java/com/android/server/status/目录下,现在做了代码归整
首先需要注册
671
IntentFilter filter = new IntentFilter();
672
673
// Register for Intent broadcasts for...
674
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
675
filter.addAction(Intent.ACTION_BATTERY_LOW);
676
filter.addAction(Intent.ACTION_BATTERY_OKAY);
700
mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
接收部分
529
public void onReceive(Context context, Intent intent) {
530
String action = intent.getAction();
531
if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
532
updateBattery(intent);
533
}
534
else if (action.equals(Intent.ACTION_ALARM_CHANGED)) {
535
updateAlarm(intent);
536
}
537
else if (action.equals(Intent.ACTION_SYNC_STATE_CHANGED)) {
538
updateSyncState(intent);
539
}
540
else if (action.equals(Intent.ACTION_BATTERY_LOW)) {
541
onBatteryLow(intent);
542
}
543
else if (action.equals(Intent.ACTION_BATTERY_OKAY)
544
|| action.equals(Intent.ACTION_POWER_CONNECTED)) {
545
onBatteryOkay(intent);
546
}
当收到Intent.ACTION_BATTERY_LOW时,做如下处理
759
private void onBatteryLow(Intent intent) {
760
if (SHOW_LOW_BATTERY_WARNING) {
761
if (false) {
762
Slog.d(TAG, "mPhoneState=" + mPhoneState
763
+ " mLowBatteryDialog=" + mLowBatteryDialog
764
+ " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
765
}
766
767
if (SHOW_BATTERY_WARNINGS_IN_CALL || mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
768
showLowBatteryWarning();
769
} else {
770
mBatteryShowLowOnEndCall = true;
771
}
772
}
773
}
其中SHOW_LOW_BATTERY_WARNING和SHOW_BATTERY_WARNINGS_IN_CALL 定义为常量
115
private static final boolean SHOW_LOW_BATTERY_WARNING = true;
116
private static final boolean SHOW_BATTERY_WARNINGS_IN_CALL = true;
1.1)
可以看到当此时电话状态为TelephonyManager.CALL_STATE_IDLE状态时,做如下处理,否则标记mBatteryShowLowOnEndCall为true
794
private void showLowBatteryWarning() {
795
closeLastBatteryView();
796
797
// Show exact battery level.
798
CharSequence levelText = mContext.getString(
799
R.string.battery_low_percent_format, mBatteryLevel);
800
801
if (
mBatteryLevelTextView != null
) {
802
mBatteryLevelTextView.setText(levelText);
803
} else {
804
View v = View.inflate(mContext, R.layout.battery_low, null);
805
mBatteryLevelTextView=(TextView)v.findViewById(R.id.level_percent);
806
807
mBatteryLevelTextView.setText(levelText);
808
809
AlertDialog.Builder b = new AlertDialog.Builder(mContext);
810
b.setCancelable(true);
811
b.setTitle(R.string.battery_low_title);
812
b.setView(v);
813
b.setIcon(android.R.drawable.ic_dialog_alert);
814
b.setPositiveButton(android.R.string.ok, null);
815
816
final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
817
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
818
| Intent.FLAG_ACTIVITY_MULTIPLE_TASK
819
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
820
| Intent.FLAG_ACTIVITY_NO_HISTORY);
821
if (intent.resolveActivity(mContext.getPackageManager()) != null) {
822
b.setNegativeButton(R.string.battery_low_why,
823
new DialogInterface.OnClickListener() {
824
public void onClick(DialogInterface dialog, int which) {
825
mContext.startActivity(intent);
826
if (mLowBatteryDialog != null) {
827
mLowBatteryDialog.dismiss();
828
}
829
}
830
});
831
}
832
833
AlertDialog d = b.create();
834
d.setOnDismissListener(mLowBatteryListener);
835
d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
836
d.show();
837
mLowBatteryDialog = d;
838
}
839
840
final ContentResolver cr = mContext.getContentResolver();
841
if (Settings.System.getInt(cr,
842
Settings.System.POWER_SOUNDS_ENABLED, 1) == 1)
843
{
844
final String soundPath = Settings.System.getString(cr,
845
Settings.System.LOW_BATTERY_SOUND);
846
if (soundPath != null) {
847
final Uri soundUri = Uri.parse("file://" + soundPath);
848
if (soundUri != null) {
849
final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
850
if (sfx != null) {
851
sfx.setStreamType(AudioManager.STREAM_SYSTEM);
852
sfx.play();
853
}
854
}
855
}
856
}
857
}
此函数主要分两部分,第一部分是根据mBatteryLevelTextView是否为null,有选择的创建并显示mLowBatteryDialog,为什么不始终创建呢?
这时候需要看一下mLowBatteryListener
881
private DialogInterface.OnDismissListener mLowBatteryListener
882
= new DialogInterface.OnDismissListener() {
883
public void onDismiss(DialogInterface dialog) {
884
mLowBatteryDialog = null;
885
mBatteryLevelTextView = null;
886
}
887
};
可以看出,如果mLowBatteryDialog已经被Dismiss掉,则选择创建mLowBatteryDialog,如果没有Dismiss,为了节省资源,只需要更新mLowBatteryDialog中的界面就可以了。
第二部分则是低电提示音,在网上看帖子,这个功能令很多人恼火啊,特别是学生深受其害,那么是否让用户有选择的打开此功能呢?
实现这部分功能需要两步
POWER_SOUNDS_ENABLED
841
if (Settings.System.getInt(cr,
842
Settings.System.POWER_SOUNDS_ENABLED, 1) == 1)
我们知道Settings.System.POWER_SOUNDS_ENABLED是一个系统属性,通过在源码中并没有查找到有用此属性的地方
Settings.System.LOW_BATTERY_SOUND
844
final String soundPath = Settings.System.getString(cr,
845
Settings.System.LOW_BATTERY_SOUND);
846
if (soundPath != null) {
定义在文件Settings.java
frameworks/base
/
core
/
java
/
android
/
provider
/
Settings.java
1656
/**
1657
* Whether to play a sound for low-battery alerts.
1658
* @hide
1659
*/
1660
public static final String POWER_SOUNDS_ENABLED = "power_sounds_enabled";
1674
/**
1675
* URI for the low battery sound file.
1676
* @hide
1677
*/
1678
public static final String LOW_BATTERY_SOUND = "low_battery_sound";
通过如下方式读取默认值
frameworks/base/
packages
/
SettingsProvider
/
src
/
com
/
android
/
providers
/
settings
/
DatabaseHelper.java
1081
private void loadUISoundEffectsSettings(SQLiteStatement stmt) {
1082
loadIntegerSetting(stmt, Settings.System.POWER_SOUNDS_ENABLED,
1083
R.integer.def_power_sounds_enabled);
1084
loadStringSetting(stmt, Settings.System.LOW_BATTERY_SOUND,