BatteryStatsService.java revision fa97fcf7b9b9999102bc0c6b298114375b27a1c3
1/*
2 * Copyright (C) 2006-2007 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.am;
18
19import android.annotation.Nullable;
20import android.bluetooth.BluetoothActivityEnergyInfo;
21import android.bluetooth.BluetoothAdapter;
22import android.content.Context;
23import android.content.pm.ApplicationInfo;
24import android.content.pm.PackageManager;
25import android.net.wifi.IWifiManager;
26import android.net.wifi.WifiActivityEnergyInfo;
27import android.os.BatteryStats;
28import android.os.Binder;
29import android.os.Handler;
30import android.os.IBinder;
31import android.os.Looper;
32import android.os.Message;
33import android.os.Parcel;
34import android.os.ParcelFileDescriptor;
35import android.os.ParcelFormatException;
36import android.os.Parcelable;
37import android.os.PowerManagerInternal;
38import android.os.Process;
39import android.os.RemoteException;
40import android.os.ServiceManager;
41import android.os.SynchronousResultReceiver;
42import android.os.SystemClock;
43import android.os.UserHandle;
44import android.os.WorkSource;
45import android.os.health.HealthStatsParceler;
46import android.os.health.HealthStatsWriter;
47import android.os.health.UidHealthStats;
48import android.telephony.DataConnectionRealTimeInfo;
49import android.telephony.ModemActivityInfo;
50import android.telephony.SignalStrength;
51import android.telephony.TelephonyManager;
52import android.util.IntArray;
53import android.util.Slog;
54
55import android.util.TimeUtils;
56import com.android.internal.annotations.GuardedBy;
57import com.android.internal.app.IBatteryStats;
58import com.android.internal.os.BatteryStatsHelper;
59import com.android.internal.os.BatteryStatsImpl;
60import com.android.internal.os.PowerProfile;
61import com.android.server.LocalServices;
62import com.android.server.ServiceThread;
63
64import java.io.File;
65import java.io.FileDescriptor;
66import java.io.IOException;
67import java.io.PrintWriter;
68import java.nio.ByteBuffer;
69import java.nio.CharBuffer;
70import java.nio.charset.CharsetDecoder;
71import java.nio.charset.CodingErrorAction;
72import java.nio.charset.StandardCharsets;
73import java.util.Arrays;
74import java.util.List;
75import java.util.concurrent.TimeoutException;
76
77/**
78 * All information we are collecting about things that can happen that impact
79 * battery life.
80 */
81public final class BatteryStatsService extends IBatteryStats.Stub
82        implements PowerManagerInternal.LowPowerModeListener,
83        BatteryStatsImpl.PlatformIdleStateCallback {
84    static final String TAG = "BatteryStatsService";
85
86    /**
87     * How long to wait on an individual subsystem to return its stats.
88     */
89    private static final long EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS = 2000;
90
91    private static IBatteryStats sService;
92
93    final BatteryStatsImpl mStats;
94    private final BatteryStatsHandler mHandler;
95    private Context mContext;
96    private IWifiManager mWifiManager;
97    private TelephonyManager mTelephony;
98
99    // Lock acquired when extracting data from external sources.
100    private final Object mExternalStatsLock = new Object();
101
102    // WiFi keeps an accumulated total of stats, unlike Bluetooth.
103    // Keep the last WiFi stats so we can compute a delta.
104    @GuardedBy("mExternalStatsLock")
105    private WifiActivityEnergyInfo mLastInfo =
106            new WifiActivityEnergyInfo(0, 0, 0, new long[]{0}, 0, 0, 0);
107
108    class BatteryStatsHandler extends Handler implements BatteryStatsImpl.ExternalStatsSync {
109        public static final int MSG_SYNC_EXTERNAL_STATS = 1;
110        public static final int MSG_WRITE_TO_DISK = 2;
111
112        private int mUpdateFlags = 0;
113        private IntArray mUidsToRemove = new IntArray();
114
115        public BatteryStatsHandler(Looper looper) {
116            super(looper);
117        }
118
119        @Override
120        public void handleMessage(Message msg) {
121            switch (msg.what) {
122                case MSG_SYNC_EXTERNAL_STATS:
123                    final int updateFlags;
124                    synchronized (this) {
125                        removeMessages(MSG_SYNC_EXTERNAL_STATS);
126                        updateFlags = mUpdateFlags;
127                        mUpdateFlags = 0;
128                    }
129                    updateExternalStatsSync((String)msg.obj, updateFlags);
130
131                    // other parts of the system could be calling into us
132                    // from mStats in order to report of changes. We must grab the mStats
133                    // lock before grabbing our own or we'll end up in a deadlock.
134                    synchronized (mStats) {
135                        synchronized (this) {
136                            final int numUidsToRemove = mUidsToRemove.size();
137                            for (int i = 0; i < numUidsToRemove; i++) {
138                                mStats.removeIsolatedUidLocked(mUidsToRemove.get(i));
139                            }
140                        }
141                        mUidsToRemove.clear();
142                    }
143                    break;
144
145                case MSG_WRITE_TO_DISK:
146                    updateExternalStatsSync("write", UPDATE_ALL);
147                    synchronized (mStats) {
148                        mStats.writeAsyncLocked();
149                    }
150                    break;
151            }
152        }
153
154        @Override
155        public void scheduleSync(String reason, int updateFlags) {
156            synchronized (this) {
157                scheduleSyncLocked(reason, updateFlags);
158            }
159        }
160
161        @Override
162        public void scheduleCpuSyncDueToRemovedUid(int uid) {
163            synchronized (this) {
164                scheduleSyncLocked("remove-uid", UPDATE_CPU);
165                mUidsToRemove.add(uid);
166            }
167        }
168
169        private void scheduleSyncLocked(String reason, int updateFlags) {
170            if (mUpdateFlags == 0) {
171                sendMessage(Message.obtain(this, MSG_SYNC_EXTERNAL_STATS, reason));
172            }
173            mUpdateFlags |= updateFlags;
174        }
175    }
176
177    private native int getPlatformLowPowerStats(ByteBuffer outBuffer);
178    private CharsetDecoder mDecoderStat = StandardCharsets.UTF_8
179                    .newDecoder()
180                    .onMalformedInput(CodingErrorAction.REPLACE)
181                    .onUnmappableCharacter(CodingErrorAction.REPLACE)
182                    .replaceWith("?");
183    private ByteBuffer mUtf8BufferStat = ByteBuffer.allocateDirect(MAX_LOW_POWER_STATS_SIZE);
184    private CharBuffer mUtf16BufferStat = CharBuffer.allocate(MAX_LOW_POWER_STATS_SIZE);
185    private static final int MAX_LOW_POWER_STATS_SIZE = 512;
186
187    @Override
188    public String getPlatformLowPowerStats() {
189        mUtf8BufferStat.clear();
190        mUtf16BufferStat.clear();
191        mDecoderStat.reset();
192        int bytesWritten = getPlatformLowPowerStats(mUtf8BufferStat);
193        if (bytesWritten < 0) {
194            return null;
195        } else if (bytesWritten == 0) {
196            return "Empty";
197        }
198        mUtf8BufferStat.limit(bytesWritten);
199        mDecoderStat.decode(mUtf8BufferStat, mUtf16BufferStat, true);
200        mUtf16BufferStat.flip();
201        return mUtf16BufferStat.toString();
202    }
203
204    BatteryStatsService(File systemDir, Handler handler) {
205        // Our handler here will be accessing the disk, use a different thread than
206        // what the ActivityManagerService gave us (no I/O on that one!).
207        final ServiceThread thread = new ServiceThread("batterystats-sync",
208                Process.THREAD_PRIORITY_DEFAULT, true);
209        thread.start();
210        mHandler = new BatteryStatsHandler(thread.getLooper());
211
212        // BatteryStatsImpl expects the ActivityManagerService handler, so pass that one through.
213        mStats = new BatteryStatsImpl(systemDir, handler, mHandler, this);
214    }
215
216    public void publish(Context context) {
217        mContext = context;
218        mStats.setRadioScanningTimeout(mContext.getResources().getInteger(
219                com.android.internal.R.integer.config_radioScanningTimeout)
220                * 1000L);
221        mStats.setPowerProfile(new PowerProfile(context));
222        ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder());
223    }
224
225    /**
226     * At the time when the constructor runs, the power manager has not yet been
227     * initialized.  So we initialize the low power observer later.
228     */
229    public void initPowerManagement() {
230        final PowerManagerInternal powerMgr = LocalServices.getService(PowerManagerInternal.class);
231        powerMgr.registerLowPowerModeObserver(this);
232        mStats.notePowerSaveMode(powerMgr.getLowPowerModeEnabled());
233        (new WakeupReasonThread()).start();
234    }
235
236    public void shutdown() {
237        Slog.w("BatteryStats", "Writing battery stats before shutdown...");
238
239        updateExternalStatsSync("shutdown", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
240        synchronized (mStats) {
241            mStats.shutdownLocked();
242        }
243
244        // Shutdown the thread we made.
245        mHandler.getLooper().quit();
246    }
247
248    public static IBatteryStats getService() {
249        if (sService != null) {
250            return sService;
251        }
252        IBinder b = ServiceManager.getService(BatteryStats.SERVICE_NAME);
253        sService = asInterface(b);
254        return sService;
255    }
256
257    @Override
258    public void onLowPowerModeChanged(boolean enabled) {
259        synchronized (mStats) {
260            mStats.notePowerSaveMode(enabled);
261        }
262    }
263
264    /**
265     * @return the current statistics object, which may be modified
266     * to reflect events that affect battery usage.  You must lock the
267     * stats object before doing anything with it.
268     */
269    public BatteryStatsImpl getActiveStatistics() {
270        return mStats;
271    }
272
273    /**
274     * Schedules a write to disk to occur. This will cause the BatteryStatsImpl
275     * object to update with the latest info, then write to disk.
276     */
277    public void scheduleWriteToDisk() {
278        mHandler.sendEmptyMessage(BatteryStatsHandler.MSG_WRITE_TO_DISK);
279    }
280
281    // These are for direct use by the activity manager...
282
283    /**
284     * Remove a UID from the BatteryStats and BatteryStats' external dependencies.
285     */
286    void removeUid(int uid) {
287        synchronized (mStats) {
288            mStats.removeUidStatsLocked(uid);
289        }
290    }
291
292    void addIsolatedUid(int isolatedUid, int appUid) {
293        synchronized (mStats) {
294            mStats.addIsolatedUidLocked(isolatedUid, appUid);
295        }
296    }
297
298    void removeIsolatedUid(int isolatedUid, int appUid) {
299        synchronized (mStats) {
300            mStats.scheduleRemoveIsolatedUidLocked(isolatedUid, appUid);
301        }
302    }
303
304    void noteProcessStart(String name, int uid) {
305        synchronized (mStats) {
306            mStats.noteProcessStartLocked(name, uid);
307        }
308    }
309
310    void noteProcessCrash(String name, int uid) {
311        synchronized (mStats) {
312            mStats.noteProcessCrashLocked(name, uid);
313        }
314    }
315
316    void noteProcessAnr(String name, int uid) {
317        synchronized (mStats) {
318            mStats.noteProcessAnrLocked(name, uid);
319        }
320    }
321
322    void noteProcessFinish(String name, int uid) {
323        synchronized (mStats) {
324            mStats.noteProcessFinishLocked(name, uid);
325        }
326    }
327
328    void noteUidProcessState(int uid, int state) {
329        synchronized (mStats) {
330            mStats.noteUidProcessStateLocked(uid, state);
331        }
332    }
333
334    // Public interface...
335
336    public byte[] getStatistics() {
337        mContext.enforceCallingPermission(
338                android.Manifest.permission.BATTERY_STATS, null);
339        //Slog.i("foo", "SENDING BATTERY INFO:");
340        //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
341        Parcel out = Parcel.obtain();
342        updateExternalStatsSync("get-stats", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
343        synchronized (mStats) {
344            mStats.writeToParcel(out, 0);
345        }
346        byte[] data = out.marshall();
347        out.recycle();
348        return data;
349    }
350
351    public ParcelFileDescriptor getStatisticsStream() {
352        mContext.enforceCallingPermission(
353                android.Manifest.permission.BATTERY_STATS, null);
354        //Slog.i("foo", "SENDING BATTERY INFO:");
355        //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
356        Parcel out = Parcel.obtain();
357        updateExternalStatsSync("get-stats", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
358        synchronized (mStats) {
359            mStats.writeToParcel(out, 0);
360        }
361        byte[] data = out.marshall();
362        out.recycle();
363        try {
364            return ParcelFileDescriptor.fromData(data, "battery-stats");
365        } catch (IOException e) {
366            Slog.w(TAG, "Unable to create shared memory", e);
367            return null;
368        }
369    }
370
371    public boolean isCharging() {
372        synchronized (mStats) {
373            return mStats.isCharging();
374        }
375    }
376
377    public long computeBatteryTimeRemaining() {
378        synchronized (mStats) {
379            long time = mStats.computeBatteryTimeRemaining(SystemClock.elapsedRealtime());
380            return time >= 0 ? (time/1000) : time;
381        }
382    }
383
384    public long computeChargeTimeRemaining() {
385        synchronized (mStats) {
386            long time = mStats.computeChargeTimeRemaining(SystemClock.elapsedRealtime());
387            return time >= 0 ? (time/1000) : time;
388        }
389    }
390
391    public void noteEvent(int code, String name, int uid) {
392        enforceCallingPermission();
393        synchronized (mStats) {
394            mStats.noteEventLocked(code, name, uid);
395        }
396    }
397
398    public void noteSyncStart(String name, int uid) {
399        enforceCallingPermission();
400        synchronized (mStats) {
401            mStats.noteSyncStartLocked(name, uid);
402        }
403    }
404
405    public void noteSyncFinish(String name, int uid) {
406        enforceCallingPermission();
407        synchronized (mStats) {
408            mStats.noteSyncFinishLocked(name, uid);
409        }
410    }
411
412    public void noteJobStart(String name, int uid) {
413        enforceCallingPermission();
414        synchronized (mStats) {
415            mStats.noteJobStartLocked(name, uid);
416        }
417    }
418
419    public void noteJobFinish(String name, int uid) {
420        enforceCallingPermission();
421        synchronized (mStats) {
422            mStats.noteJobFinishLocked(name, uid);
423        }
424    }
425
426    public void noteAlarmStart(String name, int uid) {
427        enforceCallingPermission();
428        synchronized (mStats) {
429            mStats.noteAlarmStartLocked(name, uid);
430        }
431    }
432
433    public void noteAlarmFinish(String name, int uid) {
434        enforceCallingPermission();
435        synchronized (mStats) {
436            mStats.noteAlarmFinishLocked(name, uid);
437        }
438    }
439
440    public void noteStartWakelock(int uid, int pid, String name, String historyName, int type,
441            boolean unimportantForLogging) {
442        enforceCallingPermission();
443        synchronized (mStats) {
444            mStats.noteStartWakeLocked(uid, pid, name, historyName, type, unimportantForLogging,
445                    SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
446        }
447    }
448
449    public void noteStopWakelock(int uid, int pid, String name, String historyName, int type) {
450        enforceCallingPermission();
451        synchronized (mStats) {
452            mStats.noteStopWakeLocked(uid, pid, name, historyName, type,
453                    SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
454        }
455    }
456
457    public void noteStartWakelockFromSource(WorkSource ws, int pid, String name,
458            String historyName, int type, boolean unimportantForLogging) {
459        enforceCallingPermission();
460        synchronized (mStats) {
461            mStats.noteStartWakeFromSourceLocked(ws, pid, name, historyName,
462                    type, unimportantForLogging);
463        }
464    }
465
466    public void noteChangeWakelockFromSource(WorkSource ws, int pid, String name,
467            String historyName, int type, WorkSource newWs, int newPid, String newName,
468            String newHistoryName, int newType, boolean newUnimportantForLogging) {
469        enforceCallingPermission();
470        synchronized (mStats) {
471            mStats.noteChangeWakelockFromSourceLocked(ws, pid, name, historyName, type,
472                    newWs, newPid, newName, newHistoryName, newType, newUnimportantForLogging);
473        }
474    }
475
476    public void noteStopWakelockFromSource(WorkSource ws, int pid, String name, String historyName,
477            int type) {
478        enforceCallingPermission();
479        synchronized (mStats) {
480            mStats.noteStopWakeFromSourceLocked(ws, pid, name, historyName, type);
481        }
482    }
483
484    public void noteStartSensor(int uid, int sensor) {
485        enforceCallingPermission();
486        synchronized (mStats) {
487            mStats.noteStartSensorLocked(uid, sensor);
488        }
489    }
490
491    public void noteStopSensor(int uid, int sensor) {
492        enforceCallingPermission();
493        synchronized (mStats) {
494            mStats.noteStopSensorLocked(uid, sensor);
495        }
496    }
497
498    public void noteVibratorOn(int uid, long durationMillis) {
499        enforceCallingPermission();
500        synchronized (mStats) {
501            mStats.noteVibratorOnLocked(uid, durationMillis);
502        }
503    }
504
505    public void noteVibratorOff(int uid) {
506        enforceCallingPermission();
507        synchronized (mStats) {
508            mStats.noteVibratorOffLocked(uid);
509        }
510    }
511
512    public void noteStartGps(int uid) {
513        enforceCallingPermission();
514        synchronized (mStats) {
515            mStats.noteStartGpsLocked(uid);
516        }
517    }
518
519    public void noteStopGps(int uid) {
520        enforceCallingPermission();
521        synchronized (mStats) {
522            mStats.noteStopGpsLocked(uid);
523        }
524    }
525
526    public void noteScreenState(int state) {
527        enforceCallingPermission();
528        synchronized (mStats) {
529            mStats.noteScreenStateLocked(state);
530        }
531    }
532
533    public void noteScreenBrightness(int brightness) {
534        enforceCallingPermission();
535        synchronized (mStats) {
536            mStats.noteScreenBrightnessLocked(brightness);
537        }
538    }
539
540    public void noteUserActivity(int uid, int event) {
541        enforceCallingPermission();
542        synchronized (mStats) {
543            mStats.noteUserActivityLocked(uid, event);
544        }
545    }
546
547    public void noteWakeUp(String reason, int reasonUid) {
548        enforceCallingPermission();
549        synchronized (mStats) {
550            mStats.noteWakeUpLocked(reason, reasonUid);
551        }
552    }
553
554    public void noteInteractive(boolean interactive) {
555        enforceCallingPermission();
556        synchronized (mStats) {
557            mStats.noteInteractiveLocked(interactive);
558        }
559    }
560
561    public void noteConnectivityChanged(int type, String extra) {
562        enforceCallingPermission();
563        synchronized (mStats) {
564            mStats.noteConnectivityChangedLocked(type, extra);
565        }
566    }
567
568    public void noteMobileRadioPowerState(int powerState, long timestampNs, int uid) {
569        enforceCallingPermission();
570        synchronized (mStats) {
571            mStats.noteMobileRadioPowerState(powerState, timestampNs, uid);
572        }
573    }
574
575    public void notePhoneOn() {
576        enforceCallingPermission();
577        synchronized (mStats) {
578            mStats.notePhoneOnLocked();
579        }
580    }
581
582    public void notePhoneOff() {
583        enforceCallingPermission();
584        synchronized (mStats) {
585            mStats.notePhoneOffLocked();
586        }
587    }
588
589    public void notePhoneSignalStrength(SignalStrength signalStrength) {
590        enforceCallingPermission();
591        synchronized (mStats) {
592            mStats.notePhoneSignalStrengthLocked(signalStrength);
593        }
594    }
595
596    public void notePhoneDataConnectionState(int dataType, boolean hasData) {
597        enforceCallingPermission();
598        synchronized (mStats) {
599            mStats.notePhoneDataConnectionStateLocked(dataType, hasData);
600        }
601    }
602
603    public void notePhoneState(int state) {
604        enforceCallingPermission();
605        int simState = TelephonyManager.getDefault().getSimState();
606        synchronized (mStats) {
607            mStats.notePhoneStateLocked(state, simState);
608        }
609    }
610
611    public void noteWifiOn() {
612        enforceCallingPermission();
613        synchronized (mStats) {
614            mStats.noteWifiOnLocked();
615        }
616    }
617
618    public void noteWifiOff() {
619        enforceCallingPermission();
620        synchronized (mStats) {
621            mStats.noteWifiOffLocked();
622        }
623    }
624
625    public void noteStartAudio(int uid) {
626        enforceCallingPermission();
627        synchronized (mStats) {
628            mStats.noteAudioOnLocked(uid);
629        }
630    }
631
632    public void noteStopAudio(int uid) {
633        enforceCallingPermission();
634        synchronized (mStats) {
635            mStats.noteAudioOffLocked(uid);
636        }
637    }
638
639    public void noteStartVideo(int uid) {
640        enforceCallingPermission();
641        synchronized (mStats) {
642            mStats.noteVideoOnLocked(uid);
643        }
644    }
645
646    public void noteStopVideo(int uid) {
647        enforceCallingPermission();
648        synchronized (mStats) {
649            mStats.noteVideoOffLocked(uid);
650        }
651    }
652
653    public void noteResetAudio() {
654        enforceCallingPermission();
655        synchronized (mStats) {
656            mStats.noteResetAudioLocked();
657        }
658    }
659
660    public void noteResetVideo() {
661        enforceCallingPermission();
662        synchronized (mStats) {
663            mStats.noteResetVideoLocked();
664        }
665    }
666
667    public void noteFlashlightOn(int uid) {
668        enforceCallingPermission();
669        synchronized (mStats) {
670            mStats.noteFlashlightOnLocked(uid);
671        }
672    }
673
674    public void noteFlashlightOff(int uid) {
675        enforceCallingPermission();
676        synchronized (mStats) {
677            mStats.noteFlashlightOffLocked(uid);
678        }
679    }
680
681    public void noteStartCamera(int uid) {
682        enforceCallingPermission();
683        synchronized (mStats) {
684            mStats.noteCameraOnLocked(uid);
685        }
686    }
687
688    public void noteStopCamera(int uid) {
689        enforceCallingPermission();
690        synchronized (mStats) {
691            mStats.noteCameraOffLocked(uid);
692        }
693    }
694
695    public void noteResetCamera() {
696        enforceCallingPermission();
697        synchronized (mStats) {
698            mStats.noteResetCameraLocked();
699        }
700    }
701
702    public void noteResetFlashlight() {
703        enforceCallingPermission();
704        synchronized (mStats) {
705            mStats.noteResetFlashlightLocked();
706        }
707    }
708
709    @Override
710    public void noteWifiRadioPowerState(int powerState, long tsNanos) {
711        enforceCallingPermission();
712
713        // There was a change in WiFi power state.
714        // Collect data now for the past activity.
715        synchronized (mStats) {
716            if (mStats.isOnBattery()) {
717                final String type = (powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH ||
718                        powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM) ? "active"
719                        : "inactive";
720                mHandler.scheduleSync("wifi-data: " + type,
721                        BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI);
722            }
723            mStats.noteWifiRadioPowerState(powerState, tsNanos);
724        }
725    }
726
727    public void noteWifiRunning(WorkSource ws) {
728        enforceCallingPermission();
729        synchronized (mStats) {
730            mStats.noteWifiRunningLocked(ws);
731        }
732    }
733
734    public void noteWifiRunningChanged(WorkSource oldWs, WorkSource newWs) {
735        enforceCallingPermission();
736        synchronized (mStats) {
737            mStats.noteWifiRunningChangedLocked(oldWs, newWs);
738        }
739    }
740
741    public void noteWifiStopped(WorkSource ws) {
742        enforceCallingPermission();
743        synchronized (mStats) {
744            mStats.noteWifiStoppedLocked(ws);
745        }
746    }
747
748    public void noteWifiState(int wifiState, String accessPoint) {
749        enforceCallingPermission();
750        synchronized (mStats) {
751            mStats.noteWifiStateLocked(wifiState, accessPoint);
752        }
753    }
754
755    public void noteWifiSupplicantStateChanged(int supplState, boolean failedAuth) {
756        enforceCallingPermission();
757        synchronized (mStats) {
758            mStats.noteWifiSupplicantStateChangedLocked(supplState, failedAuth);
759        }
760    }
761
762    public void noteWifiRssiChanged(int newRssi) {
763        enforceCallingPermission();
764        synchronized (mStats) {
765            mStats.noteWifiRssiChangedLocked(newRssi);
766        }
767    }
768
769    public void noteFullWifiLockAcquired(int uid) {
770        enforceCallingPermission();
771        synchronized (mStats) {
772            mStats.noteFullWifiLockAcquiredLocked(uid);
773        }
774    }
775
776    public void noteFullWifiLockReleased(int uid) {
777        enforceCallingPermission();
778        synchronized (mStats) {
779            mStats.noteFullWifiLockReleasedLocked(uid);
780        }
781    }
782
783    public void noteWifiScanStarted(int uid) {
784        enforceCallingPermission();
785        synchronized (mStats) {
786            mStats.noteWifiScanStartedLocked(uid);
787        }
788    }
789
790    public void noteWifiScanStopped(int uid) {
791        enforceCallingPermission();
792        synchronized (mStats) {
793            mStats.noteWifiScanStoppedLocked(uid);
794        }
795    }
796
797    public void noteWifiMulticastEnabled(int uid) {
798        enforceCallingPermission();
799        synchronized (mStats) {
800            mStats.noteWifiMulticastEnabledLocked(uid);
801        }
802    }
803
804    public void noteWifiMulticastDisabled(int uid) {
805        enforceCallingPermission();
806        synchronized (mStats) {
807            mStats.noteWifiMulticastDisabledLocked(uid);
808        }
809    }
810
811    public void noteFullWifiLockAcquiredFromSource(WorkSource ws) {
812        enforceCallingPermission();
813        synchronized (mStats) {
814            mStats.noteFullWifiLockAcquiredFromSourceLocked(ws);
815        }
816    }
817
818    public void noteFullWifiLockReleasedFromSource(WorkSource ws) {
819        enforceCallingPermission();
820        synchronized (mStats) {
821            mStats.noteFullWifiLockReleasedFromSourceLocked(ws);
822        }
823    }
824
825    public void noteWifiScanStartedFromSource(WorkSource ws) {
826        enforceCallingPermission();
827        synchronized (mStats) {
828            mStats.noteWifiScanStartedFromSourceLocked(ws);
829        }
830    }
831
832    public void noteWifiScanStoppedFromSource(WorkSource ws) {
833        enforceCallingPermission();
834        synchronized (mStats) {
835            mStats.noteWifiScanStoppedFromSourceLocked(ws);
836        }
837    }
838
839    public void noteWifiBatchedScanStartedFromSource(WorkSource ws, int csph) {
840        enforceCallingPermission();
841        synchronized (mStats) {
842            mStats.noteWifiBatchedScanStartedFromSourceLocked(ws, csph);
843        }
844    }
845
846    public void noteWifiBatchedScanStoppedFromSource(WorkSource ws) {
847        enforceCallingPermission();
848        synchronized (mStats) {
849            mStats.noteWifiBatchedScanStoppedFromSourceLocked(ws);
850        }
851    }
852
853    public void noteWifiMulticastEnabledFromSource(WorkSource ws) {
854        enforceCallingPermission();
855        synchronized (mStats) {
856            mStats.noteWifiMulticastEnabledFromSourceLocked(ws);
857        }
858    }
859
860    @Override
861    public void noteWifiMulticastDisabledFromSource(WorkSource ws) {
862        enforceCallingPermission();
863        synchronized (mStats) {
864            mStats.noteWifiMulticastDisabledFromSourceLocked(ws);
865        }
866    }
867
868    @Override
869    public void noteNetworkInterfaceType(String iface, int networkType) {
870        enforceCallingPermission();
871        synchronized (mStats) {
872            mStats.noteNetworkInterfaceTypeLocked(iface, networkType);
873        }
874    }
875
876    @Override
877    public void noteNetworkStatsEnabled() {
878        enforceCallingPermission();
879        synchronized (mStats) {
880            mStats.noteNetworkStatsEnabledLocked();
881        }
882    }
883
884    @Override
885    public void noteDeviceIdleMode(int mode, String activeReason, int activeUid) {
886        enforceCallingPermission();
887        synchronized (mStats) {
888            mStats.noteDeviceIdleModeLocked(mode, activeReason, activeUid);
889        }
890    }
891
892    public void notePackageInstalled(String pkgName, int versionCode) {
893        enforceCallingPermission();
894        synchronized (mStats) {
895            mStats.notePackageInstalledLocked(pkgName, versionCode);
896        }
897    }
898
899    public void notePackageUninstalled(String pkgName) {
900        enforceCallingPermission();
901        synchronized (mStats) {
902            mStats.notePackageUninstalledLocked(pkgName);
903        }
904    }
905
906    @Override
907    public void noteBleScanStarted(WorkSource ws) {
908        enforceCallingPermission();
909        synchronized (mStats) {
910            mStats.noteBluetoothScanStartedFromSourceLocked(ws);
911        }
912    }
913
914    @Override
915    public void noteBleScanStopped(WorkSource ws) {
916        enforceCallingPermission();
917        synchronized (mStats) {
918            mStats.noteBluetoothScanStoppedFromSourceLocked(ws);
919        }
920    }
921
922    @Override
923    public void noteResetBleScan() {
924        enforceCallingPermission();
925        synchronized (mStats) {
926            mStats.noteResetBluetoothScanLocked();
927        }
928    }
929
930    @Override
931    public void noteWifiControllerActivity(WifiActivityEnergyInfo info) {
932        enforceCallingPermission();
933
934        if (info == null || !info.isValid()) {
935            Slog.e(TAG, "invalid wifi data given: " + info);
936            return;
937        }
938
939        synchronized (mStats) {
940            mStats.updateWifiStateLocked(info);
941        }
942    }
943
944    @Override
945    public void noteBluetoothControllerActivity(BluetoothActivityEnergyInfo info) {
946        enforceCallingPermission();
947        if (info == null || !info.isValid()) {
948            Slog.e(TAG, "invalid bluetooth data given: " + info);
949            return;
950        }
951
952        synchronized (mStats) {
953            mStats.updateBluetoothStateLocked(info);
954        }
955    }
956
957    @Override
958    public void noteModemControllerActivity(ModemActivityInfo info) {
959        enforceCallingPermission();
960
961        if (info == null || !info.isValid()) {
962            Slog.e(TAG, "invalid modem data given: " + info);
963            return;
964        }
965
966        synchronized (mStats) {
967            mStats.updateMobileRadioStateLocked(SystemClock.elapsedRealtime(), info);
968        }
969    }
970
971    public boolean isOnBattery() {
972        return mStats.isOnBattery();
973    }
974
975    @Override
976    public void setBatteryState(final int status, final int health, final int plugType,
977            final int level, final int temp, final int volt, final int chargeUAh) {
978        enforceCallingPermission();
979
980        // BatteryService calls us here and we may update external state. It would be wrong
981        // to block such a low level service like BatteryService on external stats like WiFi.
982        mHandler.post(new Runnable() {
983            @Override
984            public void run() {
985                synchronized (mStats) {
986                    final boolean onBattery = plugType == BatteryStatsImpl.BATTERY_PLUGGED_NONE;
987                    if (mStats.isOnBattery() == onBattery) {
988                        // The battery state has not changed, so we don't need to sync external
989                        // stats immediately.
990                        mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt,
991                                chargeUAh);
992                        return;
993                    }
994                }
995
996                // Sync external stats first as the battery has changed states. If we don't sync
997                // immediately here, we may not collect the relevant data later.
998                updateExternalStatsSync("battery-state", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
999                synchronized (mStats) {
1000                    mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt,
1001                            chargeUAh);
1002                }
1003            }
1004        });
1005    }
1006
1007    public long getAwakeTimeBattery() {
1008        mContext.enforceCallingOrSelfPermission(
1009                android.Manifest.permission.BATTERY_STATS, null);
1010        return mStats.getAwakeTimeBattery();
1011    }
1012
1013    public long getAwakeTimePlugged() {
1014        mContext.enforceCallingOrSelfPermission(
1015                android.Manifest.permission.BATTERY_STATS, null);
1016        return mStats.getAwakeTimePlugged();
1017    }
1018
1019    public void enforceCallingPermission() {
1020        if (Binder.getCallingPid() == Process.myPid()) {
1021            return;
1022        }
1023        mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
1024                Binder.getCallingPid(), Binder.getCallingUid(), null);
1025    }
1026
1027    final class WakeupReasonThread extends Thread {
1028        private static final int MAX_REASON_SIZE = 512;
1029        private CharsetDecoder mDecoder;
1030        private ByteBuffer mUtf8Buffer;
1031        private CharBuffer mUtf16Buffer;
1032
1033        WakeupReasonThread() {
1034            super("BatteryStats_wakeupReason");
1035        }
1036
1037        public void run() {
1038            Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
1039
1040            mDecoder = StandardCharsets.UTF_8
1041                    .newDecoder()
1042                    .onMalformedInput(CodingErrorAction.REPLACE)
1043                    .onUnmappableCharacter(CodingErrorAction.REPLACE)
1044                    .replaceWith("?");
1045
1046            mUtf8Buffer = ByteBuffer.allocateDirect(MAX_REASON_SIZE);
1047            mUtf16Buffer = CharBuffer.allocate(MAX_REASON_SIZE);
1048
1049            try {
1050                String reason;
1051                while ((reason = waitWakeup()) != null) {
1052                    synchronized (mStats) {
1053                        mStats.noteWakeupReasonLocked(reason);
1054                    }
1055                }
1056            } catch (RuntimeException e) {
1057                Slog.e(TAG, "Failure reading wakeup reasons", e);
1058            }
1059        }
1060
1061        private String waitWakeup() {
1062            mUtf8Buffer.clear();
1063            mUtf16Buffer.clear();
1064            mDecoder.reset();
1065
1066            int bytesWritten = nativeWaitWakeup(mUtf8Buffer);
1067            if (bytesWritten < 0) {
1068                return null;
1069            } else if (bytesWritten == 0) {
1070                return "unknown";
1071            }
1072
1073            // Set the buffer's limit to the number of bytes written.
1074            mUtf8Buffer.limit(bytesWritten);
1075
1076            // Decode the buffer from UTF-8 to UTF-16.
1077            // Unmappable characters will be replaced.
1078            mDecoder.decode(mUtf8Buffer, mUtf16Buffer, true);
1079            mUtf16Buffer.flip();
1080
1081            // Create a String from the UTF-16 buffer.
1082            return mUtf16Buffer.toString();
1083        }
1084    }
1085
1086    private static native int nativeWaitWakeup(ByteBuffer outBuffer);
1087
1088    private void dumpHelp(PrintWriter pw) {
1089        pw.println("Battery stats (batterystats) dump options:");
1090        pw.println("  [--checkin] [--history] [--history-start] [--charged] [-c]");
1091        pw.println("  [--daily] [--reset] [--write] [--new-daily] [--read-daily] [-h] [<package.name>]");
1092        pw.println("  --checkin: generate output for a checkin report; will write (and clear) the");
1093        pw.println("             last old completed stats when they had been reset.");
1094        pw.println("  -c: write the current stats in checkin format.");
1095        pw.println("  --history: show only history data.");
1096        pw.println("  --history-start <num>: show only history data starting at given time offset.");
1097        pw.println("  --charged: only output data since last charged.");
1098        pw.println("  --daily: only output full daily data.");
1099        pw.println("  --reset: reset the stats, clearing all current data.");
1100        pw.println("  --write: force write current collected stats to disk.");
1101        pw.println("  --new-daily: immediately create and write new daily stats record.");
1102        pw.println("  --read-daily: read-load last written daily stats.");
1103        pw.println("  <package.name>: optional name of package to filter output by.");
1104        pw.println("  -h: print this help text.");
1105        pw.println("Battery stats (batterystats) commands:");
1106        pw.println("  enable|disable <option>");
1107        pw.println("    Enable or disable a running option.  Option state is not saved across boots.");
1108        pw.println("    Options are:");
1109        pw.println("      full-history: include additional detailed events in battery history:");
1110        pw.println("          wake_lock_in, alarms and proc events");
1111        pw.println("      no-auto-reset: don't automatically reset stats when unplugged");
1112    }
1113
1114    private int doEnableOrDisable(PrintWriter pw, int i, String[] args, boolean enable) {
1115        i++;
1116        if (i >= args.length) {
1117            pw.println("Missing option argument for " + (enable ? "--enable" : "--disable"));
1118            dumpHelp(pw);
1119            return -1;
1120        }
1121        if ("full-wake-history".equals(args[i]) || "full-history".equals(args[i])) {
1122            synchronized (mStats) {
1123                mStats.setRecordAllHistoryLocked(enable);
1124            }
1125        } else if ("no-auto-reset".equals(args[i])) {
1126            synchronized (mStats) {
1127                mStats.setNoAutoReset(enable);
1128            }
1129        } else {
1130            pw.println("Unknown enable/disable option: " + args[i]);
1131            dumpHelp(pw);
1132            return -1;
1133        }
1134        return i;
1135    }
1136
1137
1138    @Override
1139    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1140        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1141                != PackageManager.PERMISSION_GRANTED) {
1142            pw.println("Permission Denial: can't dump BatteryStats from from pid="
1143                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
1144                    + " without permission " + android.Manifest.permission.DUMP);
1145            return;
1146        }
1147
1148        int flags = 0;
1149        boolean useCheckinFormat = false;
1150        boolean isRealCheckin = false;
1151        boolean noOutput = false;
1152        boolean writeData = false;
1153        long historyStart = -1;
1154        int reqUid = -1;
1155        if (args != null) {
1156            for (int i=0; i<args.length; i++) {
1157                String arg = args[i];
1158                if ("--checkin".equals(arg)) {
1159                    useCheckinFormat = true;
1160                    isRealCheckin = true;
1161                } else if ("--history".equals(arg)) {
1162                    flags |= BatteryStats.DUMP_HISTORY_ONLY;
1163                } else if ("--history-start".equals(arg)) {
1164                    flags |= BatteryStats.DUMP_HISTORY_ONLY;
1165                    i++;
1166                    if (i >= args.length) {
1167                        pw.println("Missing time argument for --history-since");
1168                        dumpHelp(pw);
1169                        return;
1170                    }
1171                    historyStart = Long.parseLong(args[i]);
1172                    writeData = true;
1173                } else if ("-c".equals(arg)) {
1174                    useCheckinFormat = true;
1175                    flags |= BatteryStats.DUMP_INCLUDE_HISTORY;
1176                } else if ("--charged".equals(arg)) {
1177                    flags |= BatteryStats.DUMP_CHARGED_ONLY;
1178                } else if ("--daily".equals(arg)) {
1179                    flags |= BatteryStats.DUMP_DAILY_ONLY;
1180                } else if ("--reset".equals(arg)) {
1181                    synchronized (mStats) {
1182                        mStats.resetAllStatsCmdLocked();
1183                        pw.println("Battery stats reset.");
1184                        noOutput = true;
1185                    }
1186                    updateExternalStatsSync("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
1187                } else if ("--write".equals(arg)) {
1188                    updateExternalStatsSync("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
1189                    synchronized (mStats) {
1190                        mStats.writeSyncLocked();
1191                        pw.println("Battery stats written.");
1192                        noOutput = true;
1193                    }
1194                } else if ("--new-daily".equals(arg)) {
1195                    synchronized (mStats) {
1196                        mStats.recordDailyStatsLocked();
1197                        pw.println("New daily stats written.");
1198                        noOutput = true;
1199                    }
1200                } else if ("--read-daily".equals(arg)) {
1201                    synchronized (mStats) {
1202                        mStats.readDailyStatsLocked();
1203                        pw.println("Last daily stats read.");
1204                        noOutput = true;
1205                    }
1206                } else if ("--enable".equals(arg) || "enable".equals(arg)) {
1207                    i = doEnableOrDisable(pw, i, args, true);
1208                    if (i < 0) {
1209                        return;
1210                    }
1211                    pw.println("Enabled: " + args[i]);
1212                    return;
1213                } else if ("--disable".equals(arg) || "disable".equals(arg)) {
1214                    i = doEnableOrDisable(pw, i, args, false);
1215                    if (i < 0) {
1216                        return;
1217                    }
1218                    pw.println("Disabled: " + args[i]);
1219                    return;
1220                } else if ("-h".equals(arg)) {
1221                    dumpHelp(pw);
1222                    return;
1223                } else if ("-a".equals(arg)) {
1224                    flags |= BatteryStats.DUMP_VERBOSE;
1225                } else if (arg.length() > 0 && arg.charAt(0) == '-'){
1226                    pw.println("Unknown option: " + arg);
1227                    dumpHelp(pw);
1228                    return;
1229                } else {
1230                    // Not an option, last argument must be a package name.
1231                    try {
1232                        reqUid = mContext.getPackageManager().getPackageUidAsUser(arg,
1233                                UserHandle.getCallingUserId());
1234                    } catch (PackageManager.NameNotFoundException e) {
1235                        pw.println("Unknown package: " + arg);
1236                        dumpHelp(pw);
1237                        return;
1238                    }
1239                }
1240            }
1241        }
1242        if (noOutput) {
1243            return;
1244        }
1245
1246        long ident = Binder.clearCallingIdentity();
1247        try {
1248            if (BatteryStatsHelper.checkWifiOnly(mContext)) {
1249                flags |= BatteryStats.DUMP_DEVICE_WIFI_ONLY;
1250            }
1251            // Fetch data from external sources and update the BatteryStatsImpl object with them.
1252            updateExternalStatsSync("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
1253        } finally {
1254            Binder.restoreCallingIdentity(ident);
1255        }
1256
1257        if (reqUid >= 0) {
1258            // By default, if the caller is only interested in a specific package, then
1259            // we only dump the aggregated data since charged.
1260            if ((flags&(BatteryStats.DUMP_HISTORY_ONLY|BatteryStats.DUMP_CHARGED_ONLY)) == 0) {
1261                flags |= BatteryStats.DUMP_CHARGED_ONLY;
1262                // Also if they are doing -c, we don't want history.
1263                flags &= ~BatteryStats.DUMP_INCLUDE_HISTORY;
1264            }
1265        }
1266
1267        if (useCheckinFormat) {
1268            List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(
1269                    PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.MATCH_ALL);
1270            if (isRealCheckin) {
1271                // For a real checkin, first we want to prefer to use the last complete checkin
1272                // file if there is one.
1273                synchronized (mStats.mCheckinFile) {
1274                    if (mStats.mCheckinFile.exists()) {
1275                        try {
1276                            byte[] raw = mStats.mCheckinFile.readFully();
1277                            if (raw != null) {
1278                                Parcel in = Parcel.obtain();
1279                                in.unmarshall(raw, 0, raw.length);
1280                                in.setDataPosition(0);
1281                                BatteryStatsImpl checkinStats = new BatteryStatsImpl(
1282                                        null, mStats.mHandler, null);
1283                                checkinStats.readSummaryFromParcel(in);
1284                                in.recycle();
1285                                checkinStats.dumpCheckinLocked(mContext, pw, apps, flags,
1286                                        historyStart);
1287                                mStats.mCheckinFile.delete();
1288                                return;
1289                            }
1290                        } catch (IOException | ParcelFormatException e) {
1291                            Slog.w(TAG, "Failure reading checkin file "
1292                                    + mStats.mCheckinFile.getBaseFile(), e);
1293                        }
1294                    }
1295                }
1296            }
1297            synchronized (mStats) {
1298                mStats.dumpCheckinLocked(mContext, pw, apps, flags, historyStart);
1299                if (writeData) {
1300                    mStats.writeAsyncLocked();
1301                }
1302            }
1303        } else {
1304            synchronized (mStats) {
1305                mStats.dumpLocked(mContext, pw, flags, reqUid, historyStart);
1306                if (writeData) {
1307                    mStats.writeAsyncLocked();
1308                }
1309            }
1310        }
1311    }
1312
1313    private WifiActivityEnergyInfo extractDelta(WifiActivityEnergyInfo latest) {
1314        final long timePeriodMs = latest.mTimestamp - mLastInfo.mTimestamp;
1315        final long lastIdleMs = mLastInfo.mControllerIdleTimeMs;
1316        final long lastTxMs = mLastInfo.mControllerTxTimeMs;
1317        final long lastRxMs = mLastInfo.mControllerRxTimeMs;
1318        final long lastEnergy = mLastInfo.mControllerEnergyUsed;
1319
1320        // We will modify the last info object to be the delta, and store the new
1321        // WifiActivityEnergyInfo object as our last one.
1322        final WifiActivityEnergyInfo delta = mLastInfo;
1323        delta.mTimestamp = latest.getTimeStamp();
1324        delta.mStackState = latest.getStackState();
1325
1326        // These times seem to be the most reliable.
1327        delta.mControllerTxTimeMs = latest.mControllerTxTimeMs - lastTxMs;
1328        delta.mControllerRxTimeMs = latest.mControllerRxTimeMs - lastRxMs;
1329
1330        // WiFi calculates the idle time as a difference from the on time and the various
1331        // Rx + Tx times. There seems to be some missing time there because this sometimes
1332        // becomes negative. Just cap it at 0 and move on.
1333        // b/21613534
1334        delta.mControllerIdleTimeMs = Math.max(0, latest.mControllerIdleTimeMs - lastIdleMs);
1335        delta.mControllerEnergyUsed = Math.max(0, latest.mControllerEnergyUsed - lastEnergy);
1336
1337        if (delta.mControllerTxTimeMs < 0 || delta.mControllerRxTimeMs < 0) {
1338            // The stats were reset by the WiFi system (which is why our delta is negative).
1339            // Returns the unaltered stats.
1340            delta.mControllerEnergyUsed = latest.mControllerEnergyUsed;
1341            delta.mControllerRxTimeMs = latest.mControllerRxTimeMs;
1342            delta.mControllerTxTimeMs = latest.mControllerTxTimeMs;
1343            delta.mControllerIdleTimeMs = latest.mControllerIdleTimeMs;
1344
1345            Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + delta);
1346        }
1347
1348        // There is some accuracy error in reports so allow some slop in the results.
1349        final long SAMPLE_ERROR_MILLIS = 750;
1350        final long totalTimeMs = delta.mControllerIdleTimeMs + delta.mControllerRxTimeMs +
1351                delta.mControllerTxTimeMs;
1352        if (totalTimeMs > timePeriodMs + SAMPLE_ERROR_MILLIS) {
1353            StringBuilder sb = new StringBuilder();
1354            sb.append("Total time ");
1355            TimeUtils.formatDuration(totalTimeMs, sb);
1356            sb.append(" is longer than sample period ");
1357            TimeUtils.formatDuration(timePeriodMs, sb);
1358            sb.append(".\n");
1359            sb.append("Previous WiFi snapshot: ").append("idle=");
1360            TimeUtils.formatDuration(lastIdleMs, sb);
1361            sb.append(" rx=");
1362            TimeUtils.formatDuration(lastRxMs, sb);
1363            sb.append(" tx=");
1364            TimeUtils.formatDuration(lastTxMs, sb);
1365            sb.append(" e=").append(lastEnergy);
1366            sb.append("\n");
1367            sb.append("Current WiFi snapshot: ").append("idle=");
1368            TimeUtils.formatDuration(latest.mControllerIdleTimeMs, sb);
1369            sb.append(" rx=");
1370            TimeUtils.formatDuration(latest.mControllerRxTimeMs, sb);
1371            sb.append(" tx=");
1372            TimeUtils.formatDuration(latest.mControllerTxTimeMs, sb);
1373            sb.append(" e=").append(latest.mControllerEnergyUsed);
1374            Slog.wtf(TAG, sb.toString());
1375        }
1376
1377        mLastInfo = latest;
1378        return delta;
1379    }
1380
1381    /**
1382     * Helper method to extract the Parcelable controller info from a
1383     * SynchronousResultReceiver.
1384     */
1385    private static <T extends Parcelable> T awaitControllerInfo(
1386            @Nullable SynchronousResultReceiver receiver) throws TimeoutException {
1387        if (receiver == null) {
1388            return null;
1389        }
1390
1391        final SynchronousResultReceiver.Result result =
1392                receiver.awaitResult(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS);
1393        if (result.bundle != null) {
1394            // This is the final destination for the Bundle.
1395            result.bundle.setDefusable(true);
1396
1397            final T data = result.bundle.getParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY);
1398            if (data != null) {
1399                return data;
1400            }
1401        }
1402        Slog.e(TAG, "no controller energy info supplied");
1403        return null;
1404    }
1405
1406    /**
1407     * Fetches data from external sources (WiFi controller, bluetooth chipset) and updates
1408     * batterystats with that information.
1409     *
1410     * We first grab a lock specific to this method, then once all the data has been collected,
1411     * we grab the mStats lock and update the data.
1412     *
1413     * @param reason The reason why this collection was requested. Useful for debugging.
1414     * @param updateFlags Which external stats to update. Can be a combination of
1415     *                    {@link BatteryStatsImpl.ExternalStatsSync#UPDATE_CPU},
1416     *                    {@link BatteryStatsImpl.ExternalStatsSync#UPDATE_RADIO},
1417     *                    {@link BatteryStatsImpl.ExternalStatsSync#UPDATE_WIFI},
1418     *                    and {@link BatteryStatsImpl.ExternalStatsSync#UPDATE_BT}.
1419     */
1420    void updateExternalStatsSync(final String reason, int updateFlags) {
1421        SynchronousResultReceiver wifiReceiver = null;
1422        SynchronousResultReceiver bluetoothReceiver = null;
1423        SynchronousResultReceiver modemReceiver = null;
1424
1425        synchronized (mExternalStatsLock) {
1426            if (mContext == null) {
1427                // Don't do any work yet.
1428                return;
1429            }
1430
1431            if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) {
1432                if (mWifiManager == null) {
1433                    mWifiManager = IWifiManager.Stub.asInterface(
1434                            ServiceManager.getService(Context.WIFI_SERVICE));
1435                }
1436
1437                if (mWifiManager != null) {
1438                    try {
1439                        wifiReceiver = new SynchronousResultReceiver();
1440                        mWifiManager.requestActivityInfo(wifiReceiver);
1441                    } catch (RemoteException e) {
1442                        // Oh well.
1443                    }
1444                }
1445            }
1446
1447            if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_BT) != 0) {
1448                final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
1449                if (adapter != null) {
1450                    bluetoothReceiver = new SynchronousResultReceiver();
1451                    adapter.requestControllerActivityEnergyInfo(
1452                            BluetoothAdapter.ACTIVITY_ENERGY_INFO_REFRESHED,
1453                            bluetoothReceiver);
1454                }
1455            }
1456
1457            if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO) != 0) {
1458                if (mTelephony == null) {
1459                    mTelephony = TelephonyManager.from(mContext);
1460                }
1461
1462                if (mTelephony != null) {
1463                    modemReceiver = new SynchronousResultReceiver();
1464                    mTelephony.requestModemActivityInfo(modemReceiver);
1465                }
1466            }
1467
1468            WifiActivityEnergyInfo wifiInfo = null;
1469            BluetoothActivityEnergyInfo bluetoothInfo = null;
1470            ModemActivityInfo modemInfo = null;
1471            try {
1472                wifiInfo = awaitControllerInfo(wifiReceiver);
1473            } catch (TimeoutException e) {
1474                Slog.w(TAG, "Timeout reading wifi stats");
1475            }
1476
1477            try {
1478                bluetoothInfo = awaitControllerInfo(bluetoothReceiver);
1479            } catch (TimeoutException e) {
1480                Slog.w(TAG, "Timeout reading bt stats");
1481            }
1482
1483            try {
1484                modemInfo = awaitControllerInfo(modemReceiver);
1485            } catch (TimeoutException e) {
1486                Slog.w(TAG, "Timeout reading modem stats");
1487            }
1488
1489            synchronized (mStats) {
1490                mStats.addHistoryEventLocked(
1491                        SystemClock.elapsedRealtime(),
1492                        SystemClock.uptimeMillis(),
1493                        BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS,
1494                        reason, 0);
1495
1496                mStats.updateCpuTimeLocked();
1497                mStats.updateKernelWakelocksLocked();
1498
1499                if (wifiInfo != null) {
1500                    if (wifiInfo.isValid()) {
1501                        mStats.updateWifiStateLocked(extractDelta(wifiInfo));
1502                    } else {
1503                        Slog.e(TAG, "wifi info is invalid: " + wifiInfo);
1504                    }
1505                }
1506
1507                if (bluetoothInfo != null) {
1508                    if (bluetoothInfo.isValid()) {
1509                        mStats.updateBluetoothStateLocked(bluetoothInfo);
1510                    } else {
1511                        Slog.e(TAG, "bluetooth info is invalid: " + bluetoothInfo);
1512                    }
1513                }
1514
1515                if (modemInfo != null) {
1516                    if (modemInfo.isValid()) {
1517                        mStats.updateMobileRadioStateLocked(SystemClock.elapsedRealtime(),
1518                                modemInfo);
1519                    } else {
1520                        Slog.e(TAG, "modem info is invalid: " + modemInfo);
1521                    }
1522                }
1523            }
1524        }
1525    }
1526
1527    /**
1528     * Gets a snapshot of the system health for a particular uid.
1529     */
1530    @Override
1531    public HealthStatsParceler takeUidSnapshot(int requestUid) {
1532        if (requestUid != Binder.getCallingUid()) {
1533            mContext.enforceCallingOrSelfPermission(
1534                    android.Manifest.permission.BATTERY_STATS, null);
1535        }
1536        long ident = Binder.clearCallingIdentity();
1537        try {
1538            updateExternalStatsSync("get-health-stats-for-uid",
1539                    BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
1540            synchronized (mStats) {
1541                return getHealthStatsForUidLocked(requestUid);
1542            }
1543        } catch (Exception ex) {
1544            Slog.d(TAG, "Crashed while writing for takeUidSnapshot(" + requestUid + ")", ex);
1545            throw ex;
1546        } finally {
1547            Binder.restoreCallingIdentity(ident);
1548        }
1549    }
1550
1551    /**
1552     * Gets a snapshot of the system health for a number of uids.
1553     */
1554    @Override
1555    public HealthStatsParceler[] takeUidSnapshots(int[] requestUids) {
1556        if (!onlyCaller(requestUids)) {
1557            mContext.enforceCallingOrSelfPermission(
1558                    android.Manifest.permission.BATTERY_STATS, null);
1559        }
1560        long ident = Binder.clearCallingIdentity();
1561        int i=-1;
1562        try {
1563            updateExternalStatsSync("get-health-stats-for-uids",
1564                    BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
1565            synchronized (mStats) {
1566                final int N = requestUids.length;
1567                final HealthStatsParceler[] results = new HealthStatsParceler[N];
1568                for (i=0; i<N; i++) {
1569                    results[i] = getHealthStatsForUidLocked(requestUids[i]);
1570                }
1571                return results;
1572            }
1573        } catch (Exception ex) {
1574            Slog.d(TAG, "Crashed while writing for takeUidSnapshots("
1575                    + Arrays.toString(requestUids) + ") i=" + i, ex);
1576            throw ex;
1577        } finally {
1578            Binder.restoreCallingIdentity(ident);
1579        }
1580    }
1581
1582    /**
1583     * Returns whether the Binder.getCallingUid is the only thing in requestUids.
1584     */
1585    private static boolean onlyCaller(int[] requestUids) {
1586        final int caller = Binder.getCallingUid();
1587        final int N = requestUids.length;
1588        for (int i=0; i<N; i++) {
1589            if (requestUids[i] != caller) {
1590                return false;
1591            }
1592        }
1593        return true;
1594    }
1595
1596    /**
1597     * Gets a HealthStatsParceler for the given uid. You should probably call
1598     * updateExternalStatsSync first.
1599     */
1600    HealthStatsParceler getHealthStatsForUidLocked(int requestUid) {
1601        final HealthStatsBatteryStatsWriter writer = new HealthStatsBatteryStatsWriter();
1602        final HealthStatsWriter uidWriter = new HealthStatsWriter(UidHealthStats.CONSTANTS);
1603        final BatteryStats.Uid uid = mStats.getUidStats().get(requestUid);
1604        if (uid != null) {
1605            writer.writeUid(uidWriter, mStats, uid);
1606        }
1607        return new HealthStatsParceler(uidWriter);
1608    }
1609
1610}
1611