zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

android 4.4 batteryservice 电池电量显示分析

Android 分析 显示 4.4
2023-09-14 09:13:52 时间

转载地址:http://blog.csdn.net/daweibalang717/article/details/40615453

最近工作接触到这么的东西,这是我对整个电池管理方面Java 层的分析。如果想了解底层的话,请看我的博客:

android 4.4 电池电量管理底层分析(C\C++层) (http://blog.csdn.net/daweibalang717/article/details/41446993)
先贴一张类与类之间的关系图:

android开机过程中会加载系统BatteryService ,说一下电池电量相关的,本文主要讲述关于JAVA 层代码。文件路径:\frameworks\base\services\java\com\android\server\BatteryService.java   下面贴出源码。我把注释加上。个人理解,仅参考。

[java] view plain copy
/*

  • Copyright © 2006 The Android Open Source Project
  • Licensed under the Apache License, Version 2.0 (the “License”);
  • you may not use this file except in compliance with the License.
  • You may obtain a copy of the License at
  •  http://www.apache.org/licenses/LICENSE-2.0 
    
  • Unless required by applicable law or agreed to in writing, software
  • distributed under the License is distributed on an “AS IS” BASIS,
  • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  • See the License for the specific language governing permissions and
  • limitations under the License.
    */

package com.android.server;

import android.os.BatteryStats;
import com.android.internal.app.IBatteryStats;
import com.android.server.am.BatteryStatsService;

import android.app.ActivityManagerNative;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.BatteryManager;
import android.os.BatteryProperties;
import android.os.Binder;
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBatteryPropertiesListener;
import android.os.IBatteryPropertiesRegistrar;
import android.os.IBinder;
import android.os.DropBoxManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UEventObserver;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.EventLog;
import android.util.Slog;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;

/**

  • BatteryService monitors the charging status, and charge level of the device

  • battery. When these values change this service broadcasts the new values
  • to all {@link android.content.BroadcastReceiver IntentReceivers} that are
  • watching the {@link android.content.Intent#ACTION_BATTERY_CHANGED
  • BATTERY_CHANGED} action.
  • The new values are stored in the Intent data and can be retrieved by

  • calling {@link android.content.Intent#getExtra Intent.getExtra} with the
  • following keys:
  • "scale" - int, the maximum value for the charge level

  • "level" - int, charge level, from 0 through "scale" inclusive

  • "status" - String, the current charging status.

  • "health" - String, the current battery health.

  • "present" - boolean, true if the battery is present

  • "icon-small" - int, suggested small icon to use for this state

  • "plugged" - int, 0 if the device is not plugged in; 1 if plugged

  • into an AC power adapter; 2 if plugged in via USB.
  • "voltage" - int, current battery voltage in millivolts

  • "temperature" - int, current battery temperature in tenths of

  • a degree Centigrade
  • "technology" - String, the type of battery installed, e.g. "Li-ion"

  • The battery service may be called by the power manager while holding its locks so
  • we take care to post all outcalls into the activity manager to a handler.
  • FIXME: Ideally the power manager would perform all of its calls into the battery
  • service asynchronously itself.

*/
public final class BatteryService extends Binder {
private static final String TAG = BatteryService.class.getSimpleName();

private static final boolean DEBUG = false;  

private static final int BATTERY_SCALE = 100;    // battery capacity is a percentage  

// Used locally for determining when to make a last ditch effort to log  
// discharge stats before the device dies.  
private int mCriticalBatteryLevel;  

private static final int DUMP_MAX_LENGTH = 24 * 1024;  
private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "--unplugged" };  

private static final String DUMPSYS_DATA_PATH = "/data/system/";  

// This should probably be exposed in the API, though it's not critical  
private static final int BATTERY_PLUGGED_NONE = 0;  

private final Context mContext;  
private final IBatteryStats mBatteryStats;  
private final Handler mHandler;  

private final Object mLock = new Object();  

private BatteryProperties mBatteryProps;  
private boolean mBatteryLevelCritical;  
private int mLastBatteryStatus;  
private int mLastBatteryHealth;  
private boolean mLastBatteryPresent;  
private int mLastBatteryLevel;  
private int mLastBatteryVoltage;  
private int mLastBatteryTemperature;  
private boolean mLastBatteryLevelCritical;  

private int mInvalidCharger;  
private int mLastInvalidCharger;  

private int mLowBatteryWarningLevel;  
private int mLowBatteryCloseWarningLevel;  
private int mShutdownBatteryTemperature;  

private int mPlugType;  
private int mLastPlugType = -1; // Extra state so we can detect first run  

private long mDischargeStartTime;  
private int mDischargeStartLevel;  

private boolean mUpdatesStopped;  

private Led mLed;  

private boolean mSentLowBatteryBroadcast = false;  

private BatteryListener mBatteryPropertiesListener;  
private IBatteryPropertiesRegistrar mBatteryPropertiesRegistrar;  

//构造函数
public BatteryService(Context context, LightsService lights) {
mContext = context;
mHandler = new Handler(true /async/);
mLed = new Led(context, lights);//这个应该是指示灯,没实验
mBatteryStats = BatteryStatsService.getService();

//低电量临界值,这个数我看的源码版本值是4(在这个类里只是用来写日志)
mCriticalBatteryLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_criticalBatteryWarningLevel);

//低电量告警值,值15,下面会根据这个变量发送低电量的广播Intent.ACTION_BATTERY_LOW(这个跟系统低电量提醒没关系,只是发出去了)
mLowBatteryWarningLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryWarningLevel);

//电量告警取消值,值20 , 就是手机电量大于等于20的话发送Intent.ACTION_BATTERY_OKAY
mLowBatteryCloseWarningLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);

//值是680 ,温度过高,超过这个值就发送广播,跳转到将要关机提醒。
mShutdownBatteryTemperature = mContext.getResources().getInteger(
com.android.internal.R.integer.config_shutdownBatteryTemperature);

    // watch for invalid charger messages if the invalid_charger switch exists  
    if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {  
        mInvalidChargerObserver.startObserving(  
                "DEVPATH=/devices/virtual/switch/invalid_charger");  
    }  

//电池监听,这个应该是注册到底层去了。当底层电量改变会调用此监听。然后执行update(BatteryProperties props);
mBatteryPropertiesListener = new BatteryListener();

    IBinder b = ServiceManager.getService("batterypropreg");  
    mBatteryPropertiesRegistrar = IBatteryPropertiesRegistrar.Stub.asInterface(b);  

    try {  

//这里注册
mBatteryPropertiesRegistrar.registerListener(mBatteryPropertiesListener);
} catch (RemoteException e) {
// Should never happen.
}
}
//开机后先去看看是否没电了或者温度太高了。如果是,就关机提示(关机提示我等会介绍)。
void systemReady() {
// check our power situation now that it is safe to display the shutdown dialog.
synchronized (mLock) {
shutdownIfNoPowerLocked();
shutdownIfOverTempLocked();
}
}
//返回是否在充电,这个函数在PowerManagerService.java 中调用
/**
* Returns true if the device is plugged into any of the specified plug types.
*/
public boolean isPowered(int plugTypeSet) {
synchronized (mLock) {
return isPoweredLocked(plugTypeSet);
}
}
//就是这里,通过充电器类型判断是否充电
private boolean isPoweredLocked(int plugTypeSet) {
//我这英语小白猜着翻译下:就是开机后,电池状态不明了,那我们就认为就在充电,以便设备正常工作。
// assume we are powered if battery state is unknown so
// the “stay on while plugged in” option will work.
if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
return true;
}
//充电器
if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0 && mBatteryProps.chargerAcOnline) {
return true;
}
//USB,插电脑上充电
if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0 && mBatteryProps.chargerUsbOnline) {
return true;
}
//电源是无线的。 (我没见过…)
if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 && mBatteryProps.chargerWirelessOnline) {
return true;
}
return false;
}

/** 
 * Returns the current plug type. 
 */  

//充电器类型
public int getPlugType() {
synchronized (mLock) {
return mPlugType;
}
}

/** 
 * Returns battery level as a percentage. 
 */  

//电池属性:电量等级(0-100)
public int getBatteryLevel() {
synchronized (mLock) {
return mBatteryProps.batteryLevel;
}
}

/** 
 * Returns true if battery level is below the first warning threshold. 
 */  

//低电量
public boolean isBatteryLow() {
synchronized (mLock) {
return mBatteryProps.batteryPresent && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel;
}
}

/** 
 * Returns a non-zero value if an  unsupported charger is attached. 
 */  

//不支持的充电器类型
public int getInvalidCharger() {
synchronized (mLock) {
return mInvalidCharger;
}
}

//这里就是没电了,要关机的提示。
private void shutdownIfNoPowerLocked() {
// shut down gracefully if our battery is critically low and we are not powered.
// wait until the system has booted before attempting to display the shutdown dialog.
if (mBatteryProps.batteryLevel == 0 && (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING)) {
mHandler.post(new Runnable() {
@Override
public void run() {
if (ActivityManagerNative.isSystemReady()) {
Intent intent = new Intent(“android.intent.action.ACTION_REQUEST_SHUTDOWN_LOWBATTERY”);//ACTION_REQUEST_SHUTDOWN
intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
intent.putExtra(“cant_be_cancel_by_button”, true);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
}
}
});
}
}

//温度过高,关机提示(个人感觉这里有问题,温度过高为啥子跳转到没电关机提示界面)
private void shutdownIfOverTempLocked() {
// shut down gracefully if temperature is too high (> 68.0C by default)
// wait until the system has booted before attempting to display the
// shutdown dialog.
if (mBatteryProps.batteryTemperature > mShutdownBatteryTemperature) {
mHandler.post(new Runnable() {
@Override
public void run() {
if (ActivityManagerNative.isSystemReady()) {
Intent intent = new Intent(“android.intent.action.ACTION_REQUEST_SHUTDOWN_LOWBATTERY”);//ACTION_REQUEST_SHUTDOWN
intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
}
}
});
}
}
//这个方法就是被JNI回调的。用来更新上层状态的方法。
private void update(BatteryProperties props) {
synchronized (mLock) {
if (!mUpdatesStopped) {
mBatteryProps = props;
// Process the new values.
processValuesLocked();
}
}
}
//嗯。这个就是最主要的方法了。
private void processValuesLocked() {
boolean logOutlier = false;
long dischargeDuration = 0;

    mBatteryLevelCritical = (mBatteryProps.batteryLevel <= mCriticalBatteryLevel);  

//充电器类型
if (mBatteryProps.chargerAcOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
} else if (mBatteryProps.chargerUsbOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
} else if (mBatteryProps.chargerWirelessOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
} else {
mPlugType = BATTERY_PLUGGED_NONE;
}

    if (DEBUG) {//日志,略过  
        Slog.d(TAG, "Processing new values: "  
                + "chargerAcOnline=" + mBatteryProps.chargerAcOnline  
                + ", chargerUsbOnline=" + mBatteryProps.chargerUsbOnline  
                + ", chargerWirelessOnline=" + mBatteryProps.chargerWirelessOnline  
                + ", batteryStatus=" + mBatteryProps.batteryStatus  
                + ", batteryHealth=" + mBatteryProps.batteryHealth  
                + ", batteryPresent=" + mBatteryProps.batteryPresent  
                + ", batteryLevel=" + mBatteryProps.batteryLevel  
                + ", batteryTechnology=" + mBatteryProps.batteryTechnology  
                + ", batteryVoltage=" + mBatteryProps.batteryVoltage  
                + ", batteryCurrentNow=" + mBatteryProps.batteryCurrentNow  
                + ", batteryChargeCounter=" + mBatteryProps.batteryChargeCounter  
                + ", batteryTemperature=" + mBatteryProps.batteryTemperature  
                + ", mBatteryLevelCritical=" + mBatteryLevelCritical  
                + ", mPlugType=" + mPlugType);  
    }  

    // Let the battery stats keep track of the current level.  
    try {  

//把电池属性放到状态里面
mBatteryStats.setBatteryState(mBatteryProps.batteryStatus, mBatteryProps.batteryHealth,
mPlugType, mBatteryProps.batteryLevel, mBatteryProps.batteryTemperature,
mBatteryProps.batteryVoltage);
} catch (RemoteException e) {
// Should never happen.
}
//没电了
shutdownIfNoPowerLocked();
//温度过高了
shutdownIfOverTempLocked();

    if (mBatteryProps.batteryStatus != mLastBatteryStatus ||  
            mBatteryProps.batteryHealth != mLastBatteryHealth ||  
            mBatteryProps.batteryPresent != mLastBatteryPresent ||  
            mBatteryProps.batteryLevel != mLastBatteryLevel ||  
            mPlugType != mLastPlugType ||  
            mBatteryProps.batteryVoltage != mLastBatteryVoltage ||  
            mBatteryProps.batteryTemperature != mLastBatteryTemperature ||  
            mInvalidCharger != mLastInvalidCharger) {  

        if (mPlugType != mLastPlugType) {//当前充电器类型与上次的不一样  

//并且上次充电器类型是no one ,那就可以知道,现在是插上充电器了。
if (mLastPlugType == BATTERY_PLUGGED_NONE) {
// discharging -> charging

                // There's no value in this data unless we've discharged at least once and the  
                // battery level has changed; so don't log until it does.  
                if (mDischargeStartTime != 0 && mDischargeStartLevel != mBatteryProps.batteryLevel) {  
                    dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;  
                    logOutlier = true;  
                    EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,  
                            mDischargeStartLevel, mBatteryProps.batteryLevel);  
                    // make sure we see a discharge event before logging again  
                    mDischargeStartTime = 0;  
                }  

//并且本次充电器类型是no one ,那就可以知道,现在是拔掉充电器了。
} else if (mPlugType == BATTERY_PLUGGED_NONE) {
// charging -> discharging or we just powered up
mDischargeStartTime = SystemClock.elapsedRealtime();
mDischargeStartLevel = mBatteryProps.batteryLevel;
}
}
if (mBatteryProps.batteryStatus != mLastBatteryStatus ||//写日志,略过
mBatteryProps.batteryHealth != mLastBatteryHealth ||
mBatteryProps.batteryPresent != mLastBatteryPresent ||
mPlugType != mLastPlugType) {
EventLog.writeEvent(EventLogTags.BATTERY_STATUS,
mBatteryProps.batteryStatus, mBatteryProps.batteryHealth, mBatteryProps.batteryPresent ? 1 : 0,
mPlugType, mBatteryProps.batteryTechnology);
}
if (mBatteryProps.batteryLevel != mLastBatteryLevel) {
// Don’t do this just from voltage or temperature changes, that is
// too noisy.
EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
mBatteryProps.batteryLevel, mBatteryProps.batteryVoltage, mBatteryProps.batteryTemperature);
}
if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
mPlugType == BATTERY_PLUGGED_NONE) {
// We want to make sure we log discharge cycle outliers
// if the battery is about to die.
dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
logOutlier = true;
}
//本次调用,当前的充电状态
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). 
         */  

