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