OSDN Git Service

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