//用于发送低电量广播的判断
final boolean sendBatteryLow = !plugged//(按sendBatteryLow = true 来说) 当前没有充电
&& mBatteryProps.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN//充电状态不是UNKNOWN
&& mBatteryProps.batteryLevel <= mLowBatteryWarningLevel//当前电量小于告警值 15
&& (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);//上次状态是充电或者上次电量等级大于告警值 15

        sendIntentLocked();//发送电池电量改变的广播Intent.ACTION_BATTERY_CHANGED  

        // Separate broadcast is sent for power connected / not connected  
        // since the standard intent will not wake any applications and some  
        // applications may want to have smart behavior based on this.  
        if (mPlugType != 0 && mLastPlugType == 0) {//插上充电器了  
            mHandler.post(new Runnable() {  
                @Override  
                public void run() {  
                    Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED);  
                    statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);  
                    mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);  
                }  
            });  
        }  
        else if (mPlugType == 0 && mLastPlugType != 0) {//断开充电器了  
            mHandler.post(new Runnable() {  
                @Override  
                public void run() {  
                    Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED);  
                    statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);  
                    mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);  
                }  
            });  
        }  

//发送低电量提醒(这个跟系统低电量提醒没关系,只是发出去了)
if (sendBatteryLow) {
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);
}
});
} else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= mLowBatteryCloseWarningLevel) {//电量超过20了。电池状态OK了
mSentLowBatteryBroadcast = false;
mHandler.post(new Runnable() {
@Override
public void run() {
Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY);
statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
}
});
}

        // Update the battery LED  
        mLed.updateLightsLocked();  

        // This needs to be done after sendIntent() so that we get the lastest battery stats.  
        if (logOutlier && dischargeDuration != 0) {  
            logOutlierLocked(dischargeDuration);  
        }  

        mLastBatteryStatus = mBatteryProps.batteryStatus;  
        mLastBatteryHealth = mBatteryProps.batteryHealth;  
        mLastBatteryPresent = mBatteryProps.batteryPresent;  
        mLastBatteryLevel = mBatteryProps.batteryLevel;  
        mLastPlugType = mPlugType;  
        mLastBatteryVoltage = mBatteryProps.batteryVoltage;  
        mLastBatteryTemperature = mBatteryProps.batteryTemperature;  
        mLastBatteryLevelCritical = mBatteryLevelCritical;  
        mLastInvalidCharger = mInvalidCharger;  
    }  
}  

//电池电量改变,把属性发出去(系统低电量提醒接收的是这个广播)
private void sendIntentLocked() {
// Pack up the values and broadcast them to everyone
final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_REPLACE_PENDING);

    int icon = getIconLocked(mBatteryProps.batteryLevel);  

    intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryProps.batteryStatus);  
    intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryProps.batteryHealth);  
    intent.putExtra(BatteryManager.EXTRA_PRESENT, mBatteryProps.batteryPresent);  
    intent.putExtra(BatteryManager.EXTRA_LEVEL, mBatteryProps.batteryLevel);  
    intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);  
    intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);  
    intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);  
    intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryProps.batteryVoltage);  
    intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryProps.batteryTemperature);  
    intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryProps.batteryTechnology);  
    intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);  

    if (DEBUG) {  
        Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED.  level:" + mBatteryProps.batteryLevel +  
                ", scale:" + BATTERY_SCALE + ", status:" + mBatteryProps.batteryStatus +  
                ", health:" + mBatteryProps.batteryHealth +  ", present:" + mBatteryProps.batteryPresent +  
                ", voltage: " + mBatteryProps.batteryVoltage +  
                ", temperature: " + mBatteryProps.batteryTemperature +  
                ", technology: " + mBatteryProps.batteryTechnology +  
                ", AC powered:" + mBatteryProps.chargerAcOnline + ", USB powered:" + mBatteryProps.chargerUsbOnline +  
                ", Wireless powered:" + mBatteryProps.chargerWirelessOnline +  
                ", icon:" + icon  + ", invalid charger:" + mInvalidCharger);  
    }  

    mHandler.post(new Runnable() {  
        @Override  
        public void run() {  
            ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);  
        }  
    });  
}  

private void logBatteryStatsLocked() {  
    IBinder batteryInfoService = ServiceManager.getService(BatteryStats.SERVICE_NAME);  
    if (batteryInfoService == null) return;  

    DropBoxManager db = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);  
    if (db == null || !db.isTagEnabled("BATTERY_DISCHARGE_INFO")) return;  

    File dumpFile = null;  
    FileOutputStream dumpStream = null;  
    try {  
        // dump the service to a file  
        dumpFile = new File(DUMPSYS_DATA_PATH + BatteryStats.SERVICE_NAME + ".dump");  
        dumpStream = new FileOutputStream(dumpFile);  
        batteryInfoService.dump(dumpStream.getFD(), DUMPSYS_ARGS);  
        FileUtils.sync(dumpStream);  

        // add dump file to drop box  
        db.addFile("BATTERY_DISCHARGE_INFO", dumpFile, DropBoxManager.IS_TEXT);  
    } catch (RemoteException e) {  
        Slog.e(TAG, "failed to dump battery service", e);  
    } catch (IOException e) {  
        Slog.e(TAG, "failed to write dumpsys file", e);  
    } finally {  
        // make sure we clean up  
        if (dumpStream != null) {  
            try {  
                dumpStream.close();  
            } catch (IOException e) {  
                Slog.e(TAG, "failed to close dumpsys output stream");  
            }  
        }  
        if (dumpFile != null && !dumpFile.delete()) {  
            Slog.e(TAG, "failed to delete temporary dumpsys file: "  
                    + dumpFile.getAbsolutePath());  
        }  
    }  
}  

private void logOutlierLocked(long duration) {  
    ContentResolver cr = mContext.getContentResolver();  
    String dischargeThresholdString = Settings.Global.getString(cr,  
            Settings.Global.BATTERY_DISCHARGE_THRESHOLD);  
    String durationThresholdString = Settings.Global.getString(cr,  
            Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD);  

    if (dischargeThresholdString != null && durationThresholdString != null) {  
        try {  
            long durationThreshold = Long.parseLong(durationThresholdString);  
            int dischargeThreshold = Integer.parseInt(dischargeThresholdString);  
            if (duration <= durationThreshold &&  
                    mDischargeStartLevel - mBatteryProps.batteryLevel >= dischargeThreshold) {  
                // If the discharge cycle is bad enough we want to know about it.  
                logBatteryStatsLocked();  
            }  
            if (DEBUG) Slog.v(TAG, "duration threshold: " + durationThreshold +  
                    " discharge threshold: " + dischargeThreshold);  
            if (DEBUG) Slog.v(TAG, "duration: " + duration + " discharge: " +  
                    (mDischargeStartLevel - mBatteryProps.batteryLevel));  
        } catch (NumberFormatException e) {  
            Slog.e(TAG, "Invalid DischargeThresholds GService string: " +  
                    durationThresholdString + " or " + dischargeThresholdString);  
            return;  
        }  
    }  
}  

private int getIconLocked(int level) {  
    if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {  
        return com.android.internal.R.drawable.stat_sys_battery_charge;  
    } else if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {  
        return com.android.internal.R.drawable.stat_sys_battery;  
    } else if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING  
            || mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_FULL) {  
        if (isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)  
                && mBatteryProps.batteryLevel >= 100) {  
            return com.android.internal.R.drawable.stat_sys_battery_charge;  
        } else {  
            return com.android.internal.R.drawable.stat_sys_battery;  
        }  
    } else {  
        return com.android.internal.R.drawable.stat_sys_battery_unknown;  
    }  
}  

@Override  
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {  
    if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)  
            != PackageManager.PERMISSION_GRANTED) {  

        pw.println("Permission Denial: can't dump Battery service from from pid="  
                + Binder.getCallingPid()  
                + ", uid=" + Binder.getCallingUid());  
        return;  
    }  

    synchronized (mLock) {  
        if (args == null || args.length == 0 || "-a".equals(args[0])) {  
            pw.println("Current Battery Service state:");  
            if (mUpdatesStopped) {  
                pw.println("  (UPDATES STOPPED -- use 'reset' to restart)");  
            }  
            pw.println("  AC powered: " + mBatteryProps.chargerAcOnline);  
            pw.println("  USB powered: " + mBatteryProps.chargerUsbOnline);  
            pw.println("  Wireless powered: " + mBatteryProps.chargerWirelessOnline);  
            pw.println("  status: " + mBatteryProps.batteryStatus);  
            pw.println("  health: " + mBatteryProps.batteryHealth);  
            pw.println("  present: " + mBatteryProps.batteryPresent);  
            pw.println("  level: " + mBatteryProps.batteryLevel);  
            pw.println("  scale: " + BATTERY_SCALE);  
            pw.println("  voltage: " + mBatteryProps.batteryVoltage);  

            if (mBatteryProps.batteryCurrentNow != Integer.MIN_VALUE) {  
                pw.println("  current now: " + mBatteryProps.batteryCurrentNow);  
            }  

            if (mBatteryProps.batteryChargeCounter != Integer.MIN_VALUE) {  
                pw.println("  charge counter: " + mBatteryProps.batteryChargeCounter);  
            }  

            pw.println("  temperature: " + mBatteryProps.batteryTemperature);  
            pw.println("  technology: " + mBatteryProps.batteryTechnology);  
        } else if (args.length == 3 && "set".equals(args[0])) {  
            String key = args[1];  
            String value = args[2];  
            try {  
                boolean update = true;  
                if ("ac".equals(key)) {  
                    mBatteryProps.chargerAcOnline = Integer.parseInt(value) != 0;  
                } else if ("usb".equals(key)) {  
                    mBatteryProps.chargerUsbOnline = Integer.parseInt(value) != 0;  
                } else if ("wireless".equals(key)) {  
                    mBatteryProps.chargerWirelessOnline = Integer.parseInt(value) != 0;  
                } else if ("status".equals(key)) {  
                    mBatteryProps.batteryStatus = Integer.parseInt(value);  
                } else if ("level".equals(key)) {  
                    mBatteryProps.batteryLevel = Integer.parseInt(value);  
                } else if ("invalid".equals(key)) {  
                    mInvalidCharger = Integer.parseInt(value);  
                } else {  
                    pw.println("Unknown set option: " + key);  
                    update = false;  
                }  
                if (update) {  
                    long ident = Binder.clearCallingIdentity();  
                    try {  
                        mUpdatesStopped = true;  
                        processValuesLocked();  
                    } finally {  
                        Binder.restoreCallingIdentity(ident);  
                    }  
                }  
            } catch (NumberFormatException ex) {  
                pw.println("Bad value: " + value);  
            }  
        } else if (args.length == 1 && "reset".equals(args[0])) {  
            long ident = Binder.clearCallingIdentity();  
            try {  
                mUpdatesStopped = false;  
            } finally {  
                Binder.restoreCallingIdentity(ident);  
            }  
        } else {  
            pw.println("Dump current battery state, or:");  
            pw.println("  set ac|usb|wireless|status|level|invalid <value>");  
            pw.println("  reset");  
        }  
    }  
}  

private final UEventObserver mInvalidChargerObserver = new UEventObserver() {  
    @Override  
    public void onUEvent(UEventObserver.UEvent event) {  
        final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;  
        synchronized (mLock) {  
            if (mInvalidCharger != invalidCharger) {  
                mInvalidCharger = invalidCharger;  
            }  
        }  
    }  
};  

private final class Led {  
    private final LightsService.Light mBatteryLight;  

    private final int mBatteryLowARGB;  
    private final int mBatteryMediumARGB;  
    private final int mBatteryFullARGB;  
    private final int mBatteryLedOn;  
    private final int mBatteryLedOff;  

    public Led(Context context, LightsService lights) {  
        mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);  

        mBatteryLowARGB = context.getResources().getInteger(  
                com.android.internal.R.integer.config_notificationsBatteryLowARGB);  
        mBatteryMediumARGB = context.getResources().getInteger(  
                com.android.internal.R.integer.config_notificationsBatteryMediumARGB);  
        mBatteryFullARGB = context.getResources().getInteger(  
                com.android.internal.R.integer.config_notificationsBatteryFullARGB);  
        mBatteryLedOn = context.getResources().getInteger(  
                com.android.internal.R.integer.config_notificationsBatteryLedOn);  
        mBatteryLedOff = context.getResources().getInteger(  
                com.android.internal.R.integer.config_notificationsBatteryLedOff);  
    }  

    /** 
     * Synchronize on BatteryService. 
     */  
    public void updateLightsLocked() {  
        final int level = mBatteryProps.batteryLevel;  
        final int status = mBatteryProps.batteryStatus;  
        if (level < mLowBatteryWarningLevel) {  
            if (status == BatteryManager.BATTERY_STATUS_CHARGING) {  
                // Solid red when battery is charging  
                mBatteryLight.setColor(mBatteryLowARGB);  
            } else {  
                // Flash red when battery is low and not charging  
                mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED,  
                        mBatteryLedOn, mBatteryLedOff);  
            }  
        } else if (status == BatteryManager.BATTERY_STATUS_CHARGING  
                || status == BatteryManager.BATTERY_STATUS_FULL) {  
            if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {  
                // Solid green when full or charging and nearly full  
                mBatteryLight.setColor(mBatteryFullARGB);  
            } else {  
                // Solid orange when charging and halfway full  
                mBatteryLight.setColor(mBatteryMediumARGB);  
            }  
        } else {  
            // No lights if not charging and not low  
            mBatteryLight.turnOff();  
        }  
    }  
}  

