前段时间解了个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,

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

62

1

63

/system/media/audio/ui/LowBattery.ogg

64

0

69

0

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状态,而是如下两种状态

TelephonyManager.

CALL_STATE_OFFHOOK

TelephonyManager.

CALL_STATE_RINGING

当电话状态变更为CALL_STATE_IDLE时,会调用如下函数

859

private final void updateCallState(int state) {

860

mPhoneState = state;

861

if (false) {

862

Slog.d(TAG, "mPhoneState=" + mPhoneState

863

+ " mLowBatteryDialog=" + mLowBatteryDialog

864

+ " 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_OKAY

775

private void onBatteryOkay(Intent intent) {

776

if (mLowBatteryDialog != null

777

&& 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.java

1222

/**

1223

* Posts a PHONE_BATTERY_LOW event, causing us to play a warning

1224

* 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 by

1235

// 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 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);

309

}

310

else if (mPlugType == 0 && mLastPlugType != 0) {

311

statusIntent.setAction(Intent.ACTION_POWER_DISCONNECTED);

312

mContext.sendBroadcast(statusIntent);

313

}

android 低电量卡,Android中低电量的处置方法相关推荐

  1. Android Studio 模拟器卡慢、占内存解决方法

    Android Studio 模拟器卡慢.占内存解决方法 Android Studio 模拟器卡慢.占内存解决方法 在使用Android virtual device来进行调试运行时会比较慢,性能也不 ...

  2. Android默认电话卡,Android智能终端SIM卡自动切换方法与流程

    本发明涉及智能终端的技术领域,尤其涉及一种SIM卡的切换方法. 背景技术: 现在的手机等机器基本支持多卡配置,而且现在单卡配置基本已经不能满足人们的日常需求:并且在智能系统的潮流之下,有人喜欢用手机看 ...

  3. android gridview滑动卡,Android RecyclerView的卡顿问题

    本文其实是上一篇Android本地app操作相关基础的延伸,然而内容基本没什么联系了(初学者身份瞬间暴露,打一枪换一个地方←_←),就不好意思再添个"续"或者"(2)&q ...

  4. android标签切换卡,Android切换卡TabWidget用法示例

    本文实例讲述了Android切换卡TabWidget用法.分享给大家供大家参考,具体如下: Tab选项卡类似与电话本的界面,通过多个标签切换不同的内容,要实现这个效果,首先要知道TabHost,它是一 ...

  5. android 低版本 searchview,Android SearchView

    效果图 image.png 添加依赖 implementation 'androidx.appcompat:appcompat:1.1.0' xml代码 android:id="@+id/s ...

  6. android 低版本 searchview,Android SearchView 使用示例

    布局文件: android:layout_width="match_parent" android:layout_height="match_parent" a ...

  7. android+更换sd卡,Android手机上如何无痛替换SD卡,扩展存储空间

    新旧SD卡换装方法探讨:本文引用地址:http://www.eepw.com.cn/article/201610/305616.htm 许多用户在Android手机买来使用一段时间后,往往发现无论是手 ...

  8. android 获取sim卡,Android 获取手机SIM卡运营商

    直接上代码: /** * 获取SIM卡运营商 * * @param context * @return */ public static String getOperators(Context con ...

  9. android u盘读写权限,Android 外部SD卡/U盘无法写入解决方法(需要root)

    但今天我遇到一个问题,就是我买了只TF卡装上去以后发现:一般程序无法写入TF卡,而系统自带的文件工具能够写入. 什么原因呢? 好在这个平板已经是root的,马上调出rootexplorer文件管理器查 ...

最新文章

  1. 真机IOS8.3以上的文件夹共享
  2. 06-Java 本地文件操作
  3. python odoo_odoo开发学习 -- Python2 or Python3 ?
  4. [acm]HDOJ 1200 To and Fro
  5. mysql递归查询所有上下节点_非递归打印二叉树的所有路径,保存父节点和孩子节点到底有啥差别...
  6. 矩池云安装利用pip、apt、conda安装需要的包
  7. python爬虫什么意思-python的爬虫是什么意思
  8. java Http消息传递之POST和GET两种方法--通过实用工具类来获取服务器资源
  9. 笔记本电脑怎么录制屏幕
  10. 分享一个带语音root的CM311-1a线刷包
  11. IP地址和 MAC地址详解
  12. 实现用python刷王者荣耀金币
  13. 阿里企业邮箱POP\SMTP\IMAP地址和端口信息
  14. 「微服务系列」统一网关Gateway
  15. malloc的内存申请和释放
  16. Applet中签名与未签名代码的混合使用带来的问题
  17. 在vue添加lottie动画
  18. Async/Await FAQ (Stephen Toub)
  19. 动态规划之扔鸡蛋(或手机)问题
  20. python培训班-天津Python培训班学费多少

热门文章

  1. 阿里云服务器vCPU和CPU有区别吗?
  2. linux下sybase配置文件,linux下SYBASE数据库安装后的配置
  3. OBS 基础10 录制视频
  4. 饱和非线性、非饱和非线性
  5. 顶尖电子秤ls6恢复出厂_顶尖电子称怎么恢复出厂默认?
  6. HDFS Truncate文件截断
  7. 计算机职业价值观测评报告,职业价值观测评报告
  8. 小米应用商店上传apk报图片格式错误,小米手机调试 DELETE_FAILED_INTERNAL_ERROR错误
  9. 显微镜自动聚焦原理是什么_显微镜自动对焦
  10. Wireshark Wget bot木马分析