FileDocCategorySizeDatePackage
BatteryService.javaAPI DocAndroid 1.5 API17540Wed May 06 22:42:00 BST 2009com.android.server

BatteryService

public class BatteryService extends android.os.Binder

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"

Fields Summary
private static final String
TAG
private static final boolean
LOCAL_LOGV
static final int
LOG_BATTERY_LEVEL
static final int
LOG_BATTERY_STATUS
static final int
LOG_BATTERY_DISCHARGE_STATUS
static final int
BATTERY_SCALE
private static final int
CRITICAL_BATTERY_LEVEL
private static final int
DUMP_MAX_LENGTH
private static final String[]
DUMPSYS_ARGS
private static final String
BATTERY_STATS_SERVICE_NAME
private static final String
DUMPSYS_DATA_PATH
private static final int
BATTERY_PLUGGED_NONE
private final android.content.Context
mContext
private final com.android.internal.app.IBatteryStats
mBatteryStats
private boolean
mAcOnline
private boolean
mUsbOnline
private int
mBatteryStatus
private int
mBatteryHealth
private boolean
mBatteryPresent
private int
mBatteryLevel
private int
mBatteryVoltage
private int
mBatteryTemperature
private String
mBatteryTechnology
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
mPlugType
private int
mLastPlugType
private long
mDischargeStartTime
private int
mDischargeStartLevel
private android.os.UEventObserver
mUEventObserver
Constructors Summary
public BatteryService(android.content.Context context)

    
    
       
        mContext = context;
        mBatteryStats = BatteryStatsService.getService();

        mUEventObserver.startObserving("SUBSYSTEM=power_supply");

        // set initial status
        update();
    