private final class BatteryListener extends IBatteryPropertiesListener.Stub {  
    public void batteryPropertiesChanged(BatteryProperties props) {  
        BatteryService.this.update(props);  
   }  
}  

}
/*

  • Copyright © 2006 The Android Open Source Project
  • Licensed under the Apache License, Version 2.0 (the “License”);
  • you may not use this file except in compliance with the License.
  • You may obtain a copy of the License at
  •  http://www.apache.org/licenses/LICENSE-2.0
    
  • Unless required by applicable law or agreed to in writing, software
  • distributed under the License is distributed on an “AS IS” BASIS,
  • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  • See the License for the specific language governing permissions and
  • limitations under the License.
    */

package com.android.server;

import android.os.BatteryStats;
import com.android.internal.app.IBatteryStats;
import com.android.server.am.BatteryStatsService;

import android.app.ActivityManagerNative;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.BatteryManager;
import android.os.BatteryProperties;
import android.os.Binder;
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBatteryPropertiesListener;
import android.os.IBatteryPropertiesRegistrar;
import android.os.IBinder;
import android.os.DropBoxManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UEventObserver;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.EventLog;
import android.util.Slog;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;

/**

  • BatteryService monitors the charging status, and charge level of the device

  • battery. When these values change this service broadcasts the new values
  • to all {@link android.content.BroadcastReceiver IntentReceivers} that are
  • watching the {@link android.content.Intent#ACTION_BATTERY_CHANGED
  • BATTERY_CHANGED} action.
  • The new values are stored in the Intent data and can be retrieved by

  • calling {@link android.content.Intent#getExtra Intent.getExtra} with the
  • following keys:
  • "scale" - int, the maximum value for the charge level

  • "level" - int, charge level, from 0 through "scale" inclusive

  • "status" - String, the current charging status.

  • "health" - String, the current battery health.

  • "present" - boolean, true if the battery is present

  • "icon-small" - int, suggested small icon to use for this state

  • "plugged" - int, 0 if the device is not plugged in; 1 if plugged

  • into an AC power adapter; 2 if plugged in via USB.
  • "voltage" - int, current battery voltage in millivolts

  • "temperature" - int, current battery temperature in tenths of

  • a degree Centigrade
  • "technology" - String, the type of battery installed, e.g. "Li-ion"

  • The battery service may be called by the power manager while holding its locks so
  • we take care to post all outcalls into the activity manager to a handler.
  • FIXME: Ideally the power manager would perform all of its calls into the battery
  • service asynchronously itself.

*/
public final class BatteryService extends Binder {
private static final String TAG = BatteryService.class.getSimpleName();

private static final boolean DEBUG = false;

private static final int BATTERY_SCALE = 100;    // battery capacity is a percentage

// Used locally for determining when to make a last ditch effort to log
// discharge stats before the device dies.
private int mCriticalBatteryLevel;

private static final int DUMP_MAX_LENGTH = 24 * 1024;
private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "--unplugged" };

private static final String DUMPSYS_DATA_PATH = "/data/system/";

// This should probably be exposed in the API, though it's not critical
private static final int BATTERY_PLUGGED_NONE = 0;

private final Context mContext;
private final IBatteryStats mBatteryStats;
private final Handler mHandler;

private final Object mLock = new Object();

private BatteryProperties mBatteryProps;
private boolean mBatteryLevelCritical;
private int mLastBatteryStatus;
private int mLastBatteryHealth;
private boolean mLastBatteryPresent;
private int mLastBatteryLevel;
private int mLastBatteryVoltage;
private int mLastBatteryTemperature;
private boolean mLastBatteryLevelCritical;

private int mInvalidCharger;
private int mLastInvalidCharger;

private int mLowBatteryWarningLevel;
private int mLowBatteryCloseWarningLevel;
private int mShutdownBatteryTemperature;

private int mPlugType;
private int mLastPlugType = -1; // Extra state so we can detect first run

private long mDischargeStartTime;
private int mDischargeStartLevel;

private boolean mUpdatesStopped;

private Led mLed;

private boolean mSentLowBatteryBroadcast = false;

private BatteryListener mBatteryPropertiesListener;
private IBatteryPropertiesRegistrar mBatteryPropertiesRegistrar;

//构造函数
public BatteryService(Context context, LightsService lights) {
mContext = context;
mHandler = new Handler(true /async/);
mLed = new Led(context, lights);//这个应该是指示灯,没实验
mBatteryStats = BatteryStatsService.getService();

//低电量临界值,这个数我看的源码版本值是4(在这个类里只是用来写日志)
mCriticalBatteryLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_criticalBatteryWarningLevel);

//低电量告警值,值15,下面会根据这个变量发送低电量的广播Intent.ACTION_BATTERY_LOW(这个跟系统低电量提醒没关系,只是发出去了)
mLowBatteryWarningLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryWarningLevel);

//电量告警取消值,值20 , 就是手机电量大于等于20的话发送Intent.ACTION_BATTERY_OKAY
mLowBatteryCloseWarningLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);

//值是680 ,温度过高,超过这个值就发送广播,跳转到将要关机提醒。
mShutdownBatteryTemperature = mContext.getResources().getInteger(
com.android.internal.R.integer.config_shutdownBatteryTemperature);

    // watch for invalid charger messages if the invalid_charger switch exists
    if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {
        mInvalidChargerObserver.startObserving(
                "DEVPATH=/devices/virtual/switch/invalid_charger");
    }

//电池监听,这个应该是注册到底层去了。当底层电量改变会调用此监听。然后执行update(BatteryProperties props);
mBatteryPropertiesListener = new BatteryListener();

    IBinder b = ServiceManager.getService("batterypropreg");
    mBatteryPropertiesRegistrar = IBatteryPropertiesRegistrar.Stub.asInterface(b);

    try {

//这里注册
mBatteryPropertiesRegistrar.registerListener(mBatteryPropertiesListener);
} catch (RemoteException e) {
// Should never happen.
}
}
//开机后先去看看是否没电了或者温度太高了。如果是,就关机提示(关机提示我等会介绍)。
void systemReady() {
// check our power situation now that it is safe to display the shutdown dialog.
synchronized (mLock) {
shutdownIfNoPowerLocked();
shutdownIfOverTempLocked();
}
}
//返回是否在充电,这个函数在PowerManagerService.java 中调用
/**
* Returns true if the device is plugged into any of the specified plug types.
*/
public boolean isPowered(int plugTypeSet) {
synchronized (mLock) {
return isPoweredLocked(plugTypeSet);
}
}
//就是这里,通过充电器类型判断是否充电
private boolean isPoweredLocked(int plugTypeSet) {
//我这英语小白猜着翻译下:就是开机后,电池状态不明了,那我们就认为就在充电,以便设备正常工作。
// assume we are powered if battery state is unknown so
// the “stay on while plugged in” option will work.
if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
return true;
}
//充电器
if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0 && mBatteryProps.chargerAcOnline) {
return true;
}
//USB,插电脑上充电
if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0 && mBatteryProps.chargerUsbOnline) {
return true;
}
//电源是无线的。 (我没见过…)
if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 && mBatteryProps.chargerWirelessOnline) {
return true;
}
return false;
}

/**
 * Returns the current plug type.
 */

//充电器类型
public int getPlugType() {
synchronized (mLock) {
return mPlugType;
}
}

/**
 * Returns battery level as a percentage.
 */

//电池属性:电量等级(0-100)
public int getBatteryLevel() {
synchronized (mLock) {
return mBatteryProps.batteryLevel;
}
}

/**
 * Returns true if battery level is below the first warning threshold.
 */

//低电量
public boolean isBatteryLow() {
synchronized (mLock) {
return mBatteryProps.batteryPresent && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel;
}
}

/**
 * Returns a non-zero value if an  unsupported charger is attached.
 */

//不支持的充电器类型
public int getInvalidCharger() {
synchronized (mLock) {
return mInvalidCharger;
}
}

//这里就是没电了,要关机的提示。
private void shutdownIfNoPowerLocked() {
// shut down gracefully if our battery is critically low and we are not powered.
// wait until the system has booted before attempting to display the shutdown dialog.
if (mBatteryProps.batteryLevel == 0 && (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING)) {
mHandler.post(new Runnable() {
@Override
public void run() {
if (ActivityManagerNative.isSystemReady()) {
Intent intent = new Intent(“android.intent.action.ACTION_REQUEST_SHUTDOWN_LOWBATTERY”);//ACTION_REQUEST_SHUTDOWN
intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
intent.putExtra(“cant_be_cancel_by_button”, true);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
}
}
});
}
}

//温度过高,关机提示(个人感觉这里有问题,温度过高为啥子跳转到没电关机提示界面)
private void shutdownIfOverTempLocked() {
// shut down gracefully if temperature is too high (> 68.0C by default)
// wait until the system has booted before attempting to display the
// shutdown dialog.
if (mBatteryProps.batteryTemperature > mShutdownBatteryTemperature) {
mHandler.post(new Runnable() {
@Override
public void run() {
if (ActivityManagerNative.isSystemReady()) {
Intent intent = new Intent(“android.intent.action.ACTION_REQUEST_SHUTDOWN_LOWBATTERY”);//ACTION_REQUEST_SHUTDOWN
intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
}
}
});
}
}
//这个方法就是被JNI回调的。用来更新上层状态的方法。
private void update(BatteryProperties props) {
synchronized (mLock) {
if (!mUpdatesStopped) {
mBatteryProps = props;
// Process the new values.
processValuesLocked();
}
}
}
//嗯。这个就是最主要的方法了。
private void processValuesLocked() {
boolean logOutlier = false;
long dischargeDuration = 0;

    mBatteryLevelCritical = (mBatteryProps.batteryLevel <= mCriticalBatteryLevel);

//充电器类型
if (mBatteryProps.chargerAcOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
} else if (mBatteryProps.chargerUsbOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
} else if (mBatteryProps.chargerWirelessOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
} else {
mPlugType = BATTERY_PLUGGED_NONE;
}

    if (DEBUG) {//日志,略过
        Slog.d(TAG, "Processing new values: "
                + "chargerAcOnline=" + mBatteryProps.chargerAcOnline
                + ", chargerUsbOnline=" + mBatteryProps.chargerUsbOnline
                + ", chargerWirelessOnline=" + mBatteryProps.chargerWirelessOnline
                + ", batteryStatus=" + mBatteryProps.batteryStatus
                + ", batteryHealth=" + mBatteryProps.batteryHealth
                + ", batteryPresent=" + mBatteryProps.batteryPresent
                + ", batteryLevel=" + mBatteryProps.batteryLevel
                + ", batteryTechnology=" + mBatteryProps.batteryTechnology
                + ", batteryVoltage=" + mBatteryProps.batteryVoltage
                + ", batteryCurrentNow=" + mBatteryProps.batteryCurrentNow
                + ", batteryChargeCounter=" + mBatteryProps.batteryChargeCounter
                + ", batteryTemperature=" + mBatteryProps.batteryTemperature
                + ", mBatteryLevelCritical=" + mBatteryLevelCritical
                + ", mPlugType=" + mPlugType);
    }

    // Let the battery stats keep track of the current level.
    try {

//把电池属性放到状态里面
mBatteryStats.setBatteryState(mBatteryProps.batteryStatus, mBatteryProps.batteryHealth,
mPlugType, mBatteryProps.batteryLevel, mBatteryProps.batteryTemperature,
mBatteryProps.batteryVoltage);
} catch (RemoteException e) {
// Should never happen.
}
//没电了
shutdownIfNoPowerLocked();
//温度过高了
shutdownIfOverTempLocked();

    if (mBatteryProps.batteryStatus != mLastBatteryStatus ||
            mBatteryProps.batteryHealth != mLastBatteryHealth ||
            mBatteryProps.batteryPresent != mLastBatteryPresent ||
            mBatteryProps.batteryLevel != mLastBatteryLevel ||
            mPlugType != mLastPlugType ||
            mBatteryProps.batteryVoltage != mLastBatteryVoltage ||
            mBatteryProps.batteryTemperature != mLastBatteryTemperature ||
            mInvalidCharger != mLastInvalidCharger) {

        if (mPlugType != mLastPlugType) {//当前充电器类型与上次的不一样

//并且上次充电器类型是no one ,那就可以知道,现在是插上充电器了。
if (mLastPlugType == BATTERY_PLUGGED_NONE) {
// discharging -> charging

                // There's no value in this data unless we've discharged at least once and the
                // battery level has changed; so don't log until it does.
                if (mDischargeStartTime != 0 && mDischargeStartLevel != mBatteryProps.batteryLevel) {
                    dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
                    logOutlier = true;
                    EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,
                            mDischargeStartLevel, mBatteryProps.batteryLevel);
                    // make sure we see a discharge event before logging again
                    mDischargeStartTime = 0;
                }

//并且本次充电器类型是no one ,那就可以知道,现在是拔掉充电器了。
} else if (mPlugType == BATTERY_PLUGGED_NONE) {
// charging -> discharging or we just powered up
mDischargeStartTime = SystemClock.elapsedRealtime();
mDischargeStartLevel = mBatteryProps.batteryLevel;
}
}
if (mBatteryProps.batteryStatus != mLastBatteryStatus ||//写日志,略过
mBatteryProps.batteryHealth != mLastBatteryHealth ||
mBatteryProps.batteryPresent != mLastBatteryPresent ||
mPlugType != mLastPlugType) {
EventLog.writeEvent(EventLogTags.BATTERY_STATUS,
mBatteryProps.batteryStatus, mBatteryProps.batteryHealth, mBatteryProps.batteryPresent ? 1 : 0,
mPlugType, mBatteryProps.batteryTechnology);
}
if (mBatteryProps.batteryLevel != mLastBatteryLevel) {
// Don’t do this just from voltage or temperature changes, that is
// too noisy.
EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
mBatteryProps.batteryLevel, mBatteryProps.batteryVoltage, mBatteryProps.batteryTemperature);
}
if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
mPlugType == BATTERY_PLUGGED_NONE) {
// We want to make sure we log discharge cycle outliers
// if the battery is about to die.
dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
logOutlier = true;
}
//本次调用,当前的充电状态
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).
         */

