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.bluetooth.BluetoothAdapter;
20import android.bluetooth.BluetoothHeadset;
21import android.bluetooth.BluetoothProfile;
22import android.content.Context;
23import android.content.pm.ApplicationInfo;
24import android.content.pm.PackageManager;
25import android.os.BatteryStats;
26import android.os.Binder;
27import android.os.Handler;
28import android.os.IBinder;
29import android.os.Parcel;
30import android.os.ParcelFileDescriptor;
31import android.os.PowerManagerInternal;
32import android.os.Process;
33import android.os.ServiceManager;
34import android.os.SystemClock;
35import android.os.UserHandle;
36import android.os.WorkSource;
37import android.telephony.SignalStrength;
38import android.telephony.TelephonyManager;
39import android.util.Slog;
40
41import com.android.internal.app.IBatteryStats;
42import com.android.internal.os.BatteryStatsHelper;
43import com.android.internal.os.BatteryStatsImpl;
44import com.android.internal.os.PowerProfile;
45import com.android.server.LocalServices;
46
47import java.io.File;
48import java.io.FileDescriptor;
49import java.io.FileOutputStream;
50import java.io.IOException;
51import java.io.PrintWriter;
52import java.util.List;
53
54/**
55 * All information we are collecting about things that can happen that impact
56 * battery life.
57 */
58public final class BatteryStatsService extends IBatteryStats.Stub
59        implements PowerManagerInternal.LowPowerModeListener {
60    static final String TAG = "BatteryStatsService";
61
62    static IBatteryStats sService;
63
64    final BatteryStatsImpl mStats;
65    Context mContext;
66    private boolean mBluetoothPendingStats;
67    private BluetoothHeadset mBluetoothHeadset;
68    PowerManagerInternal mPowerManagerInternal;
69
70    BatteryStatsService(File systemDir, Handler handler) {
71        mStats = new BatteryStatsImpl(systemDir, handler);
72    }
73
74    public void publish(Context context) {
75        mContext = context;
76        ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder());
77        mStats.setNumSpeedSteps(new PowerProfile(mContext).getNumSpeedSteps());
78        mStats.setRadioScanningTimeout(mContext.getResources().getInteger(
79                com.android.internal.R.integer.config_radioScanningTimeout)
80                * 1000L);
81    }
82
83    /**
84     * At the time when the constructor runs, the power manager has not yet been
85     * initialized.  So we initialize the low power observer later.
86     */
87    public void initPowerManagement() {
88        mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
89        mPowerManagerInternal.registerLowPowerModeObserver(this);
90        mStats.noteLowPowerMode(mPowerManagerInternal.getLowPowerModeEnabled());
91        (new WakeupReasonThread()).start();
92    }
93
94    public void shutdown() {
95        Slog.w("BatteryStats", "Writing battery stats before shutdown...");
96        synchronized (mStats) {
97            mStats.shutdownLocked();
98        }
99    }
100
101    public static IBatteryStats getService() {
102        if (sService != null) {
103            return sService;
104        }
105        IBinder b = ServiceManager.getService(BatteryStats.SERVICE_NAME);
106        sService = asInterface(b);
107        return sService;
108    }
109
110    @Override
111    public void onLowPowerModeChanged(boolean enabled) {
112        synchronized (mStats) {
113            mStats.noteLowPowerMode(enabled);
114        }
115    }
116
117    /**
118     * @return the current statistics object, which may be modified
119     * to reflect events that affect battery usage.  You must lock the
120     * stats object before doing anything with it.
121     */
122    public BatteryStatsImpl getActiveStatistics() {
123        return mStats;
124    }
125
126    // These are for direct use by the activity manager...
127
128    void addIsolatedUid(int isolatedUid, int appUid) {
129        synchronized (mStats) {
130            mStats.addIsolatedUidLocked(isolatedUid, appUid);
131        }
132    }
133
134    void removeIsolatedUid(int isolatedUid, int appUid) {
135        synchronized (mStats) {
136            mStats.removeIsolatedUidLocked(isolatedUid, appUid);
137        }
138    }
139
140    void noteProcessStart(String name, int uid) {
141        synchronized (mStats) {
142            mStats.noteProcessStartLocked(name, uid);
143        }
144    }
145
146    void noteProcessCrash(String name, int uid) {
147        synchronized (mStats) {
148            mStats.noteProcessCrashLocked(name, uid);
149        }
150    }
151
152    void noteProcessAnr(String name, int uid) {
153        synchronized (mStats) {
154            mStats.noteProcessAnrLocked(name, uid);
155        }
156    }
157
158    void noteProcessState(String name, int uid, int state) {
159        synchronized (mStats) {
160            mStats.noteProcessStateLocked(name, uid, state);
161        }
162    }
163
164    void noteProcessFinish(String name, int uid) {
165        synchronized (mStats) {
166            mStats.noteProcessFinishLocked(name, uid);
167        }
168    }
169
170    // Public interface...
171
172    public byte[] getStatistics() {
173        mContext.enforceCallingPermission(
174                android.Manifest.permission.BATTERY_STATS, null);
175        //Slog.i("foo", "SENDING BATTERY INFO:");
176        //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
177        Parcel out = Parcel.obtain();
178        mStats.writeToParcel(out, 0);
179        byte[] data = out.marshall();
180        out.recycle();
181        return data;
182    }
183
184    public ParcelFileDescriptor getStatisticsStream() {
185        mContext.enforceCallingPermission(
186                android.Manifest.permission.BATTERY_STATS, null);
187        //Slog.i("foo", "SENDING BATTERY INFO:");
188        //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
189        Parcel out = Parcel.obtain();
190        mStats.writeToParcel(out, 0);
191        byte[] data = out.marshall();
192        out.recycle();
193        try {
194            return ParcelFileDescriptor.fromData(data, "battery-stats");
195        } catch (IOException e) {
196            Slog.w(TAG, "Unable to create shared memory", e);
197            return null;
198        }
199    }
200
201    public long computeBatteryTimeRemaining() {
202        synchronized (mStats) {
203            long time = mStats.computeBatteryTimeRemaining(SystemClock.elapsedRealtime());
204            return time >= 0 ? (time/1000) : time;
205        }
206    }
207
208    public long computeChargeTimeRemaining() {
209        synchronized (mStats) {
210            long time = mStats.computeChargeTimeRemaining(SystemClock.elapsedRealtime());
211            return time >= 0 ? (time/1000) : time;
212        }
213    }
214
215    public void noteEvent(int code, String name, int uid) {
216        enforceCallingPermission();
217        synchronized (mStats) {
218            mStats.noteEventLocked(code, name, uid);
219        }
220    }
221
222    public void noteSyncStart(String name, int uid) {
223        enforceCallingPermission();
224        synchronized (mStats) {
225            mStats.noteSyncStartLocked(name, uid);
226        }
227    }
228
229    public void noteSyncFinish(String name, int uid) {
230        enforceCallingPermission();
231        synchronized (mStats) {
232            mStats.noteSyncFinishLocked(name, uid);
233        }
234    }
235
236    public void noteJobStart(String name, int uid) {
237        enforceCallingPermission();
238        synchronized (mStats) {
239            mStats.noteJobStartLocked(name, uid);
240        }
241    }
242
243    public void noteJobFinish(String name, int uid) {
244        enforceCallingPermission();
245        synchronized (mStats) {
246            mStats.noteJobFinishLocked(name, uid);
247        }
248    }
249
250    public void noteStartWakelock(int uid, int pid, String name, String historyName, int type,
251            boolean unimportantForLogging) {
252        enforceCallingPermission();
253        synchronized (mStats) {
254            mStats.noteStartWakeLocked(uid, pid, name, historyName, type, unimportantForLogging,
255                    SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
256        }
257    }
258
259    public void noteStopWakelock(int uid, int pid, String name, String historyName, int type) {
260        enforceCallingPermission();
261        synchronized (mStats) {
262            mStats.noteStopWakeLocked(uid, pid, name, historyName, type,
263                    SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
264        }
265    }
266
267    public void noteStartWakelockFromSource(WorkSource ws, int pid, String name,
268            String historyName, int type, boolean unimportantForLogging) {
269        enforceCallingPermission();
270        synchronized (mStats) {
271            mStats.noteStartWakeFromSourceLocked(ws, pid, name, historyName,
272                    type, unimportantForLogging);
273        }
274    }
275
276    public void noteChangeWakelockFromSource(WorkSource ws, int pid, String name,
277            String historyName, int type, WorkSource newWs, int newPid, String newName,
278            String newHistoryName, int newType, boolean newUnimportantForLogging) {
279        enforceCallingPermission();
280        synchronized (mStats) {
281            mStats.noteChangeWakelockFromSourceLocked(ws, pid, name, historyName, type,
282                    newWs, newPid, newName, newHistoryName, newType, newUnimportantForLogging);
283        }
284    }
285
286    public void noteStopWakelockFromSource(WorkSource ws, int pid, String name, String historyName,
287            int type) {
288        enforceCallingPermission();
289        synchronized (mStats) {
290            mStats.noteStopWakeFromSourceLocked(ws, pid, name, historyName, type);
291        }
292    }
293
294    public void noteStartSensor(int uid, int sensor) {
295        enforceCallingPermission();
296        synchronized (mStats) {
297            mStats.noteStartSensorLocked(uid, sensor);
298        }
299    }
300
301    public void noteStopSensor(int uid, int sensor) {
302        enforceCallingPermission();
303        synchronized (mStats) {
304            mStats.noteStopSensorLocked(uid, sensor);
305        }
306    }
307
308    public void noteVibratorOn(int uid, long durationMillis) {
309        enforceCallingPermission();
310        synchronized (mStats) {
311            mStats.noteVibratorOnLocked(uid, durationMillis);
312        }
313    }
314
315    public void noteVibratorOff(int uid) {
316        enforceCallingPermission();
317        synchronized (mStats) {
318            mStats.noteVibratorOffLocked(uid);
319        }
320    }
321
322    public void noteStartGps(int uid) {
323        enforceCallingPermission();
324        synchronized (mStats) {
325            mStats.noteStartGpsLocked(uid);
326        }
327    }
328
329    public void noteStopGps(int uid) {
330        enforceCallingPermission();
331        synchronized (mStats) {
332            mStats.noteStopGpsLocked(uid);
333        }
334    }
335
336    public void noteScreenState(int state) {
337        enforceCallingPermission();
338        synchronized (mStats) {
339            mStats.noteScreenStateLocked(state);
340        }
341    }
342
343    public void noteScreenBrightness(int brightness) {
344        enforceCallingPermission();
345        synchronized (mStats) {
346            mStats.noteScreenBrightnessLocked(brightness);
347        }
348    }
349
350    public void noteUserActivity(int uid, int event) {
351        enforceCallingPermission();
352        synchronized (mStats) {
353            mStats.noteUserActivityLocked(uid, event);
354        }
355    }
356
357    public void noteInteractive(boolean interactive) {
358        enforceCallingPermission();
359        synchronized (mStats) {
360            mStats.noteInteractiveLocked(interactive);
361        }
362    }
363
364    public void noteConnectivityChanged(int type, String extra) {
365        enforceCallingPermission();
366        synchronized (mStats) {
367            mStats.noteConnectivityChangedLocked(type, extra);
368        }
369    }
370
371    public void noteMobileRadioPowerState(int powerState, long timestampNs) {
372        enforceCallingPermission();
373        synchronized (mStats) {
374            mStats.noteMobileRadioPowerState(powerState, timestampNs);
375        }
376    }
377
378    public void notePhoneOn() {
379        enforceCallingPermission();
380        synchronized (mStats) {
381            mStats.notePhoneOnLocked();
382        }
383    }
384
385    public void notePhoneOff() {
386        enforceCallingPermission();
387        synchronized (mStats) {
388            mStats.notePhoneOffLocked();
389        }
390    }
391
392    public void notePhoneSignalStrength(SignalStrength signalStrength) {
393        enforceCallingPermission();
394        synchronized (mStats) {
395            mStats.notePhoneSignalStrengthLocked(signalStrength);
396        }
397    }
398
399    public void notePhoneDataConnectionState(int dataType, boolean hasData) {
400        enforceCallingPermission();
401        synchronized (mStats) {
402            mStats.notePhoneDataConnectionStateLocked(dataType, hasData);
403        }
404    }
405
406    public void notePhoneState(int state) {
407        enforceCallingPermission();
408        int simState = TelephonyManager.getDefault().getSimState();
409        synchronized (mStats) {
410            mStats.notePhoneStateLocked(state, simState);
411        }
412    }
413
414    public void noteWifiOn() {
415        enforceCallingPermission();
416        synchronized (mStats) {
417            mStats.noteWifiOnLocked();
418        }
419    }
420
421    public void noteWifiOff() {
422        enforceCallingPermission();
423        synchronized (mStats) {
424            mStats.noteWifiOffLocked();
425        }
426    }
427
428    public void noteStartAudio(int uid) {
429        enforceCallingPermission();
430        synchronized (mStats) {
431            mStats.noteAudioOnLocked(uid);
432        }
433    }
434
435    public void noteStopAudio(int uid) {
436        enforceCallingPermission();
437        synchronized (mStats) {
438            mStats.noteAudioOffLocked(uid);
439        }
440    }
441
442    public void noteStartVideo(int uid) {
443        enforceCallingPermission();
444        synchronized (mStats) {
445            mStats.noteVideoOnLocked(uid);
446        }
447    }
448
449    public void noteStopVideo(int uid) {
450        enforceCallingPermission();
451        synchronized (mStats) {
452            mStats.noteVideoOffLocked(uid);
453        }
454    }
455
456    public void noteResetAudio() {
457        enforceCallingPermission();
458        synchronized (mStats) {
459            mStats.noteResetAudioLocked();
460        }
461    }
462
463    public void noteResetVideo() {
464        enforceCallingPermission();
465        synchronized (mStats) {
466            mStats.noteResetVideoLocked();
467        }
468    }
469
470    public void noteFlashlightOn() {
471        enforceCallingPermission();
472        synchronized (mStats) {
473            mStats.noteFlashlightOnLocked();
474        }
475    }
476
477    public void noteFlashlightOff() {
478        enforceCallingPermission();
479        synchronized (mStats) {
480            mStats.noteFlashlightOffLocked();
481        }
482    }
483
484    public void noteWifiRunning(WorkSource ws) {
485        enforceCallingPermission();
486        synchronized (mStats) {
487            mStats.noteWifiRunningLocked(ws);
488        }
489    }
490
491    public void noteWifiRunningChanged(WorkSource oldWs, WorkSource newWs) {
492        enforceCallingPermission();
493        synchronized (mStats) {
494            mStats.noteWifiRunningChangedLocked(oldWs, newWs);
495        }
496    }
497
498    public void noteWifiStopped(WorkSource ws) {
499        enforceCallingPermission();
500        synchronized (mStats) {
501            mStats.noteWifiStoppedLocked(ws);
502        }
503    }
504
505    public void noteWifiState(int wifiState, String accessPoint) {
506        enforceCallingPermission();
507        synchronized (mStats) {
508            mStats.noteWifiStateLocked(wifiState, accessPoint);
509        }
510    }
511
512    public void noteWifiSupplicantStateChanged(int supplState, boolean failedAuth) {
513        enforceCallingPermission();
514        synchronized (mStats) {
515            mStats.noteWifiSupplicantStateChangedLocked(supplState, failedAuth);
516        }
517    }
518
519    public void noteWifiRssiChanged(int newRssi) {
520        enforceCallingPermission();
521        synchronized (mStats) {
522            mStats.noteWifiRssiChangedLocked(newRssi);
523        }
524    }
525
526    public void noteBluetoothOn() {
527        enforceCallingPermission();
528        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
529        if (adapter != null) {
530            adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
531                                    BluetoothProfile.HEADSET);
532        }
533        synchronized (mStats) {
534            if (mBluetoothHeadset != null) {
535                mStats.noteBluetoothOnLocked();
536                mStats.setBtHeadset(mBluetoothHeadset);
537            } else {
538                mBluetoothPendingStats = true;
539            }
540        }
541    }
542
543    private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
544        new BluetoothProfile.ServiceListener() {
545        public void onServiceConnected(int profile, BluetoothProfile proxy) {
546            mBluetoothHeadset = (BluetoothHeadset) proxy;
547            synchronized (mStats) {
548                if (mBluetoothPendingStats) {
549                    mStats.noteBluetoothOnLocked();
550                    mStats.setBtHeadset(mBluetoothHeadset);
551                    mBluetoothPendingStats = false;
552                }
553            }
554        }
555
556        public void onServiceDisconnected(int profile) {
557            mBluetoothHeadset = null;
558        }
559    };
560
561    public void noteBluetoothOff() {
562        enforceCallingPermission();
563        synchronized (mStats) {
564            mBluetoothPendingStats = false;
565            mStats.noteBluetoothOffLocked();
566        }
567    }
568
569    public void noteBluetoothState(int bluetoothState) {
570        enforceCallingPermission();
571        synchronized (mStats) {
572            mStats.noteBluetoothStateLocked(bluetoothState);
573        }
574    }
575
576    public void noteFullWifiLockAcquired(int uid) {
577        enforceCallingPermission();
578        synchronized (mStats) {
579            mStats.noteFullWifiLockAcquiredLocked(uid);
580        }
581    }
582
583    public void noteFullWifiLockReleased(int uid) {
584        enforceCallingPermission();
585        synchronized (mStats) {
586            mStats.noteFullWifiLockReleasedLocked(uid);
587        }
588    }
589
590    public void noteWifiScanStarted(int uid) {
591        enforceCallingPermission();
592        synchronized (mStats) {
593            mStats.noteWifiScanStartedLocked(uid);
594        }
595    }
596
597    public void noteWifiScanStopped(int uid) {
598        enforceCallingPermission();
599        synchronized (mStats) {
600            mStats.noteWifiScanStoppedLocked(uid);
601        }
602    }
603
604    public void noteWifiMulticastEnabled(int uid) {
605        enforceCallingPermission();
606        synchronized (mStats) {
607            mStats.noteWifiMulticastEnabledLocked(uid);
608        }
609    }
610
611    public void noteWifiMulticastDisabled(int uid) {
612        enforceCallingPermission();
613        synchronized (mStats) {
614            mStats.noteWifiMulticastDisabledLocked(uid);
615        }
616    }
617
618    public void noteFullWifiLockAcquiredFromSource(WorkSource ws) {
619        enforceCallingPermission();
620        synchronized (mStats) {
621            mStats.noteFullWifiLockAcquiredFromSourceLocked(ws);
622        }
623    }
624
625    public void noteFullWifiLockReleasedFromSource(WorkSource ws) {
626        enforceCallingPermission();
627        synchronized (mStats) {
628            mStats.noteFullWifiLockReleasedFromSourceLocked(ws);
629        }
630    }
631
632    public void noteWifiScanStartedFromSource(WorkSource ws) {
633        enforceCallingPermission();
634        synchronized (mStats) {
635            mStats.noteWifiScanStartedFromSourceLocked(ws);
636        }
637    }
638
639    public void noteWifiScanStoppedFromSource(WorkSource ws) {
640        enforceCallingPermission();
641        synchronized (mStats) {
642            mStats.noteWifiScanStoppedFromSourceLocked(ws);
643        }
644    }
645
646    public void noteWifiBatchedScanStartedFromSource(WorkSource ws, int csph) {
647        enforceCallingPermission();
648        synchronized (mStats) {
649            mStats.noteWifiBatchedScanStartedFromSourceLocked(ws, csph);
650        }
651    }
652
653    public void noteWifiBatchedScanStoppedFromSource(WorkSource ws) {
654        enforceCallingPermission();
655        synchronized (mStats) {
656            mStats.noteWifiBatchedScanStoppedFromSourceLocked(ws);
657        }
658    }
659
660    public void noteWifiMulticastEnabledFromSource(WorkSource ws) {
661        enforceCallingPermission();
662        synchronized (mStats) {
663            mStats.noteWifiMulticastEnabledFromSourceLocked(ws);
664        }
665    }
666
667    public void noteWifiMulticastDisabledFromSource(WorkSource ws) {
668        enforceCallingPermission();
669        synchronized (mStats) {
670            mStats.noteWifiMulticastDisabledFromSourceLocked(ws);
671        }
672    }
673
674    @Override
675    public void noteNetworkInterfaceType(String iface, int type) {
676        enforceCallingPermission();
677        synchronized (mStats) {
678            mStats.noteNetworkInterfaceTypeLocked(iface, type);
679        }
680    }
681
682    @Override
683    public void noteNetworkStatsEnabled() {
684        enforceCallingPermission();
685        synchronized (mStats) {
686            mStats.noteNetworkStatsEnabledLocked();
687        }
688    }
689
690    public boolean isOnBattery() {
691        return mStats.isOnBattery();
692    }
693
694    public void setBatteryState(int status, int health, int plugType, int level,
695            int temp, int volt) {
696        enforceCallingPermission();
697        mStats.setBatteryState(status, health, plugType, level, temp, volt);
698    }
699
700    public long getAwakeTimeBattery() {
701        mContext.enforceCallingOrSelfPermission(
702                android.Manifest.permission.BATTERY_STATS, null);
703        return mStats.getAwakeTimeBattery();
704    }
705
706    public long getAwakeTimePlugged() {
707        mContext.enforceCallingOrSelfPermission(
708                android.Manifest.permission.BATTERY_STATS, null);
709        return mStats.getAwakeTimePlugged();
710    }
711
712    public void enforceCallingPermission() {
713        if (Binder.getCallingPid() == Process.myPid()) {
714            return;
715        }
716        mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
717                Binder.getCallingPid(), Binder.getCallingUid(), null);
718    }
719
720    final class WakeupReasonThread extends Thread {
721        final int[] mIrqs = new int[32];
722        final String[] mReasons = new String[32];
723
724        WakeupReasonThread() {
725            super("BatteryStats_wakeupReason");
726        }
727
728        public void run() {
729            Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
730
731            try {
732                int num;
733                while ((num=nativeWaitWakeup(mIrqs, mReasons)) >= 0) {
734                    synchronized (mStats) {
735                        if (num > 0) {
736                            for (int i=0; i<num; i++) {
737                                mStats.noteWakeupReasonLocked(mReasons[i]);
738                            }
739                        } else {
740                            mStats.noteWakeupReasonLocked("unknown");
741                        }
742                    }
743                }
744            } catch (RuntimeException e) {
745                Slog.e(TAG, "Failure reading wakeup reasons", e);
746            }
747        }
748    }
749
750    private static native int nativeWaitWakeup(int[] outIrqs, String[] outReasons);
751
752    private void dumpHelp(PrintWriter pw) {
753        pw.println("Battery stats (batterystats) dump options:");
754        pw.println("  [--checkin] [--history] [--history-start] [--unplugged] [--charged] [-c]");
755        pw.println("  [--reset] [--write] [-h] [<package.name>]");
756        pw.println("  --checkin: format output for a checkin report.");
757        pw.println("  --history: show only history data.");
758        pw.println("  --history-start <num>: show only history data starting at given time offset.");
759        pw.println("  --unplugged: only output data since last unplugged.");
760        pw.println("  --charged: only output data since last charged.");
761        pw.println("  --reset: reset the stats, clearing all current data.");
762        pw.println("  --write: force write current collected stats to disk.");
763        pw.println("  <package.name>: optional name of package to filter output by.");
764        pw.println("  -h: print this help text.");
765        pw.println("Battery stats (batterystats) commands:");
766        pw.println("  enable|disable <option>");
767        pw.println("    Enable or disable a running option.  Option state is not saved across boots.");
768        pw.println("    Options are:");
769        pw.println("      full-history: include additional detailed events in battery history:");
770        pw.println("          wake_lock_in and proc events");
771        pw.println("      no-auto-reset: don't automatically reset stats when unplugged");
772    }
773
774    private int doEnableOrDisable(PrintWriter pw, int i, String[] args, boolean enable) {
775        i++;
776        if (i >= args.length) {
777            pw.println("Missing option argument for " + (enable ? "--enable" : "--disable"));
778            dumpHelp(pw);
779            return -1;
780        }
781        if ("full-wake-history".equals(args[i]) || "full-history".equals(args[i])) {
782            synchronized (mStats) {
783                mStats.setRecordAllHistoryLocked(enable);
784            }
785        } else if ("no-auto-reset".equals(args[i])) {
786            synchronized (mStats) {
787                mStats.setNoAutoReset(enable);
788            }
789        } else {
790            pw.println("Unknown enable/disable option: " + args[i]);
791            dumpHelp(pw);
792            return -1;
793        }
794        return i;
795    }
796
797    @Override
798    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
799        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
800                != PackageManager.PERMISSION_GRANTED) {
801            pw.println("Permission Denial: can't dump BatteryStats from from pid="
802                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
803                    + " without permission " + android.Manifest.permission.DUMP);
804            return;
805        }
806
807        int flags = 0;
808        boolean useCheckinFormat = false;
809        boolean isRealCheckin = false;
810        boolean noOutput = false;
811        boolean writeData = false;
812        long historyStart = -1;
813        int reqUid = -1;
814        if (args != null) {
815            for (int i=0; i<args.length; i++) {
816                String arg = args[i];
817                if ("--checkin".equals(arg)) {
818                    useCheckinFormat = true;
819                    isRealCheckin = true;
820                } else if ("--history".equals(arg)) {
821                    flags |= BatteryStats.DUMP_HISTORY_ONLY;
822                } else if ("--history-start".equals(arg)) {
823                    flags |= BatteryStats.DUMP_HISTORY_ONLY;
824                    i++;
825                    if (i >= args.length) {
826                        pw.println("Missing time argument for --history-since");
827                        dumpHelp(pw);
828                        return;
829                    }
830                    historyStart = Long.parseLong(args[i]);
831                    writeData = true;
832                } else if ("-c".equals(arg)) {
833                    useCheckinFormat = true;
834                    flags |= BatteryStats.DUMP_INCLUDE_HISTORY;
835                } else if ("--unplugged".equals(arg)) {
836                    flags |= BatteryStats.DUMP_UNPLUGGED_ONLY;
837                } else if ("--charged".equals(arg)) {
838                    flags |= BatteryStats.DUMP_CHARGED_ONLY;
839                } else if ("--reset".equals(arg)) {
840                    synchronized (mStats) {
841                        mStats.resetAllStatsCmdLocked();
842                        pw.println("Battery stats reset.");
843                        noOutput = true;
844                    }
845                } else if ("--write".equals(arg)) {
846                    synchronized (mStats) {
847                        mStats.writeSyncLocked();
848                        pw.println("Battery stats written.");
849                        noOutput = true;
850                    }
851                } else if ("--enable".equals(arg) || "enable".equals(arg)) {
852                    i = doEnableOrDisable(pw, i, args, true);
853                    if (i < 0) {
854                        return;
855                    }
856                    pw.println("Enabled: " + args[i]);
857                    return;
858                } else if ("--disable".equals(arg) || "disable".equals(arg)) {
859                    i = doEnableOrDisable(pw, i, args, false);
860                    if (i < 0) {
861                        return;
862                    }
863                    pw.println("Disabled: " + args[i]);
864                    return;
865                } else if ("-h".equals(arg)) {
866                    dumpHelp(pw);
867                    return;
868                } else if ("-a".equals(arg)) {
869                    flags |= BatteryStats.DUMP_VERBOSE;
870                } else if (arg.length() > 0 && arg.charAt(0) == '-'){
871                    pw.println("Unknown option: " + arg);
872                    dumpHelp(pw);
873                    return;
874                } else {
875                    // Not an option, last argument must be a package name.
876                    try {
877                        reqUid = mContext.getPackageManager().getPackageUid(arg,
878                                UserHandle.getCallingUserId());
879                    } catch (PackageManager.NameNotFoundException e) {
880                        pw.println("Unknown package: " + arg);
881                        dumpHelp(pw);
882                        return;
883                    }
884                }
885            }
886        }
887        if (noOutput) {
888            return;
889        }
890        if (BatteryStatsHelper.checkWifiOnly(mContext)) {
891            flags |= BatteryStats.DUMP_DEVICE_WIFI_ONLY;
892        }
893        if (reqUid >= 0) {
894            // By default, if the caller is only interested in a specific package, then
895            // we only dump the aggregated data since charged.
896            if ((flags&(BatteryStats.DUMP_HISTORY_ONLY|BatteryStats.DUMP_UNPLUGGED_ONLY
897                    |BatteryStats.DUMP_CHARGED_ONLY)) == 0) {
898                flags |= BatteryStats.DUMP_CHARGED_ONLY;
899                // Also if they are doing -c, we don't want history.
900                flags &= ~BatteryStats.DUMP_INCLUDE_HISTORY;
901            }
902        }
903        if (useCheckinFormat) {
904            List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0);
905            if (isRealCheckin) {
906                // For a real checkin, first we want to prefer to use the last complete checkin
907                // file if there is one.
908                synchronized (mStats.mCheckinFile) {
909                    if (mStats.mCheckinFile.exists()) {
910                        try {
911                            byte[] raw = mStats.mCheckinFile.readFully();
912                            if (raw != null) {
913                                Parcel in = Parcel.obtain();
914                                in.unmarshall(raw, 0, raw.length);
915                                in.setDataPosition(0);
916                                BatteryStatsImpl checkinStats = new BatteryStatsImpl(
917                                        null, mStats.mHandler);
918                                checkinStats.readSummaryFromParcel(in);
919                                in.recycle();
920                                checkinStats.dumpCheckinLocked(mContext, pw, apps, flags,
921                                        historyStart);
922                                mStats.mCheckinFile.delete();
923                                return;
924                            }
925                        } catch (IOException e) {
926                            Slog.w(TAG, "Failure reading checkin file "
927                                    + mStats.mCheckinFile.getBaseFile(), e);
928                        }
929                    }
930                }
931            }
932            synchronized (mStats) {
933                mStats.dumpCheckinLocked(mContext, pw, apps, flags, historyStart);
934                if (writeData) {
935                    mStats.writeAsyncLocked();
936                }
937            }
938        } else {
939            synchronized (mStats) {
940                mStats.dumpLocked(mContext, pw, flags, reqUid, historyStart);
941                if (writeData) {
942                    mStats.writeAsyncLocked();
943                }
944            }
945        }
946    }
947}
948