Methods Summary
protected voiddump(java.io.FileDescriptor fd, java.io.PrintWriter pw, java.lang.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 (this) {
            pw.println("Current Battery Service state:");
            pw.println("  AC powered: " + mAcOnline);
            pw.println("  USB powered: " + mUsbOnline);
            pw.println("  status: " + mBatteryStatus);
            pw.println("  health: " + mBatteryHealth);
            pw.println("  present: " + mBatteryPresent);
            pw.println("  level: " + mBatteryLevel);
            pw.println("  scale: " + BATTERY_SCALE);
            pw.println("  voltage:" + mBatteryVoltage);
            pw.println("  temperature: " + mBatteryTemperature);
            pw.println("  technology: " + mBatteryTechnology);
        }
    
final intgetBatteryLevel()


    // returns battery level as a percentage
       
        return mBatteryLevel;
    
private final intgetIcon(int level)

        if (mBatteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {
            return com.android.internal.R.drawable.stat_sys_battery_charge;
        } else if (mBatteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING ||
                mBatteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING ||
                mBatteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
            return com.android.internal.R.drawable.stat_sys_battery;
        } else {
            return com.android.internal.R.drawable.stat_sys_battery_unknown;
        }
    
final intgetPlugType()

        return mPlugType;
    
final booleanisPowered()

        // assume we are powered if battery state is unknown so the "stay on while plugged in" option will work.
        return (mAcOnline || mUsbOnline || mBatteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN);
    
final booleanisPowered(int plugTypeSet)

        // assume we are powered if battery state is unknown so
        // the "stay on while plugged in" option will work.
        if (mBatteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
            return true;
        }
        if (plugTypeSet == 0) {
            return false;
        }
        int plugTypeBit = 0;
        if (mAcOnline) {
            plugTypeBit |= BatteryManager.BATTERY_PLUGGED_AC;
        }
        if (mUsbOnline) {
            plugTypeBit |= BatteryManager.BATTERY_PLUGGED_USB;
        }
        return (plugTypeSet & plugTypeBit) != 0;
    
private final voidlogBatteryStats()

        
        IBinder batteryInfoService = ServiceManager.getService(BATTERY_STATS_SERVICE_NAME);
        if (batteryInfoService != null) {
            byte[] buffer = new byte[DUMP_MAX_LENGTH];
            File dumpFile = null;
            FileOutputStream dumpStream = null;
            try {
                // dump the service to a file
                dumpFile = new File(DUMPSYS_DATA_PATH + BATTERY_STATS_SERVICE_NAME + ".dump");
                dumpStream = new FileOutputStream(dumpFile);
                batteryInfoService.dump(dumpStream.getFD(), DUMPSYS_ARGS);
                dumpStream.getFD().sync();

                // read dumped file above into buffer truncated to DUMP_MAX_LENGTH
                // and insert into events table.
                int length = (int) Math.min(dumpFile.length(), DUMP_MAX_LENGTH);
                FileInputStream fileInputStream = new FileInputStream(dumpFile);
                int nread = fileInputStream.read(buffer, 0, length);
                if (nread > 0) {
                    Checkin.logEvent(mContext.getContentResolver(), 
                            Checkin.Events.Tag.BATTERY_DISCHARGE_INFO, 
                            new String(buffer, 0, nread));
                    if (LOCAL_LOGV) Log.v(TAG, "dumped " + nread + "b from " + 
                            batteryInfoService + "to log");
                    if (LOCAL_LOGV) Log.v(TAG, "actual dump:" + new String(buffer, 0, nread));
                }
            } catch (RemoteException e) {
                Log.e(TAG, "failed to dump service '" + BATTERY_STATS_SERVICE_NAME + 
                        "':" + e);
            } catch (IOException e) {
                Log.e(TAG, "failed to write dumpsys file: " +  e);
            } finally {
                // make sure we clean up
                if (dumpStream != null) {
                    try {
                        dumpStream.close();
                    } catch (IOException e) {
                        Log.e(TAG, "failed to close dumpsys output stream");
                    }
                }
                if (dumpFile != null && !dumpFile.delete()) {
                    Log.e(TAG, "failed to delete temporary dumpsys file: "
                            + dumpFile.getAbsolutePath());
                }
            }
        }
    
private final voidlogOutlier(long duration)

        ContentResolver cr = mContext.getContentResolver();
        String dischargeThresholdString = Settings.Gservices.getString(cr,
                Settings.Gservices.BATTERY_DISCHARGE_THRESHOLD);
        String durationThresholdString = Settings.Gservices.getString(cr,
                Settings.Gservices.BATTERY_DISCHARGE_DURATION_THRESHOLD);
        
        if (dischargeThresholdString != null && durationThresholdString != null) {
            try {
                long durationThreshold = Long.parseLong(durationThresholdString);
                int dischargeThreshold = Integer.parseInt(dischargeThresholdString);
                if (duration <= durationThreshold && 
                        mDischargeStartLevel - mBatteryLevel >= dischargeThreshold) {
                    // If the discharge cycle is bad enough we want to know about it.
                    logBatteryStats();
                }
                if (LOCAL_LOGV) Log.v(TAG, "duration threshold: " + durationThreshold + 
                        " discharge threshold: " + dischargeThreshold);
                if (LOCAL_LOGV) Log.v(TAG, "duration: " + duration + " discharge: " + 
                        (mDischargeStartLevel - mBatteryLevel));
            } catch (NumberFormatException e) {
                Log.e(TAG, "Invalid DischargeThresholds GService string: " + 
                        durationThresholdString + " or " + dischargeThresholdString);
                return;
            }
        }
    
private native voidnative_update()

private final voidsendIntent()

        //  Pack up the values and broadcast them to everyone
        Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
        try {
            mBatteryStats.setOnBattery(mPlugType == BATTERY_PLUGGED_NONE, mBatteryLevel);
        } catch (RemoteException e) {
            // Should never happen.
        }
        
        int icon = getIcon(mBatteryLevel);

        intent.putExtra("status", mBatteryStatus);
        intent.putExtra("health", mBatteryHealth);
        intent.putExtra("present", mBatteryPresent);
        intent.putExtra("level", mBatteryLevel);
        intent.putExtra("scale", BATTERY_SCALE);
        intent.putExtra("icon-small", icon);
        intent.putExtra("plugged", mPlugType);
        intent.putExtra("voltage", mBatteryVoltage);
        intent.putExtra("temperature", mBatteryTemperature);
        intent.putExtra("technology", mBatteryTechnology);

        if (false) {
            Log.d(TAG, "updateBattery level:" + mBatteryLevel +
                    " scale:" + BATTERY_SCALE + " status:" + mBatteryStatus + 
                    " health:" + mBatteryHealth +  " present:" + mBatteryPresent + 
                    " voltage: " + mBatteryVoltage +
                    " temperature: " + mBatteryTemperature +
                    " technology: " + mBatteryTechnology +
                    " AC powered:" + mAcOnline + " USB powered:" + mUsbOnline +
                    " icon:" + icon );
        }

        ActivityManagerNative.broadcastStickyIntent(intent, null);
    
private final synchronized voidupdate()

        native_update();

        boolean logOutlier = false;
        long dischargeDuration = 0;
        
        mBatteryLevelCritical = mBatteryLevel <= CRITICAL_BATTERY_LEVEL;
        if (mAcOnline) {
            mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
        } else if (mUsbOnline) {
            mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
        } else {
            mPlugType = BATTERY_PLUGGED_NONE;
        }
        if (mBatteryStatus != mLastBatteryStatus ||
                mBatteryHealth != mLastBatteryHealth ||
                mBatteryPresent != mLastBatteryPresent ||
                mBatteryLevel != mLastBatteryLevel ||
                mPlugType != mLastPlugType ||
                mBatteryVoltage != mLastBatteryVoltage ||
                mBatteryTemperature != mLastBatteryTemperature) {
            
            if (mPlugType != mLastPlugType) {
                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 != mBatteryLevel) {
                        dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
                        logOutlier = true;
                        EventLog.writeEvent(LOG_BATTERY_DISCHARGE_STATUS, dischargeDuration,
                                mDischargeStartLevel, mBatteryLevel);
                        // make sure we see a discharge event before logging again
                        mDischargeStartTime = 0; 
                    }
                } else if (mPlugType == BATTERY_PLUGGED_NONE) {
                    // charging -> discharging or we just powered up
                    mDischargeStartTime = SystemClock.elapsedRealtime();
                    mDischargeStartLevel = mBatteryLevel;
                }
            }
            if (mBatteryStatus != mLastBatteryStatus ||
                    mBatteryHealth != mLastBatteryHealth ||
                    mBatteryPresent != mLastBatteryPresent ||
                    mPlugType != mLastPlugType) {
                EventLog.writeEvent(LOG_BATTERY_STATUS,
                        mBatteryStatus, mBatteryHealth, mBatteryPresent ? 1 : 0,
                        mPlugType, mBatteryTechnology);
            }
            if (mBatteryLevel != mLastBatteryLevel ||
                    mBatteryVoltage != mLastBatteryVoltage ||
                    mBatteryTemperature != mLastBatteryTemperature) {
                EventLog.writeEvent(LOG_BATTERY_LEVEL,
                        mBatteryLevel, mBatteryVoltage, mBatteryTemperature);
            }
            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;
            }
            
            mLastBatteryStatus = mBatteryStatus;
            mLastBatteryHealth = mBatteryHealth;
            mLastBatteryPresent = mBatteryPresent;
            mLastBatteryLevel = mBatteryLevel;
            mLastPlugType = mPlugType;
            mLastBatteryVoltage = mBatteryVoltage;
            mLastBatteryTemperature = mBatteryTemperature;
            mLastBatteryLevelCritical = mBatteryLevelCritical;
            
            sendIntent();
            
            // This needs to be done after sendIntent() so that we get the lastest battery stats.
            if (logOutlier && dischargeDuration != 0) {
                logOutlier(dischargeDuration);
            }
        }