//用于发送低电量广播的判断
final boolean sendBatteryLow = !plugged//(按sendBatteryLow = true 来说) 当前没有充电
&& mBatteryProps.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN//充电状态不是UNKNOWN
&& mBatteryProps.batteryLevel <= mLowBatteryWarningLevel//当前电量小于告警值 15
&& (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);//上次状态是充电或者上次电量等级大于告警值 15

        sendIntentLocked();//发送电池电量改变的广播Intent.ACTION_BATTERY_CHANGED

        // Separate broadcast is sent for power connected / not connected
        // since the standard intent will not wake any applications and some
        // applications may want to have smart behavior based on this.
        if (mPlugType != 0 && mLastPlugType == 0) {//插上充电器了
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED);
                    statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                    mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
                }
            });
        }
        else if (mPlugType == 0 && mLastPlugType != 0) {//断开充电器了
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED);
                    statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                    mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
                }
            });
        }

//发送低电量提醒(这个跟系统低电量提醒没关系,只是发出去了)
if (sendBatteryLow) {
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);
}
});
} else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= mLowBatteryCloseWarningLevel) {//电量超过20了。电池状态OK了
mSentLowBatteryBroadcast = false;
mHandler.post(new Runnable() {
@Override
public void run() {
Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY);
statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
}
});
}

        // Update the battery LED
        mLed.updateLightsLocked();

        // This needs to be done after sendIntent() so that we get the lastest battery stats.
        if (logOutlier && dischargeDuration != 0) {
            logOutlierLocked(dischargeDuration);
        }

        mLastBatteryStatus = mBatteryProps.batteryStatus;
        mLastBatteryHealth = mBatteryProps.batteryHealth;
        mLastBatteryPresent = mBatteryProps.batteryPresent;
        mLastBatteryLevel = mBatteryProps.batteryLevel;
        mLastPlugType = mPlugType;
        mLastBatteryVoltage = mBatteryProps.batteryVoltage;
        mLastBatteryTemperature = mBatteryProps.batteryTemperature;
        mLastBatteryLevelCritical = mBatteryLevelCritical;
        mLastInvalidCharger = mInvalidCharger;
    }
}

//电池电量改变,把属性发出去(系统低电量提醒接收的是这个广播)
private void sendIntentLocked() {
// Pack up the values and broadcast them to everyone
final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_REPLACE_PENDING);

    int icon = getIconLocked(mBatteryProps.batteryLevel);

    intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryProps.batteryStatus);
    intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryProps.batteryHealth);
    intent.putExtra(BatteryManager.EXTRA_PRESENT, mBatteryProps.batteryPresent);
    intent.putExtra(BatteryManager.EXTRA_LEVEL, mBatteryProps.batteryLevel);
    intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
    intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);
    intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);
    intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryProps.batteryVoltage);
    intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryProps.batteryTemperature);
    intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryProps.batteryTechnology);
    intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);

    if (DEBUG) {
        Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED.  level:" + mBatteryProps.batteryLevel +
                ", scale:" + BATTERY_SCALE + ", status:" + mBatteryProps.batteryStatus +
                ", health:" + mBatteryProps.batteryHealth +  ", present:" + mBatteryProps.batteryPresent +
                ", voltage: " + mBatteryProps.batteryVoltage +
                ", temperature: " + mBatteryProps.batteryTemperature +
                ", technology: " + mBatteryProps.batteryTechnology +
                ", AC powered:" + mBatteryProps.chargerAcOnline + ", USB powered:" + mBatteryProps.chargerUsbOnline +
                ", Wireless powered:" + mBatteryProps.chargerWirelessOnline +
                ", icon:" + icon  + ", invalid charger:" + mInvalidCharger);
    }

    mHandler.post(new Runnable() {
        @Override
        public void run() {
            ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
        }
    });
}

private void logBatteryStatsLocked() {
    IBinder batteryInfoService = ServiceManager.getService(BatteryStats.SERVICE_NAME);
    if (batteryInfoService == null) return;

    DropBoxManager db = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);
    if (db == null || !db.isTagEnabled("BATTERY_DISCHARGE_INFO")) return;

    File dumpFile = null;
    FileOutputStream dumpStream = null;
    try {
        // dump the service to a file
        dumpFile = new File(DUMPSYS_DATA_PATH + BatteryStats.SERVICE_NAME + ".dump");
        dumpStream = new FileOutputStream(dumpFile);
        batteryInfoService.dump(dumpStream.getFD(), DUMPSYS_ARGS);
        FileUtils.sync(dumpStream);

        // add dump file to drop box
        db.addFile("BATTERY_DISCHARGE_INFO", dumpFile, DropBoxManager.IS_TEXT);
    } catch (RemoteException e) {
        Slog.e(TAG, "failed to dump battery service", e);
    } catch (IOException e) {
        Slog.e(TAG, "failed to write dumpsys file", e);
    } finally {
        // make sure we clean up
        if (dumpStream != null) {
            try {
                dumpStream.close();
            } catch (IOException e) {
                Slog.e(TAG, "failed to close dumpsys output stream");
            }
        }
        if (dumpFile != null && !dumpFile.delete()) {
            Slog.e(TAG, "failed to delete temporary dumpsys file: "
                    + dumpFile.getAbsolutePath());
        }
    }
}

private void logOutlierLocked(long duration) {
    ContentResolver cr = mContext.getContentResolver();
    String dischargeThresholdString = Settings.Global.getString(cr,
            Settings.Global.BATTERY_DISCHARGE_THRESHOLD);
    String durationThresholdString = Settings.Global.getString(cr,
            Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD);

    if (dischargeThresholdString != null && durationThresholdString != null) {
        try {
            long durationThreshold = Long.parseLong(durationThresholdString);
            int dischargeThreshold = Integer.parseInt(dischargeThresholdString);
            if (duration <= durationThreshold &&
                    mDischargeStartLevel - mBatteryProps.batteryLevel >= dischargeThreshold) {
                // If the discharge cycle is bad enough we want to know about it.
                logBatteryStatsLocked();
            }
            if (DEBUG) Slog.v(TAG, "duration threshold: " + durationThreshold +
                    " discharge threshold: " + dischargeThreshold);
            if (DEBUG) Slog.v(TAG, "duration: " + duration + " discharge: " +
                    (mDischargeStartLevel - mBatteryProps.batteryLevel));
        } catch (NumberFormatException e) {
            Slog.e(TAG, "Invalid DischargeThresholds GService string: " +
                    durationThresholdString + " or " + dischargeThresholdString);
            return;
        }
    }
}

private int getIconLocked(int level) {
    if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {
        return com.android.internal.R.drawable.stat_sys_battery_charge;
    } else if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {
        return com.android.internal.R.drawable.stat_sys_battery;
    } else if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING
            || mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
        if (isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)
                && mBatteryProps.batteryLevel >= 100) {
            return com.android.internal.R.drawable.stat_sys_battery_charge;
        } else {
            return com.android.internal.R.drawable.stat_sys_battery;
        }
    } else {
        return com.android.internal.R.drawable.stat_sys_battery_unknown;
    }
}

@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
            != PackageManager.PERMISSION_GRANTED) {

        pw.println("Permission Denial: can't dump Battery service from from pid="
                + Binder.getCallingPid()
                + ", uid=" + Binder.getCallingUid());
        return;
    }

    synchronized (mLock) {
        if (args == null || args.length == 0 || "-a".equals(args[0])) {
            pw.println("Current Battery Service state:");
            if (mUpdatesStopped) {
                pw.println("  (UPDATES STOPPED -- use 'reset' to restart)");
            }
            pw.println("  AC powered: " + mBatteryProps.chargerAcOnline);
            pw.println("  USB powered: " + mBatteryProps.chargerUsbOnline);
            pw.println("  Wireless powered: " + mBatteryProps.chargerWirelessOnline);
            pw.println("  status: " + mBatteryProps.batteryStatus);
            pw.println("  health: " + mBatteryProps.batteryHealth);
            pw.println("  present: " + mBatteryProps.batteryPresent);
            pw.println("  level: " + mBatteryProps.batteryLevel);
            pw.println("  scale: " + BATTERY_SCALE);
            pw.println("  voltage: " + mBatteryProps.batteryVoltage);

            if (mBatteryProps.batteryCurrentNow != Integer.MIN_VALUE) {
                pw.println("  current now: " + mBatteryProps.batteryCurrentNow);
            }

            if (mBatteryProps.batteryChargeCounter != Integer.MIN_VALUE) {
                pw.println("  charge counter: " + mBatteryProps.batteryChargeCounter);
            }

            pw.println("  temperature: " + mBatteryProps.batteryTemperature);
            pw.println("  technology: " + mBatteryProps.batteryTechnology);
        } else if (args.length == 3 && "set".equals(args[0])) {
            String key = args[1];
            String value = args[2];
            try {
                boolean update = true;
                if ("ac".equals(key)) {
                    mBatteryProps.chargerAcOnline = Integer.parseInt(value) != 0;
                } else if ("usb".equals(key)) {
                    mBatteryProps.chargerUsbOnline = Integer.parseInt(value) != 0;
                } else if ("wireless".equals(key)) {
                    mBatteryProps.chargerWirelessOnline = Integer.parseInt(value) != 0;
                } else if ("status".equals(key)) {
                    mBatteryProps.batteryStatus = Integer.parseInt(value);
                } else if ("level".equals(key)) {
                    mBatteryProps.batteryLevel = Integer.parseInt(value);
                } else if ("invalid".equals(key)) {
                    mInvalidCharger = Integer.parseInt(value);
                } else {
                    pw.println("Unknown set option: " + key);
                    update = false;
                }
                if (update) {
                    long ident = Binder.clearCallingIdentity();
                    try {
                        mUpdatesStopped = true;
                        processValuesLocked();
                    } finally {
                        Binder.restoreCallingIdentity(ident);
                    }
                }
            } catch (NumberFormatException ex) {
                pw.println("Bad value: " + value);
            }
        } else if (args.length == 1 && "reset".equals(args[0])) {
            long ident = Binder.clearCallingIdentity();
            try {
                mUpdatesStopped = false;
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        } else {
            pw.println("Dump current battery state, or:");
            pw.println("  set ac|usb|wireless|status|level|invalid <value>");
            pw.println("  reset");
        }
    }
}

private final UEventObserver mInvalidChargerObserver = new UEventObserver() {
    @Override
    public void onUEvent(UEventObserver.UEvent event) {
        final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
        synchronized (mLock) {
            if (mInvalidCharger != invalidCharger) {
                mInvalidCharger = invalidCharger;
            }
        }
    }
};

private final class Led {
    private final LightsService.Light mBatteryLight;

    private final int mBatteryLowARGB;
    private final int mBatteryMediumARGB;
    private final int mBatteryFullARGB;
    private final int mBatteryLedOn;
    private final int mBatteryLedOff;

    public Led(Context context, LightsService lights) {
        mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);

        mBatteryLowARGB = context.getResources().getInteger(
                com.android.internal.R.integer.config_notificationsBatteryLowARGB);
        mBatteryMediumARGB = context.getResources().getInteger(
                com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
        mBatteryFullARGB = context.getResources().getInteger(
                com.android.internal.R.integer.config_notificationsBatteryFullARGB);
        mBatteryLedOn = context.getResources().getInteger(
                com.android.internal.R.integer.config_notificationsBatteryLedOn);
        mBatteryLedOff = context.getResources().getInteger(
                com.android.internal.R.integer.config_notificationsBatteryLedOff);
    }

    /**
     * Synchronize on BatteryService.
     */
    public void updateLightsLocked() {
        final int level = mBatteryProps.batteryLevel;
        final int status = mBatteryProps.batteryStatus;
        if (level < mLowBatteryWarningLevel) {
            if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
                // Solid red when battery is charging
                mBatteryLight.setColor(mBatteryLowARGB);
            } else {
                // Flash red when battery is low and not charging
                mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED,
                        mBatteryLedOn, mBatteryLedOff);
            }
        } else if (status == BatteryManager.BATTERY_STATUS_CHARGING
                || status == BatteryManager.BATTERY_STATUS_FULL) {
            if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {
                // Solid green when full or charging and nearly full
                mBatteryLight.setColor(mBatteryFullARGB);
            } else {
                // Solid orange when charging and halfway full
                mBatteryLight.setColor(mBatteryMediumARGB);
            }
        } else {
            // No lights if not charging and not low
            mBatteryLight.turnOff();
        }
    }
}

private final class BatteryListener extends IBatteryPropertiesListener.Stub {
    public void batteryPropertiesChanged(BatteryProperties props) {
        BatteryService.this.update(props);
   }
}

}

总结如下:此服务构造时会注册监听到系统JNI层。 当电池电量改变的时候会调用update(BatteryProperties props) -----》processValuesLocked() 。 而processValuesLocked() 函数会把电池状态把广播发送出去。其他类再接收广播进行处理

下一个类就是低电量提醒了,文件目录:\frameworks\base\packages\SystemUI\src\com\android\systemui\power\PowerUI.java

[java] view plain copy
/*

  • Copyright © 2008 The Android Open Source Project
  • Licensed under the Apache License, Version 2.0 (the “License”);
  • you may not use this file except in compliance with the License.
  • You may obtain a copy of the License at
  •  http://www.apache.org/licenses/LICENSE-2.0 
    
  • Unless required by applicable law or agreed to in writing, software
  • distributed under the License is distributed on an “AS IS” BASIS,
  • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  • See the License for the specific language governing permissions and
  • limitations under the License.
    */

package com.android.systemui.power;

import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;

import com.android.systemui.R;
import com.android.systemui.SystemUI;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;

