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

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

android 4.4 电池电量管理底层分析(C\C++层) (http://blog.csdn.net/daweibalang717/article/details/41446993)

先贴一张类与类之间的关系图:

android 4.4 电池电量显示分析(低电量提醒与电池图标)Java 层_第1张图片

    android开机过程中会加载系统BatteryService ,说一下电池电量相关的,本文主要讲述关于JAVA 层代码。文件路径:\frameworks\base\services\java\com\android\server\BatteryService.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() 函数会把电池状态把广播发送出去。其他类再接收广播进行处理

下一个类就是低电量提醒了,文件目录:\frameworks\base\packages\SystemUI\src\com\android\systemui\power\PowerUI.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)));
    }
}

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

android 4.4 电池电量显示分析(低电量提醒与电池图标)Java 层_第2张图片

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

这个Activity的配置文件是这样的(\frameworks\base\core\res\AndroidManifest.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){
                	
                }
            }
        });
    }
}
总结如下:这个类就是弹窗计时,提醒不连充电器就要关机了。

最后一个相关的类,文件路径:\frameworks\base\packages\SystemUI\src\com\android\systemui\BatteryMeterView.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 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= 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();
        }
    }
}

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


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

你可能感兴趣的:(android,系统源码分析)