当前位置:   article > 正文

android 低电量卡,Android中低电量的处置方法

android13 updatelowbatterywarning

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

}

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/902009
推荐阅读
相关标签
  

闽ICP备14008679号