public class PowerUI extends SystemUI { //总体说一下,这里才是处理低电量提醒的地方,他接收的广播是Intent.ACTION_BATTERY_CHANGED
static final String TAG = “PowerUI”;

static final boolean DEBUG = false;  

Handler mHandler = new Handler();  

int mBatteryLevel = 100;  
int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;  
int mPlugType = 0;  
int mInvalidCharger = 0;  

int mLowBatteryAlertCloseLevel;  
int[] mLowBatteryReminderLevels = new int[2];  

AlertDialog mInvalidChargerDialog;  
AlertDialog mLowBatteryDialog;  
TextView mBatteryLevelTextView;  

private long mScreenOffTime = -1;  

public void start() {  //这个类会在手机启动后,在SystemUI 里启动(就是系统界面)。  

    mLowBatteryAlertCloseLevel = mContext.getResources().getInteger( //电量告警取消值,值20   
            com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);  
    mLowBatteryReminderLevels[0] = mContext.getResources().getInteger( //低电量提醒 15  
            com.android.internal.R.integer.config_lowBatteryWarningLevel);  
    mLowBatteryReminderLevels[1] = mContext.getResources().getInteger( //低电量临界值 4  
            com.android.internal.R.integer.config_criticalBatteryWarningLevel);  

    final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);  
    mScreenOffTime = pm.isScreenOn() ? -1 : SystemClock.elapsedRealtime();  

    // 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);  
    mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);  
}  

/** 
 * Buckets the battery level. 
 * 
 * The code in this function is a little weird because I couldn't comprehend 
 * the bucket going up when the battery level was going down. --joeo 
 * 
 * 1 means that the battery is "ok" 
 * 0 means that the battery is between "ok" and what we should warn about. 
 * less than 0 means that the battery is low 
 */  
private int findBatteryLevelBucket(int level) {   //这个方法是用来警告判断用的。  
    if (level >= mLowBatteryAlertCloseLevel) {  
        return 1;  
    }  
    if (level >= mLowBatteryReminderLevels[0]) {  
        return 0;  
    }  
    final int N = mLowBatteryReminderLevels.length;  
    for (int i=N-1; i>=0; i--) {  
        if (level <= mLowBatteryReminderLevels[i]) {  
            return -1-i;  
        }  
    }  
    throw new RuntimeException("not possible!");  
}  

private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { //重点,广播接收处理  
    @Override  
    public void onReceive(Context context, Intent intent) {  
        String action = intent.getAction();  
        if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {  //电池电量改变  
            final int oldBatteryLevel = mBatteryLevel; //下边就是根据Intent获取BatteryService传过来的电池属性  
            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);  

            if (DEBUG) {  
                Slog.d(TAG, "buckets   ....." + mLowBatteryAlertCloseLevel  
                        + " .. " + mLowBatteryReminderLevels[0]  
                        + " .. " + mLowBatteryReminderLevels[1]);  
                Slog.d(TAG, "level          " + oldBatteryLevel + " --> " + mBatteryLevel);  
                Slog.d(TAG, "status         " + oldBatteryStatus + " --> " + mBatteryStatus);  
                Slog.d(TAG, "plugType       " + oldPlugType + " --> " + mPlugType);  
                Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);  
                Slog.d(TAG, "bucket         " + oldBucket + " --> " + bucket);  
                Slog.d(TAG, "plugged        " + oldPlugged + " --> " + plugged);  
            }  

            if (oldInvalidCharger == 0 && mInvalidCharger != 0) {  
                Slog.d(TAG, "showing invalid charger warning");  
                showInvalidChargerDialog(); //就是充电器不识别的弹窗  
                return;  
            } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {  
                dismissInvalidChargerDialog();  
            } else if (mInvalidChargerDialog != null) {  
                // if invalid charger is showing, don't show low battery  
                return;  
            }  

            if (!plugged  
                    && (bucket < oldBucket || oldPlugged)  
                    && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN  
                    && bucket < 0) {  
                showLowBatteryWarning(); <span style="color:#FF0000;">//这里哈,低电量提醒的弹窗</span>  

                // only play SFX when the dialog comes up or the bucket changes  
                if (bucket != oldBucket || oldPlugged) {  
                    playLowBatterySound();  
                }  
            } else if (plugged || (bucket > oldBucket && bucket > 0)) { //插上充电器或者充电电池电量超过20取消弹窗  
                dismissLowBatteryWarning();  
            } else if (mBatteryLevelTextView != null) {  
                showLowBatteryWarning();  
            }  
        } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {  
            mScreenOffTime = SystemClock.elapsedRealtime();  
        } else if (Intent.ACTION_SCREEN_ON.equals(action)) {  
            mScreenOffTime = -1;  
        } else {  
            Slog.w(TAG, "unknown intent: " + intent);  
        }  
    }  
};  

void dismissLowBatteryWarning() {  
    if (mLowBatteryDialog != null) {  
        Slog.i(TAG, "closing low battery warning: level=" + mBatteryLevel);  
        mLowBatteryDialog.dismiss();  
    }  
}  

void showLowBatteryWarning() {  
    Slog.i(TAG,  
            ((mBatteryLevelTextView == null) ? "showing" : "updating")  
            + " low battery warning: level=" + mBatteryLevel  
            + " [" + findBatteryLevelBucket(mBatteryLevel) + "]");  

    CharSequence levelText = mContext.getString(  
            R.string.battery_low_percent_format, mBatteryLevel);  

    if (mBatteryLevelTextView != null) {  
        mBatteryLevelTextView.setText(levelText);  
    } else {  
        View v = View.inflate(mContext, R.layout.battery_low, null);  
        mBatteryLevelTextView = (TextView)v.findViewById(R.id.level_percent);  

        mBatteryLevelTextView.setText(levelText);  

        AlertDialog.Builder b = new AlertDialog.Builder(mContext);  
            b.setCancelable(true);  
            b.setTitle(R.string.battery_low_title);  
            b.setView(v);  
            b.setIconAttribute(android.R.attr.alertDialogIcon);  
            b.setPositiveButton(android.R.string.ok, null);  

        final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);  
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK  
                | Intent.FLAG_ACTIVITY_MULTIPLE_TASK  
                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS  
                | Intent.FLAG_ACTIVITY_NO_HISTORY);  
        if (intent.resolveActivity(mContext.getPackageManager()) != null) {  
            b.setNegativeButton(R.string.battery_low_why,  
                    new DialogInterface.OnClickListener() {  
                @Override  
                public void onClick(DialogInterface dialog, int which) {  
                    mContext.startActivityAsUser(intent, UserHandle.CURRENT);  
                    dismissLowBatteryWarning();  
                }  
            });  
        }  

        AlertDialog d = b.create();  
        d.setOnDismissListener(new DialogInterface.OnDismissListener() {  
                @Override  
                public void onDismiss(DialogInterface dialog) {  
                    mLowBatteryDialog = null;  
                    mBatteryLevelTextView = null;  
                }  
            });  
        d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);  
        d.getWindow().getAttributes().privateFlags |=  
                WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;  
        d.show();  
        mLowBatteryDialog = d;  
    }  
}  

void playLowBatterySound() {  
    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) {  
                final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);  
                if (sfx != null) {  
                    sfx.setStreamType(AudioManager.STREAM_SYSTEM);  
                    sfx.play();  
                }  
            }  
        }  
    }  
}  

void dismissInvalidChargerDialog() {  
    if (mInvalidChargerDialog != null) {  
        mInvalidChargerDialog.dismiss();  
    }  
}  

void showInvalidChargerDialog() {  
    Slog.d(TAG, "showing invalid charger dialog");  

    dismissLowBatteryWarning();  

    AlertDialog.Builder b = new AlertDialog.Builder(mContext);  
        b.setCancelable(true);  
        b.setMessage(R.string.invalid_charger);  
        b.setIconAttribute(android.R.attr.alertDialogIcon);  
        b.setPositiveButton(android.R.string.ok, null);  

    AlertDialog d = b.create();  
        d.setOnDismissListener(new DialogInterface.OnDismissListener() {  
                public void onDismiss(DialogInterface dialog) {  
                    mInvalidChargerDialog = null;  
                    mBatteryLevelTextView = null;  
                }  
            });  

    d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);  
    d.show();  
    mInvalidChargerDialog = d;  
}  

public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {  
    pw.print("mLowBatteryAlertCloseLevel=");  
    pw.println(mLowBatteryAlertCloseLevel);  
    pw.print("mLowBatteryReminderLevels=");  
    pw.println(Arrays.toString(mLowBatteryReminderLevels));  
    pw.print("mInvalidChargerDialog=");  
    pw.println(mInvalidChargerDialog == null ? "null" : mInvalidChargerDialog.toString());  
    pw.print("mLowBatteryDialog=");  
    pw.println(mLowBatteryDialog == null ? "null" : mLowBatteryDialog.toString());  
    pw.print("mBatteryLevel=");  
    pw.println(Integer.toString(mBatteryLevel));  
    pw.print("mBatteryStatus=");  
    pw.println(Integer.toString(mBatteryStatus));  
    pw.print("mPlugType=");  
    pw.println(Integer.toString(mPlugType));  
    pw.print("mInvalidCharger=");  
    pw.println(Integer.toString(mInvalidCharger));  
    pw.print("mScreenOffTime=");  
    pw.print(mScreenOffTime);  
    if (mScreenOffTime >= 0) {  
        pw.print(" (");  
        pw.print(SystemClock.elapsedRealtime() - mScreenOffTime);  
        pw.print(" ago)");  
    }  
    pw.println();  
    pw.print("soundTimeout=");  
    pw.println(Settings.Global.getInt(mContext.getContentResolver(),  
            Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));  
    pw.print("bucket: ");  
    pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));  
}  

}
/*

  • Copyright © 2008 The Android Open Source Project
  • Licensed under the Apache License, Version 2.0 (the “License”);
  • you may not use this file except in compliance with the License.
  • You may obtain a copy of the License at
  •  http://www.apache.org/licenses/LICENSE-2.0
    
  • Unless required by applicable law or agreed to in writing, software
  • distributed under the License is distributed on an “AS IS” BASIS,
  • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  • See the License for the specific language governing permissions and
  • limitations under the License.
    */

package com.android.systemui.power;

import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;

import com.android.systemui.R;
import com.android.systemui.SystemUI;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;

public class PowerUI extends SystemUI { //总体说一下,这里才是处理低电量提醒的地方,他接收的广播是Intent.ACTION_BATTERY_CHANGED
static final String TAG = “PowerUI”;

static final boolean DEBUG = false;

Handler mHandler = new Handler();

int mBatteryLevel = 100;
int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
int mPlugType = 0;
int mInvalidCharger = 0;

int mLowBatteryAlertCloseLevel;
int[] mLowBatteryReminderLevels = new int[2];

AlertDialog mInvalidChargerDialog;
AlertDialog mLowBatteryDialog;
TextView mBatteryLevelTextView;

private long mScreenOffTime = -1;

public void start() {  //这个类会在手机启动后,在SystemUI 里启动(就是系统界面)。

    mLowBatteryAlertCloseLevel = mContext.getResources().getInteger( //电量告警取消值,值20 
            com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
    mLowBatteryReminderLevels[0] = mContext.getResources().getInteger( //低电量提醒 15
            com.android.internal.R.integer.config_lowBatteryWarningLevel);
    mLowBatteryReminderLevels[1] = mContext.getResources().getInteger( //低电量临界值 4
            com.android.internal.R.integer.config_criticalBatteryWarningLevel);

    final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
    mScreenOffTime = pm.isScreenOn() ? -1 : SystemClock.elapsedRealtime();

    // 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);
    mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
}

/**
 * Buckets the battery level.
 *
 * The code in this function is a little weird because I couldn't comprehend
 * the bucket going up when the battery level was going down. --joeo
 *
 * 1 means that the battery is "ok"
 * 0 means that the battery is between "ok" and what we should warn about.
 * less than 0 means that the battery is low
 */
private int findBatteryLevelBucket(int level) {   //这个方法是用来警告判断用的。
    if (level >= mLowBatteryAlertCloseLevel) {
        return 1;
    }
    if (level >= mLowBatteryReminderLevels[0]) {
        return 0;
    }
    final int N = mLowBatteryReminderLevels.length;
    for (int i=N-1; i>=0; i--) {
        if (level <= mLowBatteryReminderLevels[i]) {
            return -1-i;
        }
    }
    throw new RuntimeException("not possible!");
}

private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { //重点,广播接收处理
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {  //电池电量改变
            final int oldBatteryLevel = mBatteryLevel; //下边就是根据Intent获取BatteryService传过来的电池属性
            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);

            if (DEBUG) {
                Slog.d(TAG, "buckets   ....." + mLowBatteryAlertCloseLevel
                        + " .. " + mLowBatteryReminderLevels[0]
                        + " .. " + mLowBatteryReminderLevels[1]);
                Slog.d(TAG, "level          " + oldBatteryLevel + " --> " + mBatteryLevel);
                Slog.d(TAG, "status         " + oldBatteryStatus + " --> " + mBatteryStatus);
                Slog.d(TAG, "plugType       " + oldPlugType + " --> " + mPlugType);
                Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
                Slog.d(TAG, "bucket         " + oldBucket + " --> " + bucket);
                Slog.d(TAG, "plugged        " + oldPlugged + " --> " + plugged);
            }

            if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
                Slog.d(TAG, "showing invalid charger warning");
                showInvalidChargerDialog(); //就是充电器不识别的弹窗
                return;
            } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
                dismissInvalidChargerDialog();
            } else if (mInvalidChargerDialog != null) {
                // if invalid charger is showing, don't show low battery
                return;
            }

            if (!plugged
                    && (bucket < oldBucket || oldPlugged)
                    && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
                    && bucket < 0) {
                showLowBatteryWarning(); <span style="color:#FF0000;">//这里哈,低电量提醒的弹窗</span>

                // only play SFX when the dialog comes up or the bucket changes
                if (bucket != oldBucket || oldPlugged) {
                    playLowBatterySound();
                }
            } else if (plugged || (bucket > oldBucket && bucket > 0)) { //插上充电器或者充电电池电量超过20取消弹窗
                dismissLowBatteryWarning();
            } else if (mBatteryLevelTextView != null) {
                showLowBatteryWarning();
            }
        } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
            mScreenOffTime = SystemClock.elapsedRealtime();
        } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
            mScreenOffTime = -1;
        } else {
            Slog.w(TAG, "unknown intent: " + intent);
        }
    }
};

