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     private void shutdownIfNoPowerLocked() {
    240         // shut down gracefully if our battery is critically low and we are not powered.
    241         // wait until the system has booted before attempting to display the shutdown dialog.
    242         if (mBatteryLevel == 0 && !isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)) {
    243             mHandler.post(new Runnable() {
    244                 @Override
    245                 public void run() {
    246                     if (ActivityManagerNative.isSystemReady()) {
    247                         Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
    248                         intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
    249                         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    250                         mContext.startActivityAsUser(intent, UserHandle.CURRENT);
    251                     }
    252                 }
    253             });
    254         }
    255     }
    256 
    257     private void shutdownIfOverTempLocked() {
    258         // shut down gracefully if temperature is too high (> 68.0C by default)
    259         // wait until the system has booted before attempting to display the
    260         // shutdown dialog.
    261         if (mBatteryTemperature > mShutdownBatteryTemperature) {
    262             mHandler.post(new Runnable() {
    263                 @Override
    264                 public void run() {
    265                     if (ActivityManagerNative.isSystemReady()) {
    266                         Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
    267                         intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
    268                         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    269                         mContext.startActivityAsUser(intent, UserHandle.CURRENT);
    270                     }
    271                 }
    272             });
    273         }
    274     }
    275 
    276     private void updateLocked() {
    277         if (!mUpdatesStopped) {
    278             // Update the values of mAcOnline, et. all.
    279             native_update();
    280 
    281             // Process the new values.
    282             processValuesLocked();
    283         }
    284     }
    285 
    286     private void processValuesLocked() {
    287         boolean logOutlier = false;
    288         long dischargeDuration = 0;
    289 
    290         mBatteryLevelCritical = (mBatteryLevel <= mCriticalBatteryLevel);
    291         if (mAcOnline) {
    292             mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
    293         } else if (mUsbOnline) {
    294             mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
    295         } else if (mWirelessOnline) {
    296             mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
    297         } else {
    298             mPlugType = BATTERY_PLUGGED_NONE;
    299         }
    300 
    301         if (DEBUG) {
    302             Slog.d(TAG, "Processing new values: "
    303                     + "mAcOnline=" + mAcOnline
    304                     + ", mUsbOnline=" + mUsbOnline
    305                     + ", mWirelessOnline=" + mWirelessOnline
    306                     + ", mBatteryStatus=" + mBatteryStatus
    307                     + ", mBatteryHealth=" + mBatteryHealth
    308                     + ", mBatteryPresent=" + mBatteryPresent
    309                     + ", mBatteryLevel=" + mBatteryLevel
    310                     + ", mBatteryTechnology=" + mBatteryTechnology
    311                     + ", mBatteryVoltage=" + mBatteryVoltage
    312                     + ", mBatteryTemperature=" + mBatteryTemperature
    313                     + ", mBatteryLevelCritical=" + mBatteryLevelCritical
    314                     + ", mPlugType=" + mPlugType);
    315         }
    316 
    317         // Let the battery stats keep track of the current level.
    318         try {
    319             mBatteryStats.setBatteryState(mBatteryStatus, mBatteryHealth,
    320                     mPlugType, mBatteryLevel, mBatteryTemperature,
    321                     mBatteryVoltage);
    322         } catch (RemoteException e) {
    323             // Should never happen.
    324         }
    325 
    326         shutdownIfNoPowerLocked();
    327         shutdownIfOverTempLocked();
    328 
    329         if (mBatteryStatus != mLastBatteryStatus ||
    330                 mBatteryHealth != mLastBatteryHealth ||
    331                 mBatteryPresent != mLastBatteryPresent ||
    332                 mBatteryLevel != mLastBatteryLevel ||
    333                 mPlugType != mLastPlugType ||
    334                 mBatteryVoltage != mLastBatteryVoltage ||
    335                 mBatteryTemperature != mLastBatteryTemperature ||
    336                 mInvalidCharger != mLastInvalidCharger) {
    337 
    338             if (mPlugType != mLastPlugType) {
    339                 if (mLastPlugType == BATTERY_PLUGGED_NONE) {
    340                     // discharging -> charging
    341 
    342                     // There's no value in this data unless we've discharged at least once and the
    343                     // battery level has changed; so don't log until it does.
    344                     if (mDischargeStartTime != 0 && mDischargeStartLevel != mBatteryLevel) {
    345                         dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
    346                         logOutlier = true;
    347                         EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,
    348                                 mDischargeStartLevel, mBatteryLevel);
    349                         // make sure we see a discharge event before logging again
    350                         mDischargeStartTime = 0;
    351                     }
    352                 } else if (mPlugType == BATTERY_PLUGGED_NONE) {
    353                     // charging -> discharging or we just powered up
    354                     mDischargeStartTime = SystemClock.elapsedRealtime();
    355                     mDischargeStartLevel = mBatteryLevel;
    356                 }
    357             }
    358             if (mBatteryStatus != mLastBatteryStatus ||
    359                     mBatteryHealth != mLastBatteryHealth ||
    360                     mBatteryPresent != mLastBatteryPresent ||
    361                     mPlugType != mLastPlugType) {
    362                 EventLog.writeEvent(EventLogTags.BATTERY_STATUS,
    363                         mBatteryStatus, mBatteryHealth, mBatteryPresent ? 1 : 0,
    364                         mPlugType, mBatteryTechnology);
    365             }
    366             if (mBatteryLevel != mLastBatteryLevel ||
    367                     mBatteryVoltage != mLastBatteryVoltage ||
    368                     mBatteryTemperature != mLastBatteryTemperature) {
    369                 EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
    370                         mBatteryLevel, mBatteryVoltage, mBatteryTemperature);
    371             }
    372             if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
    373                     mPlugType == BATTERY_PLUGGED_NONE) {
    374                 // We want to make sure we log discharge cycle outliers
    375                 // if the battery is about to die.
    376                 dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
    377                 logOutlier = true;
    378             }
    379 
    380             final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
    381             final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;
    382 
    383             /* The ACTION_BATTERY_LOW broadcast is sent in these situations:
    384              * - is just un-plugged (previously was plugged) and battery level is
    385              *   less than or equal to WARNING, or
    386              * - is not plugged and battery level falls to WARNING boundary
    387              *   (becomes <= mLowBatteryWarningLevel).
    388              */
    389             final boolean sendBatteryLow = !plugged
    390                     && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
    391                     && mBatteryLevel <= mLowBatteryWarningLevel
    392                     && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
    393 
    394             sendIntentLocked();
    395 
    396             // Separate broadcast is sent for power connected / not connected
    397             // since the standard intent will not wake any applications and some
    398             // applications may want to have smart behavior based on this.
    399             if (mPlugType != 0 && mLastPlugType == 0) {
    400                 mHandler.post(new Runnable() {
    401                     @Override
    402                     public void run() {
    403                         Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED);
    404                         statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    405                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
    406                     }
    407                 });
    408             }
    409             else if (mPlugType == 0 && mLastPlugType != 0) {
    410                 mHandler.post(new Runnable() {
    411                     @Override
    412                     public void run() {
    413                         Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED);
    414                         statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    415                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
    416                     }
    417                 });
    418             }
    419 
    420             if (sendBatteryLow) {
    421                 mSentLowBatteryBroadcast = true;
    422                 mHandler.post(new Runnable() {
    423                     @Override
    424                     public void run() {
    425                         Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW);
    426                         statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    427                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
    428                     }
    429                 });
    430             } else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= mLowBatteryCloseWarningLevel) {
    431                 mSentLowBatteryBroadcast = false;
    432                 mHandler.post(new Runnable() {
    433                     @Override
    434                     public void run() {
    435                         Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY);
    436                         statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    437                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
    438                     }
    439                 });
    440             }
    441 
    442             // Update the battery LED
    443             mLed.updateLightsLocked();
    444 
    445             // This needs to be done after sendIntent() so that we get the lastest battery stats.
    446             if (logOutlier && dischargeDuration != 0) {
    447                 logOutlierLocked(dischargeDuration);
    448             }
    449 
    450             mLastBatteryStatus = mBatteryStatus;
    451             mLastBatteryHealth = mBatteryHealth;
    452             mLastBatteryPresent = mBatteryPresent;
    453             mLastBatteryLevel = mBatteryLevel;
    454             mLastPlugType = mPlugType;
    455             mLastBatteryVoltage = mBatteryVoltage;
    456             mLastBatteryTemperature = mBatteryTemperature;
    457             mLastBatteryLevelCritical = mBatteryLevelCritical;
    458             mLastInvalidCharger = mInvalidCharger;
    459         }
    460     }
    461 
    462     private void sendIntentLocked() {
    463         //  Pack up the values and broadcast them to everyone
    464         final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
    465         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
    466                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
    467 
    468         int icon = getIconLocked(mBatteryLevel);
    469 
    470         intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryStatus);
    471         intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryHealth);
    472         intent.putExtra(BatteryManager.EXTRA_PRESENT, mBatteryPresent);
    473         intent.putExtra(BatteryManager.EXTRA_LEVEL, mBatteryLevel);
    474         intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
    475         intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);
    476         intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);
    477         intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryVoltage);
    478         intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryTemperature);
    479         intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryTechnology);
    480         intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
    481 
    482         if (DEBUG) {
    483             Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED.  level:" + mBatteryLevel +
    484                     ", scale:" + BATTERY_SCALE + ", status:" + mBatteryStatus +
    485                     ", health:" + mBatteryHealth +  ", present:" + mBatteryPresent +
    486                     ", voltage: " + mBatteryVoltage +
    487                     ", temperature: " + mBatteryTemperature +
    488                     ", technology: " + mBatteryTechnology +
    489                     ", AC powered:" + mAcOnline + ", USB powered:" + mUsbOnline +
    490                     ", Wireless powered:" + mWirelessOnline +
    491                     ", icon:" + icon  + ", invalid charger:" + mInvalidCharger);
    492         }
    493 
    494         mHandler.post(new Runnable() {
    495             @Override
    496             public void run() {
    497                 ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
    498             }
    499         });
    500     }
    501 
    502     private void logBatteryStatsLocked() {
    503         IBinder batteryInfoService = ServiceManager.getService(BATTERY_STATS_SERVICE_NAME);
    504         if (batteryInfoService == null) return;
    505 
    506         DropBoxManager db = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);
    507         if (db == null || !db.isTagEnabled("BATTERY_DISCHARGE_INFO")) return;
    508 
    509         File dumpFile = null;
    510         FileOutputStream dumpStream = null;
    511         try {
    512             // dump the service to a file
    513             dumpFile = new File(DUMPSYS_DATA_PATH + BATTERY_STATS_SERVICE_NAME + ".dump");
    514             dumpStream = new FileOutputStream(dumpFile);
    515             batteryInfoService.dump(dumpStream.getFD(), DUMPSYS_ARGS);
    516             FileUtils.sync(dumpStream);
    517 
    518             // add dump file to drop box
    519             db.addFile("BATTERY_DISCHARGE_INFO", dumpFile, DropBoxManager.IS_TEXT);
    520         } catch (RemoteException e) {
    521             Slog.e(TAG, "failed to dump battery service", e);
    522         } catch (IOException e) {
    523             Slog.e(TAG, "failed to write dumpsys file", e);
    524         } finally {
    525             // make sure we clean up
    526             if (dumpStream != null) {
    527                 try {
    528                     dumpStream.close();
    529                 } catch (IOException e) {
    530                     Slog.e(TAG, "failed to close dumpsys output stream");
    531                 }
    532             }
    533             if (dumpFile != null && !dumpFile.delete()) {
    534                 Slog.e(TAG, "failed to delete temporary dumpsys file: "
    535                         + dumpFile.getAbsolutePath());
    536             }
    537         }
    538     }
    539 
    540     private void logOutlierLocked(long duration) {
    541         ContentResolver cr = mContext.getContentResolver();
    542         String dischargeThresholdString = Settings.Global.getString(cr,
    543                 Settings.Global.BATTERY_DISCHARGE_THRESHOLD);
    544         String durationThresholdString = Settings.Global.getString(cr,
    545                 Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD);
    546 
    547         if (dischargeThresholdString != null && durationThresholdString != null) {
    548             try {
    549                 long durationThreshold = Long.parseLong(durationThresholdString);
    550                 int dischargeThreshold = Integer.parseInt(dischargeThresholdString);
    551                 if (duration <= durationThreshold &&
    552                         mDischargeStartLevel - mBatteryLevel >= dischargeThreshold) {
    553                     // If the discharge cycle is bad enough we want to know about it.
    554                     logBatteryStatsLocked();
    555                 }
    556                 if (DEBUG) Slog.v(TAG, "duration threshold: " + durationThreshold +
    557                         " discharge threshold: " + dischargeThreshold);
    558                 if (DEBUG) Slog.v(TAG, "duration: " + duration + " discharge: " +
    559                         (mDischargeStartLevel - mBatteryLevel));
    560             } catch (NumberFormatException e) {
    561                 Slog.e(TAG, "Invalid DischargeThresholds GService string: " +
    562                         durationThresholdString + " or " + dischargeThresholdString);
    563                 return;
    564             }
    565         }
    566     }
    567 
    568     private int getIconLocked(int level) {
    569         if (mBatteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {
    570             return com.android.internal.R.drawable.stat_sys_battery_charge;
    571         } else if (mBatteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {
    572             return com.android.internal.R.drawable.stat_sys_battery;
    573         } else if (mBatteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING
    574                 || mBatteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
    575             if (isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)
    576                     && mBatteryLevel >= 100) {
    577                 return com.android.internal.R.drawable.stat_sys_battery_charge;
    578             } else {
    579                 return com.android.internal.R.drawable.stat_sys_battery;
    580             }
    581         } else {
    582             return com.android.internal.R.drawable.stat_sys_battery_unknown;
    583         }
    584     }
    585 
    586     @Override
    587     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    588         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
    589                 != PackageManager.PERMISSION_GRANTED) {
    590 
    591             pw.println("Permission Denial: can't dump Battery service from from pid="
    592                     + Binder.getCallingPid()
    593                     + ", uid=" + Binder.getCallingUid());
    594             return;
    595         }
    596 
    597         synchronized (mLock) {
    598             if (args == null || args.length == 0 || "-a".equals(args[0])) {
    599                 pw.println("Current Battery Service state:");
    600                 if (mUpdatesStopped) {
    601                     pw.println("  (UPDATES STOPPED -- use 'reset' to restart)");
    602                 }
    603                 pw.println("  AC powered: " + mAcOnline);
    604                 pw.println("  USB powered: " + mUsbOnline);
    605                 pw.println("  Wireless powered: " + mWirelessOnline);
    606                 pw.println("  status: " + mBatteryStatus);
    607                 pw.println("  health: " + mBatteryHealth);
    608                 pw.println("  present: " + mBatteryPresent);
    609                 pw.println("  level: " + mBatteryLevel);
    610                 pw.println("  scale: " + BATTERY_SCALE);
    611                 pw.println("  voltage:" + mBatteryVoltage);
    612                 pw.println("  temperature: " + mBatteryTemperature);
    613                 pw.println("  technology: " + mBatteryTechnology);
    614             } else if (args.length == 3 && "set".equals(args[0])) {
    615                 String key = args[1];
    616                 String value = args[2];
    617                 try {
    618                     boolean update = true;
    619                     if ("ac".equals(key)) {
    620                         mAcOnline = Integer.parseInt(value) != 0;
    621                     } else if ("usb".equals(key)) {
    622                         mUsbOnline = Integer.parseInt(value) != 0;
    623                     } else if ("wireless".equals(key)) {
    624                         mWirelessOnline = Integer.parseInt(value) != 0;
    625                     } else if ("status".equals(key)) {
    626                         mBatteryStatus = Integer.parseInt(value);
    627                     } else if ("level".equals(key)) {
    628                         mBatteryLevel = Integer.parseInt(value);
    629                     } else if ("invalid".equals(key)) {
    630                         mInvalidCharger = Integer.parseInt(value);
    631                     } else {
    632                         pw.println("Unknown set option: " + key);
    633                         update = false;
    634                     }
    635                     if (update) {
    636                         mUpdatesStopped = true;
    637                         processValuesLocked();
    638                     }
    639                 } catch (NumberFormatException ex) {
    640                     pw.println("Bad value: " + value);
    641                 }
    642             } else if (args.length == 1 && "reset".equals(args[0])) {
    643                 mUpdatesStopped = false;
    644                 updateLocked();
    645             } else {
    646                 pw.println("Dump current battery state, or:");
    647                 pw.println("  set ac|usb|wireless|status|level|invalid <value>");
    648                 pw.println("  reset");
    649             }
    650         }
    651     }
    652 
    653     private final UEventObserver mPowerSupplyObserver = new UEventObserver() {
    654         @Override
    655         public void onUEvent(UEventObserver.UEvent event) {
    656             synchronized (mLock) {
    657                 updateLocked();
    658             }
    659         }
    660     };
    661 
    662     private final UEventObserver mInvalidChargerObserver = new UEventObserver() {
    663         @Override
    664         public void onUEvent(UEventObserver.UEvent event) {
    665             final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
    666             synchronized (mLock) {
    667                 if (mInvalidCharger != invalidCharger) {
    668                     mInvalidCharger = invalidCharger;
    669                     updateLocked();
    670                 }
    671             }
    672         }
    673     };
    674 
    675     private final class Led {
    676         private final LightsService.Light mBatteryLight;
    677 
    678         private final int mBatteryLowARGB;
    679         private final int mBatteryMediumARGB;
    680         private final int mBatteryFullARGB;
    681         private final int mBatteryLedOn;
    682         private final int mBatteryLedOff;
    683 
    684         public Led(Context context, LightsService lights) {
    685             mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);
    686 
    687             mBatteryLowARGB = context.getResources().getInteger(
    688                     com.android.internal.R.integer.config_notificationsBatteryLowARGB);
    689             mBatteryMediumARGB = context.getResources().getInteger(
    690                     com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
    691             mBatteryFullARGB = context.getResources().getInteger(
    692                     com.android.internal.R.integer.config_notificationsBatteryFullARGB);
    693             mBatteryLedOn = context.getResources().getInteger(
    694                     com.android.internal.R.integer.config_notificationsBatteryLedOn);
    695             mBatteryLedOff = context.getResources().getInteger(
    696                     com.android.internal.R.integer.config_notificationsBatteryLedOff);
    697         }
    698 
    699         /**
    700          * Synchronize on BatteryService.
    701          */
    702         public void updateLightsLocked() {
    703             final int level = mBatteryLevel;
    704             final int status = mBatteryStatus;
    705             if (level < mLowBatteryWarningLevel) {
    706                 if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
    707                     // Solid red when battery is charging
    708                     mBatteryLight.setColor(mBatteryLowARGB);
    709                 } else {
    710                     // Flash red when battery is low and not charging
    711                     mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED,
    712                             mBatteryLedOn, mBatteryLedOff);
    713                 }
    714             } else if (status == BatteryManager.BATTERY_STATUS_CHARGING
    715                     || status == BatteryManager.BATTERY_STATUS_FULL) {
    716                 if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {
    717                     // Solid green when full or charging and nearly full
    718                     mBatteryLight.setColor(mBatteryFullARGB);
    719                 } else {
    720                     // Solid orange when charging and halfway full
    721                     mBatteryLight.setColor(mBatteryMediumARGB);
    722                 }
    723             } else {
    724                 // No lights if not charging and not low
    725                 mBatteryLight.turnOff();
    726             }
    727         }
    728     }
    729 }
    730