Android中低电量的处理方法
前段时间解了个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?is290 ?????????????*???less?than?or?equal?to?WARNING,?or291 ?????????????*?-?is?not?plugged?and?battery?level?falls?to?WARNING?boundary292 ?????????????*???(becomes?<=?mLowBatteryWarningLevel).293 ?????????????*/294 ????????????final?boolean?sendBatteryLow?=?!plugged295 ????????????????&&?mBatteryStatus?!=?BatteryManager.BATTERY_STATUS_UNKNOWN296 ????????????????&&?mBatteryLevel?<=?mLowBatteryWarningLevel297 ????????????????&&?(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.xml261 ????<!--?Display?low?battery?warning?when?battery?level?dips?to?this?value?-->262 ????<integer?name="config_lowBatteryWarningLevel">15</integer>?下面代码是具体发送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.xml264 ????<!--?Close?low?battery?warning?when?battery?level?reaches?this?value?-->265 ????<integer?name="config_lowBatteryCloseWarningLevel">20</integer>?按照如上分析,貌似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="?+?mPhoneState763 ??????????????????????+?"?mLowBatteryDialog="?+?mLowBatteryDialog764 ??????????????????????+?"?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为true794 ????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_TASK818 ????????????????????????|?Intent.FLAG_ACTIVITY_MULTIPLE_TASK819 ????????????????????????|?Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS820 ????????????????????????|?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,为什么不始终创建呢?这时候需要看一下mLowBatteryListener881 ????private?DialogInterface.OnDismissListener?mLowBatteryListener882 ????????????=?new?DialogInterface.OnDismissListener()?{883 ????????public?void?onDismiss(DialogInterface?dialog)?{884 ????????????mLowBatteryDialog?=?null;885 ????????????mBatteryLevelTextView?=?null;886 ????????}887 ????};可以看出,如果mLowBatteryDialog已经被Dismiss掉,则选择创建mLowBatteryDialog,如果没有Dismiss,为了节省资源,只需要更新mLowBatteryDialog中的界面就可以了。?第二部分则是低电提示音,在网上看帖子,这个功能令很多人恼火啊,特别是学生深受其害,那么是否让用户有选择的打开此功能呢?实现这部分功能需要两步POWER_SOUNDS_ENABLED841 ????????if?(Settings.System.getInt(cr,842 ????????????????Settings.System.POWER_SOUNDS_ENABLED,?1)?==?1)我们知道Settings.System.POWER_SOUNDS_ENABLED是一个系统属性,通过在源码中并没有查找到有用此属性的地方Settings.System.LOW_BATTERY_SOUND844 ????????????final?String?soundPath?=?Settings.System.getString(cr,845 ????????????????Settings.System.LOW_BATTERY_SOUND);846 ????????????if?(soundPath?!=?null)?{?定义在文件Settings.javaframeworks/base/core/java/android/provider/Settings.java1656 ????????/**1657 ?????????*?Whether?to?play?a?sound?for?low-battery?alerts.1658 ?????????*?@hide1659 ?????????*/1660 ????????public?static?final?String?POWER_SOUNDS_ENABLED?=?"power_sounds_enabled";1674 ????????/**1675 ?????????*?URI?for?the?low?battery?sound?file.1676 ?????????*?@hide1677 ?????????*/1678 ????????public?static?final?String?LOW_BATTERY_SOUND?=?"low_battery_sound";通过如下方式读取默认值frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java1081 ????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);上面包括是否开启解锁屏声音,默认值定义在如下文件中61 ????<!--?user?interface?sound?effects?-->62 ????<integer?name="def_power_sounds_enabled">1</integer>63????<string?name="def_low_battery_sound"?translatable="false">/system/media/audio/ui/LowBattery.ogg</string>64 ????<integer?name="def_dock_sounds_enabled">0</integer>69 ????<integer?name="def_lockscreen_sounds_enabled">0</integer>70 ????<string?name="def_lock_sound"?translatable="false">/system/media/audio/ui/Lock.ogg</string>71 ????<string?name="def_unlock_sound"?translatable="false">/system/media/audio/ui/Unlock.ogg</string>?通过以上分析可以知道,低点提示音始终是有的,并且用户不可以定制有了以上的分析,可以很清楚的知道,可以在Settings应用里加个可以配置低点提示音的功能如可以在SoundSettings中多定义一个CheckboxPreference用来控制,当用户选中时,操作也很简单final?ContentResolver?cr?=?mContext.getContentResolver();Settings.System.putInt(cr, Settings.System.POWER_SOUNDS_ENABLED,?0);?1.2)如果此时电话不为TelephonyManager.CALL_STATE_IDLE状态,而是如下两种状态TelephonyManager.CALL_STATE_OFFHOOKTelephonyManager.CALL_STATE_RINGING当电话状态变更为CALL_STATE_IDLE时,会调用如下函数859 ????private?final?void?updateCallState(int?state)?{860 ????????mPhoneState?=?state;861 ????????if?(false)?{862 ????????????Slog.d(TAG,?"mPhoneState="?+?mPhoneState863 ????????????????????+?"?mLowBatteryDialog="?+?mLowBatteryDialog864 ????????????????????+?"?mBatteryShowLowOnEndCall="?+?mBatteryShowLowOnEndCall);865 ????????}866 ????????if?(mPhoneState?==?TelephonyManager.CALL_STATE_IDLE)?{867 ????????????if?(mBatteryShowLowOnEndCall)?{868 ????????????????if?(!mBatteryPlugged)?{869 ????????????????????showLowBatteryWarning();870 ????????????????}871 ????????????????mBatteryShowLowOnEndCall?=?false;872 ????????????}873 ????????}?else?{874 ????????????if?(mLowBatteryDialog?!=?null)?{875 ????????????????mLowBatteryDialog.dismiss();876 ????????????????mBatteryShowLowOnEndCall?=?true;877 ????????????}878 ????????}879 ????}由于mBatteryShowLowOnEndCall已经被置为true,如果此时没有在充电状态,则也会调到showLowBatteryWarning,与以上的处理方式一样。??以上知识处理了Intent.ACTION_BATTERY_LOW,下面简单看一下如何处理Intent.ACTION_BATTERY_OKAY775 ????private?void?onBatteryOkay(Intent?intent)?{776 ????????if?(mLowBatteryDialog?!=?null777 ????????????????&&?SHOW_LOW_BATTERY_WARNING)?{778?????????????mLowBatteryDialog?.dismiss();779 ????????????mBatteryShowLowOnEndCall?=?false;780 ????????}781 ????}从以上代码中可以看出只是把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来处理信息packages/apps/Phone/src/com/android/phone/CallNotifier.java1222 ????/**1223 ?????*?Posts?a?PHONE_BATTERY_LOW?event,?causing?us?to?play?a?warning1224 ?????*?tone?if?the?user?is?in-call.1225 ?????*/1226 ????/*?package?*/?void?sendBatteryLow()?{1227 ????????Message?message?=?Message.obtain(this,?PHONE_BATTERY_LOW);1228 ????????sendMessage(message);1229 ????}通过Handler来处理PHONE_BATTERY_LOW信息266 ????????????case?PHONE_BATTERY_LOW:267 ????????????????onBatteryLow();268 ????????????????break;
?
1231 ????private?void?onBatteryLow()?{1232 ????????if?(DBG)?log("onBatteryLow()...");1233 1234 ????????//?A?"low?battery"?warning?tone?is?now?played?by1235 ????????//?StatusBarPolicy.updateBattery().1236 ????}从以上代码可以看出,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?connected302 ????????????//?since?the?standard?intent?will?not?wake?any?applications?and?some303 ????????????//?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);309 ????????????}310 ????????????else?if?(mPlugType?==?0?&&?mLastPlugType?!=?0)?{311 ????????????????statusIntent.setAction(Intent.ACTION_POWER_DISCONNECTED);312 ????????????????mContext.sendBroadcast(statusIntent);313 ????????????}