void dismissLowBatteryWarning() {
    if (mLowBatteryDialog != null) {
        Slog.i(TAG, "closing low battery warning: level=" + mBatteryLevel);
        mLowBatteryDialog.dismiss();
    }
}

void showLowBatteryWarning() {
    Slog.i(TAG,
            ((mBatteryLevelTextView == null) ? "showing" : "updating")
            + " low battery warning: level=" + mBatteryLevel
            + " [" + findBatteryLevelBucket(mBatteryLevel) + "]");

    CharSequence levelText = mContext.getString(
            R.string.battery_low_percent_format, mBatteryLevel);

    if (mBatteryLevelTextView != null) {
        mBatteryLevelTextView.setText(levelText);
    } else {
        View v = View.inflate(mContext, R.layout.battery_low, null);
        mBatteryLevelTextView = (TextView)v.findViewById(R.id.level_percent);

        mBatteryLevelTextView.setText(levelText);

        AlertDialog.Builder b = new AlertDialog.Builder(mContext);
            b.setCancelable(true);
            b.setTitle(R.string.battery_low_title);
            b.setView(v);
            b.setIconAttribute(android.R.attr.alertDialogIcon);
            b.setPositiveButton(android.R.string.ok, null);

        final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
                | Intent.FLAG_ACTIVITY_NO_HISTORY);
        if (intent.resolveActivity(mContext.getPackageManager()) != null) {
            b.setNegativeButton(R.string.battery_low_why,
                    new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    mContext.startActivityAsUser(intent, UserHandle.CURRENT);
                    dismissLowBatteryWarning();
                }
            });
        }

        AlertDialog d = b.create();
        d.setOnDismissListener(new DialogInterface.OnDismissListener() {
                @Override
                public void onDismiss(DialogInterface dialog) {
                    mLowBatteryDialog = null;
                    mBatteryLevelTextView = null;
                }
            });
        d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
        d.getWindow().getAttributes().privateFlags |=
                WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
        d.show();
        mLowBatteryDialog = d;
    }
}

void playLowBatterySound() {
    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) {
                final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
                if (sfx != null) {
                    sfx.setStreamType(AudioManager.STREAM_SYSTEM);
                    sfx.play();
                }
            }
        }
    }
}

void dismissInvalidChargerDialog() {
    if (mInvalidChargerDialog != null) {
        mInvalidChargerDialog.dismiss();
    }
}

void showInvalidChargerDialog() {
    Slog.d(TAG, "showing invalid charger dialog");

    dismissLowBatteryWarning();

    AlertDialog.Builder b = new AlertDialog.Builder(mContext);
        b.setCancelable(true);
        b.setMessage(R.string.invalid_charger);
        b.setIconAttribute(android.R.attr.alertDialogIcon);
        b.setPositiveButton(android.R.string.ok, null);

    AlertDialog d = b.create();
        d.setOnDismissListener(new DialogInterface.OnDismissListener() {
                public void onDismiss(DialogInterface dialog) {
                    mInvalidChargerDialog = null;
                    mBatteryLevelTextView = null;
                }
            });

    d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
    d.show();
    mInvalidChargerDialog = d;
}

public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    pw.print("mLowBatteryAlertCloseLevel=");
    pw.println(mLowBatteryAlertCloseLevel);
    pw.print("mLowBatteryReminderLevels=");
    pw.println(Arrays.toString(mLowBatteryReminderLevels));
    pw.print("mInvalidChargerDialog=");
    pw.println(mInvalidChargerDialog == null ? "null" : mInvalidChargerDialog.toString());
    pw.print("mLowBatteryDialog=");
    pw.println(mLowBatteryDialog == null ? "null" : mLowBatteryDialog.toString());
    pw.print("mBatteryLevel=");
    pw.println(Integer.toString(mBatteryLevel));
    pw.print("mBatteryStatus=");
    pw.println(Integer.toString(mBatteryStatus));
    pw.print("mPlugType=");
    pw.println(Integer.toString(mPlugType));
    pw.print("mInvalidCharger=");
    pw.println(Integer.toString(mInvalidCharger));
    pw.print("mScreenOffTime=");
    pw.print(mScreenOffTime);
    if (mScreenOffTime >= 0) {
        pw.print(" (");
        pw.print(SystemClock.elapsedRealtime() - mScreenOffTime);
        pw.print(" ago)");
    }
    pw.println();
    pw.print("soundTimeout=");
    pw.println(Settings.Global.getInt(mContext.getContentResolver(),
            Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));
    pw.print("bucket: ");
    pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
}

}

总结一下:这个类就是接收电池电量改变的广播,然后弹窗提醒。

下一个类就是没电关机界面了,文件目录:\frameworks\base\services\java\com\android\server\ShutdownLowBatteryActivity.java

这个Activity的配置文件是这样的(\frameworks\base\core\res\AndroidManifest.xml):

[html] view plain copy











android:theme="@android:style/Theme.Translucent.NoTitleBar"是API小于11的透明主题。这样弹出的dialog会是老版本的主题。(这个Activity是透明的)

对于Theme,因为是4.4,我更倾向于:android:theme=“@android:style/Theme.Holo.Panel”

ShutdownLowBatteryActivity.java:

[java] view plain copy
/*

  • Copyright © 2009 The Android Open Source Project
  • Licensed under the Apache License, Version 2.0 (the “License”);
  • you may not use this file except in compliance with the License.
  • You may obtain a copy of the License at
  •  http://www.apache.org/licenses/LICENSE-2.0 
    
  • Unless required by applicable law or agreed to in writing, software
  • distributed under the License is distributed on an “AS IS” BASIS,
  • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  • See the License for the specific language governing permissions and
  • limitations under the License.
    */

package com.android.server;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.KeyguardManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.util.Slog;
import android.view.Window;
import android.view.WindowManager;
import android.view.View;

//import com.android.internal.app.ShutdownThread;
import com.android.server.power.ShutdownThread;

import android.telephony.TelephonyManager;
import android.telephony.PhoneStateListener;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.content.ContentResolver;
import android.provider.Settings;

import java.io.IOException;

public class ShutdownLowBatteryActivity extends Activity {

private static final String TAG = "ShutdownLowBatteryActivity";  
private boolean mConfirm;  
private int mSeconds = 15;//电池电量等于0 15秒内不插充电器就自动关机  
private AlertDialog mDialog;  
private Handler myHandler = new Handler();  
private Runnable myRunnable = new Runnable() { //这里数秒关机  
    @Override  
    public void run() {  
        mSeconds --;  
        if(mSeconds <1)  
            mSeconds=0;  
        mDialog.setMessage(getString(com.android.internal.R.string.low_battery_shutdown_after_seconds,mSeconds));  

        if(mSeconds <= 1){  
            myHandler.removeCallbacks(myRunnable);  
            Handler h = new Handler();  
            h.post(new Runnable() {  
                public void run() {  
                    ShutdownThread.shutdown(ShutdownLowBatteryActivity.this, mConfirm);  
                }  
            });  
        }  
        myHandler.postDelayed(myRunnable,1000);  
    }  
};  

private BroadcastReceiver mReceiver;  
private MediaPlayer mplayer;  

@Override  
protected void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  

    mConfirm = getIntent().getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false);  
    Slog.i(TAG, "onCreate(): confirm=" + mConfirm);  

    //if(getIntent().getBooleanExtra("can_be_cancel", false)) { //这行注释掉了: 然后当连上充电器后或者电量涨到20的时候,就取消倒计时关机  
            mReceiver = new BroadcastReceiver() {  
            @Override  
            public void onReceive(Context context, Intent intent) {  
                if(Intent.ACTION_BATTERY_OKAY.equals(intent.getAction())|  
                    Intent.ACTION_POWER_CONNECTED.equals(intent.getAction())){  
                    ShutDownWakeLock.releaseCpuLock();  
                    myHandler.removeCallbacks(myRunnable);  
                    if(mReceiver != null)  
                        unregisterReceiver(mReceiver);  
                    finish();  
                }  
            }  
        };  

     IntentFilter filter=new IntentFilter(Intent.ACTION_POWER_CONNECTED);  
     filter.addAction(Intent.ACTION_BATTERY_OKAY);  
     registerReceiver(mReceiver, filter);  
     //}  


    PhoneStateListener mPhoneStateListener = new PhoneStateListener() { //如果正数秒呢,电话呼入了。取消自动关机  
        @Override  
        public void onCallStateChanged(int state, String ignored) {  
            if (state == TelephonyManager.CALL_STATE_RINGING) {  
                ShutDownWakeLock.releaseCpuLock();  
                myHandler.removeCallbacks(myRunnable);  
                finish();  
            }  
        }  
    };  

    TelephonyManager mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);  
    mTelephonyManager.listen(mPhoneStateListener,  
            PhoneStateListener.LISTEN_CALL_STATE);  
    requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);  
    Window win = getWindow();  
    win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED  
            | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);  
    win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON  
                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);  

    setContentView(new View(this));  
    mDialog=new AlertDialog.Builder(this).create();  
    mDialog.setTitle(com.android.internal.R.string.low_battery_shutdown_title);  
    mDialog.setMessage(getString(com.android.internal.R.string.low_battery_shutdown_after_seconds,mSeconds));  
    if(!getIntent().getBooleanExtra("cant_be_cancel_by_button", false)) {//读取配置文件,是否运行取消,然后根据这个显示取消自动关机按钮  
        mDialog.setButton(DialogInterface.BUTTON_NEUTRAL,getText(com.android.internal.R.string.cancel), new DialogInterface.OnClickListener() {  
                @Override  
                public void onClick(DialogInterface dialog, int which) {  
                    myHandler.removeCallbacks(myRunnable);  
                    dialog.cancel();  
                    if(mReceiver != null)  
                        unregisterReceiver(mReceiver);  
                    finish();  
                }});  
    }  
    mDialog.setCancelable(false);  
    //mDialog.getWindow().setType( WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);  
    mDialog.show();  
    if(mConfirm == false){  
        myHandler.postDelayed(myRunnable, 1000);  
    }  
    myHandler.post(new Runnable(){  
        public void run(){  
            final ContentResolver cr = getContentResolver();  
            String path=Settings.System.getString(cr,Settings.System.NOTIFICATION_SOUND);  

           mplayer=new MediaPlayer();  
            try{  
                mplayer.reset();  
                mplayer.setDataSource("system/media/audio/ui/LowBattery.ogg");                    
                mplayer.prepare();  
                mplayer.start();  
                mplayer.setOnCompletionListener(new OnCompletionListener() {  
                    @Override  
                    public void onCompletion(MediaPlayer mp) {  
                        if(null != mplayer){  
                            mplayer.stop();  
                            mplayer.release();  
                            mplayer = null;  
                        }  
                    }  
                });  
            }  
            catch(IOException e){  
                  
            }  
        }  
    });  
}  

}
/*

  • Copyright © 2009 The Android Open Source Project
  • Licensed under the Apache License, Version 2.0 (the “License”);
  • you may not use this file except in compliance with the License.
  • You may obtain a copy of the License at
  •  http://www.apache.org/licenses/LICENSE-2.0
    
  • Unless required by applicable law or agreed to in writing, software
  • distributed under the License is distributed on an “AS IS” BASIS,
  • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  • See the License for the specific language governing permissions and
  • limitations under the License.
    */

package com.android.server;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.KeyguardManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.util.Slog;
import android.view.Window;
import android.view.WindowManager;
import android.view.View;

//import com.android.internal.app.ShutdownThread;
import com.android.server.power.ShutdownThread;

import android.telephony.TelephonyManager;
import android.telephony.PhoneStateListener;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.content.ContentResolver;
import android.provider.Settings;

import java.io.IOException;

public class ShutdownLowBatteryActivity extends Activity {

private static final String TAG = "ShutdownLowBatteryActivity";
private boolean mConfirm;
private int mSeconds = 15;//电池电量等于0 15秒内不插充电器就自动关机
private AlertDialog mDialog;
private Handler myHandler = new Handler();
private Runnable myRunnable = new Runnable() { //这里数秒关机
    @Override
    public void run() {
        mSeconds --;
        if(mSeconds <1)
        	mSeconds=0;
        mDialog.setMessage(getString(com.android.internal.R.string.low_battery_shutdown_after_seconds,mSeconds));

        if(mSeconds <= 1){
            myHandler.removeCallbacks(myRunnable);
            Handler h = new Handler();
            h.post(new Runnable() {
                public void run() {
					ShutdownThread.shutdown(ShutdownLowBatteryActivity.this, mConfirm);
                }
            });
        }
        myHandler.postDelayed(myRunnable,1000);
    }
};

private BroadcastReceiver mReceiver;
private MediaPlayer mplayer;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mConfirm = getIntent().getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false);
    Slog.i(TAG, "onCreate(): confirm=" + mConfirm);

    //if(getIntent().getBooleanExtra("can_be_cancel", false)) { //这行注释掉了: 然后当连上充电器后或者电量涨到20的时候,就取消倒计时关机
            mReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if(Intent.ACTION_BATTERY_OKAY.equals(intent.getAction())|
                    Intent.ACTION_POWER_CONNECTED.equals(intent.getAction())){
                    ShutDownWakeLock.releaseCpuLock();
                    myHandler.removeCallbacks(myRunnable);
                    if(mReceiver != null)
                        unregisterReceiver(mReceiver);
                    finish();
                }
            }
        };

     IntentFilter filter=new IntentFilter(Intent.ACTION_POWER_CONNECTED);
     filter.addAction(Intent.ACTION_BATTERY_OKAY);
     registerReceiver(mReceiver, filter);
     //}


    PhoneStateListener mPhoneStateListener = new PhoneStateListener() { //如果正数秒呢,电话呼入了。取消自动关机
        @Override
        public void onCallStateChanged(int state, String ignored) {
            if (state == TelephonyManager.CALL_STATE_RINGING) {
                ShutDownWakeLock.releaseCpuLock();
                myHandler.removeCallbacks(myRunnable);
                finish();
            }
        }
    };

    TelephonyManager mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
    mTelephonyManager.listen(mPhoneStateListener,
            PhoneStateListener.LISTEN_CALL_STATE);
    requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);
    Window win = getWindow();
    win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
            | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
    win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);

    setContentView(new View(this));
    mDialog=new AlertDialog.Builder(this).create();
    mDialog.setTitle(com.android.internal.R.string.low_battery_shutdown_title);
    mDialog.setMessage(getString(com.android.internal.R.string.low_battery_shutdown_after_seconds,mSeconds));
    if(!getIntent().getBooleanExtra("cant_be_cancel_by_button", false)) {//读取配置文件,是否运行取消,然后根据这个显示取消自动关机按钮
        mDialog.setButton(DialogInterface.BUTTON_NEUTRAL,getText(com.android.internal.R.string.cancel), new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    myHandler.removeCallbacks(myRunnable);
                    dialog.cancel();
                    if(mReceiver != null)
                        unregisterReceiver(mReceiver);
                    finish();
                }});
    }
    mDialog.setCancelable(false);
    //mDialog.getWindow().setType( WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
    mDialog.show();
    if(mConfirm == false){
        myHandler.postDelayed(myRunnable, 1000);
    }
    myHandler.post(new Runnable(){
        public void run(){
            final ContentResolver cr = getContentResolver();
            String path=Settings.System.getString(cr,Settings.System.NOTIFICATION_SOUND);

           mplayer=new MediaPlayer();
            try{
                mplayer.reset();
                mplayer.setDataSource("system/media/audio/ui/LowBattery.ogg");                  
                mplayer.prepare();
                mplayer.start();
                mplayer.setOnCompletionListener(new OnCompletionListener() {
                    @Override
                    public void onCompletion(MediaPlayer mp) {
                       	if(null != mplayer){
                       		mplayer.stop();
                       		mplayer.release();
                       		mplayer = null;
                    	}
                    }
                });
            }
            catch(IOException e){
            	
            }
        }
    });
}

}

