前段时间解了个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).
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
下面代码是具体发送
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);
其中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
按照如上分析,貌似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();
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);
534 else if (action.equals(Intent.ACTION_ALARM_CHANGED)) {
537 else if (action.equals(Intent.ACTION_SYNC_STATE_CHANGED)) {
538 updateSyncState(intent);
540 else if (action.equals(Intent.ACTION_BATTERY_LOW)) {
541 onBatteryLow(intent);
543 else if (action.equals(Intent.ACTION_BATTERY_OKAY)
544 || action.equals(Intent.ACTION_POWER_CONNECTED)) {
545 onBatteryOkay(intent);
当收到Intent.ACTION_BATTERY_LOW时,做如下处理
759 private void onBatteryLow(Intent intent) {
760 if (SHOW_LOW_BATTERY_WARNING) {
762 Slog.d(TAG, "mPhoneState=" + mPhoneState
763 + " mLowBatteryDialog=" + mLowBatteryDialog
764 + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
767 if (SHOW_BATTERY_WARNINGS_IN_CALL || mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
768 showLowBatteryWarning();
770 mBatteryShowLowOnEndCall = true;
其中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();
797 // Show exact battery level.
798 CharSequence levelText = mContext.getString(
799 R.string.battery_low_percent_format, mBatteryLevel);
801 if (
mBatteryLevelTextView != null) {
802 mBatteryLevelTextView.setText(levelText);
804 View v = View.inflate(mContext, R.layout.battery_low, null);
805 mBatteryLevelTextView=(TextView)v.findViewById(R.id.level_percent);
807 mBatteryLevelTextView.setText(levelText);
809 AlertDialog.Builder b = new AlertDialog.Builder(mContext);
810 b.setCancelable(true);
811 b.setTitle(R.string.battery_low_title);
813 b.setIcon(android.R.drawable.ic_dialog_alert);
814 b.setPositiveButton(android.R.string.ok, null);
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();
833 AlertDialog d = b.create();
834 d.setOnDismissListener(mLowBatteryListener);
835 d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
837 mLowBatteryDialog = d;
840 final ContentResolver cr = mContext.getContentResolver();
841 if (Settings.System.getInt(cr,
842 Settings.System.POWER_SOUNDS_ENABLED, 1) == 1)
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);
851 sfx.setStreamType(AudioManager.STREAM_SYSTEM);
此函数主要分两部分,第一部分是根据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; 可以看出,如果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
1657 * Whether to play a sound for low-battery alerts.
1660 public static final String POWER_SOUNDS_ENABLED = "power_sounds_enabled";
1675 * URI for the low battery sound file.
1678 public static final String LOW_BATTERY_SOUND = "low_battery_sound";
通过如下方式读取默认值
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,
1085 R.string.def_low_battery_sound);
1098 loadIntegerSetting(stmt, Settings.System.LOCKSCREEN_SOUNDS_ENABLED,
1099 R.integer.def_lockscreen_sounds_enabled);
1100 loadStringSetting(stmt, Settings.System.LOCK_SOUND,
1101 R.string.def_lock_sound);
1102 loadStringSetting(stmt, Settings.System.UNLOCK_SOUND,
1103 R.string.def_unlock_sound);
上面包括是否开启解锁屏声音,默认值定义在如下文件中
63 /system/media/audio/ui/LowBattery.ogg 70 /system/media/audio/ui/Lock.ogg 71 /system/media/audio/ui/Unlock.ogg
通过以上分析可以知道,低点提示音始终是有的,并且用户不可以定制
有了以上的分析,可以很清楚的知道,可以在Settings应用里加个可以配置低点提示音的功能
如可以在SoundSettings中多定义一个CheckboxPreference用来控制,当用户选中时,操作也很简单
final ContentResolver cr = mContext.getContentResolver();
Settings.System.putInt(cr, Settings.System.POWER_SOUNDS_ENABLED, 0);
1.2)如果此时电话不为TelephonyManager.CALL_STATE_IDLE状态,而是如下两种状态
当电话状态变更为CALL_STATE_IDLE时,会调用如下函数
859 private final void updateCallState(int state) {
862 Slog.d(TAG, "mPhoneState=" + mPhoneState
863 + " mLowBatteryDialog=" + mLowBatteryDialog
864 + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
866 if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
867 if (mBatteryShowLowOnEndCall) {
868 if (!mBatteryPlugged) {
869 showLowBatteryWarning();
871 mBatteryShowLowOnEndCall = false;
874 if (mLowBatteryDialog != null) {
875 mLowBatteryDialog.dismiss();
876 mBatteryShowLowOnEndCall = true;
由于mBatteryShowLowOnEndCall已经被置为true,如果此时没有在充电状态,则也会调到showLowBatteryWarning,与以上的处理方式一样。
以上知识处理了Intent.ACTION_BATTERY_LOW,下面简单看一下如何处理Intent.ACTION_BATTERY_OKAY
775 private void onBatteryOkay(Intent intent) {
776 if (mLowBatteryDialog != null
777 && SHOW_LOW_BATTERY_WARNING) {
778 mLowBatteryDialog .dismiss();
779 mBatteryShowLowOnEndCall = false;
从以上代码中可以看出只是把mLowBatteryDialog给Dismiss掉,并把mBatteryShowLowOnEndCall 复位
1)Phone应用
还有Phone应用也会接收Intent.ACTION_BATTERY_LOW消息,但是跟下去,看懂了代码实现者的意图,如果在通话中,电池电量较低时,会给用户一个提示,但是最终并没有真正的实现这一功能。
首先PhoneApp中注册监听Intent.ACTION_BATTERY_LOW
packages/apps/Phone/src/com/android/phone/PhoneApp.java
499 intentFilter.addAction(Intent.ACTION_BATTERY_LOW);
1440 } else if (action.equals(Intent.ACTION_BATTERY_LOW)) {
1441 if (VDBG) Log.d(LOG_TAG, "mReceiver: ACTION_BATTERY_LOW");
1442 notifier.sendBatteryLow(); // Play a warning tone if in-call
通过CallNotifier来处理信息
1223 * Posts a PHONE_BATTERY_LOW event, causing us to play a warning
1224 * tone if the user is in-call.
1226 /* package */ void sendBatteryLow() {
1227 Message message = Message.obtain(this, PHONE_BATTERY_LOW);
1228 sendMessage(message);
通过Handler来处理PHONE_BATTERY_LOW信息
266 case PHONE_BATTERY_LOW:
1231 private void onBatteryLow() {
1232 if (DBG) log("onBatteryLow()...");
1234 // A "low battery" warning tone is now played by
1235 // StatusBarPolicy.updateBattery().
从以上代码可以看出,Phone中并没有实现此功能,说是通过StatusBarPolicy.updateBattery()来实现
而StatusBarPolicy中的代码非常简单,根本就没有实现此功能
PS:
其实在BatteryService函数update中还会发送如下信息,如果有哪位同学想要研究的话,可以接着研究
1)Intent.ACTION_POWER_CONNECTED
2)Intent.ACTION_POWER_DISCONNECTED
301 // Separate broadcast is sent for power connected / not connected
302 // since the standard intent will not wake any applications and some
303 // applications may want to have smart behavior based on this.
304 Intent statusIntent = new Intent();
305 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
306 if (mPlugType != 0 && mLastPlugType == 0) {
307 statusIntent.setAction(Intent.ACTION_POWER_CONNECTED);
308 mContext.sendBroadcast(statusIntent);
310 else if (mPlugType == 0 && mLastPlugType != 0) {
311 statusIntent.setAction(Intent.ACTION_POWER_DISCONNECTED);
312 mContext.sendBroadcast(statusIntent);
阅读(1088) | 评论(0) | 转发(0) |