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