总结如下:这个类就是弹窗计时,提醒不连充电器就要关机了。

最后一个相关的类,文件路径:\frameworks\base\packages\SystemUI\src\com\android\systemui\BatteryMeterView.java

这个类是自定义控件,定义在手机状态栏里:

[java] view plain copy
/*

  • Copyright © 2013 The Android Open Source Project
  • Licensed under the Apache License, Version 2.0 (the “License”);
  • you may not use this file except in compliance with the License.
  • You may obtain a copy of the License at
  •  http://www.apache.org/licenses/LICENSE-2.0 
    
  • Unless required by applicable law or agreed to in writing, software
  • distributed under the License is distributed on an “AS IS” BASIS,
  • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  • See the License for the specific language governing permissions and
  • limitations under the License.
    */

package com.android.systemui;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.os.BatteryManager;
import android.os.Bundle;
import android.provider.Settings;
import android.util.AttributeSet;
import android.view.View;

public class BatteryMeterView extends View implements DemoMode {
public static final String TAG = BatteryMeterView.class.getSimpleName();
public static final String ACTION_LEVEL_TEST = “com.android.systemui.BATTERY_LEVEL_TEST”;

public static final boolean ENABLE_PERCENT = true;  
public static final boolean SINGLE_DIGIT_PERCENT = false;  
public static final boolean SHOW_100_PERCENT = false;  

public static final int FULL = 96;  
public static final int EMPTY = 4;  

public static final float SUBPIXEL = 0.4f;  // inset rects for softer edges  

int[] mColors;  

boolean mShowPercent = true;  
Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint;  
int mButtonHeight;  
private float mTextHeight, mWarningTextHeight;  

private int mHeight;  
private int mWidth;  
private String mWarningString;  
private final int mChargeColor;  
private final float[] mBoltPoints;  
private final Path mBoltPath = new Path();  

private final RectF mFrame = new RectF();  
private final RectF mButtonFrame = new RectF();  
private final RectF mClipFrame = new RectF();  
private final RectF mBoltFrame = new RectF();  

private class BatteryTracker extends BroadcastReceiver {  
    public static final int UNKNOWN_LEVEL = -1;  

    // current battery status  
    int level = UNKNOWN_LEVEL;  
    String percentStr;  
    int plugType;  
    boolean plugged;  
    int health;  
    int status;  
    String technology;  
    int voltage;  
    int temperature;  
    boolean testmode = false;  

    @Override  
    public void onReceive(Context context, Intent intent) {  
        final String action = intent.getAction();  
        if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { //接收电量改变的广播,取电池状态的属性  
            if (testmode && ! intent.getBooleanExtra("testmode", false)) return;  

            level = (int)(100f  
                    * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)  
                    / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));  

            plugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);  
            plugged = plugType != 0;  
            health = intent.getIntExtra(BatteryManager.EXTRA_HEALTH,  
                    BatteryManager.BATTERY_HEALTH_UNKNOWN);  
            status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,  
                    BatteryManager.BATTERY_STATUS_UNKNOWN);  
            technology = intent.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY);  
            voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, 0);  
            temperature = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0);  

            setContentDescription(  
                    context.getString(R.string.accessibility_battery_level, level));  
            postInvalidate();  
        } else if (action.equals(ACTION_LEVEL_TEST)) {  
            testmode = true;  
            post(new Runnable() {  
                int curLevel = 0;  
                int incr = 1;  
                int saveLevel = level;  
                int savePlugged = plugType;  
                Intent dummy = new Intent(Intent.ACTION_BATTERY_CHANGED);  
                @Override  
                public void run() {  
                    if (curLevel < 0) {  
                        testmode = false;  
                        dummy.putExtra("level", saveLevel);  
                        dummy.putExtra("plugged", savePlugged);  
                        dummy.putExtra("testmode", false);  
                    } else {  
                        dummy.putExtra("level", curLevel);  
                        dummy.putExtra("plugged", incr > 0 ? BatteryManager.BATTERY_PLUGGED_AC : 0);  
                        dummy.putExtra("testmode", true);  
                    }  
                    getContext().sendBroadcast(dummy);  

                    if (!testmode) return;  

                    curLevel += incr;  
                    if (curLevel == 100) {  
                        incr *= -1;  
                    }  
                    postDelayed(this, 200);  
                }  
            });  
        }  
    }  
}  

BatteryTracker mTracker = new BatteryTracker();  

@Override  
public void onAttachedToWindow() {  
    super.onAttachedToWindow();  

    IntentFilter filter = new IntentFilter();  
    filter.addAction(Intent.ACTION_BATTERY_CHANGED);  
    filter.addAction(ACTION_LEVEL_TEST);  
    final Intent sticky = getContext().registerReceiver(mTracker, filter);  
    if (sticky != null) {  
        // preload the battery level  
        mTracker.onReceive(getContext(), sticky);  
    }  
}  

@Override  
public void onDetachedFromWindow() {  
    super.onDetachedFromWindow();  

    getContext().unregisterReceiver(mTracker);  
}  

public BatteryMeterView(Context context) {  
    this(context, null, 0);  
}  

public BatteryMeterView(Context context, AttributeSet attrs) {  
    this(context, attrs, 0);  
}  

public BatteryMeterView(Context context, AttributeSet attrs, int defStyle) {  
    super(context, attrs, defStyle);  

    final Resources res = context.getResources();  
    TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels);  
    TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values);  

    final int N = levels.length();  
    mColors = new int[2*N];  
    for (int i=0; i<N; i++) {  
        mColors[2*i] = levels.getInt(i, 0);  
        mColors[2*i+1] = colors.getColor(i, 0);  
    }  
    levels.recycle();  
    colors.recycle();  
    mShowPercent = ENABLE_PERCENT && 0 != Settings.System.getInt(  
            context.getContentResolver(), "status_bar_show_battery_percent", 0);  

    mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol);  

    mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
    mFramePaint.setColor(res.getColor(R.color.batterymeter_frame_color));  
    mFramePaint.setDither(true);  
    mFramePaint.setStrokeWidth(0);  
    mFramePaint.setStyle(Paint.Style.FILL_AND_STROKE);  
    mFramePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));  

    mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
    mBatteryPaint.setDither(true);  
    mBatteryPaint.setStrokeWidth(0);  
    mBatteryPaint.setStyle(Paint.Style.FILL_AND_STROKE);  

    mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
    mTextPaint.setColor(0xFFFFFFFF);  
    Typeface font = Typeface.create("sans-serif-condensed", Typeface.NORMAL);  
    mTextPaint.setTypeface(font);  
    mTextPaint.setTextAlign(Paint.Align.CENTER);  

    mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
    mWarningTextPaint.setColor(mColors[1]);  
    font = Typeface.create("sans-serif", Typeface.BOLD);  
    mWarningTextPaint.setTypeface(font);  
    mWarningTextPaint.setTextAlign(Paint.Align.CENTER);  

    mChargeColor = getResources().getColor(R.color.batterymeter_charge_color);  

    mBoltPaint = new Paint();  
    mBoltPaint.setAntiAlias(true);  
    mBoltPaint.setColor(res.getColor(R.color.batterymeter_bolt_color));  
    mBoltPoints = loadBoltPoints(res);  
    setLayerType(View.LAYER_TYPE_SOFTWARE, null);  
}  

private static float[] loadBoltPoints(Resources res) {  
    final int[] pts = res.getIntArray(R.array.batterymeter_bolt_points);  
    int maxX = 0, maxY = 0;  
    for (int i = 0; i < pts.length; i += 2) {  
        maxX = Math.max(maxX, pts[i]);  
        maxY = Math.max(maxY, pts[i + 1]);  
    }  
    final float[] ptsF = new float[pts.length];  
    for (int i = 0; i < pts.length; i += 2) {  
        ptsF[i] = (float)pts[i] / maxX;  
        ptsF[i + 1] = (float)pts[i + 1] / maxY;  
    }  
    return ptsF;  
}  

@Override  
protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
    mHeight = h;  
    mWidth = w;  
    mWarningTextPaint.setTextSize(h * 0.75f);  
    mWarningTextHeight = -mWarningTextPaint.getFontMetrics().ascent;  
}  

private int getColorForLevel(int percent) {  
    int thresh, color = 0;  
    for (int i=0; i<mColors.length; i+=2) {  
        thresh = mColors[i];  
        color = mColors[i+1];  
        if (percent <= thresh) return color;  
    }  
    return color;  
}  

@Override  
public void draw(Canvas c) {  //通过电池属性绘制图标  
    BatteryTracker tracker = mDemoMode ? mDemoTracker : mTracker;  
    final int level = tracker.level;  

    if (level == BatteryTracker.UNKNOWN_LEVEL) return;  

    float drawFrac = (float) level / 100f;  
    final int pt = getPaddingTop();  
    final int pl = getPaddingLeft();  
    final int pr = getPaddingRight();  
    final int pb = getPaddingBottom();  
    int height = mHeight - pt - pb;  
    int width = mWidth - pl - pr;  

    mButtonHeight = (int) (height * 0.12f);  

    mFrame.set(0, 0, width, height);  
    mFrame.offset(pl, pt);  

    mButtonFrame.set(  //这个就是电池图标上边的突起  
            mFrame.left + width * 0.25f,  
            mFrame.top,  
            mFrame.right - width * 0.25f,  
            mFrame.top + mButtonHeight + 5 /*cover frame border of intersecting area*/);  

    mButtonFrame.top += SUBPIXEL;  
    mButtonFrame.left += SUBPIXEL;  
    mButtonFrame.right -= SUBPIXEL;  

    mFrame.top += mButtonHeight; //电池图标桶装图  
    mFrame.left += SUBPIXEL;  
    mFrame.top += SUBPIXEL;  
    mFrame.right -= SUBPIXEL;  
    mFrame.bottom -= SUBPIXEL;  

    // first, draw the battery shape  
    c.drawRect(mFrame, mFramePaint);  

    // fill 'er up  
    final int color = tracker.plugged ? mChargeColor : getColorForLevel(level); //根据等级获取颜色,如果是15以下,就是红色。15以上是白色  
    mBatteryPaint.setColor(color);  

    if (level >= FULL) { //96时,电池图标里面填充完整,满电。  
        drawFrac = 1f;  
    } else if (level <= EMPTY) { //这个变量是说,当电量小于4的时候,电池图标里面没有填充色,空电量  
        drawFrac = 0f;  
    }  

    c.drawRect(mButtonFrame, drawFrac == 1f ? mBatteryPaint : mFramePaint);  

    mClipFrame.set(mFrame);  
    mClipFrame.top += (mFrame.height() * (1f - drawFrac));  

    c.save(Canvas.CLIP_SAVE_FLAG);  
    c.clipRect(mClipFrame);//在mFrame区域中保持mClipFrame的区域不变,参考下句注释  
    c.drawRect(mFrame, mBatteryPaint);//在mFrame中,把除去mClipFrame的区域绘制。(电量状态,如果是15以下,就是红色。15以上是白色)  
    c.restore();  

    if (tracker.plugged) { //这里就犀利了。这是说插上充电器就画一个闪电  
        // draw the bolt  
        final float bl = mFrame.left + mFrame.width() / 4.5f;  
        final float bt = mFrame.top + mFrame.height() / 6f;  
        final float br = mFrame.right - mFrame.width() / 7f;  
        final float bb = mFrame.bottom - mFrame.height() / 10f;  
        if (mBoltFrame.left != bl || mBoltFrame.top != bt  
                || mBoltFrame.right != br || mBoltFrame.bottom != bb) {  
            mBoltFrame.set(bl, bt, br, bb);  
            mBoltPath.reset();  
            mBoltPath.moveTo(  
                    mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),  
                    mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());  
            for (int i = 2; i < mBoltPoints.length; i += 2) {  
                mBoltPath.lineTo(  
                        mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(),  
                        mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height());  
            }  
            mBoltPath.lineTo(  
                    mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),  
                    mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());  
        }  
        c.drawPath(mBoltPath, mBoltPaint);  
    } else if (level <= EMPTY) { //这个是空电量的时候,画一个红色的感叹号  
        final float x = mWidth * 0.5f;  
        final float y = (mHeight + mWarningTextHeight) * 0.48f;  
        c.drawText(mWarningString, x, y, mWarningTextPaint);  
    } else if (mShowPercent && !(tracker.level == 100 && !SHOW_100_PERCENT)) { //以文字显示电量。不过这个没有用到(除非mShowPercent变量获取的配置属性为true)  
        mTextPaint.setTextSize(height *  
                (SINGLE_DIGIT_PERCENT ? 0.75f  
                        : (tracker.level == 100 ? 0.38f : 0.5f)));  
        mTextHeight = -mTextPaint.getFontMetrics().ascent;  

        final String str = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level);  
        final float x = mWidth * 0.5f;  
        final float y = (mHeight + mTextHeight) * 0.47f;  
        c.drawText(str,  
                x,  
                y,  
                mTextPaint);  
    }  
}  

