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