OSDN Git Service

Merge "Also send EXTRA_CALLING_PACKAGE for voice search intents that do web search...
[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.IBinder;
30 import android.os.DropBoxManager;
31 import android.os.RemoteException;
32 import android.os.ServiceManager;
33 import android.os.SystemClock;
34 import android.os.UEventObserver;
35 import android.provider.Settings;
36 import android.util.EventLog;
37 import android.util.Slog;
38
39 import java.io.File;
40 import java.io.FileDescriptor;
41 import java.io.FileInputStream;
42 import java.io.FileOutputStream;
43 import java.io.IOException;
44 import java.io.PrintWriter;
45
46
47 /**
48  * <p>BatteryService monitors the charging status, and charge level of the device
49  * battery.  When these values change this service broadcasts the new values
50  * to all {@link android.content.BroadcastReceiver IntentReceivers} that are
51  * watching the {@link android.content.Intent#ACTION_BATTERY_CHANGED
52  * BATTERY_CHANGED} action.</p>
53  * <p>The new values are stored in the Intent data and can be retrieved by
54  * calling {@link android.content.Intent#getExtra Intent.getExtra} with the
55  * following keys:</p>
56  * <p>&quot;scale&quot; - int, the maximum value for the charge level</p>
57  * <p>&quot;level&quot; - int, charge level, from 0 through &quot;scale&quot; inclusive</p>
58  * <p>&quot;status&quot; - String, the current charging status.<br />
59  * <p>&quot;health&quot; - String, the current battery health.<br />
60  * <p>&quot;present&quot; - boolean, true if the battery is present<br />
61  * <p>&quot;icon-small&quot; - int, suggested small icon to use for this state</p>
62  * <p>&quot;plugged&quot; - int, 0 if the device is not plugged in; 1 if plugged
63  * into an AC power adapter; 2 if plugged in via USB.</p>
64  * <p>&quot;voltage&quot; - int, current battery voltage in millivolts</p>
65  * <p>&quot;temperature&quot; - int, current battery temperature in tenths of
66  * a degree Centigrade</p>
67  * <p>&quot;technology&quot; - String, the type of battery installed, e.g. "Li-ion"</p>
68  */
69 class BatteryService extends Binder {
70     private static final String TAG = BatteryService.class.getSimpleName();
71
72     private static final boolean LOCAL_LOGV = false;
73
74     static final int BATTERY_SCALE = 100;    // battery capacity is a percentage
75
76     // Used locally for determining when to make a last ditch effort to log
77     // discharge stats before the device dies.
78     private static final int CRITICAL_BATTERY_LEVEL = 4;
79
80     private static final int DUMP_MAX_LENGTH = 24 * 1024;
81     private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "-u" };
82     private static final String BATTERY_STATS_SERVICE_NAME = "batteryinfo";
83
84     private static final String DUMPSYS_DATA_PATH = "/data/system/";
85
86     // This should probably be exposed in the API, though it's not critical
87     private static final int BATTERY_PLUGGED_NONE = 0;
88
89     private final Context mContext;
90     private final IBatteryStats mBatteryStats;
91
92     private boolean mAcOnline;
93     private boolean mUsbOnline;
94     private int mBatteryStatus;
95     private int mBatteryHealth;
96     private boolean mBatteryPresent;
97     private int mBatteryLevel;
98     private int mBatteryVoltage;
99     private int mBatteryTemperature;
100     private String mBatteryTechnology;
101     private boolean mBatteryLevelCritical;
102
103     private int mLastBatteryStatus;
104     private int mLastBatteryHealth;
105     private boolean mLastBatteryPresent;
106     private int mLastBatteryLevel;
107     private int mLastBatteryVoltage;
108     private int mLastBatteryTemperature;
109     private boolean mLastBatteryLevelCritical;
110
111     private int mLowBatteryWarningLevel;
112     private int mLowBatteryCloseWarningLevel;
113
114     private int mPlugType;
115     private int mLastPlugType = -1; // Extra state so we can detect first run
116
117     private long mDischargeStartTime;
118     private int mDischargeStartLevel;
119
120     private boolean mSentLowBatteryBroadcast = false;
121
122     public BatteryService(Context context) {
123         mContext = context;
124         mBatteryStats = BatteryStatsService.getService();
125
126         mLowBatteryWarningLevel = mContext.getResources().getInteger(
127                 com.android.internal.R.integer.config_lowBatteryWarningLevel);
128         mLowBatteryCloseWarningLevel = mContext.getResources().getInteger(
129                 com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
130
131         mUEventObserver.startObserving("SUBSYSTEM=power_supply");
132
133         // set initial status
134         update();
135     }
136
137     final boolean isPowered() {
138         // assume we are powered if battery state is unknown so the "stay on while plugged in" option will work.
139         return (mAcOnline || mUsbOnline || mBatteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN);
140     }
141
142     final boolean isPowered(int plugTypeSet) {
143         // assume we are powered if battery state is unknown so
144         // the "stay on while plugged in" option will work.
145         if (mBatteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
146             return true;
147         }
148         if (plugTypeSet == 0) {
149             return false;
150         }
151         int plugTypeBit = 0;
152         if (mAcOnline) {
153             plugTypeBit |= BatteryManager.BATTERY_PLUGGED_AC;
154         }
155         if (mUsbOnline) {
156             plugTypeBit |= BatteryManager.BATTERY_PLUGGED_USB;
157         }
158         return (plugTypeSet & plugTypeBit) != 0;
159     }
160
161     final int getPlugType() {
162         return mPlugType;
163     }
164
165     private UEventObserver mUEventObserver = new UEventObserver() {
166         @Override
167         public void onUEvent(UEventObserver.UEvent event) {
168             update();
169         }
170     };
171
172     // returns battery level as a percentage
173     final int getBatteryLevel() {
174         return mBatteryLevel;
175     }
176
177     void systemReady() {
178         // check our power situation now that it is safe to display the shutdown dialog.
179         shutdownIfNoPower();
180         shutdownIfOverTemp();
181     }
182
183     private final void shutdownIfNoPower() {
184         // shut down gracefully if our battery is critically low and we are not powered.
185         // wait until the system has booted before attempting to display the shutdown dialog.
186         if (mBatteryLevel == 0 && !isPowered() && ActivityManagerNative.isSystemReady()) {
187             Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
188             intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
189             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
190             mContext.startActivity(intent);
191         }
192     }
193
194     private final void shutdownIfOverTemp() {
195         // shut down gracefully if temperature is too high (> 68.0C)
196         // wait until the system has booted before attempting to display the shutdown dialog.
197         if (mBatteryTemperature > 680 && ActivityManagerNative.isSystemReady()) {
198             Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
199             intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
200             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
201             mContext.startActivity(intent);
202         }
203     }
204
205     private native void native_update();
206
207     private synchronized final void update() {
208         native_update();
209
210         boolean logOutlier = false;
211         long dischargeDuration = 0;
212
213         shutdownIfNoPower();
214         shutdownIfOverTemp();
215
216         mBatteryLevelCritical = mBatteryLevel <= CRITICAL_BATTERY_LEVEL;
217         if (mAcOnline) {
218             mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
219         } else if (mUsbOnline) {
220             mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
221         } else {
222             mPlugType = BATTERY_PLUGGED_NONE;
223         }
224         if (mBatteryStatus != mLastBatteryStatus ||
225                 mBatteryHealth != mLastBatteryHealth ||
226                 mBatteryPresent != mLastBatteryPresent ||
227                 mBatteryLevel != mLastBatteryLevel ||
228                 mPlugType != mLastPlugType ||
229                 mBatteryVoltage != mLastBatteryVoltage ||
230                 mBatteryTemperature != mLastBatteryTemperature) {
231
232             if (mPlugType != mLastPlugType) {
233                 if (mLastPlugType == BATTERY_PLUGGED_NONE) {
234                     // discharging -> charging
235
236                     // There's no value in this data unless we've discharged at least once and the
237                     // battery level has changed; so don't log until it does.
238                     if (mDischargeStartTime != 0 && mDischargeStartLevel != mBatteryLevel) {
239                         dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
240                         logOutlier = true;
241                         EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,
242                                 mDischargeStartLevel, mBatteryLevel);
243                         // make sure we see a discharge event before logging again
244                         mDischargeStartTime = 0;
245                     }
246                 } else if (mPlugType == BATTERY_PLUGGED_NONE) {
247                     // charging -> discharging or we just powered up
248                     mDischargeStartTime = SystemClock.elapsedRealtime();
249                     mDischargeStartLevel = mBatteryLevel;
250                 }
251             }
252             if (mBatteryStatus != mLastBatteryStatus ||
253                     mBatteryHealth != mLastBatteryHealth ||
254                     mBatteryPresent != mLastBatteryPresent ||
255                     mPlugType != mLastPlugType) {
256                 EventLog.writeEvent(EventLogTags.BATTERY_STATUS,
257                         mBatteryStatus, mBatteryHealth, mBatteryPresent ? 1 : 0,
258                         mPlugType, mBatteryTechnology);
259             }
260             if (mBatteryLevel != mLastBatteryLevel ||
261                     mBatteryVoltage != mLastBatteryVoltage ||
262                     mBatteryTemperature != mLastBatteryTemperature) {
263                 EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
264                         mBatteryLevel, mBatteryVoltage, mBatteryTemperature);
265             }
266             if (mBatteryLevel != mLastBatteryLevel && mPlugType == BATTERY_PLUGGED_NONE) {
267                 // If the battery level has changed and we are on battery, update the current level.
268                 // This is used for discharge cycle tracking so this shouldn't be updated while the
269                 // battery is charging.
270                 try {
271                     mBatteryStats.recordCurrentLevel(mBatteryLevel);
272                 } catch (RemoteException e) {
273                     // Should never happen.
274                 }
275             }
276             if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
277                     mPlugType == BATTERY_PLUGGED_NONE) {
278                 // We want to make sure we log discharge cycle outliers
279                 // if the battery is about to die.
280                 dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
281                 logOutlier = true;
282             }
283
284             final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
285             final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;
286
287             /* The ACTION_BATTERY_LOW broadcast is sent in these situations:
288              * - is just un-plugged (previously was plugged) and battery level is
289              *   less than or equal to WARNING, or
290              * - is not plugged and battery level falls to WARNING boundary
291              *   (becomes <= mLowBatteryWarningLevel).
292              */
293             final boolean sendBatteryLow = !plugged
294                 && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
295                 && mBatteryLevel <= mLowBatteryWarningLevel
296                 && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
297
298             sendIntent();
299
300             // Separate broadcast is sent for power connected / not connected
301             // since the standard intent will not wake any applications and some
302             // applications may want to have smart behavior based on this.
303             Intent statusIntent = new Intent();
304             statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
305             if (mPlugType != 0 && mLastPlugType == 0) {
306                 statusIntent.setAction(Intent.ACTION_POWER_CONNECTED);
307                 mContext.sendBroadcast(statusIntent);
308             }
309             else if (mPlugType == 0 && mLastPlugType != 0) {
310                 statusIntent.setAction(Intent.ACTION_POWER_DISCONNECTED);
311                 mContext.sendBroadcast(statusIntent);
312             }
313
314             if (sendBatteryLow) {
315                 mSentLowBatteryBroadcast = true;
316                 statusIntent.setAction(Intent.ACTION_BATTERY_LOW);
317                 mContext.sendBroadcast(statusIntent);
318             } else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= mLowBatteryCloseWarningLevel) {
319                 mSentLowBatteryBroadcast = false;
320                 statusIntent.setAction(Intent.ACTION_BATTERY_OKAY);
321                 mContext.sendBroadcast(statusIntent);
322             }
323
324             // This needs to be done after sendIntent() so that we get the lastest battery stats.
325             if (logOutlier && dischargeDuration != 0) {
326                 logOutlier(dischargeDuration);
327             }
328
329             mLastBatteryStatus = mBatteryStatus;
330             mLastBatteryHealth = mBatteryHealth;
331             mLastBatteryPresent = mBatteryPresent;
332             mLastBatteryLevel = mBatteryLevel;
333             mLastPlugType = mPlugType;
334             mLastBatteryVoltage = mBatteryVoltage;
335             mLastBatteryTemperature = mBatteryTemperature;
336             mLastBatteryLevelCritical = mBatteryLevelCritical;
337         }
338     }
339
340     private final void sendIntent() {
341         //  Pack up the values and broadcast them to everyone
342         Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
343         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
344                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
345         try {
346             mBatteryStats.setOnBattery(mPlugType == BATTERY_PLUGGED_NONE, mBatteryLevel);
347         } catch (RemoteException e) {
348             // Should never happen.
349         }
350
351         int icon = getIcon(mBatteryLevel);
352
353         intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryStatus);
354         intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryHealth);
355         intent.putExtra(BatteryManager.EXTRA_PRESENT, mBatteryPresent);
356         intent.putExtra(BatteryManager.EXTRA_LEVEL, mBatteryLevel);
357         intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
358         intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);
359         intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);
360         intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryVoltage);
361         intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryTemperature);
362         intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryTechnology);
363
364         if (false) {
365             Slog.d(TAG, "updateBattery level:" + mBatteryLevel +
366                     " scale:" + BATTERY_SCALE + " status:" + mBatteryStatus +
367                     " health:" + mBatteryHealth +  " present:" + mBatteryPresent +
368                     " voltage: " + mBatteryVoltage +
369                     " temperature: " + mBatteryTemperature +
370                     " technology: " + mBatteryTechnology +
371                     " AC powered:" + mAcOnline + " USB powered:" + mUsbOnline +
372                     " icon:" + icon );
373         }
374
375         ActivityManagerNative.broadcastStickyIntent(intent, null);
376     }
377
378     private final void logBatteryStats() {
379         IBinder batteryInfoService = ServiceManager.getService(BATTERY_STATS_SERVICE_NAME);
380         if (batteryInfoService == null) return;
381
382         DropBoxManager db = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);
383         if (db == null || !db.isTagEnabled("BATTERY_DISCHARGE_INFO")) return;
384
385         File dumpFile = null;
386         FileOutputStream dumpStream = null;
387         try {
388             // dump the service to a file
389             dumpFile = new File(DUMPSYS_DATA_PATH + BATTERY_STATS_SERVICE_NAME + ".dump");
390             dumpStream = new FileOutputStream(dumpFile);
391             batteryInfoService.dump(dumpStream.getFD(), DUMPSYS_ARGS);
392             dumpStream.getFD().sync();
393
394             // add dump file to drop box
395             db.addFile("BATTERY_DISCHARGE_INFO", dumpFile, DropBoxManager.IS_TEXT);
396         } catch (RemoteException e) {
397             Slog.e(TAG, "failed to dump battery service", e);
398         } catch (IOException e) {
399             Slog.e(TAG, "failed to write dumpsys file", e);
400         } finally {
401             // make sure we clean up
402             if (dumpStream != null) {
403                 try {
404                     dumpStream.close();
405                 } catch (IOException e) {
406                     Slog.e(TAG, "failed to close dumpsys output stream");
407                 }
408             }
409             if (dumpFile != null && !dumpFile.delete()) {
410                 Slog.e(TAG, "failed to delete temporary dumpsys file: "
411                         + dumpFile.getAbsolutePath());
412             }
413         }
414     }
415
416     private final void logOutlier(long duration) {
417         ContentResolver cr = mContext.getContentResolver();
418         String dischargeThresholdString = Settings.Secure.getString(cr,
419                 Settings.Secure.BATTERY_DISCHARGE_THRESHOLD);
420         String durationThresholdString = Settings.Secure.getString(cr,
421                 Settings.Secure.BATTERY_DISCHARGE_DURATION_THRESHOLD);
422
423         if (dischargeThresholdString != null && durationThresholdString != null) {
424             try {
425                 long durationThreshold = Long.parseLong(durationThresholdString);
426                 int dischargeThreshold = Integer.parseInt(dischargeThresholdString);
427                 if (duration <= durationThreshold &&
428                         mDischargeStartLevel - mBatteryLevel >= dischargeThreshold) {
429                     // If the discharge cycle is bad enough we want to know about it.
430                     logBatteryStats();
431                 }
432                 if (LOCAL_LOGV) Slog.v(TAG, "duration threshold: " + durationThreshold +
433                         " discharge threshold: " + dischargeThreshold);
434                 if (LOCAL_LOGV) Slog.v(TAG, "duration: " + duration + " discharge: " +
435                         (mDischargeStartLevel - mBatteryLevel));
436             } catch (NumberFormatException e) {
437                 Slog.e(TAG, "Invalid DischargeThresholds GService string: " +
438                         durationThresholdString + " or " + dischargeThresholdString);
439                 return;
440             }
441         }
442     }
443
444     private final int getIcon(int level) {
445         if (mBatteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {
446             return com.android.internal.R.drawable.stat_sys_battery_charge;
447         } else if (mBatteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING ||
448                 mBatteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING ||
449                 mBatteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
450             return com.android.internal.R.drawable.stat_sys_battery;
451         } else {
452             return com.android.internal.R.drawable.stat_sys_battery_unknown;
453         }
454     }
455
456     @Override
457     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
458         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
459                 != PackageManager.PERMISSION_GRANTED) {
460
461             pw.println("Permission Denial: can't dump Battery service from from pid="
462                     + Binder.getCallingPid()
463                     + ", uid=" + Binder.getCallingUid());
464             return;
465         }
466
467         synchronized (this) {
468             pw.println("Current Battery Service state:");
469             pw.println("  AC powered: " + mAcOnline);
470             pw.println("  USB powered: " + mUsbOnline);
471             pw.println("  status: " + mBatteryStatus);
472             pw.println("  health: " + mBatteryHealth);
473             pw.println("  present: " + mBatteryPresent);
474             pw.println("  level: " + mBatteryLevel);
475             pw.println("  scale: " + BATTERY_SCALE);
476             pw.println("  voltage:" + mBatteryVoltage);
477             pw.println("  temperature: " + mBatteryTemperature);
478             pw.println("  technology: " + mBatteryTechnology);
479         }
480     }
481 }