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