26
2017
09

android开发笔记之电池低电处理

这里写图片描述

公司最近在搞个KMS,鼓励大家技术分享。好吧,我是一个好员工,积极响应领导的各种要求。但是,这个我不会写的,你可以说我没有分享精神,可以说我技术水平low,我就是不会写。如果公司一定要强制要求我写,我也一定是乱写凑数,这完全不能怪我,只能怪这个无情的世界,无情的季节,无情的………….。(请不要问我具体的原因,谢谢!)

但是,有一个好朋友,在上面发表了一个电池低电处理的文章,我看了一下,确实是说的非常清楚,只是是一个比较低版本的代码流程,我在android 7.1的代码上看了一下,和他说的有一点出入,不过,这没有关系不影响我们来学习。

我可以不写,但是我们可以学习。

BatteryService.java

./frameworks/base/services/core/java/com/android/server/BatteryService.java

当电池电量少于我们配置的低电量警告值(config_lowBatteryWarningLevel)时,发送低电量通知,弹出低电量通知对话框。
./frameworks/base/core/res/res/values/config.xml

<integer name="config_lowBatteryWarningLevel">15</integer>

只我满足shouldSendBatteryLowLocked的条件就可以发送低电量通知:

private boolean shouldSendBatteryLowLocked() {
        final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
        final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;

        /* The ACTION_BATTERY_LOW broadcast is sent in these situations:
         * - is just un-plugged (previously was plugged) and battery level is
         *   less than or equal to WARNING, or
         * - is not plugged and battery level falls to WARNING boundary
         *   (becomes <= mLowBatteryWarningLevel).
         */
 return !plugged
                && mBatteryProps.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
                && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel
                && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
    }

发送低电量通知Intent.ACTION_BATTERY_LOW:

private void processValuesLocked(boolean force)

            if (shouldSendBatteryLowLocked()) {
                mSentLowBatteryBroadcast = true;
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW);
                        statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
                    }
                });

当电量为0时,发出关闭通知Intent.ACTION_REQUEST_SHUTDOWN:

