转载地址: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 runprivate 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 existsif (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 againmDischargeStartTime = 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() {@Overridepublic 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() {@Overridepublic 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 LEDmLed.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() {@Overridepublic 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 filedumpFile = 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 boxdb.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 upif (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() {@Overridepublic 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 chargingmBatteryLight.setColor(mBatteryLowARGB);} else {// Flash red when battery is low and not chargingmBatteryLight.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 fullmBatteryLight.setColor(mBatteryFullARGB);} else {// Solid orange when charging and halfway fullmBatteryLight.setColor(mBatteryMediumARGB);}} else {// No lights if not charging and not lowmBatteryLight.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( //低电量提醒 15com.android.internal.R.integer.config_lowBatteryWarningLevel);mLowBatteryReminderLevels[1] = mContext.getResources().getInteger( //低电量临界值 4com.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() { //重点,广播接收处理@Overridepublic 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 batteryreturn;}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 changesif (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() {@Overridepublic void onClick(DialogInterface dialog, int which) {mContext.startActivityAsUser(intent, UserHandle.CURRENT);dismissLowBatteryWarning();}});}AlertDialog d = b.create();d.setOnDismissListener(new DialogInterface.OnDismissListener() {@Overridepublic 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() { //这里数秒关机@Overridepublic 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() {@Overridepublic 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() { //如果正数秒呢,电话呼入了。取消自动关机@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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 edgesint[] 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 statusint level = UNKNOWN_LEVEL;String percentStr;int plugType;boolean plugged;int health;int status;String technology;int voltage;int temperature;boolean testmode = false;@Overridepublic 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);@Overridepublic 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 levelmTracker.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 shapec.drawRect(mFrame, mFramePaint);// fill 'er upfinal 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 boltfinal 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();}
}

}

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

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

android 4.4 batteryservice 电池电量显示分析相关推荐

  1. Android自定义View之电池电量显示

    自定义简单的电池电量显示.话不多说,直接上代码 package com.kimascend.thermometer.customview; import android.content.Context ...

  2. android 自定义View绘制电池电量(电池内带数字显示)

    最新公司需要一个电池内带数字的显示电池电量需求,百度了一下.参考下面这篇文章写的Android自定义View之电池电量显示. 增加了里面电池电量数字显示,还有就是一个屏幕适配.不管屏幕分辨率基本都能适 ...

  3. Android系统移除电池电量监测管理功能

    系统优化 - 去除电池电量监测管理功能 去除电池电量监测管理功能,去除电量提示功能. 需要去除电池电量对升级功能的影响,如低电量时无法进行系统升级. 车机平台不需要电池电量监测管理,电池管理模块对界面 ...

  4. 黑苹果中使用Config Configurator工具修复一些声卡和电池电量显示问题、隐藏多余引导、啰嗦模式

    黑苹果中使用Config Configurator工具修复一些声卡和电池电量显示问题.隐藏多余引导.啰嗦模式 好多人安装黑苹果之后没有声音.电池显示没有.引导太多.有啰嗦模式 我这里出了教程解决这些问 ...

  5. Qt(八)——电池电量显示实现

    Qt(八)--电池电量显示实现 目录 1.电池电量ui界面设计 2.订阅电量话题 3.回调函数中发布自定义信号 4.连接信号 5.槽函数更新ui显示 1.电池电量ui界面设计 2.订阅电量话题 3.回 ...

  6. Android自定义电池电量显示组件(kotlin,java)

    最近产品研发需求需要显示在线设备的电池电量状态,然后UI给出的效果的图是这样的 于是就开始了自定义个,因为是项目特定的UI所以很多属性都没有直接抽取到styles里面了,直接上代码(因为项目是使用ko ...

  7. android 电池电量显示不正常,vivo电量显示不正常怎么解决?vivo手机电量校准教程...

    vivo手机用了一段时间会发现,手机电量显示会出现不准确的情况,明明刚充满电不到10分钟,仅剩50%电量:或者充电一晚上,电量仍然显示为70%,无法充满.小伙伴们稍安勿躁,这并不是电池本身出了问题,而 ...

  8. MTK GM3.0 关于电池电量显示流程

    一.客户自查 电量相关问题,请先排查如下几点: 1.check hardware schematic 硬件设计对电量计算有极大影响,所以务必确认如下两项: (1)ISENSE/BATSNS硬件连接是否 ...

  9. lvgl 电池电量显示

    #include "lvgl/lvgl.h"#define OUTLINE_W 50 //电池图标宽度 #define OUTLINE_H 25 //电池图标高度void lv_a ...

最新文章

  1. 2060 : Minsum Plus(贪心)
  2. 科普 | CPU 是如何工作的?
  3. 2021-10-28 SAP Spartacus SSR 性能方面的一些学习笔记
  4. 您可能没有注意到的7个Ubuntu File Manager功能
  5. docker 容器启动顺序_Docker容器启动时初始化Mysql数据库
  6. ElasticSearch(笔记)
  7. c语言学习-编写函数求x的n次方的值
  8. 除了love和hate,还能怎么表达那些年的“爱恨情仇”?
  9. MySQL之 分库分表
  10. python自学行吗-python能够自学吗
  11. 企业大数据营销需要什么思路
  12. Ros安装过程及sudo rosdep init失败解决方法
  13. 潍坊首个小学“教育创客空间”落户呼家庄小学 萝卜(创客)教育走进小学课堂...
  14. python pickle反序列化漏洞_渗透测试 - 黑客技术 | 【技术分享】记CTF比赛中发现的Python反序列化漏洞_吾爱漏洞...
  15. c语言连续生成不同随机数_【转】关于C语言生成不重复的随机数
  16. 全文索引的使用(二)--使用同义词库
  17. 魔方机器人机械部分_通过强化学习解决第2部分的魔方
  18. 修改网络设备在路由器中显示名称
  19. PE安装Win10纯净版教程【附Win10企业版/专业版/64/32位系统下载地址以及系统激活工具和解压软件安装包】
  20. CodeWars刷题练习

热门文章

  1. IDEA中使用Git功能和IDEA中的Git分支管理
  2. Domain Adaptation for Object Detection using SE Adaptors and Center Loss 论文翻译
  3. Python数据库篇
  4. 论文笔记|固定效应的解释和使用
  5. 大量的Oracle数据库视频教程提供下载
  6. metis 多线程图划分论文笔记
  7. Linux之命令scp远程拷贝文件
  8. 什么浏览器有html控制台,HTML标记突破出现在浏览器br,也可作为br,开发者控制台...
  9. [ Azure | Az-900 ] 基础知识点总结(二) - 核心组件服务
  10. 语音识别基本原理学习