Home | History | Annotate | Download | only in server
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server;
     18 
     19 import com.android.internal.app.IBatteryStats;
     20 import com.android.server.am.BatteryStatsService;
     21 
     22 import android.app.ActivityManagerNative;
     23 import android.content.ContentResolver;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.content.pm.PackageManager;
     27 import android.os.BatteryManager;
     28 import android.os.Binder;
     29 import android.os.FileUtils;
     30 import android.os.Handler;
     31 import android.os.IBinder;
     32 import android.os.DropBoxManager;
     33 import android.os.RemoteException;
     34 import android.os.ServiceManager;
     35 import android.os.SystemClock;
     36 import android.os.UEventObserver;
     37 import android.os.UserHandle;
     38 import android.provider.Settings;
     39 import android.util.EventLog;
     40 import android.util.Slog;
     41 
     42 import java.io.File;
     43 import java.io.FileDescriptor;
     44 import java.io.FileOutputStream;
     45 import java.io.IOException;
     46 import java.io.PrintWriter;
     47 
     48 
     49 /**
     50  * <p>BatteryService monitors the charging status, and charge level of the device
     51  * battery.  When these values change this service broadcasts the new values
     52  * to all {@link android.content.BroadcastReceiver IntentReceivers} that are
     53  * watching the {@link android.content.Intent#ACTION_BATTERY_CHANGED
     54  * BATTERY_CHANGED} action.</p>
     55  * <p>The new values are stored in the Intent data and can be retrieved by
     56  * calling {@link android.content.Intent#getExtra Intent.getExtra} with the
     57  * following keys:</p>
     58  * <p>&quot;scale&quot; - int, the maximum value for the charge level</p>
     59  * <p>&quot;level&quot; - int, charge level, from 0 through &quot;scale&quot; inclusive</p>
     60  * <p>&quot;status&quot; - String, the current charging status.<br />
     61  * <p>&quot;health&quot; - String, the current battery health.<br />
     62  * <p>&quot;present&quot; - boolean, true if the battery is present<br />
     63  * <p>&quot;icon-small&quot; - int, suggested small icon to use for this state</p>
     64  * <p>&quot;plugged&quot; - int, 0 if the device is not plugged in; 1 if plugged
     65  * into an AC power adapter; 2 if plugged in via USB.</p>
     66  * <p>&quot;voltage&quot; - int, current battery voltage in millivolts</p>
     67  * <p>&quot;temperature&quot; - int, current battery temperature in tenths of
     68  * a degree Centigrade</p>
     69  * <p>&quot;technology&quot; - String, the type of battery installed, e.g. "Li-ion"</p>
     70  *
     71  * <p>
     72  * The battery service may be called by the power manager while holding its locks so
     73  * we take care to post all outcalls into the activity manager to a handler.
     74  *
     75  * FIXME: Ideally the power manager would perform all of its calls into the battery
     76  * service asynchronously itself.
     77  * </p>
     78  */
     79 public final class BatteryService extends Binder {
     80     private static final String TAG = BatteryService.class.getSimpleName();
     81 
     82     private static final boolean DEBUG = false;
     83 
     84     private static final int BATTERY_SCALE = 100;    // battery capacity is a percentage
     85 
     86     // Used locally for determining when to make a last ditch effort to log
     87     // discharge stats before the device dies.
     88     private int mCriticalBatteryLevel;
     89 
     90     private static final int DUMP_MAX_LENGTH = 24 * 1024;
     91     private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "-u" };
     92     private static final String BATTERY_STATS_SERVICE_NAME = "batteryinfo";
     93 
     94     private static final String DUMPSYS_DATA_PATH = "/data/system/";
     95 
     96     // This should probably be exposed in the API, though it's not critical
     97     private static final int BATTERY_PLUGGED_NONE = 0;
     98 
     99     private final Context mContext;
    100     private final IBatteryStats mBatteryStats;
    101     private final Handler mHandler;
    102 
    103     private final Object mLock = new Object();
    104 
    105     /* Begin native fields: All of these fields are set by native code. */
    106     private boolean mAcOnline;
    107     private boolean mUsbOnline;
    108     private boolean mWirelessOnline;
    109     private int mBatteryStatus;
    110     private int mBatteryHealth;
    111     private boolean mBatteryPresent;
    112     private int mBatteryLevel;
    113     private int mBatteryVoltage;
    114     private int mBatteryTemperature;
    115     private String mBatteryTechnology;
    116     private boolean mBatteryLevelCritical;
    117     /* End native fields. */
    118 
    119     private int mLastBatteryStatus;
    120     private int mLastBatteryHealth;
    121     private boolean mLastBatteryPresent;
    122     private int mLastBatteryLevel;
    123     private int mLastBatteryVoltage;
    124     private int mLastBatteryTemperature;
    125     private boolean mLastBatteryLevelCritical;
    126 
    127     private int mInvalidCharger;
    128     private int mLastInvalidCharger;
    129 
    130     private int mLowBatteryWarningLevel;
    131     private int mLowBatteryCloseWarningLevel;
    132     private int mShutdownBatteryTemperature;
    133 
    134     private int mPlugType;
    135     private int mLastPlugType = -1; // Extra state so we can detect first run
    136 
    137     private long mDischargeStartTime;
    138     private int mDischargeStartLevel;
    139 
    140     private boolean mUpdatesStopped;
    141 
    142     private Led mLed;
    143 
    144     private boolean mSentLowBatteryBroadcast = false;
    145 
    146     private native void native_update();
    147 
    148     public BatteryService(Context context, LightsService lights) {
    149         mContext = context;
    150         mHandler = new Handler(true /*async*/);
    151         mLed = new Led(context, lights);
    152         mBatteryStats = BatteryStatsService.getService();
    153 
    154         mCriticalBatteryLevel = mContext.getResources().getInteger(
    155                 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
    156         mLowBatteryWarningLevel = mContext.getResources().getInteger(
    157                 com.android.internal.R.integer.config_lowBatteryWarningLevel);
    158         mLowBatteryCloseWarningLevel = mContext.getResources().getInteger(
    159                 com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
    160         mShutdownBatteryTemperature = mContext.getResources().getInteger(
    161                 com.android.internal.R.integer.config_shutdownBatteryTemperature);
    162 
    163         mPowerSupplyObserver.startObserving("SUBSYSTEM=power_supply");
    164 
    165         // watch for invalid charger messages if the invalid_charger switch exists
    166         if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {
    167             mInvalidChargerObserver.startObserving(
    168                     "DEVPATH=/devices/virtual/switch/invalid_charger");
    169         }
    170 
    171         // set initial status
    172         synchronized (mLock) {
    173             updateLocked();
    174         }
    175     }
    176 
    177     void systemReady() {
    178         // check our power situation now that it is safe to display the shutdown dialog.
    179         synchronized (mLock) {
    180             shutdownIfNoPowerLocked();
    181             shutdownIfOverTempLocked();
    182         }
    183     }
    184 
    185     /**
    186      * Returns true if the device is plugged into any of the specified plug types.
    187      */
    188     public boolean isPowered(int plugTypeSet) {
    189         synchronized (mLock) {
    190             return isPoweredLocked(plugTypeSet);
    191         }
    192     }
    193 
    194     private boolean isPoweredLocked(int plugTypeSet) {
    195         // assume we are powered if battery state is unknown so
    196         // the "stay on while plugged in" option will work.
    197         if (mBatteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
    198             return true;
    199         }
    200         if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0 && mAcOnline) {
    201             return true;
    202         }
    203         if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0 && mUsbOnline) {
    204             return true;
    205         }
    206         if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 && mWirelessOnline) {
    207             return true;
    208         }
    209         return false;
    210     }
    211 
    212     /**
    213      * Returns the current plug type.
    214      */
    215     public int getPlugType() {
    216         synchronized (mLock) {
    217             return mPlugType;
    218         }
    219     }
    220 
    221     /**
    222      * Returns battery level as a percentage.
    223      */
    224     public int getBatteryLevel() {
    225         synchronized (mLock) {
    226             return mBatteryLevel;
    227         }
    228     }
    229 
    230     /**
    231      * Returns true if battery level is below the first warning threshold.
    232      */
    233     public boolean isBatteryLow() {
    234         synchronized (mLock) {
    235             return mBatteryPresent && mBatteryLevel <= mLowBatteryWarningLevel;
    236         }
    237     }
    238 
    239     /**
    240      * Returns a non-zero value if an  unsupported charger is attached.
    241      */
    242     public int getInvalidCharger() {
    243         synchronized (mLock) {
    244             return mInvalidCharger;
    245         }
    246     }
    247 
    248     private void shutdownIfNoPowerLocked() {
    249         // shut down gracefully if our battery is critically low and we are not powered.
    250         // wait until the system has booted before attempting to display the shutdown dialog.
    251         if (mBatteryLevel == 0 && !isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)) {
    252             mHandler.post(new Runnable() {
    253                 @Override
    254                 public void run() {
    255                     if (ActivityManagerNative.isSystemReady()) {
    256                         Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
    257                         intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
    258                         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    259                         mContext.startActivityAsUser(intent, UserHandle.CURRENT);
    260                     }
    261                 }
    262             });
    263         }
    264     }
    265 
    266     private void shutdownIfOverTempLocked() {
    267         // shut down gracefully if temperature is too high (> 68.0C by default)
    268         // wait until the system has booted before attempting to display the
    269         // shutdown dialog.
    270         if (mBatteryTemperature > mShutdownBatteryTemperature) {
    271             mHandler.post(new Runnable() {
    272                 @Override
    273                 public void run() {
    274                     if (ActivityManagerNative.isSystemReady()) {
    275                         Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
    276                         intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
    277                         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    278                         mContext.startActivityAsUser(intent, UserHandle.CURRENT);
    279                     }
    280                 }
    281             });
    282         }
    283     }
    284 
    285     private void updateLocked() {
    286         if (!mUpdatesStopped) {
    287             // Update the values of mAcOnline, et. all.
    288             native_update();
    289 
    290             // Process the new values.
    291             processValuesLocked();
    292         }
    293     }
    294 
    295     private void processValuesLocked() {
    296         boolean logOutlier = false;
    297         long dischargeDuration = 0;
    298 
    299         mBatteryLevelCritical = (mBatteryLevel <= mCriticalBatteryLevel);
    300         if (mAcOnline) {
    301             mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
    302         } else if (mUsbOnline) {
    303             mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
    304         } else if (mWirelessOnline) {
    305             mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
    306         } else {
    307             mPlugType = BATTERY_PLUGGED_NONE;
    308         }
    309 
    310         if (DEBUG) {
    311             Slog.d(TAG, "Processing new values: "
    312                     + "mAcOnline=" + mAcOnline
    313                     + ", mUsbOnline=" + mUsbOnline
    314                     + ", mWirelessOnline=" + mWirelessOnline
    315                     + ", mBatteryStatus=" + mBatteryStatus
    316                     + ", mBatteryHealth=" + mBatteryHealth
    317                     + ", mBatteryPresent=" + mBatteryPresent
    318                     + ", mBatteryLevel=" + mBatteryLevel
    319                     + ", mBatteryTechnology=" + mBatteryTechnology
    320                     + ", mBatteryVoltage=" + mBatteryVoltage
    321                     + ", mBatteryTemperature=" + mBatteryTemperature
    322                     + ", mBatteryLevelCritical=" + mBatteryLevelCritical
    323                     + ", mPlugType=" + mPlugType);
    324         }
    325 
    326         // Let the battery stats keep track of the current level.
    327         try {
    328             mBatteryStats.setBatteryState(mBatteryStatus, mBatteryHealth,
    329                     mPlugType, mBatteryLevel, mBatteryTemperature,
    330                     mBatteryVoltage);
    331         } catch (RemoteException e) {
    332             // Should never happen.
    333         }
    334 
    335         shutdownIfNoPowerLocked();
    336         shutdownIfOverTempLocked();
    337 
    338         if (mBatteryStatus != mLastBatteryStatus ||
    339                 mBatteryHealth != mLastBatteryHealth ||
    340                 mBatteryPresent != mLastBatteryPresent ||
    341                 mBatteryLevel != mLastBatteryLevel ||
    342                 mPlugType != mLastPlugType ||
    343                 mBatteryVoltage != mLastBatteryVoltage ||
    344                 mBatteryTemperature != mLastBatteryTemperature ||
    345                 mInvalidCharger != mLastInvalidCharger) {
    346 
    347             if (mPlugType != mLastPlugType) {
    348                 if (mLastPlugType == BATTERY_PLUGGED_NONE) {
    349                     // discharging -> charging
    350 
    351                     // There's no value in this data unless we've discharged at least once and the
    352                     // battery level has changed; so don't log until it does.
    353                     if (mDischargeStartTime != 0 && mDischargeStartLevel != mBatteryLevel) {
    354                         dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
    355                         logOutlier = true;
    356                         EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,
    357                                 mDischargeStartLevel, mBatteryLevel);
    358                         // make sure we see a discharge event before logging again
    359                         mDischargeStartTime = 0;
    360                     }
    361                 } else if (mPlugType == BATTERY_PLUGGED_NONE) {
    362                     // charging -> discharging or we just powered up
    363                     mDischargeStartTime = SystemClock.elapsedRealtime();
    364                     mDischargeStartLevel = mBatteryLevel;
    365                 }
    366             }
    367             if (mBatteryStatus != mLastBatteryStatus ||
    368                     mBatteryHealth != mLastBatteryHealth ||
    369                     mBatteryPresent != mLastBatteryPresent ||
    370                     mPlugType != mLastPlugType) {
    371                 EventLog.writeEvent(EventLogTags.BATTERY_STATUS,
    372                         mBatteryStatus, mBatteryHealth, mBatteryPresent ? 1 : 0,
    373                         mPlugType, mBatteryTechnology);
    374             }
    375             if (mBatteryLevel != mLastBatteryLevel ||
    376                     mBatteryVoltage != mLastBatteryVoltage ||
    377                     mBatteryTemperature != mLastBatteryTemperature) {
    378                 EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
    379                         mBatteryLevel, mBatteryVoltage, mBatteryTemperature);
    380             }
    381             if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
    382                     mPlugType == BATTERY_PLUGGED_NONE) {
    383                 // We want to make sure we log discharge cycle outliers
    384                 // if the battery is about to die.
    385                 dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
    386                 logOutlier = true;
    387             }
    388 
    389             final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
    390             final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;
    391 
    392             /* The ACTION_BATTERY_LOW broadcast is sent in these situations:
    393              * - is just un-plugged (previously was plugged) and battery level is
    394              *   less than or equal to WARNING, or
    395              * - is not plugged and battery level falls to WARNING boundary
    396              *   (becomes <= mLowBatteryWarningLevel).
    397              */
    398             final boolean sendBatteryLow = !plugged
    399                     && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
    400                     && mBatteryLevel <= mLowBatteryWarningLevel
    401                     && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
    402 
    403             sendIntentLocked();
    404 
    405             // Separate broadcast is sent for power connected / not connected
    406             // since the standard intent will not wake any applications and some
    407             // applications may want to have smart behavior based on this.
    408             if (mPlugType != 0 && mLastPlugType == 0) {
    409                 mHandler.post(new Runnable() {
    410                     @Override
    411                     public void run() {
    412                         Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED);
    413                         statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    414                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
    415                     }
    416                 });
    417             }
    418             else if (mPlugType == 0 && mLastPlugType != 0) {
    419                 mHandler.post(new Runnable() {
    420                     @Override
    421                     public void run() {
    422                         Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED);
    423                         statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    424                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
    425                     }
    426                 });
    427             }
    428 
    429             if (sendBatteryLow) {
    430                 mSentLowBatteryBroadcast = true;
    431                 mHandler.post(new Runnable() {
    432                     @Override
    433                     public void run() {
    434                         Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW);
    435                         statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    436                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
    437                     }
    438                 });
    439             } else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= mLowBatteryCloseWarningLevel) {
    440                 mSentLowBatteryBroadcast = false;
    441                 mHandler.post(new Runnable() {
    442                     @Override
    443                     public void run() {
    444                         Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY);
    445                         statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    446                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
    447                     }
    448                 });
    449             }
    450 
    451             // Update the battery LED
    452             mLed.updateLightsLocked();
    453 
    454             // This needs to be done after sendIntent() so that we get the lastest battery stats.
    455             if (logOutlier && dischargeDuration != 0) {
    456                 logOutlierLocked(dischargeDuration);
    457             }
    458 
    459             mLastBatteryStatus = mBatteryStatus;
    460             mLastBatteryHealth = mBatteryHealth;
    461             mLastBatteryPresent = mBatteryPresent;
    462             mLastBatteryLevel = mBatteryLevel;
    463             mLastPlugType = mPlugType;
    464             mLastBatteryVoltage = mBatteryVoltage;
    465             mLastBatteryTemperature = mBatteryTemperature;
    466             mLastBatteryLevelCritical = mBatteryLevelCritical;
    467             mLastInvalidCharger = mInvalidCharger;
    468         }
    469     }
    470 
    471     private void sendIntentLocked() {
    472         //  Pack up the values and broadcast them to everyone
    473         final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
    474         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
    475                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
    476 
    477         int icon = getIconLocked(mBatteryLevel);
    478 
    479         intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryStatus);
    480         intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryHealth);
    481         intent.putExtra(BatteryManager.EXTRA_PRESENT, mBatteryPresent);
    482         intent.putExtra(BatteryManager.EXTRA_LEVEL, mBatteryLevel);
    483         intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
    484         intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);
    485         intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);
    486         intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryVoltage);
    487         intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryTemperature);
    488         intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryTechnology);
    489         intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
    490 
    491         if (DEBUG) {
    492             Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED.  level:" + mBatteryLevel +
    493                     ", scale:" + BATTERY_SCALE + ", status:" + mBatteryStatus +
    494                     ", health:" + mBatteryHealth +  ", present:" + mBatteryPresent +
    495                     ", voltage: " + mBatteryVoltage +
    496                     ", temperature: " + mBatteryTemperature +
    497                     ", technology: " + mBatteryTechnology +
    498                     ", AC powered:" + mAcOnline + ", USB powered:" + mUsbOnline +
    499                     ", Wireless powered:" + mWirelessOnline +
    500                     ", icon:" + icon  + ", invalid charger:" + mInvalidCharger);
    501         }
    502 
    503         mHandler.post(new Runnable() {
    504             @Override
    505             public void run() {
    506                 ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
    507             }
    508         });
    509     }
    510 
    511     private void logBatteryStatsLocked() {
    512         IBinder batteryInfoService = ServiceManager.getService(BATTERY_STATS_SERVICE_NAME);
    513         if (batteryInfoService == null) return;
    514 
    515         DropBoxManager db = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);
    516         if (db == null || !db.isTagEnabled("BATTERY_DISCHARGE_INFO")) return;
    517 
    518         File dumpFile = null;
    519         FileOutputStream dumpStream = null;
    520         try {
    521             // dump the service to a file
    522             dumpFile = new File(DUMPSYS_DATA_PATH + BATTERY_STATS_SERVICE_NAME + ".dump");
    523             dumpStream = new FileOutputStream(dumpFile);
    524             batteryInfoService.dump(dumpStream.getFD(), DUMPSYS_ARGS);
    525             FileUtils.sync(dumpStream);
    526 
    527             // add dump file to drop box
    528             db.addFile("BATTERY_DISCHARGE_INFO", dumpFile, DropBoxManager.IS_TEXT);
    529         } catch (RemoteException e) {
    530             Slog.e(TAG, "failed to dump battery service", e);
    531         } catch (IOException e) {
    532             Slog.e(TAG, "failed to write dumpsys file", e);
    533         } finally {
    534             // make sure we clean up
    535             if (dumpStream != null) {
    536                 try {
    537                     dumpStream.close();
    538                 } catch (IOException e) {
    539                     Slog.e(TAG, "failed to close dumpsys output stream");
    540                 }
    541             }
    542             if (dumpFile != null && !dumpFile.delete()) {
    543                 Slog.e(TAG, "failed to delete temporary dumpsys file: "
    544                         + dumpFile.getAbsolutePath());
    545             }
    546         }
    547     }
    548 
    549     private void logOutlierLocked(long duration) {
    550         ContentResolver cr = mContext.getContentResolver();
    551         String dischargeThresholdString = Settings.Global.getString(cr,
    552                 Settings.Global.BATTERY_DISCHARGE_THRESHOLD);
    553         String durationThresholdString = Settings.Global.getString(cr,
    554                 Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD);
    555 
    556         if (dischargeThresholdString != null && durationThresholdString != null) {
    557             try {
    558                 long durationThreshold = Long.parseLong(durationThresholdString);
    559                 int dischargeThreshold = Integer.parseInt(dischargeThresholdString);
    560                 if (duration <= durationThreshold &&
    561                         mDischargeStartLevel - mBatteryLevel >= dischargeThreshold) {
    562                     // If the discharge cycle is bad enough we want to know about it.
    563                     logBatteryStatsLocked();
    564                 }
    565                 if (DEBUG) Slog.v(TAG, "duration threshold: " + durationThreshold +
    566                         " discharge threshold: " + dischargeThreshold);
    567                 if (DEBUG) Slog.v(TAG, "duration: " + duration + " discharge: " +
    568                         (mDischargeStartLevel - mBatteryLevel));
    569             } catch (NumberFormatException e) {
    570                 Slog.e(TAG, "Invalid DischargeThresholds GService string: " +
    571                         durationThresholdString + " or " + dischargeThresholdString);
    572                 return;
    573             }
    574         }
    575     }
    576 
    577     private int getIconLocked(int level) {
    578         if (mBatteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {
    579             return com.android.internal.R.drawable.stat_sys_battery_charge;
    580         } else if (mBatteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {
    581             return com.android.internal.R.drawable.stat_sys_battery;
    582         } else if (mBatteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING
    583                 || mBatteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
    584             if (isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)
    585                     && mBatteryLevel >= 100) {
    586                 return com.android.internal.R.drawable.stat_sys_battery_charge;
    587             } else {
    588                 return com.android.internal.R.drawable.stat_sys_battery;
    589             }
    590         } else {
    591             return com.android.internal.R.drawable.stat_sys_battery_unknown;
    592         }
    593     }
    594 
    595     @Override
    596     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    597         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
    598                 != PackageManager.PERMISSION_GRANTED) {
    599 
    600             pw.println("Permission Denial: can't dump Battery service from from pid="
    601                     + Binder.getCallingPid()
    602                     + ", uid=" + Binder.getCallingUid());
    603             return;
    604         }
    605 
    606         synchronized (mLock) {
    607             if (args == null || args.length == 0 || "-a".equals(args[0])) {
    608                 pw.println("Current Battery Service state:");
    609                 if (mUpdatesStopped) {
    610                     pw.println("  (UPDATES STOPPED -- use 'reset' to restart)");
    611                 }
    612                 pw.println("  AC powered: " + mAcOnline);
    613                 pw.println("  USB powered: " + mUsbOnline);
    614                 pw.println("  Wireless powered: " + mWirelessOnline);
    615                 pw.println("  status: " + mBatteryStatus);
    616                 pw.println("  health: " + mBatteryHealth);
    617                 pw.println("  present: " + mBatteryPresent);
    618                 pw.println("  level: " + mBatteryLevel);
    619                 pw.println("  scale: " + BATTERY_SCALE);
    620                 pw.println("  voltage:" + mBatteryVoltage);
    621                 pw.println("  temperature: " + mBatteryTemperature);
    622                 pw.println("  technology: " + mBatteryTechnology);
    623             } else if (args.length == 3 && "set".equals(args[0])) {
    624                 String key = args[1];
    625                 String value = args[2];
    626                 try {
    627                     boolean update = true;
    628                     if ("ac".equals(key)) {
    629                         mAcOnline = Integer.parseInt(value) != 0;
    630                     } else if ("usb".equals(key)) {
    631                         mUsbOnline = Integer.parseInt(value) != 0;
    632                     } else if ("wireless".equals(key)) {
    633                         mWirelessOnline = Integer.parseInt(value) != 0;
    634                     } else if ("status".equals(key)) {
    635                         mBatteryStatus = Integer.parseInt(value);
    636                     } else if ("level".equals(key)) {
    637                         mBatteryLevel = Integer.parseInt(value);
    638                     } else if ("invalid".equals(key)) {
    639                         mInvalidCharger = Integer.parseInt(value);
    640                     } else {
    641                         pw.println("Unknown set option: " + key);
    642                         update = false;
    643                     }
    644                     if (update) {
    645                         mUpdatesStopped = true;
    646                         processValuesLocked();
    647                     }
    648                 } catch (NumberFormatException ex) {
    649                     pw.println("Bad value: " + value);
    650                 }
    651             } else if (args.length == 1 && "reset".equals(args[0])) {
    652                 mUpdatesStopped = false;
    653                 updateLocked();
    654             } else {
    655                 pw.println("Dump current battery state, or:");
    656                 pw.println("  set ac|usb|wireless|status|level|invalid <value>");
    657                 pw.println("  reset");
    658             }
    659         }
    660     }
    661 
    662     private final UEventObserver mPowerSupplyObserver = new UEventObserver() {
    663         @Override
    664         public void onUEvent(UEventObserver.UEvent event) {
    665             synchronized (mLock) {
    666                 updateLocked();
    667             }
    668         }
    669     };
    670 
    671     private final UEventObserver mInvalidChargerObserver = new UEventObserver() {
    672         @Override
    673         public void onUEvent(UEventObserver.UEvent event) {
    674             final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
    675             synchronized (mLock) {
    676                 if (mInvalidCharger != invalidCharger) {
    677                     mInvalidCharger = invalidCharger;
    678                     updateLocked();
    679                 }
    680             }
    681         }
    682     };
    683 
    684     private final class Led {
    685         private final LightsService.Light mBatteryLight;
    686 
    687         private final int mBatteryLowARGB;
    688         private final int mBatteryMediumARGB;
    689         private final int mBatteryFullARGB;
    690         private final int mBatteryLedOn;
    691         private final int mBatteryLedOff;
    692 
    693         public Led(Context context, LightsService lights) {
    694             mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);
    695 
    696             mBatteryLowARGB = context.getResources().getInteger(
    697                     com.android.internal.R.integer.config_notificationsBatteryLowARGB);
    698             mBatteryMediumARGB = context.getResources().getInteger(
    699                     com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
    700             mBatteryFullARGB = context.getResources().getInteger(
    701                     com.android.internal.R.integer.config_notificationsBatteryFullARGB);
    702             mBatteryLedOn = context.getResources().getInteger(
    703                     com.android.internal.R.integer.config_notificationsBatteryLedOn);
    704             mBatteryLedOff = context.getResources().getInteger(
    705                     com.android.internal.R.integer.config_notificationsBatteryLedOff);
    706         }
    707 
    708         /**
    709          * Synchronize on BatteryService.
    710          */
    711         public void updateLightsLocked() {
    712             final int level = mBatteryLevel;
    713             final int status = mBatteryStatus;
    714             if (level < mLowBatteryWarningLevel) {
    715                 if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
    716                     // Solid red when battery is charging
    717                     mBatteryLight.setColor(mBatteryLowARGB);
    718                 } else {
    719                     // Flash red when battery is low and not charging
    720                     mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED,
    721                             mBatteryLedOn, mBatteryLedOff);
    722                 }
    723             } else if (status == BatteryManager.BATTERY_STATUS_CHARGING
    724                     || status == BatteryManager.BATTERY_STATUS_FULL) {
    725                 if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {
    726                     // Solid green when full or charging and nearly full
    727                     mBatteryLight.setColor(mBatteryFullARGB);
    728                 } else {
    729                     // Solid orange when charging and halfway full
    730                     mBatteryLight.setColor(mBatteryMediumARGB);
    731                 }
    732             } else {
    733                 // No lights if not charging and not low
    734                 mBatteryLight.turnOff();
    735             }
    736         }
    737     }
    738 }
    739