private void shutdownIfWeakChargerEmptySOCLocked() {

        if (mBatteryProps.batteryLevel == 0) {
            if (mInitiateShutdown) {
               if (ActivityManagerNative.isSystemReady()) {

                    Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
                    intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    mContext.startActivityAsUser(intent, UserHandle.CURRENT);
                }   

PowerNotificationWarnings.java

./frameworks/base/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java

当电量低时,弹出低电量通知对话框:

private void showWarningNotification() {
        final int textRes = R.string.battery_low_percent_format;
        final String percentage = NumberFormat.getPercentInstance().format((double) mBatteryLevel / 100.0);
        final Notification.Builder nb = new Notification.Builder(mContext)
                .setSmallIcon(R.drawable.ic_power_low)
                // Bump the notification when the bucket dropped.
                .setWhen(mBucketDroppedNegativeTimeMs)
                .setShowWhen(false)
                .setContentTitle(mContext.getString(R.string.battery_low_title))
                .setContentText(mContext.getString(textRes, percentage))
                .setOnlyAlertOnce(true)
                .setDeleteIntent(pendingBroadcast(ACTION_DISMISSED_WARNING))
                .setPriority(Notification.PRIORITY_MAX)
                .setVisibility(Notification.VISIBILITY_PUBLIC)
                .setColor(mContext.getColor(
                        com.android.internal.R.color.battery_saver_mode_color));
        if (hasBatterySettings()) {
            nb.setContentIntent(pendingBroadcast(ACTION_SHOW_BATTERY_SETTINGS));
        }
        nb.addAction(0,
                mContext.getString(R.string.battery_saver_start_action),
                pendingBroadcast(ACTION_START_SAVER));
        if (mPlaySound) {
            attachLowBatterySound(nb);
            mPlaySound = false;
        }
        SystemUI.overrideNotificationAppName(mContext, nb);
        mNoMan.notifyAsUser(TAG_NOTIFICATION, R.id.notification_power, nb.build(), UserHandle.ALL);
    }

并播放低电量声音:

private void attachLowBatterySound(Notification.Builder b) {
        final ContentResolver cr = mContext.getContentResolver();

        final int silenceAfter = Settings.Global.getInt(cr,
                Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0);
        final long offTime = SystemClock.elapsedRealtime() - mScreenOffTime;
        if (silenceAfter > 0
                && mScreenOffTime > 0
                && offTime > silenceAfter) {
            Slog.i(TAG, "screen off too long (" + offTime + "ms, limit " + silenceAfter
                    + "ms): not waking up the user with low battery sound");
            return;
        }

        if (DEBUG) {
            Slog.d(TAG, "playing low battery sound. pick-a-doop!"); // WOMP-WOMP is deprecated
        }

        if (Settings.Global.getInt(cr, Settings.Global.POWER_SOUNDS_ENABLED, 1) == 1) {
            final String soundPath = Settings.Global.getString(cr,
                    Settings.Global.LOW_BATTERY_SOUND);
            if (soundPath != null) {
                final Uri soundUri = Uri.parse("file://" + soundPath);
                if (soundUri != null) {
                    b.setSound(soundUri, AUDIO_ATTRIBUTES);
                    if (DEBUG) Slog.d(TAG, "playing sound " + soundUri);
                }
            }
        }
    }

我们一般在配置文件中设置低电量的声音文件和超时时间:
./frameworks/base/packages/SettingsProvider/res/values/defaults.xml

<string name="def_low_battery_sound" translatable="false">/system/media/audio/ui/LowBattery.ogg</string>
<!-- Default for Settings.Global.LOW_BATTERY_SOUND_TIMEOUT.
<integer name="def_low_battery_sound_timeout">0</integer>

而事实上调用showWarningNotification方法,弹出低电量通知对话框的地方为updateNotification():

    private void updateNotification() {

        if (mInvalidCharger) {
            showInvalidChargerNotification();
            mShowing = SHOWING_INVALID_CHARGER;
        } else if (mWarning) {
            showWarningNotification();

再往上查找调用updateNotification() 为:

    public void showLowBatteryWarning(boolean playSound) {

        mPlaySound = playSound;
        mWarning = true;
        updateNotification();
    }

再往上查找调用showLowBatteryWarning() 为:
./frameworks/base/packages/SystemUI/src/com/android/systemui/power/PowerUI.java

private final class Receiver extends BroadcastReceiver {

        public void init() {
            // Register for Intent broadcasts for...
            IntentFilter filter = new IntentFilter();
            filter.addAction(Intent.ACTION_BATTERY_CHANGED);
            filter.addAction(Intent.ACTION_SCREEN_OFF);
            filter.addAction(Intent.ACTION_SCREEN_ON);
            filter.addAction(Intent.ACTION_USER_SWITCHED);
            filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
            filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
            mContext.registerReceiver(this, filter, null, mHandler);
        }

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
                final int oldBatteryLevel = mBatteryLevel;
                mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
                final int oldBatteryStatus = mBatteryStatus;
                mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
                        BatteryManager.BATTERY_STATUS_UNKNOWN);
                final int oldPlugType = mPlugType;
                mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
                final int oldInvalidCharger = mInvalidCharger;
                mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);

                final boolean plugged = mPlugType != 0;
                final boolean oldPlugged = oldPlugType != 0;

                int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
                int bucket = findBatteryLevelBucket(mBatteryLevel);

                mWarnings.update(mBatteryLevel, bucket, mScreenOffTime);
                if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
                    mWarnings.showInvalidChargerWarning();
                    return;
                } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
                    mWarnings.dismissInvalidChargerWarning();
                } else if (mWarnings.isInvalidChargerWarningShowing()) {
                    return;
                }

                boolean isPowerSaver = mPowerManager.isPowerSaveMode();
                if (!plugged
                        && !isPowerSaver
                        && (bucket < oldBucket || oldPlugged)
                        && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
                        && bucket < 0) {
                    // only play SFX when the dialog comes up or the bucket changes
                    final boolean playSound = bucket != oldBucket || oldPlugged;
                    mWarnings.showLowBatteryWarning(playSound);

可见,现在弹出低电量通知对话框是接受广播Intent.ACTION_BATTERY_CHANGED来实现的。

上一篇:聊聊SQLite - 基础篇 下一篇:Java中CyclicBarrier的用法和示例