android 4.4 电池电量显示分析(低电量提醒与电池图标)Java 层

2019-04-13 15:37发布

    最近工作接触到这么的东西,这是我对整个电池管理方面Java 层的分析。如果想了解底层的话,请看我的博客:
android 4.4 电池电量管理底层分析(CC++层) (http://blog.csdn.net/daweibalang717/article/details/41446993)
先贴一张类与类之间的关系图:
    android开机过程中会加载系统BatteryService ,说一下电池电量相关的,本文主要讲述关于JAVA 层代码。文件路径:frameworksaseservicesjavacomandroidserverBatteryService.java   下面贴出源码。我把注释加上。个人理解,仅参考。  /* * Copyright (C) 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 "); pw.println(" reset"); } } } private final UEventObserver mInvalidChargerObserver = new UEventObserver() { @Override public void onUEvent(UEventObserver.UEvent event) { final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0; synchronized (mLock) { if (mInvalidCharger != invalidCharger) { mInvalidCharger = invalidCharger; } } } }; private final class Led { private final LightsService.Light mBatteryLight; private final int mBatteryLowARGB; private final int mBatteryMediumARGB; private final int mBatteryFullARGB; private final int mBatteryLedOn; private final int mBatteryLedOff; public Led(Context context, LightsService lights) { mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY); mBatteryLowARGB = context.getResources().getInteger( com.android.internal.R.integer.config_notificationsBatteryLowARGB); mBatteryMediumARGB = context.getResources().getInteger( com.android.internal.R.integer.config_notificationsBatteryMediumARGB); mBatteryFullARGB = context.getResources().getInteger( com.android.internal.R.integer.config_notificationsBatteryFullARGB); mBatteryLedOn = context.getResources().getInteger( com.android.internal.R.integer.config_notificationsBatteryLedOn); mBatteryLedOff = context.getResources().getInteger( com.android.internal.R.integer.config_notificationsBatteryLedOff); } /** * Synchronize on BatteryService. */ public void updateLightsLocked() { final int level = mBatteryProps.batteryLevel; final int status = mBatteryProps.batteryStatus; if (level < mLowBatteryWarningLevel) { if (status == BatteryManager.BATTERY_STATUS_CHARGING) { // Solid red when battery is charging mBatteryLight.setColor(mBatteryLowARGB); } else { // Flash red when battery is low and not charging mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED, mBatteryLedOn, mBatteryLedOff); } } else if (status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL) { if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) { // Solid green when full or charging and nearly full mBatteryLight.setColor(mBatteryFullARGB); } else { // Solid orange when charging and halfway full mBatteryLight.setColor(mBatteryMediumARGB); } } else { // No lights if not charging and not low mBatteryLight.turnOff(); } } } private final class BatteryListener extends IBatteryPropertiesListener.Stub { public void batteryPropertiesChanged(BatteryProperties props) { BatteryService.this.update(props); } } }



总结如下:此服务构造时会注册监听到系统JNI层。 当电池电量改变的时候会调用update(BatteryProperties props) -----》processValuesLocked() 。 而processValuesLocked() 函数会把电池状态把广播发送出去。其他类再接收广播进行处理 下一个类就是低电量提醒了,文件目录:frameworksasepackagesSystemUIsrccomandroidsystemuipowerPowerUI.java /* * Copyright (C) 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(); //这里哈,低电量提醒的弹窗 // 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))); } } 总结一下:这个类就是接收电池电量改变的广播,然后弹窗提醒。 下一个类就是没电关机界面了,文件目录:frameworksaseservicesjavacomandroidserverShutdownLowBatteryActivity.java 这个Activity的配置文件是这样的(frameworksasecore esAndroidManifest.xml):
android:theme="@android:style/Theme.Translucent.NoTitleBar"API小于11的透明主题。这样弹出的dialog会是老版本的主题。(这个Activity是透明的) 对于Theme,因为是4.4,我更倾向于:android:theme="@android:style/Theme.Holo.Panel"
ShutdownLowBatteryActivity.java: /* * Copyright (C) 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){ } } }); } } 总结如下:这个类就是弹窗计时,提醒不连充电器就要关机了。

最后一个相关的类,文件路径:frameworksasepackagesSystemUIsrccomandroidsystemuiBatteryMeterView.java 这个类是自定义控件,定义在手机状态栏里: /* * Copyright (C) 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 fina