private boolean mDemoMode;  
private BatteryTracker mDemoTracker = new BatteryTracker();  

@Override  
public void dispatchDemoCommand(String command, Bundle args) {  
    if (!mDemoMode && command.equals(COMMAND_ENTER)) {  
        mDemoMode = true;  
        mDemoTracker.level = mTracker.level;  
        mDemoTracker.plugged = mTracker.plugged;  
    } else if (mDemoMode && command.equals(COMMAND_EXIT)) {  
        mDemoMode = false;  
        postInvalidate();  
    } else if (mDemoMode && command.equals(COMMAND_BATTERY)) {  
       String level = args.getString("level");  
       String plugged = args.getString("plugged");  
       if (level != null) {  
           mDemoTracker.level = Math.min(Math.max(Integer.parseInt(level), 0), 100);  
       }  
       if (plugged != null) {  
           mDemoTracker.plugged = Boolean.parseBoolean(plugged);  
       }  
       postInvalidate();  
    }  
}  

}
/*

  • Copyright © 2013 The Android Open Source Project
  • Licensed under the Apache License, Version 2.0 (the “License”);
  • you may not use this file except in compliance with the License.
  • You may obtain a copy of the License at
  •  http://www.apache.org/licenses/LICENSE-2.0
    
  • Unless required by applicable law or agreed to in writing, software
  • distributed under the License is distributed on an “AS IS” BASIS,
  • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  • See the License for the specific language governing permissions and
  • limitations under the License.
    */

package com.android.systemui;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.os.BatteryManager;
import android.os.Bundle;
import android.provider.Settings;
import android.util.AttributeSet;
import android.view.View;

public class BatteryMeterView extends View implements DemoMode {
public static final String TAG = BatteryMeterView.class.getSimpleName();
public static final String ACTION_LEVEL_TEST = “com.android.systemui.BATTERY_LEVEL_TEST”;

public static final boolean ENABLE_PERCENT = true;
public static final boolean SINGLE_DIGIT_PERCENT = false;
public static final boolean SHOW_100_PERCENT = false;

public static final int FULL = 96;
public static final int EMPTY = 4;

public static final float SUBPIXEL = 0.4f;  // inset rects for softer edges

int[] mColors;

boolean mShowPercent = true;
Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint;
int mButtonHeight;
private float mTextHeight, mWarningTextHeight;

private int mHeight;
private int mWidth;
private String mWarningString;
private final int mChargeColor;
private final float[] mBoltPoints;
private final Path mBoltPath = new Path();

private final RectF mFrame = new RectF();
private final RectF mButtonFrame = new RectF();
private final RectF mClipFrame = new RectF();
private final RectF mBoltFrame = new RectF();

private class BatteryTracker extends BroadcastReceiver {
    public static final int UNKNOWN_LEVEL = -1;

    // current battery status
    int level = UNKNOWN_LEVEL;
    String percentStr;
    int plugType;
    boolean plugged;
    int health;
    int status;
    String technology;
    int voltage;
    int temperature;
    boolean testmode = false;

    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { //接收电量改变的广播,取电池状态的属性
            if (testmode && ! intent.getBooleanExtra("testmode", false)) return;

            level = (int)(100f
                    * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
                    / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));

            plugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
            plugged = plugType != 0;
            health = intent.getIntExtra(BatteryManager.EXTRA_HEALTH,
                    BatteryManager.BATTERY_HEALTH_UNKNOWN);
            status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
                    BatteryManager.BATTERY_STATUS_UNKNOWN);
            technology = intent.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY);
            voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, 0);
            temperature = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0);

            setContentDescription(
                    context.getString(R.string.accessibility_battery_level, level));
            postInvalidate();
        } else if (action.equals(ACTION_LEVEL_TEST)) {
            testmode = true;
            post(new Runnable() {
                int curLevel = 0;
                int incr = 1;
                int saveLevel = level;
                int savePlugged = plugType;
                Intent dummy = new Intent(Intent.ACTION_BATTERY_CHANGED);
                @Override
                public void run() {
                    if (curLevel < 0) {
                        testmode = false;
                        dummy.putExtra("level", saveLevel);
                        dummy.putExtra("plugged", savePlugged);
                        dummy.putExtra("testmode", false);
                    } else {
                        dummy.putExtra("level", curLevel);
                        dummy.putExtra("plugged", incr > 0 ? BatteryManager.BATTERY_PLUGGED_AC : 0);
                        dummy.putExtra("testmode", true);
                    }
                    getContext().sendBroadcast(dummy);

                    if (!testmode) return;

                    curLevel += incr;
                    if (curLevel == 100) {
                        incr *= -1;
                    }
                    postDelayed(this, 200);
                }
            });
        }
    }
}

BatteryTracker mTracker = new BatteryTracker();

@Override
public void onAttachedToWindow() {
    super.onAttachedToWindow();

    IntentFilter filter = new IntentFilter();
    filter.addAction(Intent.ACTION_BATTERY_CHANGED);
    filter.addAction(ACTION_LEVEL_TEST);
    final Intent sticky = getContext().registerReceiver(mTracker, filter);
    if (sticky != null) {
        // preload the battery level
        mTracker.onReceive(getContext(), sticky);
    }
}

@Override
public void onDetachedFromWindow() {
    super.onDetachedFromWindow();

    getContext().unregisterReceiver(mTracker);
}

public BatteryMeterView(Context context) {
    this(context, null, 0);
}

public BatteryMeterView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

public BatteryMeterView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);

    final Resources res = context.getResources();
    TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels);
    TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values);

    final int N = levels.length();
    mColors = new int[2*N];
    for (int i=0; i<N; i++) {
        mColors[2*i] = levels.getInt(i, 0);
        mColors[2*i+1] = colors.getColor(i, 0);
    }
    levels.recycle();
    colors.recycle();
    mShowPercent = ENABLE_PERCENT && 0 != Settings.System.getInt(
            context.getContentResolver(), "status_bar_show_battery_percent", 0);

    mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol);

    mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mFramePaint.setColor(res.getColor(R.color.batterymeter_frame_color));
    mFramePaint.setDither(true);
    mFramePaint.setStrokeWidth(0);
    mFramePaint.setStyle(Paint.Style.FILL_AND_STROKE);
    mFramePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));

    mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mBatteryPaint.setDither(true);
    mBatteryPaint.setStrokeWidth(0);
    mBatteryPaint.setStyle(Paint.Style.FILL_AND_STROKE);

    mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mTextPaint.setColor(0xFFFFFFFF);
    Typeface font = Typeface.create("sans-serif-condensed", Typeface.NORMAL);
    mTextPaint.setTypeface(font);
    mTextPaint.setTextAlign(Paint.Align.CENTER);

    mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mWarningTextPaint.setColor(mColors[1]);
    font = Typeface.create("sans-serif", Typeface.BOLD);
    mWarningTextPaint.setTypeface(font);
    mWarningTextPaint.setTextAlign(Paint.Align.CENTER);

    mChargeColor = getResources().getColor(R.color.batterymeter_charge_color);

    mBoltPaint = new Paint();
    mBoltPaint.setAntiAlias(true);
    mBoltPaint.setColor(res.getColor(R.color.batterymeter_bolt_color));
    mBoltPoints = loadBoltPoints(res);
    setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}

private static float[] loadBoltPoints(Resources res) {
    final int[] pts = res.getIntArray(R.array.batterymeter_bolt_points);
    int maxX = 0, maxY = 0;
    for (int i = 0; i < pts.length; i += 2) {
        maxX = Math.max(maxX, pts[i]);
        maxY = Math.max(maxY, pts[i + 1]);
    }
    final float[] ptsF = new float[pts.length];
    for (int i = 0; i < pts.length; i += 2) {
        ptsF[i] = (float)pts[i] / maxX;
        ptsF[i + 1] = (float)pts[i + 1] / maxY;
    }
    return ptsF;
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    mHeight = h;
    mWidth = w;
    mWarningTextPaint.setTextSize(h * 0.75f);
    mWarningTextHeight = -mWarningTextPaint.getFontMetrics().ascent;
}

private int getColorForLevel(int percent) {
    int thresh, color = 0;
    for (int i=0; i<mColors.length; i+=2) {
        thresh = mColors[i];
        color = mColors[i+1];
        if (percent <= thresh) return color;
    }
    return color;
}

@Override
public void draw(Canvas c) {  //通过电池属性绘制图标
    BatteryTracker tracker = mDemoMode ? mDemoTracker : mTracker;
    final int level = tracker.level;

    if (level == BatteryTracker.UNKNOWN_LEVEL) return;

    float drawFrac = (float) level / 100f;
    final int pt = getPaddingTop();
    final int pl = getPaddingLeft();
    final int pr = getPaddingRight();
    final int pb = getPaddingBottom();
    int height = mHeight - pt - pb;
    int width = mWidth - pl - pr;

    mButtonHeight = (int) (height * 0.12f);

    mFrame.set(0, 0, width, height);
    mFrame.offset(pl, pt);

    mButtonFrame.set(  //这个就是电池图标上边的突起
            mFrame.left + width * 0.25f,
            mFrame.top,
            mFrame.right - width * 0.25f,
            mFrame.top + mButtonHeight + 5 /*cover frame border of intersecting area*/);

    mButtonFrame.top += SUBPIXEL;
    mButtonFrame.left += SUBPIXEL;
    mButtonFrame.right -= SUBPIXEL;

    mFrame.top += mButtonHeight; //电池图标桶装图
    mFrame.left += SUBPIXEL;
    mFrame.top += SUBPIXEL;
    mFrame.right -= SUBPIXEL;
    mFrame.bottom -= SUBPIXEL;

    // first, draw the battery shape
    c.drawRect(mFrame, mFramePaint);

    // fill 'er up
    final int color = tracker.plugged ? mChargeColor : getColorForLevel(level); //根据等级获取颜色,如果是15以下,就是红色。15以上是白色
    mBatteryPaint.setColor(color);

    if (level >= FULL) { //96时,电池图标里面填充完整,满电。
        drawFrac = 1f;
    } else if (level <= EMPTY) { //这个变量是说,当电量小于4的时候,电池图标里面没有填充色,空电量
        drawFrac = 0f;
    }

    c.drawRect(mButtonFrame, drawFrac == 1f ? mBatteryPaint : mFramePaint);

    mClipFrame.set(mFrame);
    mClipFrame.top += (mFrame.height() * (1f - drawFrac));

    c.save(Canvas.CLIP_SAVE_FLAG);
    c.clipRect(mClipFrame);//在mFrame区域中保持mClipFrame的区域不变,参考下句注释
    c.drawRect(mFrame, mBatteryPaint);//在mFrame中,把除去mClipFrame的区域绘制。(电量状态,如果是15以下,就是红色。15以上是白色)
    c.restore();

    if (tracker.plugged) { //这里就犀利了。这是说插上充电器就画一个闪电
        // draw the bolt
        final float bl = mFrame.left + mFrame.width() / 4.5f;
        final float bt = mFrame.top + mFrame.height() / 6f;
        final float br = mFrame.right - mFrame.width() / 7f;
        final float bb = mFrame.bottom - mFrame.height() / 10f;
        if (mBoltFrame.left != bl || mBoltFrame.top != bt
                || mBoltFrame.right != br || mBoltFrame.bottom != bb) {
            mBoltFrame.set(bl, bt, br, bb);
            mBoltPath.reset();
            mBoltPath.moveTo(
                    mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
                    mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
            for (int i = 2; i < mBoltPoints.length; i += 2) {
                mBoltPath.lineTo(
                        mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(),
                        mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height());
            }
            mBoltPath.lineTo(
                    mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
                    mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
        }
        c.drawPath(mBoltPath, mBoltPaint);
    } else if (level <= EMPTY) { //这个是空电量的时候,画一个红色的感叹号
        final float x = mWidth * 0.5f;
        final float y = (mHeight + mWarningTextHeight) * 0.48f;
        c.drawText(mWarningString, x, y, mWarningTextPaint);
    } else if (mShowPercent && !(tracker.level == 100 && !SHOW_100_PERCENT)) { //以文字显示电量。不过这个没有用到(除非mShowPercent变量获取的配置属性为true)
        mTextPaint.setTextSize(height *
                (SINGLE_DIGIT_PERCENT ? 0.75f
                        : (tracker.level == 100 ? 0.38f : 0.5f)));
        mTextHeight = -mTextPaint.getFontMetrics().ascent;

        final String str = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level);
        final float x = mWidth * 0.5f;
        final float y = (mHeight + mTextHeight) * 0.47f;
        c.drawText(str,
                x,
                y,
                mTextPaint);
    }
}

private boolean mDemoMode;
private BatteryTracker mDemoTracker = new BatteryTracker();

@Override
public void dispatchDemoCommand(String command, Bundle args) {
    if (!mDemoMode && command.equals(COMMAND_ENTER)) {
        mDemoMode = true;
        mDemoTracker.level = mTracker.level;
        mDemoTracker.plugged = mTracker.plugged;
    } else if (mDemoMode && command.equals(COMMAND_EXIT)) {
        mDemoMode = false;
        postInvalidate();
    } else if (mDemoMode && command.equals(COMMAND_BATTERY)) {
       String level = args.getString("level");
       String plugged = args.getString("plugged");
       if (level != null) {
           mDemoTracker.level = Math.min(Math.max(Integer.parseInt(level), 0), 100);
       }
       if (plugged != null) {
           mDemoTracker.plugged = Boolean.parseBoolean(plugged);
       }
       postInvalidate();
    }
}

}

总结:这个类就是绘制状态栏里面电量图标的自定义控件。

以上就是大概的逻辑,现总结,用于备忘