blob: 98d4c5976adc8e96cb5d7aea122a856aa594a21f [file] [log] [blame]
/*
* Copyright (C) 2006-2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.os;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.os.BatteryStats.Uid.NUM_PROCESS_STATE;
import static android.os.BatteryStatsManager.NUM_WIFI_STATES;
import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.UidTraffic;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.hardware.usb.UsbManager;
import android.location.GnssSignalQuality;
import android.net.NetworkStats;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.BatteryConsumer;
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.BluetoothBatteryStats;
import android.os.Build;
import android.os.Handler;
import android.os.IBatteryPropertiesRegistrar;
import android.os.Looper;
import android.os.Message;
import android.os.OsProtoEnums;
import android.os.Parcel;
import android.os.ParcelFormatException;
import android.os.Parcelable;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WakeLockStats;
import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
import android.os.connectivity.CellularBatteryStats;
import android.os.connectivity.GpsBatteryStats;
import android.os.connectivity.WifiActivityEnergyInfo;
import android.os.connectivity.WifiBatteryStats;
import android.provider.Settings;
import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation.NetworkType;
import android.telephony.CellSignalStrength;
import android.telephony.CellSignalStrengthLte;
import android.telephony.CellSignalStrengthNr;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.ModemActivityInfo;
import android.telephony.ServiceState;
import android.telephony.ServiceState.RegState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.IndentingPrintWriter;
import android.util.KeyValueListParser;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.LongSparseLongArray;
import android.util.MutableInt;
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseDoubleArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
import com.android.internal.os.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
import com.android.internal.power.MeasuredEnergyStats;
import com.android.internal.power.MeasuredEnergyStats.StandardPowerBucket;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.XmlUtils;
import com.android.net.module.util.NetworkCapabilitiesUtils;
import libcore.util.EmptyArray;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
/**
* All information we are collecting about things that can happen that impact
* battery life. All times are represented in microseconds except where indicated
* otherwise.
*/
public class BatteryStatsImpl extends BatteryStats {
private static final String TAG = "BatteryStatsImpl";
private static final boolean DEBUG = false;
public static final boolean DEBUG_ENERGY = false;
private static final boolean DEBUG_ENERGY_CPU = DEBUG_ENERGY;
private static final boolean DEBUG_BINDER_STATS = false;
private static final boolean DEBUG_MEMORY = false;
private static final boolean DEBUG_HISTORY = false;
// TODO: remove "tcp" from network methods, since we measure total stats.
// In-memory Parcel magic number, used to detect attempts to unmarshall bad data
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
static final int VERSION = 208;
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
// in to one common name.
private static final int MAX_WAKELOCKS_PER_UID;
static {
if (ActivityManager.isLowRamDeviceStatic()) {
MAX_WAKELOCKS_PER_UID = 40;
} else {
MAX_WAKELOCKS_PER_UID = 200;
}
}
// Number of transmit power states the Wifi controller can be in.
private static final int NUM_WIFI_TX_LEVELS = 1;
// Number of transmit power states the Bluetooth controller can be in.
private static final int NUM_BT_TX_LEVELS = 1;
/**
* Holding a wakelock costs more than just using the cpu.
* Currently, we assign only half the cpu time to an app that is running but
* not holding a wakelock. The apps holding wakelocks get the rest of the blame.
* If no app is holding a wakelock, then the distribution is normal.
*/
@VisibleForTesting
public static final int WAKE_LOCK_WEIGHT = 50;
public static final int RESET_REASON_CORRUPT_FILE = 1;
public static final int RESET_REASON_ADB_COMMAND = 2;
public static final int RESET_REASON_FULL_CHARGE = 3;
public static final int RESET_REASON_MEASURED_ENERGY_BUCKETS_CHANGE = 4;
protected Clock mClock;
private final AtomicFile mStatsFile;
public final AtomicFile mCheckinFile;
public final AtomicFile mDailyFile;
static final int MSG_REPORT_CPU_UPDATE_NEEDED = 1;
static final int MSG_REPORT_POWER_CHANGE = 2;
static final int MSG_REPORT_CHARGING = 3;
static final int MSG_REPORT_RESET_STATS = 4;
static final long DELAY_UPDATE_WAKELOCKS = 60 * 1000;
private static final double MILLISECONDS_IN_HOUR = 3600 * 1000;
private static final long MILLISECONDS_IN_YEAR = 365 * 24 * 3600 * 1000L;
private static final LongCounter ZERO_LONG_COUNTER = new LongCounter() {
@Override
public long getCountLocked(int which) {
return 0;
}
@Override
public long getCountForProcessState(int procState) {
return 0;
}
@Override
public void logState(Printer pw, String prefix) {
pw.println(prefix + "mCount=0");
}
};
private static final LongCounter[] ZERO_LONG_COUNTER_ARRAY =
new LongCounter[]{ZERO_LONG_COUNTER};
private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
@VisibleForTesting
protected KernelCpuUidUserSysTimeReader mCpuUidUserSysTimeReader;
@VisibleForTesting
protected KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
@VisibleForTesting
protected KernelCpuUidFreqTimeReader mCpuUidFreqTimeReader;
@VisibleForTesting
protected KernelCpuUidActiveTimeReader mCpuUidActiveTimeReader;
@VisibleForTesting
protected KernelCpuUidClusterTimeReader mCpuUidClusterTimeReader;
@VisibleForTesting
protected KernelSingleUidTimeReader mKernelSingleUidTimeReader;
@VisibleForTesting
protected SystemServerCpuThreadReader mSystemServerCpuThreadReader =
SystemServerCpuThreadReader.create();
private final KernelMemoryBandwidthStats mKernelMemoryBandwidthStats
= new KernelMemoryBandwidthStats();
private final LongSparseArray<SamplingTimer> mKernelMemoryStats = new LongSparseArray<>();
public LongSparseArray<SamplingTimer> getKernelMemoryStats() {
return mKernelMemoryStats;
}
private static final int[] SUPPORTED_PER_PROCESS_STATE_STANDARD_ENERGY_BUCKETS = {
MeasuredEnergyStats.POWER_BUCKET_CPU,
MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO,
MeasuredEnergyStats.POWER_BUCKET_WIFI,
MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH,
};
// TimeInState counters need NUM_PROCESS_STATE states in order to accommodate
// Uid.PROCESS_STATE_NONEXISTENT, which is outside the range of legitimate proc states.
private static final int PROC_STATE_TIME_COUNTER_STATE_COUNT = NUM_PROCESS_STATE + 1;
@GuardedBy("this")
public boolean mPerProcStateCpuTimesAvailable = true;
@GuardedBy("this")
private long mNumSingleUidCpuTimeReads;
@GuardedBy("this")
private long mCpuTimeReadsTrackingStartTimeMs = SystemClock.uptimeMillis();
@GuardedBy("this")
private int mNumUidsRemoved;
@GuardedBy("this")
private int mNumAllUidCpuTimeReads;
/** Container for Resource Power Manager stats. Updated by updateRpmStatsLocked. */
private RpmStats mTmpRpmStats = null;
/** The soonest the RPM stats can be updated after it was last updated. */
private static final long RPM_STATS_UPDATE_FREQ_MS = 1000;
/** Last time that RPM stats were updated by updateRpmStatsLocked. */
private long mLastRpmStatsUpdateTimeMs = -RPM_STATS_UPDATE_FREQ_MS;
/** Container for Rail Energy Data stats. */
private final RailStats mTmpRailStats = new RailStats();
/**
* Use a queue to delay removing UIDs from {@link KernelCpuUidUserSysTimeReader},
* {@link KernelCpuUidActiveTimeReader}, {@link KernelCpuUidClusterTimeReader},
* {@link KernelCpuUidFreqTimeReader} and from the Kernel.
*
* Isolated and invalid UID info must be removed to conserve memory. However, STATSD and
* Batterystats both need to access UID cpu time. To resolve this race condition, only
* Batterystats shall remove UIDs, and a delay {@link Constants#UID_REMOVE_DELAY_MS} is
* implemented so that STATSD can capture those UID times before they are deleted.
*/
@GuardedBy("this")
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
protected Queue<UidToRemove> mPendingRemovedUids = new LinkedList<>();
@VisibleForTesting
public final class UidToRemove {
private final int mStartUid;
private final int mEndUid;
private final long mUidRemovalTimestamp;
/** Remove just one UID */
public UidToRemove(int uid, long timestamp) {
this(uid, uid, timestamp);
}
/** Remove a range of UIDs, startUid must be smaller than endUid. */
public UidToRemove(int startUid, int endUid, long timestamp) {
mStartUid = startUid;
mEndUid = endUid;
mUidRemovalTimestamp = timestamp;
}
public long getUidRemovalTimestamp() {
return mUidRemovalTimestamp;
}
@GuardedBy("BatteryStatsImpl.this")
void removeLocked() {
removeCpuStatsForUidRangeLocked(mStartUid, mEndUid);
}
}
/**
* Listener for the battery stats reset.
*/
public interface BatteryResetListener {
/**
* Callback invoked immediately prior to resetting battery stats.
* @param resetReason One of the RESET_REASON_* constants.
*/
void prepareForBatteryStatsReset(int resetReason);
}
private BatteryResetListener mBatteryResetListener;
public interface BatteryCallback {
public void batteryNeedsCpuUpdate();
public void batteryPowerChanged(boolean onBattery);
public void batterySendBroadcast(Intent intent);
public void batteryStatsReset();
}
public interface PlatformIdleStateCallback {
public void fillLowPowerStats(RpmStats rpmStats);
public String getSubsystemLowPowerStats();
}
/** interface to update rail information for power monitor */
public interface MeasuredEnergyRetriever {
/** Function to fill the map for the rail data stats
* Used for power monitoring feature
* @param railStats
*/
void fillRailDataStats(RailStats railStats);
}
public static abstract class UserInfoProvider {
private int[] userIds;
protected abstract @Nullable int[] getUserIds();
@VisibleForTesting
public final void refreshUserIds() {
userIds = getUserIds();
}
@VisibleForTesting
public boolean exists(int userId) {
return userIds != null ? ArrayUtils.contains(userIds, userId) : true;
}
}
private final PlatformIdleStateCallback mPlatformIdleStateCallback;
private final Runnable mDeferSetCharging = new Runnable() {
@Override
public void run() {
synchronized (BatteryStatsImpl.this) {
if (mOnBattery) {
// if the device gets unplugged in the time between this runnable being
// executed and the lock being taken, we don't want to set charging state
return;
}
boolean changed = setChargingLocked(true);
if (changed) {
final long uptimeMs = mClock.uptimeMillis();
final long elapsedRealtimeMs = mClock.elapsedRealtime();
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
}
}
};
public final MeasuredEnergyRetriever mMeasuredEnergyRetriever;
/**
* This handler is running on {@link BackgroundThread}.
*/
final class MyHandler extends Handler {
public MyHandler(Looper looper) {
super(looper, null, true);
}
@Override
public void handleMessage(Message msg) {
BatteryCallback cb = mCallback;
switch (msg.what) {
case MSG_REPORT_CPU_UPDATE_NEEDED:
if (cb != null) {
cb.batteryNeedsCpuUpdate();
}
break;
case MSG_REPORT_POWER_CHANGE:
if (cb != null) {
cb.batteryPowerChanged(msg.arg1 != 0);
}
break;
case MSG_REPORT_CHARGING:
if (cb != null) {
final String action;
synchronized (BatteryStatsImpl.this) {
action = mCharging ? BatteryManager.ACTION_CHARGING
: BatteryManager.ACTION_DISCHARGING;
}
Intent intent = new Intent(action);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
cb.batterySendBroadcast(intent);
}
break;
case MSG_REPORT_RESET_STATS:
if (cb != null) {
cb.batteryStatsReset();
}
}
}
}
public void postBatteryNeedsCpuUpdateMsg() {
mHandler.sendEmptyMessage(MSG_REPORT_CPU_UPDATE_NEEDED);
}
/**
* Update per-freq cpu times for the supplied UID.
*/
@GuardedBy("this")
@SuppressWarnings("GuardedBy") // errorprone false positive on getProcStateTimeCounter
@VisibleForTesting
public void updateProcStateCpuTimesLocked(int uid, long timestampMs) {
if (!initKernelSingleUidTimeReaderLocked()) {
return;
}
final Uid u = getUidStatsLocked(uid);
mNumSingleUidCpuTimeReads++;
LongArrayMultiStateCounter onBatteryCounter =
u.getProcStateTimeCounter(timestampMs).getCounter();
LongArrayMultiStateCounter onBatteryScreenOffCounter =
u.getProcStateScreenOffTimeCounter(timestampMs).getCounter();
mKernelSingleUidTimeReader.addDelta(uid, onBatteryCounter, timestampMs);
mKernelSingleUidTimeReader.addDelta(uid, onBatteryScreenOffCounter, timestampMs);
if (u.mChildUids != null) {
LongArrayMultiStateCounter.LongArrayContainer deltaContainer =
getCpuTimeInFreqContainer();
int childUidCount = u.mChildUids.size();
for (int j = childUidCount - 1; j >= 0; --j) {
LongArrayMultiStateCounter cpuTimeInFreqCounter =
u.mChildUids.valueAt(j).cpuTimeInFreqCounter;
if (cpuTimeInFreqCounter != null) {
mKernelSingleUidTimeReader.addDelta(u.mChildUids.keyAt(j),
cpuTimeInFreqCounter, timestampMs, deltaContainer);
onBatteryCounter.addCounts(deltaContainer);
onBatteryScreenOffCounter.addCounts(deltaContainer);
}
}
}
}
/**
* Removes kernel CPU stats for removed UIDs, in the order they were added to the
* mPendingRemovedUids queue.
*/
@GuardedBy("this")
@SuppressWarnings("GuardedBy") // errorprone false positive on removeLocked
public void clearPendingRemovedUidsLocked() {
long cutOffTimeMs = mClock.elapsedRealtime() - mConstants.UID_REMOVE_DELAY_MS;
while (!mPendingRemovedUids.isEmpty()
&& mPendingRemovedUids.peek().getUidRemovalTimestamp() < cutOffTimeMs) {
mPendingRemovedUids.poll().removeLocked();
}
}
/**
* When the battery/screen state changes, we don't attribute the cpu times to any process
* but we still need to take snapshots of all uids to get correct deltas later on.
*/
@SuppressWarnings("GuardedBy") // errorprone false positive on getProcStateTimeCounter
public void updateCpuTimesForAllUids() {
synchronized (BatteryStatsImpl.this) {
if (!trackPerProcStateCpuTimes()) {
return;
}
if(!initKernelSingleUidTimeReaderLocked()) {
return;
}
// TODO(b/197162116): just get a list of UIDs
final SparseArray<long[]> allUidCpuFreqTimesMs =
mCpuUidFreqTimeReader.getAllUidCpuFreqTimeMs();
for (int i = allUidCpuFreqTimesMs.size() - 1; i >= 0; --i) {
final int uid = allUidCpuFreqTimesMs.keyAt(i);
final int parentUid = mapUid(uid);
final Uid u = getAvailableUidStatsLocked(parentUid);
if (u == null) {
continue;
}
final int procState = u.mProcessState;
if (procState == Uid.PROCESS_STATE_NONEXISTENT) {
continue;
}
final long timestampMs = mClock.elapsedRealtime();
final LongArrayMultiStateCounter onBatteryCounter =
u.getProcStateTimeCounter(timestampMs).getCounter();
final LongArrayMultiStateCounter onBatteryScreenOffCounter =
u.getProcStateScreenOffTimeCounter(timestampMs).getCounter();
if (uid == parentUid || Process.isSdkSandboxUid(uid)) {
mKernelSingleUidTimeReader.addDelta(parentUid, onBatteryCounter, timestampMs);
mKernelSingleUidTimeReader.addDelta(parentUid, onBatteryScreenOffCounter,
timestampMs);
} else {
Uid.ChildUid childUid = u.getChildUid(uid);
if (childUid != null) {
final LongArrayMultiStateCounter counter = childUid.cpuTimeInFreqCounter;
if (counter != null) {
final LongArrayMultiStateCounter.LongArrayContainer deltaContainer =
getCpuTimeInFreqContainer();
mKernelSingleUidTimeReader.addDelta(uid, counter, timestampMs,
deltaContainer);
onBatteryCounter.addCounts(deltaContainer);
onBatteryScreenOffCounter.addCounts(deltaContainer);
}
}
}
}
}
}
@VisibleForTesting
public static long[] addCpuTimes(long[] timesA, long[] timesB) {
if (timesA != null && timesB != null) {
for (int i = timesA.length - 1; i >= 0; --i) {
timesA[i] += timesB[i];
}
return timesA;
}
return timesA == null ? (timesB == null ? null : timesB) : timesA;
}
@GuardedBy("this")
private boolean initKernelSingleUidTimeReaderLocked() {
if (mKernelSingleUidTimeReader == null) {
if (mPowerProfile == null) {
return false;
}
if (mCpuFreqs == null) {
mCpuFreqs = mCpuUidFreqTimeReader.readFreqs(mPowerProfile);
}
if (mCpuFreqs != null) {
mKernelSingleUidTimeReader = new KernelSingleUidTimeReader(mCpuFreqs.length);
} else {
mPerProcStateCpuTimesAvailable = mCpuUidFreqTimeReader.allUidTimesAvailable();
return false;
}
}
mPerProcStateCpuTimesAvailable = mCpuUidFreqTimeReader.allUidTimesAvailable()
&& mKernelSingleUidTimeReader.singleUidCpuTimesAvailable();
return true;
}
public interface ExternalStatsSync {
int UPDATE_CPU = 0x01;
int UPDATE_WIFI = 0x02;
int UPDATE_RADIO = 0x04;
int UPDATE_BT = 0x08;
int UPDATE_RPM = 0x10;
int UPDATE_DISPLAY = 0x20;
int RESET = 0x40;
int UPDATE_ALL =
UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM | UPDATE_DISPLAY;
int UPDATE_ON_PROC_STATE_CHANGE = UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT;
int UPDATE_ON_RESET = UPDATE_ALL | RESET;
@IntDef(flag = true, prefix = "UPDATE_", value = {
UPDATE_CPU,
UPDATE_WIFI,
UPDATE_RADIO,
UPDATE_BT,
UPDATE_RPM,
UPDATE_DISPLAY,
UPDATE_ALL,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ExternalUpdateFlag {
}
Future<?> scheduleSync(String reason, int flags);
Future<?> scheduleCpuSyncDueToRemovedUid(int uid);
Future<?> scheduleCpuSyncDueToSettingChange();
/**
* Schedule a sync because of a screen state change.
*/
Future<?> scheduleSyncDueToScreenStateChange(int flags, boolean onBattery,
boolean onBatteryScreenOff, int screenState, int[] perDisplayScreenStates);
Future<?> scheduleCpuSyncDueToWakelockChange(long delayMillis);
void cancelCpuSyncDueToWakelockChange();
Future<?> scheduleSyncDueToBatteryLevelChange(long delayMillis);
/** Schedule removal of UIDs corresponding to a removed user */
Future<?> scheduleCleanupDueToRemovedUser(int userId);
/** Schedule a sync because of a process state change */
void scheduleSyncDueToProcessStateChange(int flags, long delayMillis);
}
public Handler mHandler;
private ExternalStatsSync mExternalSync = null;
@VisibleForTesting
protected UserInfoProvider mUserInfoProvider = null;
private BatteryCallback mCallback;
/**
* Mapping isolated uids to the actual owning app uid.
*/
final SparseIntArray mIsolatedUids = new SparseIntArray();
/**
* Internal reference count of isolated uids.
*/
final SparseIntArray mIsolatedUidRefCounts = new SparseIntArray();
/**
* The statistics we have collected organized by uids.
*/
final SparseArray<BatteryStatsImpl.Uid> mUidStats = new SparseArray<>();
// A set of pools of currently active timers. When a timer is queried, we will divide the
// elapsed time by the number of active timers to arrive at that timer's share of the time.
// In order to do this, we must refresh each timer whenever the number of active timers
// changes.
@VisibleForTesting
@UnsupportedAppUsage
protected ArrayList<StopwatchTimer> mPartialTimers = new ArrayList<>();
@UnsupportedAppUsage
final ArrayList<StopwatchTimer> mFullTimers = new ArrayList<>();
@UnsupportedAppUsage
final ArrayList<StopwatchTimer> mWindowTimers = new ArrayList<>();
final ArrayList<StopwatchTimer> mDrawTimers = new ArrayList<>();
final SparseArray<ArrayList<StopwatchTimer>> mSensorTimers = new SparseArray<>();
final ArrayList<StopwatchTimer> mWifiRunningTimers = new ArrayList<>();
final ArrayList<StopwatchTimer> mFullWifiLockTimers = new ArrayList<>();
final ArrayList<StopwatchTimer> mWifiMulticastTimers = new ArrayList<>();
final ArrayList<StopwatchTimer> mWifiScanTimers = new ArrayList<>();
final SparseArray<ArrayList<StopwatchTimer>> mWifiBatchedScanTimers = new SparseArray<>();
final ArrayList<StopwatchTimer> mAudioTurnedOnTimers = new ArrayList<>();
final ArrayList<StopwatchTimer> mVideoTurnedOnTimers = new ArrayList<>();
final ArrayList<StopwatchTimer> mFlashlightTurnedOnTimers = new ArrayList<>();
final ArrayList<StopwatchTimer> mCameraTurnedOnTimers = new ArrayList<>();
final ArrayList<StopwatchTimer> mBluetoothScanOnTimers = new ArrayList<>();
// Last partial timers we use for distributing CPU usage.
@VisibleForTesting
protected ArrayList<StopwatchTimer> mLastPartialTimers = new ArrayList<>();
// These are the objects that will want to do something when the device
// is unplugged from power.
protected final TimeBase mOnBatteryTimeBase = new TimeBase(true);
// These are the objects that will want to do something when the device
// is unplugged from power *and* the screen is off or doze.
protected final TimeBase mOnBatteryScreenOffTimeBase = new TimeBase(true);
// Set to true when we want to distribute CPU across wakelocks for the next
// CPU update, even if we aren't currently running wake locks.
boolean mDistributeWakelockCpu;
private boolean mSystemReady;
boolean mShuttingDown;
final HistoryEventTracker mActiveEvents = new HistoryEventTracker();
long mHistoryBaseTimeMs;
protected boolean mHaveBatteryLevel = false;
protected boolean mRecordingHistory = false;
int mNumHistoryItems;
private static final int HISTORY_TAG_INDEX_LIMIT = 0x7ffe;
private static final int MAX_HISTORY_TAG_STRING_LENGTH = 1024;
final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<>();
private SparseArray<HistoryTag> mHistoryTags;
final Parcel mHistoryBuffer = Parcel.obtain();
final HistoryItem mHistoryLastWritten = new HistoryItem();
final HistoryItem mHistoryLastLastWritten = new HistoryItem();
final HistoryItem mHistoryAddTmp = new HistoryItem();
int mNextHistoryTagIdx = 0;
int mNumHistoryTagChars = 0;
int mHistoryBufferLastPos = -1;
int mActiveHistoryStates = 0xffffffff;
int mActiveHistoryStates2 = 0xffffffff;
long mLastHistoryElapsedRealtimeMs = 0;
long mTrackRunningHistoryElapsedRealtimeMs = 0;
long mTrackRunningHistoryUptimeMs = 0;
@NonNull
final BatteryStatsHistory mBatteryStatsHistory;
final HistoryItem mHistoryCur = new HistoryItem();
HistoryItem mHistory;
HistoryItem mHistoryEnd;
HistoryItem mHistoryLastEnd;
HistoryItem mHistoryCache;
// Used by computeHistoryStepDetails
HistoryStepDetails mLastHistoryStepDetails = null;
byte mLastHistoryStepLevel = 0;
final HistoryStepDetails mCurHistoryStepDetails = new HistoryStepDetails();
final HistoryStepDetails mReadHistoryStepDetails = new HistoryStepDetails();
final HistoryStepDetails mTmpHistoryStepDetails = new HistoryStepDetails();
/**
* Total time (in milliseconds) spent executing in user code.
*/
long mLastStepCpuUserTimeMs;
long mCurStepCpuUserTimeMs;
/**
* Total time (in milliseconds) spent executing in kernel code.
*/
long mLastStepCpuSystemTimeMs;
long mCurStepCpuSystemTimeMs;
/**
* Times from /proc/stat (but measured in milliseconds).
*/
long mLastStepStatUserTimeMs;
long mLastStepStatSystemTimeMs;
long mLastStepStatIOWaitTimeMs;
long mLastStepStatIrqTimeMs;
long mLastStepStatSoftIrqTimeMs;
long mLastStepStatIdleTimeMs;
long mCurStepStatUserTimeMs;
long mCurStepStatSystemTimeMs;
long mCurStepStatIOWaitTimeMs;
long mCurStepStatIrqTimeMs;
long mCurStepStatSoftIrqTimeMs;
long mCurStepStatIdleTimeMs;
private BatteryStatsHistoryIterator mBatteryStatsHistoryIterator;
private HistoryItem mHistoryIterator;
int mStartCount;
/**
* Set to true when a reset occurs, informing us that the next time BatteryExternalStatsWorker
* gives us data, we mustn't process it since this data includes pre-reset-period data.
*/
@GuardedBy("this")
boolean mIgnoreNextExternalStats = false;
long mStartClockTimeMs;
String mStartPlatformVersion;
String mEndPlatformVersion;
long mUptimeUs;
long mUptimeStartUs;
long mRealtimeUs;
long mRealtimeStartUs;
int mWakeLockNesting;
boolean mWakeLockImportant;
public boolean mRecordAllHistory;
boolean mNoAutoReset;
/**
* Overall screen state. For multidisplay devices, this represents the current highest screen
* state of the displays.
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
protected int mScreenState = Display.STATE_UNKNOWN;
/**
* Overall screen on timer. For multidisplay devices, this represents the time spent with at
* least one display in the screen on state.
*/
StopwatchTimer mScreenOnTimer;
/**
* Overall screen doze timer. For multidisplay devices, this represents the time spent with
* screen doze being the highest screen state.
*/
StopwatchTimer mScreenDozeTimer;
/**
* Overall screen brightness bin. For multidisplay devices, this represents the current
* brightest screen.
*/
int mScreenBrightnessBin = -1;
/**
* Overall screen brightness timers. For multidisplay devices, the {@link mScreenBrightnessBin}
* timer will be active at any given time
*/
final StopwatchTimer[] mScreenBrightnessTimer =
new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS];
boolean mPretendScreenOff;
private static class DisplayBatteryStats {
/**
* Per display screen state.
*/
public int screenState = Display.STATE_UNKNOWN;
/**
* Per display screen on timers.
*/
public StopwatchTimer screenOnTimer;
/**
* Per display screen doze timers.
*/
public StopwatchTimer screenDozeTimer;
/**
* Per display screen brightness bins.
*/
public int screenBrightnessBin = -1;
/**
* Per display screen brightness timers.
*/
public StopwatchTimer[] screenBrightnessTimers =
new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS];
/**
* Per display screen state the last time {@link #updateDisplayMeasuredEnergyStatsLocked}
* was called.
*/
public int screenStateAtLastEnergyMeasurement = Display.STATE_UNKNOWN;
DisplayBatteryStats(Clock clock, TimeBase timeBase) {
screenOnTimer = new StopwatchTimer(clock, null, -1, null,
timeBase);
screenDozeTimer = new StopwatchTimer(clock, null, -1, null,
timeBase);
for (int i = 0; i < NUM_SCREEN_BRIGHTNESS_BINS; i++) {
screenBrightnessTimers[i] = new StopwatchTimer(clock, null, -100 - i, null,
timeBase);
}
}
/**
* Reset display timers.
*/
public void reset(long elapsedRealtimeUs) {
screenOnTimer.reset(false, elapsedRealtimeUs);
screenDozeTimer.reset(false, elapsedRealtimeUs);
for (int i = 0; i < NUM_SCREEN_BRIGHTNESS_BINS; i++) {
screenBrightnessTimers[i].reset(false, elapsedRealtimeUs);
}
}
}
DisplayBatteryStats[] mPerDisplayBatteryStats;
private int mDisplayMismatchWtfCount = 0;
boolean mInteractive;
StopwatchTimer mInteractiveTimer;
boolean mPowerSaveModeEnabled;
StopwatchTimer mPowerSaveModeEnabledTimer;
boolean mDeviceIdling;
StopwatchTimer mDeviceIdlingTimer;
boolean mDeviceLightIdling;
StopwatchTimer mDeviceLightIdlingTimer;
int mDeviceIdleMode;
long mLastIdleTimeStartMs;
long mLongestLightIdleTimeMs;
long mLongestFullIdleTimeMs;
StopwatchTimer mDeviceIdleModeLightTimer;
StopwatchTimer mDeviceIdleModeFullTimer;
boolean mPhoneOn;
StopwatchTimer mPhoneOnTimer;
int mAudioOnNesting;
StopwatchTimer mAudioOnTimer;
int mVideoOnNesting;
StopwatchTimer mVideoOnTimer;
int mFlashlightOnNesting;
StopwatchTimer mFlashlightOnTimer;
int mCameraOnNesting;
StopwatchTimer mCameraOnTimer;
private static final int USB_DATA_UNKNOWN = 0;
private static final int USB_DATA_DISCONNECTED = 1;
private static final int USB_DATA_CONNECTED = 2;
int mUsbDataState = USB_DATA_UNKNOWN;
int mGpsSignalQualityBin = -1;
final StopwatchTimer[] mGpsSignalQualityTimer =
new StopwatchTimer[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS];
int mPhoneSignalStrengthBin = -1;
int mPhoneSignalStrengthBinRaw = -1;
final StopwatchTimer[] mPhoneSignalStrengthsTimer =
new StopwatchTimer[CellSignalStrength.getNumSignalStrengthLevels()];
StopwatchTimer mPhoneSignalScanningTimer;
int mPhoneDataConnectionType = -1;
final StopwatchTimer[] mPhoneDataConnectionsTimer =
new StopwatchTimer[NUM_DATA_CONNECTION_TYPES];
@RadioAccessTechnology
int mActiveRat = RADIO_ACCESS_TECHNOLOGY_OTHER;
private static class RadioAccessTechnologyBatteryStats {
/**
* This RAT is currently being used.
*/
private boolean mActive = false;
/**
* Current active frequency range for this RAT.
*/
@ServiceState.FrequencyRange
private int mFrequencyRange = ServiceState.FREQUENCY_RANGE_UNKNOWN;
/**
* Current signal strength for this RAT.
*/
private int mSignalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
/**
* Timers for each combination of frequency range and signal strength.
*/
public final StopwatchTimer[][] perStateTimers;
/**
* Counters tracking the time (in milliseconds) spent transmitting data in a given state.
*/
@Nullable
private LongSamplingCounter[][] mPerStateTxDurationMs = null;
/**
* Counters tracking the time (in milliseconds) spent receiving data in at given frequency.
*/
@Nullable
private LongSamplingCounter[] mPerFrequencyRxDurationMs = null;
RadioAccessTechnologyBatteryStats(int freqCount, Clock clock, TimeBase timeBase) {
perStateTimers =
new StopwatchTimer[freqCount][CellSignalStrength.NUM_SIGNAL_STRENGTH_BINS];
for (int i = 0; i < freqCount; i++) {
for (int j = 0; j < CellSignalStrength.NUM_SIGNAL_STRENGTH_BINS; j++) {
perStateTimers[i][j] = new StopwatchTimer(clock, null, -1, null, timeBase);
}
}
}
/**
* Note this RAT is currently being used.
*/
public void noteActive(boolean active, long elapsedRealtimeMs) {
if (mActive == active) return;
mActive = active;
if (mActive) {
perStateTimers[mFrequencyRange][mSignalStrength].startRunningLocked(
elapsedRealtimeMs);
} else {
perStateTimers[mFrequencyRange][mSignalStrength].stopRunningLocked(
elapsedRealtimeMs);
}
}
/**
* Note current frequency range has changed.
*/
public void noteFrequencyRange(@ServiceState.FrequencyRange int frequencyRange,
long elapsedRealtimeMs) {
if (mFrequencyRange == frequencyRange) return;
if (!mActive) {
// RAT not in use, note the frequency change and move on.
mFrequencyRange = frequencyRange;
return;
}
perStateTimers[mFrequencyRange][mSignalStrength].stopRunningLocked(elapsedRealtimeMs);
perStateTimers[frequencyRange][mSignalStrength].startRunningLocked(elapsedRealtimeMs);
mFrequencyRange = frequencyRange;
}
/**
* Note current signal strength has changed.
*/
public void noteSignalStrength(int signalStrength, long elapsedRealtimeMs) {
if (mSignalStrength == signalStrength) return;
if (!mActive) {
// RAT not in use, note the signal strength change and move on.
mSignalStrength = signalStrength;
return;
}
perStateTimers[mFrequencyRange][mSignalStrength].stopRunningLocked(elapsedRealtimeMs);
perStateTimers[mFrequencyRange][signalStrength].startRunningLocked(elapsedRealtimeMs);
mSignalStrength = signalStrength;
}
/**
* Returns the duration in milliseconds spent in a given state since the last mark.
*/
public long getTimeSinceMark(@ServiceState.FrequencyRange int frequencyRange,
int signalStrength, long elapsedRealtimeMs) {
return perStateTimers[frequencyRange][signalStrength].getTimeSinceMarkLocked(
elapsedRealtimeMs * 1000) / 1000;
}
/**
* Set mark for all timers.
*/
public void setMark(long elapsedRealtimeMs) {
final int size = perStateTimers.length;
for (int i = 0; i < size; i++) {
for (int j = 0; j < CellSignalStrength.NUM_SIGNAL_STRENGTH_BINS; j++) {
perStateTimers[i][j].setMark(elapsedRealtimeMs);
}
}
}
/**
* Returns numbers of frequencies tracked for this RAT.
*/
public int getFrequencyRangeCount() {
return perStateTimers.length;
}
/**
* Add TX time for a given state.
*/
public void incrementTxDuration(@ServiceState.FrequencyRange int frequencyRange,
int signalStrength, long durationMs) {
getTxDurationCounter(frequencyRange, signalStrength, true).addCountLocked(durationMs);
}
/**
* Add TX time for a given frequency.
*/
public void incrementRxDuration(@ServiceState.FrequencyRange int frequencyRange,
long durationMs) {
getRxDurationCounter(frequencyRange, true).addCountLocked(durationMs);
}
/**
* Reset radio access technology timers and counts.
*/
public void reset(long elapsedRealtimeUs) {
final int size = perStateTimers.length;
for (int i = 0; i < size; i++) {
for (int j = 0; j < CellSignalStrength.NUM_SIGNAL_STRENGTH_BINS; j++) {
perStateTimers[i][j].reset(false, elapsedRealtimeUs);
if (mPerStateTxDurationMs == null) continue;
mPerStateTxDurationMs[i][j].reset(false, elapsedRealtimeUs);
}
if (mPerFrequencyRxDurationMs == null) continue;
mPerFrequencyRxDurationMs[i].reset(false, elapsedRealtimeUs);
}
}
/**
* Write data to summary parcel
*/
public void writeSummaryToParcel(Parcel out, long elapsedRealtimeUs) {
final int freqCount = perStateTimers.length;
out.writeInt(freqCount);
out.writeInt(CellSignalStrength.NUM_SIGNAL_STRENGTH_BINS);
for (int i = 0; i < freqCount; i++) {
for (int j = 0; j < CellSignalStrength.NUM_SIGNAL_STRENGTH_BINS; j++) {
perStateTimers[i][j].writeSummaryFromParcelLocked(out, elapsedRealtimeUs);
}
}
if (mPerStateTxDurationMs == null) {
out.writeInt(0);
} else {
out.writeInt(1);
for (int i = 0; i < freqCount; i++) {
for (int j = 0; j < CellSignalStrength.NUM_SIGNAL_STRENGTH_BINS; j++) {
mPerStateTxDurationMs[i][j].writeSummaryFromParcelLocked(out);
}
}
}
if (mPerFrequencyRxDurationMs == null) {
out.writeInt(0);
} else {
out.writeInt(1);
for (int i = 0; i < freqCount; i++) {
mPerFrequencyRxDurationMs[i].writeSummaryFromParcelLocked(out);
}
}
}
/**
* Read data from summary parcel
*/
public void readSummaryFromParcel(Parcel in) {
final int oldFreqCount = in.readInt();
final int oldSignalStrengthCount = in.readInt();
final int currFreqCount = perStateTimers.length;
final int currSignalStrengthCount = CellSignalStrength.NUM_SIGNAL_STRENGTH_BINS;
for (int freq = 0; freq < oldFreqCount; freq++) {
for (int strength = 0; strength < oldSignalStrengthCount; strength++) {
if (freq >= currFreqCount || strength >= currSignalStrengthCount) {
// Mismatch with the summary parcel. Consume the data but don't use it.
final StopwatchTimer temp = new StopwatchTimer(null, null, -1, null,
new TimeBase());
// Consume perStateTimers data.
temp.readSummaryFromParcelLocked(in);
} else {
perStateTimers[freq][strength].readSummaryFromParcelLocked(in);
}
}
}
if (in.readInt() == 1) {
for (int freq = 0; freq < oldFreqCount; freq++) {
for (int strength = 0; strength < oldSignalStrengthCount; strength++) {
if (freq >= currFreqCount || strength >= currSignalStrengthCount) {
// Mismatch with the summary parcel. Consume the data but don't use it.
final StopwatchTimer temp = new StopwatchTimer(null, null, -1, null,
new TimeBase());
// Consume mPerStateTxDurationMs data.
temp.readSummaryFromParcelLocked(in);
}
getTxDurationCounter(freq, strength, true).readSummaryFromParcelLocked(in);
}
}
}
if (in.readInt() == 1) {
for (int freq = 0; freq < oldFreqCount; freq++) {
if (freq >= currFreqCount) {
// Mismatch with the summary parcel. Consume the data but don't use it.
final StopwatchTimer
temp = new StopwatchTimer(null, null, -1, null, new TimeBase());
// Consume mPerFrequencyRxDurationMs data.
temp.readSummaryFromParcelLocked(in);
continue;
}
getRxDurationCounter(freq, true).readSummaryFromParcelLocked(in);
}
}
}
private LongSamplingCounter getTxDurationCounter(
@ServiceState.FrequencyRange int frequencyRange, int signalStrength, boolean make) {
if (mPerStateTxDurationMs == null) {
if (!make) return null;
final int freqCount = getFrequencyRangeCount();
final int signalStrengthCount = perStateTimers[0].length;
final TimeBase timeBase = perStateTimers[0][0].mTimeBase;
mPerStateTxDurationMs = new LongSamplingCounter[freqCount][signalStrengthCount];
for (int freq = 0; freq < freqCount; freq++) {
for (int strength = 0; strength < signalStrengthCount; strength++) {
mPerStateTxDurationMs[freq][strength] = new LongSamplingCounter(timeBase);
}
}
}
if (frequencyRange < 0 || frequencyRange >= getFrequencyRangeCount()) {
Slog.w(TAG, "Unexpected frequency range (" + frequencyRange
+ ") requested in getTxDurationCounter");
return null;
}
if (signalStrength < 0 || signalStrength >= perStateTimers[0].length) {
Slog.w(TAG, "Unexpected signal strength (" + signalStrength
+ ") requested in getTxDurationCounter");
return null;
}
return mPerStateTxDurationMs[frequencyRange][signalStrength];
}
private LongSamplingCounter getRxDurationCounter(
@ServiceState.FrequencyRange int frequencyRange, boolean make) {
if (mPerFrequencyRxDurationMs == null) {
if (!make) return null;
final int freqCount = getFrequencyRangeCount();
final TimeBase timeBase = perStateTimers[0][0].mTimeBase;
mPerFrequencyRxDurationMs = new LongSamplingCounter[freqCount];
for (int freq = 0; freq < freqCount; freq++) {
mPerFrequencyRxDurationMs[freq] = new LongSamplingCounter(timeBase);
}
}
if (frequencyRange < 0 || frequencyRange >= getFrequencyRangeCount()) {
Slog.w(TAG, "Unexpected frequency range (" + frequencyRange
+ ") requested in getRxDurationCounter");
return null;
}
return mPerFrequencyRxDurationMs[frequencyRange];
}
}
/**
* Number of frequency ranges, keep in sync with {@link ServiceState.FrequencyRange}
*/
private static final int NR_FREQUENCY_COUNT = 5;
RadioAccessTechnologyBatteryStats[] mPerRatBatteryStats =
new RadioAccessTechnologyBatteryStats[RADIO_ACCESS_TECHNOLOGY_COUNT];
@GuardedBy("this")
private RadioAccessTechnologyBatteryStats getRatBatteryStatsLocked(
@RadioAccessTechnology int rat) {
RadioAccessTechnologyBatteryStats stats = mPerRatBatteryStats[rat];
if (stats == null) {
final int freqCount = rat == RADIO_ACCESS_TECHNOLOGY_NR ? NR_FREQUENCY_COUNT : 1;
stats = new RadioAccessTechnologyBatteryStats(freqCount, mClock, mOnBatteryTimeBase);
mPerRatBatteryStats[rat] = stats;
}
return stats;
}
final LongSamplingCounter[] mNetworkByteActivityCounters =
new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
final LongSamplingCounter[] mNetworkPacketActivityCounters =
new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
/**
* The WiFi Overall wakelock timer
* This timer tracks the actual aggregate time for which MC wakelocks are enabled
* since addition of per UID timers would not result in an accurate value due to overlapp of
* per uid wakelock timers
*/
StopwatchTimer mWifiMulticastWakelockTimer;
/**
* The WiFi controller activity (time in tx, rx, idle, and power consumed) for the device.
*/
ControllerActivityCounterImpl mWifiActivity;
/**
* The Bluetooth controller activity (time in tx, rx, idle, and power consumed) for the device.
*/
ControllerActivityCounterImpl mBluetoothActivity;
/**
* The Modem controller activity (time in tx, rx, idle, and power consumed) for the device.
*/
ControllerActivityCounterImpl mModemActivity;
/**
* Whether the device supports WiFi controller energy reporting. This is set to true on
* the first WiFi energy report. See {@link #mWifiActivity}.
*/
boolean mHasWifiReporting = false;
/**
* Whether the device supports Bluetooth controller energy reporting. This is set to true on
* the first Bluetooth energy report. See {@link #mBluetoothActivity}.
*/
boolean mHasBluetoothReporting = false;
/**
* Whether the device supports Modem controller energy reporting. This is set to true on
* the first Modem energy report. See {@link #mModemActivity}.
*/
boolean mHasModemReporting = false;
boolean mWifiOn;
StopwatchTimer mWifiOnTimer;
boolean mGlobalWifiRunning;
StopwatchTimer mGlobalWifiRunningTimer;
int mWifiState = -1;
final StopwatchTimer[] mWifiStateTimer = new StopwatchTimer[NUM_WIFI_STATES];
int mWifiSupplState = -1;
final StopwatchTimer[] mWifiSupplStateTimer = new StopwatchTimer[NUM_WIFI_SUPPL_STATES];
int mWifiSignalStrengthBin = -1;
final StopwatchTimer[] mWifiSignalStrengthsTimer =
new StopwatchTimer[NUM_WIFI_SIGNAL_STRENGTH_BINS];
StopwatchTimer mWifiActiveTimer;
int mBluetoothScanNesting;
StopwatchTimer mBluetoothScanTimer;
int mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
long mMobileRadioActiveStartTimeMs;
StopwatchTimer mMobileRadioActiveTimer;
StopwatchTimer mMobileRadioActivePerAppTimer;
LongSamplingCounter mMobileRadioActiveAdjustedTime;
LongSamplingCounter mMobileRadioActiveUnknownTime;
LongSamplingCounter mMobileRadioActiveUnknownCount;
int mWifiRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
@GuardedBy("this")
@VisibleForTesting
protected @Nullable MeasuredEnergyStats.Config mMeasuredEnergyStatsConfig;
/**
* Accumulated global (generally, device-wide total) charge consumption of various consumers
* while on battery.
* Its '<b>custom</b> power buckets' correspond to the
* {@link android.hardware.power.stats.EnergyConsumer.ordinal}s of (custom) energy consumer
* type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
*
* If energy consumer data is completely unavailable this will be null.
*/
@GuardedBy("this")
@VisibleForTesting
protected @Nullable MeasuredEnergyStats mGlobalMeasuredEnergyStats;
/** Bluetooth Power calculator for attributing measured bluetooth charge consumption to uids */
@Nullable BluetoothPowerCalculator mBluetoothPowerCalculator = null;
/** Cpu Power calculator for attributing measured cpu charge consumption to uids */
@Nullable CpuPowerCalculator mCpuPowerCalculator = null;
/** Mobile Radio Power calculator for attributing measured radio charge consumption to uids */
@Nullable
MobileRadioPowerCalculator mMobileRadioPowerCalculator = null;
/** Wifi Power calculator for attributing measured wifi charge consumption to uids */
@Nullable WifiPowerCalculator mWifiPowerCalculator = null;
/**
* These provide time bases that discount the time the device is plugged
* in to power.
*/
boolean mOnBattery;
@VisibleForTesting
protected boolean mOnBatteryInternal;
/**
* External reporting of whether the device is actually charging.
*/
boolean mCharging = true;
int mLastChargingStateLevel;
/*
* These keep track of battery levels (1-100) at the last plug event and the last unplug event.
*/
int mDischargeStartLevel;
int mDischargeUnplugLevel;
int mDischargePlugLevel;
int mDischargeCurrentLevel;
int mCurrentBatteryLevel;
int mLowDischargeAmountSinceCharge;
int mHighDischargeAmountSinceCharge;
int mDischargeScreenOnUnplugLevel;
int mDischargeScreenOffUnplugLevel;
int mDischargeScreenDozeUnplugLevel;
int mDischargeAmountScreenOn;
int mDischargeAmountScreenOnSinceCharge;
int mDischargeAmountScreenOff;
int mDischargeAmountScreenOffSinceCharge;
int mDischargeAmountScreenDoze;
int mDischargeAmountScreenDozeSinceCharge;
private LongSamplingCounter mDischargeScreenOffCounter;
private LongSamplingCounter mDischargeScreenDozeCounter;
private LongSamplingCounter mDischargeCounter;
private LongSamplingCounter mDischargeLightDozeCounter;
private LongSamplingCounter mDischargeDeepDozeCounter;
static final int MAX_LEVEL_STEPS = 200;
int mInitStepMode = 0;
int mCurStepMode = 0;
int mModStepMode = 0;
int mLastDischargeStepLevel;
int mMinDischargeStepLevel;
final LevelStepTracker mDischargeStepTracker = new LevelStepTracker(MAX_LEVEL_STEPS);
final LevelStepTracker mDailyDischargeStepTracker = new LevelStepTracker(MAX_LEVEL_STEPS*2);
ArrayList<PackageChange> mDailyPackageChanges;
int mLastChargeStepLevel;
int mMaxChargeStepLevel;
final LevelStepTracker mChargeStepTracker = new LevelStepTracker(MAX_LEVEL_STEPS);
final LevelStepTracker mDailyChargeStepTracker = new LevelStepTracker(MAX_LEVEL_STEPS*2);
static final int MAX_DAILY_ITEMS = 10;
long mDailyStartTimeMs = 0;
long mNextMinDailyDeadlineMs = 0;
long mNextMaxDailyDeadlineMs = 0;
final ArrayList<DailyItem> mDailyItems = new ArrayList<>();
long mLastWriteTimeMs = 0; // Milliseconds
private int mPhoneServiceState = -1;
private int mPhoneServiceStateRaw = -1;
private int mPhoneSimStateRaw = -1;
private int mNumConnectivityChange;
private int mBatteryVoltageMv = -1;
private int mEstimatedBatteryCapacityMah = -1;
private int mLastLearnedBatteryCapacityUah = -1;
private int mMinLearnedBatteryCapacityUah = -1;
private int mMaxLearnedBatteryCapacityUah = -1;
private long mBatteryTimeToFullSeconds = -1;
private boolean mCpuFreqsInitialized;
private long[] mCpuFreqs;
private LongArrayMultiStateCounter.LongArrayContainer mTmpCpuTimeInFreq;
/**
* Times spent by the system server threads handling incoming binder requests.
*/
private LongSamplingCounterArray mBinderThreadCpuTimesUs;
@VisibleForTesting
protected PowerProfile mPowerProfile;
@VisibleForTesting
@GuardedBy("this")
protected final Constants mConstants;
/*
* Holds a SamplingTimer associated with each Resource Power Manager state and voter,
* recording their times when on-battery (regardless of screen state).
*/
private final HashMap<String, SamplingTimer> mRpmStats = new HashMap<>();
/** Times for each Resource Power Manager state and voter when screen-off and on-battery. */
private final HashMap<String, SamplingTimer> mScreenOffRpmStats = new HashMap<>();
@Override
public Map<String, ? extends Timer> getRpmStats() {
return mRpmStats;
}
// TODO: Note: screenOffRpmStats has been disabled via SCREEN_OFF_RPM_STATS_ENABLED.
@Override
public Map<String, ? extends Timer> getScreenOffRpmStats() {
return mScreenOffRpmStats;
}
/*
* Holds a SamplingTimer associated with each kernel wakelock name being tracked.
*/
private final HashMap<String, SamplingTimer> mKernelWakelockStats = new HashMap<>();
@UnsupportedAppUsage
public Map<String, ? extends Timer> getKernelWakelockStats() {
return mKernelWakelockStats;
}
@Override
public WakeLockStats getWakeLockStats() {
final long realtimeMs = mClock.elapsedRealtime();
final long realtimeUs = realtimeMs * 1000;
List<WakeLockStats.WakeLock> uidWakeLockStats = new ArrayList<>();
for (int i = mUidStats.size() - 1; i >= 0; i--) {
final Uid uid = mUidStats.valueAt(i);
final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats =
uid.mWakelockStats.getMap();
for (int j = wakelockStats.size() - 1; j >= 0; j--) {
final String name = wakelockStats.keyAt(j);
final Uid.Wakelock wakelock = (Uid.Wakelock) wakelockStats.valueAt(j);
final DualTimer timer = wakelock.mTimerPartial;
if (timer != null) {
final long totalTimeLockHeldMs =
timer.getTotalTimeLocked(realtimeUs, STATS_SINCE_CHARGED) / 1000;
if (totalTimeLockHeldMs != 0) {
uidWakeLockStats.add(
new WakeLockStats.WakeLock(uid.getUid(), name,
timer.getCountLocked(STATS_SINCE_CHARGED),
totalTimeLockHeldMs,
timer.isRunningLocked()
? timer.getCurrentDurationMsLocked(realtimeMs)
: 0));
}
}
}
}
return new WakeLockStats(uidWakeLockStats);
}
@Override
@GuardedBy("this")
public BluetoothBatteryStats getBluetoothBatteryStats() {
final long elapsedRealtimeUs = mClock.elapsedRealtime() * 1000;
ArrayList<BluetoothBatteryStats.UidStats> uidStats = new ArrayList<>();
for (int i = mUidStats.size() - 1; i >= 0; i--) {
final Uid uid = mUidStats.valueAt(i);
final Timer scanTimer = uid.getBluetoothScanTimer();
final long scanTimeMs =
scanTimer != null ? scanTimer.getTotalTimeLocked(
elapsedRealtimeUs, STATS_SINCE_CHARGED) / 1000 : 0;
final Timer unoptimizedScanTimer = uid.getBluetoothUnoptimizedScanTimer();
final long unoptimizedScanTimeMs =
unoptimizedScanTimer != null ? unoptimizedScanTimer.getTotalTimeLocked(
elapsedRealtimeUs, STATS_SINCE_CHARGED) / 1000 : 0;
final Counter scanResultCounter = uid.getBluetoothScanResultCounter();
final int scanResultCount =
scanResultCounter != null ? scanResultCounter.getCountLocked(
STATS_SINCE_CHARGED) : 0;
final ControllerActivityCounter counter = uid.getBluetoothControllerActivity();
final long rxTimeMs = counter != null ? counter.getRxTimeCounter().getCountLocked(
STATS_SINCE_CHARGED) : 0;
final long txTimeMs = counter != null ? counter.getTxTimeCounters()[0].getCountLocked(
STATS_SINCE_CHARGED) : 0;
if (scanTimeMs != 0 || unoptimizedScanTimeMs != 0 || scanResultCount != 0
|| rxTimeMs != 0 || txTimeMs != 0) {
uidStats.add(new BluetoothBatteryStats.UidStats(uid.getUid(),
scanTimeMs,
unoptimizedScanTimeMs,
scanResultCount,
rxTimeMs,
txTimeMs));
}
}
return new BluetoothBatteryStats(uidStats);
}
String mLastWakeupReason = null;
long mLastWakeupUptimeMs = 0;
private final HashMap<String, SamplingTimer> mWakeupReasonStats = new HashMap<>();
public Map<String, ? extends Timer> getWakeupReasonStats() {
return mWakeupReasonStats;
}
@Override
public long getUahDischarge(int which) {
return mDischargeCounter.getCountLocked(which);
}
@Override
public long getUahDischargeScreenOff(int which) {
return mDischargeScreenOffCounter.getCountLocked(which);
}
@Override
public long getUahDischargeScreenDoze(int which) {
return mDischargeScreenDozeCounter.getCountLocked(which);
}
@Override
public long getUahDischargeLightDoze(int which) {
return mDischargeLightDozeCounter.getCountLocked(which);
}
@Override
public long getUahDischargeDeepDoze(int which) {
return mDischargeDeepDozeCounter.getCountLocked(which);
}
@Override
public int getEstimatedBatteryCapacity() {
return mEstimatedBatteryCapacityMah;
}
@Override
public int getLearnedBatteryCapacity() {
return mLastLearnedBatteryCapacityUah;
}
@Override
public int getMinLearnedBatteryCapacity() {
return mMinLearnedBatteryCapacityUah;
}
@Override
public int getMaxLearnedBatteryCapacity() {
return mMaxLearnedBatteryCapacityUah;
}
public BatteryStatsImpl() {
this(Clock.SYSTEM_CLOCK);
}
public BatteryStatsImpl(Clock clock) {
this(clock, (File) null);
}
public BatteryStatsImpl(Clock clock, File historyDirectory) {
init(clock);
mStartClockTimeMs = clock.currentTimeMillis();
mCheckinFile = null;
mDailyFile = null;
if (historyDirectory == null) {
mStatsFile = null;
mBatteryStatsHistory = new BatteryStatsHistory(mHistoryBuffer);
} else {
mStatsFile = new AtomicFile(new File(historyDirectory, "batterystats.bin"));
mBatteryStatsHistory = new BatteryStatsHistory(this, historyDirectory, mHistoryBuffer);
}
mHandler = null;
mPlatformIdleStateCallback = null;
mMeasuredEnergyRetriever = null;
mUserInfoProvider = null;
mConstants = new Constants(mHandler);
clearHistoryLocked();
}
private void init(Clock clock) {
mClock = clock;
mCpuUidUserSysTimeReader = new KernelCpuUidUserSysTimeReader(true, clock);
mCpuUidFreqTimeReader = new KernelCpuUidFreqTimeReader(true, clock);
mCpuUidActiveTimeReader = new KernelCpuUidActiveTimeReader(true, clock);
mCpuUidClusterTimeReader = new KernelCpuUidClusterTimeReader(true, clock);
}
/**
* TimeBase observer.
*/
public interface TimeBaseObs {
void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs);
void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs);
/**
* Reset the observer's state, returns true if the timer/counter is inactive
* so it can be destroyed.
* @param detachIfReset detach if true, no-op if false.
* @return Returns true if the timer/counter is inactive and can be destroyed.
*/
default boolean reset(boolean detachIfReset) {
return reset(detachIfReset, SystemClock.elapsedRealtime() * 1000);
}
/**
* @see #reset(boolean)
* @param detachIfReset detach if true, no-op if false.
* @param elapsedRealtimeUs the timestamp when this reset is actually reequested
* @return Returns true if the timer/counter is inactive and can be destroyed.
*/
boolean reset(boolean detachIfReset, long elapsedRealtimeUs);
/**
* Detach the observer from TimeBase.
*/
void detach();
}
// methods are protected not private to be VisibleForTesting
public static class TimeBase {
protected final Collection<TimeBaseObs> mObservers;
// All below time metrics are in microseconds.
protected long mUptimeUs;
protected long mRealtimeUs;
protected boolean mRunning;
protected long mPastUptimeUs;
protected long mUptimeStartUs;
protected long mPastRealtimeUs;
protected long mRealtimeStartUs;
protected long mUnpluggedUptimeUs;
protected long mUnpluggedRealtimeUs;
public void dump(PrintWriter pw, String prefix) {
StringBuilder sb = new StringBuilder(128);
pw.print(prefix); pw.print("mRunning="); pw.println(mRunning);
sb.setLength(0);
sb.append(prefix);
sb.append("mUptime=");
formatTimeMs(sb, mUptimeUs / 1000);
pw.println(sb.toString());
sb.setLength(0);
sb.append(prefix);
sb.append("mRealtime=");
formatTimeMs(sb, mRealtimeUs / 1000);
pw.println(sb.toString());
sb.setLength(0);
sb.append(prefix);
sb.append("mPastUptime=");
formatTimeMs(sb, mPastUptimeUs / 1000); sb.append("mUptimeStart=");
formatTimeMs(sb, mUptimeStartUs / 1000);
sb.append("mUnpluggedUptime="); formatTimeMs(sb, mUnpluggedUptimeUs / 1000);
pw.println(sb.toString());
sb.setLength(0);
sb.append(prefix);
sb.append("mPastRealtime=");
formatTimeMs(sb, mPastRealtimeUs / 1000); sb.append("mRealtimeStart=");
formatTimeMs(sb, mRealtimeStartUs / 1000);
sb.append("mUnpluggedRealtime="); formatTimeMs(sb, mUnpluggedRealtimeUs / 1000);
pw.println(sb.toString());
}
/**
* The mObservers of TimeBase in BatteryStatsImpl object can contain up to 20k entries.
* The mObservers of TimeBase in BatteryStatsImpl.Uid object only contains a few or tens of
* entries.
* mObservers must have good performance on add(), remove(), also be memory efficient.
* This is why we provide isLongList parameter for long and short list user cases.
* @param isLongList If true, use HashSet for mObservers list.
* If false, use ArrayList for mObservers list.
*/
public TimeBase(boolean isLongList) {
mObservers = isLongList ? new HashSet<>() : new ArrayList<>();
}
public TimeBase() {
this(false);
}
public void add(TimeBaseObs observer) {
mObservers.add(observer);
}
public void remove(TimeBaseObs observer) {
mObservers.remove(observer);
}
public boolean hasObserver(TimeBaseObs observer) {
return mObservers.contains(observer);
}
public void init(long uptimeUs, long elapsedRealtimeUs) {
mRealtimeUs = 0;
mUptimeUs = 0;
mPastUptimeUs = 0;
mPastRealtimeUs = 0;
mUptimeStartUs = uptimeUs;
mRealtimeStartUs = elapsedRealtimeUs;
mUnpluggedUptimeUs = getUptime(mUptimeStartUs);
mUnpluggedRealtimeUs = getRealtime(mRealtimeStartUs);
}
public void reset(long uptimeUs, long elapsedRealtimeUs) {
if (!mRunning) {
mPastUptimeUs = 0;
mPastRealtimeUs = 0;
} else {
mUptimeStartUs = uptimeUs;
mRealtimeStartUs = elapsedRealtimeUs;
// TODO: Since mUptimeStartUs was just reset and we are running, getUptime will
// just return mPastUptimeUs. Also, are we sure we don't want to reset that?
mUnpluggedUptimeUs = getUptime(uptimeUs);
// TODO: likewise.
mUnpluggedRealtimeUs = getRealtime(elapsedRealtimeUs);
}
}
public long computeUptime(long curTimeUs, int which) {
return mUptimeUs + getUptime(curTimeUs);
}
public long computeRealtime(long curTimeUs, int which) {
return mRealtimeUs + getRealtime(curTimeUs);
}
public long getUptime(long curTimeUs) {
long time = mPastUptimeUs;
if (mRunning) {
time += curTimeUs - mUptimeStartUs;
}
return time;
}
public long getRealtime(long curTimeUs) {
long time = mPastRealtimeUs;
if (mRunning) {
time += curTimeUs - mRealtimeStartUs;
}
return time;
}
public long getUptimeStart() {
return mUptimeStartUs;
}
public long getRealtimeStart() {
return mRealtimeStartUs;
}
public boolean isRunning() {
return mRunning;
}
public boolean setRunning(boolean running, long uptimeUs, long elapsedRealtimeUs) {
if (mRunning != running) {
mRunning = running;
if (running) {
mUptimeStartUs = uptimeUs;
mRealtimeStartUs = elapsedRealtimeUs;
long batteryUptimeUs = mUnpluggedUptimeUs = getUptime(uptimeUs);
long batteryRealtimeUs = mUnpluggedRealtimeUs = getRealtime(elapsedRealtimeUs);
// Normally we do not use Iterator in framework code to avoid alloc/dealloc
// Iterator object, here is an exception because mObservers' type is Collection
// instead of list.
final Iterator<TimeBaseObs> iter = mObservers.iterator();
while (iter.hasNext()) {
iter.next().onTimeStarted(
elapsedRealtimeUs, batteryUptimeUs, batteryRealtimeUs);
}
} else {
mPastUptimeUs += uptimeUs - mUptimeStartUs;
mPastRealtimeUs += elapsedRealtimeUs - mRealtimeStartUs;
long batteryUptimeUs = getUptime(uptimeUs);
long batteryRealtimeUs = getRealtime(elapsedRealtimeUs);
// Normally we do not use Iterator in framework code to avoid alloc/dealloc
// Iterator object, here is an exception because mObservers' type is Collection
// instead of list.
final Iterator<TimeBaseObs> iter = mObservers.iterator();
while (iter.hasNext()) {
iter.next().onTimeStopped(
elapsedRealtimeUs, batteryUptimeUs, batteryRealtimeUs);
}
}
return true;
}
return false;
}
public void readSummaryFromParcel(Parcel in) {
mUptimeUs = in.readLong();
mRealtimeUs = in.readLong();
}
public void writeSummaryToParcel(Parcel out, long uptimeUs, long elapsedRealtimeUs) {
out.writeLong(computeUptime(uptimeUs, STATS_SINCE_CHARGED));
out.writeLong(computeRealtime(elapsedRealtimeUs, STATS_SINCE_CHARGED));
}
public void readFromParcel(Parcel in) {
mRunning = false;
mUptimeUs = in.readLong();
mPastUptimeUs = in.readLong();
mUptimeStartUs = in.readLong();
mRealtimeUs = in.readLong();
mPastRealtimeUs = in.readLong();
mRealtimeStartUs = in.readLong();
mUnpluggedUptimeUs = in.readLong();
mUnpluggedRealtimeUs = in.readLong();
}
public void writeToParcel(Parcel out, long uptimeUs, long elapsedRealtimeUs) {
final long runningUptime = getUptime(uptimeUs);
final long runningRealtime = getRealtime(elapsedRealtimeUs);
out.writeLong(mUptimeUs);
out.writeLong(runningUptime);
out.writeLong(mUptimeStartUs);
out.writeLong(mRealtimeUs);
out.writeLong(runningRealtime);
out.writeLong(mRealtimeStartUs);
out.writeLong(mUnpluggedUptimeUs);
out.writeLong(mUnpluggedRealtimeUs);
}
}
/**
* State for keeping track of counting information.
*/
public static class Counter extends BatteryStats.Counter implements TimeBaseObs {
@UnsupportedAppUsage
final AtomicInteger mCount = new AtomicInteger();
final TimeBase mTimeBase;
public Counter(TimeBase timeBase, Parcel in) {
mTimeBase = timeBase;
mCount.set(in.readInt());
timeBase.add(this);
}
public Counter(TimeBase timeBase) {
mTimeBase = timeBase;
timeBase.add(this);
}
public void writeToParcel(Parcel out) {
out.writeInt(mCount.get());
}
@Override
public void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
}
@Override
public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
}
/**
* Writes a possibly null Counter to a Parcel.
*
* @param out the Parcel to be written to.
* @param counter a Counter, or null.
*/
public static void writeCounterToParcel(Parcel out, @Nullable Counter counter) {
if (counter == null) {
out.writeInt(0); // indicates null
return;
}
out.writeInt(1); // indicates non-null
counter.writeToParcel(out);
}
/**
* Reads a Counter that was written using {@link #writeCounterToParcel(Parcel, Counter)}.
* @param timeBase the timebase to assign to the Counter
* @param in the parcel to read from
* @return the Counter or null.
*/
@Nullable
public static Counter readCounterFromParcel(TimeBase timeBase, Parcel in) {
if (in.readInt() == 0) {
return null;
}
return new Counter(timeBase, in);
}
@Override
public int getCountLocked(int which) {
return mCount.get();
}
public void logState(Printer pw, String prefix) {
pw.println(prefix + "mCount=" + mCount.get());
}
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public void stepAtomic() {
if (mTimeBase.isRunning()) {
mCount.incrementAndGet();
}
}
void addAtomic(int delta) {
if (mTimeBase.isRunning()) {
mCount.addAndGet(delta);
}
}
/**
* Clear state of this counter.
*/
@Override
public boolean reset(boolean detachIfReset, long elapsedRealtimeUs /* unused */) {
mCount.set(0);
if (detachIfReset) {
detach();
}
return true;
}
@Override
public void detach() {
mTimeBase.remove(this);
}
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public void writeSummaryFromParcelLocked(Parcel out) {
out.writeInt(mCount.get());
}
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public void readSummaryFromParcelLocked(Parcel in) {
mCount.set(in.readInt());
}
}
@VisibleForTesting
public static class LongSamplingCounterArray extends LongCounterArray implements TimeBaseObs {
final TimeBase mTimeBase;
public long[] mCounts;
private LongSamplingCounterArray(TimeBase timeBase, Parcel in) {
mTimeBase = timeBase;
mCounts = in.createLongArray();
timeBase.add(this);
}
public LongSamplingCounterArray(TimeBase timeBase) {
mTimeBase = timeBase;
timeBase.add(this);
}
private void writeToParcel(Parcel out) {
out.writeLongArray(mCounts);
}
@Override
public void onTimeStarted(long elapsedRealTimeUs, long baseUptimeUs, long baseRealtimeUs) {
}
@Override
public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
}
@Override
public long[] getCountsLocked(int which) {
return mCounts == null ? null : Arrays.copyOf(mCounts, mCounts.length);
}
@Override
public void logState(Printer pw, String prefix) {
pw.println(prefix + "mCounts=" + Arrays.toString(mCounts));
}
public void addCountLocked(long[] counts) {
addCountLocked(counts, mTimeBase.isRunning());
}
public void addCountLocked(long[] counts, boolean isRunning) {
if (counts == null) {
return;
}
if (isRunning) {
if (mCounts == null) {
mCounts = new long[counts.length];
}
for (int i = 0; i < counts.length; ++i) {
mCounts[i] += counts[i];
}
}
}
public int getSize() {
return mCounts == null ? 0 : mCounts.length;
}
/**
* Clear state of this counter.
*/
@Override
public boolean reset(boolean detachIfReset, long elapsedRealtimeUs /* unused */) {
if (mCounts != null) {
Arrays.fill(mCounts, 0);
}
if (detachIfReset) {
detach();
}
return true;
}
@Override
public void detach() {
mTimeBase.remove(this);
}
private void writeSummaryToParcelLocked(Parcel out) {
out.writeLongArray(mCounts);
}
private void readSummaryFromParcelLocked(Parcel in) {
mCounts = in.createLongArray();
}
public static void writeToParcel(Parcel out, LongSamplingCounterArray counterArray) {
if (counterArray != null) {
out.writeInt(1);
counterArray.writeToParcel(out);
} else {
out.writeInt(0);
}
}
public static LongSamplingCounterArray readFromParcel(Parcel in, TimeBase timeBase) {
if (in.readInt() != 0) {
return new LongSamplingCounterArray(timeBase, in);
} else {
return null;
}
}
public static void writeSummaryToParcelLocked(Parcel out,
LongSamplingCounterArray counterArray) {
if (counterArray != null) {
out.writeInt(1);
counterArray.writeSummaryToParcelLocked(out);
} else {
out.writeInt(0);
}
}
public static LongSamplingCounterArray readSummaryFromParcelLocked(Parcel in,
TimeBase timeBase) {
if (in.readInt() != 0) {
final LongSamplingCounterArray counterArray
= new LongSamplingCounterArray(timeBase);
counterArray.readSummaryFromParcelLocked(in);
return counterArray;
} else {
return null;
}
}
}
private static class TimeMultiStateCounter extends LongCounter implements TimeBaseObs {
private final TimeBase mTimeBase;
private final LongMultiStateCounter mCounter;
private TimeMultiStateCounter(TimeBase timeBase, int stateCount, long timestampMs) {
this(timeBase, new LongMultiStateCounter(stateCount), timestampMs);
}
private TimeMultiStateCounter(TimeBase timeBase, LongMultiStateCounter counter,
long timestampMs) {
mTimeBase = timeBase;
mCounter = counter;
mCounter.setEnabled(mTimeBase.isRunning(), timestampMs);
timeBase.add(this);
}
@Nullable
private static TimeMultiStateCounter readFromParcel(Parcel in, TimeBase timeBase,
int stateCount, long timestampMs) {
LongMultiStateCounter counter = LongMultiStateCounter.CREATOR.createFromParcel(in);
if (counter.getStateCount() != stateCount) {
return null;
}
return new TimeMultiStateCounter(timeBase, counter, timestampMs);
}
private void writeToParcel(Parcel out) {
mCounter.writeToParcel(out, 0);
}
@Override
public void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
mCounter.setEnabled(true, elapsedRealtimeUs / 1000);
}
@Override
public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
mCounter.setEnabled(false, elapsedRealtimeUs / 1000);
}
public int getStateCount() {
return mCounter.getStateCount();
}
private void setState(@BatteryConsumer.ProcessState int processState,
long elapsedRealtimeMs) {
mCounter.setState(processState, elapsedRealtimeMs);
}
private long update(long value, long timestampMs) {
return mCounter.updateValue(value, timestampMs);
}
private void increment(long increment, long timestampMs) {
mCounter.incrementValue(increment, timestampMs);
}
/**
* Returns accumulated count for the specified state.
*/
public long getCountForProcessState(@BatteryConsumer.ProcessState int procState) {
return mCounter.getCount(procState);
}
public long getTotalCountLocked() {
return mCounter.getTotalCount();
}
@Override
public long getCountLocked(int statsType) {
return getTotalCountLocked();
}
@Override
public void logState(Printer pw, String prefix) {
pw.println(prefix + "mCounter=" + mCounter);
}
/**
* Clears state of this counter.
*/
@Override
public boolean reset(boolean detachIfReset, long elapsedRealtimeUs /* unused */) {
mCounter.reset();
if (detachIfReset) {
detach();
}
return true;
}
@Override
public void detach() {
mTimeBase.remove(this);
}
}
private static class TimeInFreqMultiStateCounter implements TimeBaseObs {
private final TimeBase mTimeBase;
private final LongArrayMultiStateCounter mCounter;
private TimeInFreqMultiStateCounter(TimeBase timeBase, int stateCount, int cpuFreqCount,
long timestampMs) {
this(timeBase, new LongArrayMultiStateCounter(stateCount, cpuFreqCount), timestampMs);
}
private TimeInFreqMultiStateCounter(TimeBase timeBase, LongArrayMultiStateCounter counter,
long timestampMs) {
mTimeBase = timeBase;
mCounter = counter;
mCounter.setEnabled(mTimeBase.isRunning(), timestampMs);
timeBase.add(this);
}
private void writeToParcel(Parcel out) {
mCounter.writeToParcel(out, 0);
}
@Nullable
private static TimeInFreqMultiStateCounter readFromParcel(Parcel in, TimeBase timeBase,
int stateCount, int cpuFreqCount, long timestampMs) {
// Read the object from the Parcel, whether it's usable or not
LongArrayMultiStateCounter counter =
LongArrayMultiStateCounter.CREATOR.createFromParcel(in);
if (counter.getStateCount() != stateCount
|| counter.getArrayLength() != cpuFreqCount) {
return null;
}
return new TimeInFreqMultiStateCounter(timeBase, counter, timestampMs);
}
@Override
public void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
mCounter.setEnabled(true, elapsedRealtimeUs / 1000);
}
@Override
public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
mCounter.setEnabled(false, elapsedRealtimeUs / 1000);
}
public LongArrayMultiStateCounter getCounter() {
return mCounter;
}
public int getStateCount() {
return mCounter.getStateCount();
}
public void setTrackingEnabled(boolean enabled, long timestampMs) {
mCounter.setEnabled(enabled && mTimeBase.isRunning(), timestampMs);
}
private void setState(int uidRunningState, long elapsedRealtimeMs) {
mCounter.setState(uidRunningState, elapsedRealtimeMs);
}
/**
* Returns accumulated counts for the specified state, or false if all counts are zero.
*/
public boolean getCountsLocked(long[] counts, int procState) {
if (counts.length != mCounter.getArrayLength()) {
return false;
}
mCounter.getCounts(counts, procState);
// Return counts only if at least one of the elements is non-zero.
for (int i = counts.length - 1; i >= 0; --i) {
if (counts[i] != 0) {
return true;
}
}
return false;
}
public void logState(Printer pw, String prefix) {
pw.println(prefix + "mCounter=" + mCounter);
}
/**
* Clears state of this counter.
*/
@Override
public boolean reset(boolean detachIfReset, long elapsedRealtimeUs /* unused */) {
mCounter.reset();
if (detachIfReset) {
detach();
}
return true;
}
@Override
public void detach() {
mTimeBase.remove(this);
}
}
@VisibleForTesting
public static class LongSamplingCounter extends LongCounter implements TimeBaseObs {
final TimeBase mTimeBase;
private long mCount;
public LongSamplingCounter(TimeBase timeBase, Parcel in) {
mTimeBase = timeBase;
mCount = in.readLong();
timeBase.add(this);
}
public LongSamplingCounter(TimeBase timeBase) {
mTimeBase = timeBase;
timeBase.add(this);
}
public void writeToParcel(Parcel out) {
out.writeLong(mCount);
}
@Override
public void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
}
@Override
public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
}
public long getCountLocked(int which) {
return mCount;
}
@Override
public long getCountForProcessState(int procState) {
if (procState == BatteryConsumer.PROCESS_STATE_ANY) {
return getCountLocked(STATS_SINCE_CHARGED);
}
return 0;
}
@Override
public void logState(Printer pw, String prefix) {
pw.println(prefix + "mCount=" + mCount);
}
public void addCountLocked(long count) {
addCountLocked(count, mTimeBase.isRunning());
}
public void addCountLocked(long count, boolean isRunning) {
if (isRunning) {
mCount += count;
}
}
/**
* Clear state of this counter.
*/
@Override
public boolean reset(boolean detachIfReset, long elapsedRealtimeUs /* unused */) {
mCount = 0;
if (detachIfReset) {
detach();
}
return true;
}
@Override
public void detach() {
mTimeBase.remove(this);
}
public void writeSummaryFromParcelLocked(Parcel out) {
out.writeLong(mCount);
}
public void readSummaryFromParcelLocked(Parcel in) {
mCount = in.readLong();
}
}
/**
* State for keeping track of timing information.
*/
public static abstract class Timer extends BatteryStats.Timer implements TimeBaseObs {
protected final Clock mClock;
protected final int mType;
protected final TimeBase mTimeBase;
protected int mCount;
// Times are in microseconds for better accuracy when dividing by the
// lock count, and are in "battery realtime" units.
/**
* The total time we have accumulated since the start of the original
* boot, to the last time something interesting happened in the
* current run.
*/
protected long mTotalTimeUs;
/**
* The total time this timer has been running until the latest mark has been set.
* Subtract this from mTotalTimeUs to get the time spent running since the mark was set.
*/
protected long mTimeBeforeMarkUs;
/**
* Constructs from a parcel.
* @param type
* @param timeBase
* @param in
*/
public Timer(Clock clock, int type, TimeBase timeBase, Parcel in) {
mClock = clock;
mType = type;
mTimeBase = timeBase;
mCount = in.readInt();
mTotalTimeUs = in.readLong();
mTimeBeforeMarkUs = in.readLong();
timeBase.add(this);
if (DEBUG) Log.i(TAG, "**** READ TIMER #" + mType + ": mTotalTime=" + mTotalTimeUs);
}
public Timer(Clock clock, int type, TimeBase timeBase) {
mClock = clock;
mType = type;
mTimeBase = timeBase;
timeBase.add(this);
}
public void writeToParcel(Parcel out, long elapsedRealtimeUs) {
if (DEBUG) {
Log.i(TAG, "**** WRITING TIMER #" + mType + ": mTotalTime="
+ computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs),
elapsedRealtimeUs));
}
out.writeInt(computeCurrentCountLocked());
out.writeLong(computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs),
elapsedRealtimeUs));
out.writeLong(mTimeBeforeMarkUs);
}
protected abstract long computeRunTimeLocked(long curBatteryRealtime,
long elapsedRealtimeUs);
protected abstract int computeCurrentCountLocked();
/**
* Clear state of this timer. Returns true if the timer is inactive
* so can be completely dropped.
*/
@Override
public boolean reset(boolean detachIfReset) {
return reset(detachIfReset, mClock.elapsedRealtime() * 1000);
}
@Override
public boolean reset(boolean detachIfReset, long elapsedRealtimeUs /* unused */) {
mTotalTimeUs = mTimeBeforeMarkUs = 0;
mCount = 0;
if (detachIfReset) {
detach();
}
return true;
}
@Override
public void detach() {
mTimeBase.remove(this);
}
@Override
public void onTimeStarted(long elapsedRealtimeUs, long timeBaseUptimeUs,
long baseRealtimeUs) {
}
@Override
public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
if (DEBUG && mType < 0) {
Log.v(TAG, "plug #" + mType + ": realtime=" + baseRealtimeUs
+ " old mTotalTime=" + mTotalTimeUs);
}
mTotalTimeUs = computeRunTimeLocked(baseRealtimeUs, elapsedRealtimeUs);
mCount = computeCurrentCountLocked();
if (DEBUG && mType < 0) {
Log.v(TAG, "plug #" + mType + ": new mTotalTime=" + mTotalTimeUs);
}
}
/**
* Writes a possibly null Timer to a Parcel.
*
* @param out the Parcel to be written to.
* @param timer a Timer, or null.
*/
@UnsupportedAppUsage
public static void writeTimerToParcel(Parcel out, Timer timer, long elapsedRealtimeUs) {
if (timer == null) {
out.writeInt(0); // indicates null
return;
}
out.writeInt(1); // indicates non-null
timer.writeToParcel(out, elapsedRealtimeUs);
}
@Override
@UnsupportedAppUsage
public long getTotalTimeLocked(long elapsedRealtimeUs, int which) {
return computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs),
elapsedRealtimeUs);
}
@Override
@UnsupportedAppUsage
public int getCountLocked(int which) {
return computeCurrentCountLocked();
}
@Override
public long getTimeSinceMarkLocked(long elapsedRealtimeUs) {
long val = computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs),
elapsedRealtimeUs);
return val - mTimeBeforeMarkUs;
}
@Override
public void logState(Printer pw, String prefix) {
pw.println(prefix + "mCount=" + mCount);
pw.println(prefix + "mTotalTime=" + mTotalTimeUs);
}
public void writeSummaryFromParcelLocked(Parcel out, long elapsedRealtimeUs) {
long runTimeUs = computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs),
elapsedRealtimeUs);
out.writeLong(runTimeUs);
out.writeInt(computeCurrentCountLocked());
}
public void readSummaryFromParcelLocked(Parcel in) {
// Multiply by 1000 for backwards compatibility
mTotalTimeUs = in.readLong();
mCount = in.readInt();
// When reading the summary, we set the mark to be the latest information.
mTimeBeforeMarkUs = mTotalTimeUs;
}
}
/**
* A counter meant to accept monotonically increasing values to its {@link #update(long, int)}
* method. The state of the timer according to its {@link TimeBase} will determine how much
* of the value is recorded.
*
* If the value being recorded resets, {@link #endSample()} can be called in order to
* account for the change. If the value passed in to {@link #update(long, int)} decreased
* between calls, the {@link #endSample()} is automatically called and the new value is
* expected to increase monotonically from that point on.
*/
public static class SamplingTimer extends Timer {
/**
* The most recent reported count from /proc/wakelocks.
*/
int mCurrentReportedCount;
/**
* The reported count from /proc/wakelocks when unplug() was last
* called.
*/
int mUnpluggedReportedCount;
/**
* The most recent reported total_time from /proc/wakelocks.
*/
long mCurrentReportedTotalTimeUs;
/**
* The reported total_time from /proc/wakelocks when unplug() was last
* called.
*/
long mUnpluggedReportedTotalTimeUs;
/**
* Whether we are currently in a discharge cycle.
*/
boolean mTimeBaseRunning;
/**
* Whether we are currently recording reported values.
*/
boolean mTrackingReportedValues;
/*
* A sequence counter, incremented once for each update of the stats.
*/
int mUpdateVersion;
@VisibleForTesting
public SamplingTimer(Clock clock, TimeBase timeBase, Parcel in) {
super(clock, 0, timeBase, in);
mCurrentReportedCount = in.readInt();
mUnpluggedReportedCount = in.readInt();
mCurrentReportedTotalTimeUs = in.readLong();
mUnpluggedReportedTotalTimeUs = in.readLong();
mTrackingReportedValues = in.readInt() == 1;
mTimeBaseRunning = timeBase.isRunning();
}
@VisibleForTesting
public SamplingTimer(Clock clock, TimeBase timeBase) {
super(clock, 0, timeBase);
mTrackingReportedValues = false;
mTimeBaseRunning = timeBase.isRunning();
}
/**
* Ends the current sample, allowing subsequent values to {@link #update(long, int)} to
* be less than the values used for a previous invocation.
*/
public void endSample() {
endSample(mClock.elapsedRealtime() * 1000);
}
/**
* @see #endSample()
*/
public void endSample(long elapsedRealtimeUs) {
mTotalTimeUs = computeRunTimeLocked(0 /* unused by us */, elapsedRealtimeUs);
mCount = computeCurrentCountLocked();
mUnpluggedReportedTotalTimeUs = mCurrentReportedTotalTimeUs = 0;
mUnpluggedReportedCount = mCurrentReportedCount = 0;
mTrackingReportedValues = false;
}
public void setUpdateVersion(int version) {
mUpdateVersion = version;
}
public int getUpdateVersion() {
return mUpdateVersion;
}
/**
* Updates the current recorded values. These are meant to be monotonically increasing
* and cumulative. If you are dealing with deltas, use {@link #add(long, int)}.
*
* If the values being recorded have been reset, the monotonically increasing requirement
* will be broken. In this case, {@link #endSample()} is automatically called and
* the total value of totalTimeUs and count are recorded, starting a new monotonically
* increasing sample.
*
* @param totalTimeUs total time of sample in microseconds.
* @param count total number of times the event being sampled occurred.
*/
public void updated(long totalTimeUs, int count) {
update(totalTimeUs, count, mClock.elapsedRealtime() * 1000);
}
/**
* @see #update(long, int)
*/
public void update(long totalTimeUs, int count, long elapsedRealtimeUs) {
if (mTimeBaseRunning && !mTrackingReportedValues) {
// Updating the reported value for the first time.
mUnpluggedReportedTotalTimeUs = totalTimeUs;
mUnpluggedReportedCount = count;
}
mTrackingReportedValues = true;
if (totalTimeUs < mCurrentReportedTotalTimeUs || count < mCurrentReportedCount) {
endSample(elapsedRealtimeUs);
}
mCurrentReportedTotalTimeUs = totalTimeUs;
mCurrentReportedCount = count;
}
/**
* Adds deltaTime and deltaCount to the current sample.
*
* @param deltaTime additional time recorded since the last sampled event, in microseconds.
* @param deltaCount additional number of times the event being sampled occurred.
*/
public void add(long deltaTimeUs, int deltaCount) {
add(deltaTimeUs, deltaCount, mClock.elapsedRealtime() * 1000);
}
/**
* @see #add(long, int)
*/
public void add(long deltaTimeUs, int deltaCount, long elapsedRealtimeUs) {
update(mCurrentReportedTotalTimeUs + deltaTimeUs, mCurrentReportedCount + deltaCount,
elapsedRealtimeUs);
}
@Override
public void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
super.onTimeStarted(elapsedRealtimeUs, baseUptimeUs, baseRealtimeUs);
if (mTrackingReportedValues) {
mUnpluggedReportedTotalTimeUs = mCurrentReportedTotalTimeUs;
mUnpluggedReportedCount = mCurrentReportedCount;
}
mTimeBaseRunning = true;
}
@Override
public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
super.onTimeStopped(elapsedRealtimeUs, baseUptimeUs, baseRealtimeUs);
mTimeBaseRunning = false;
}
@Override
public void logState(Printer pw, String prefix) {
super.logState(pw, prefix);
pw.println(prefix + "mCurrentReportedCount=" + mCurrentReportedCount
+ " mUnpluggedReportedCount=" + mUnpluggedReportedCount
+ " mCurrentReportedTotalTime=" + mCurrentReportedTotalTimeUs
+ " mUnpluggedReportedTotalTime=" + mUnpluggedReportedTotalTimeUs);
}
@Override
protected long computeRunTimeLocked(long curBatteryRealtime, long elapsedRealtimeUs) {
return mTotalTimeUs + (mTimeBaseRunning && mTrackingReportedValues
? mCurrentReportedTotalTimeUs - mUnpluggedReportedTotalTimeUs : 0);
}
@Override
protected int computeCurrentCountLocked() {
return mCount + (mTimeBaseRunning && mTrackingReportedValues
? mCurrentReportedCount - mUnpluggedReportedCount : 0);
}
@Override
public void writeToParcel(Parcel out, long elapsedRealtimeUs) {
super.writeToParcel(out, elapsedRealtimeUs);
out.writeInt(mCurrentReportedCount);
out.writeInt(mUnpluggedReportedCount);
out.writeLong(mCurrentReportedTotalTimeUs);
out.writeLong(mUnpluggedReportedTotalTimeUs);
out.writeInt(mTrackingReportedValues ? 1 : 0);
}
@Override
public boolean reset(boolean detachIfReset, long elapsedRealtimeUs) {
super.reset(detachIfReset, elapsedRealtimeUs);
mTrackingReportedValues = false;
mUnpluggedReportedTotalTimeUs = 0;
mUnpluggedReportedCount = 0;
return true;
}
}
/**
* A timer that increments in batches. It does not run for durations, but just jumps
* for a pre-determined amount.
*/
public static class BatchTimer extends Timer {
final Uid mUid;
/**
* The last time at which we updated the timer. This is in elapsed realtime microseconds.
*/
long mLastAddedTimeUs;
/**
* The last duration that we added to the timer. This is in microseconds.
*/
long mLastAddedDurationUs;
/**
* Whether we are currently in a discharge cycle.
*/
boolean mInDischarge;
BatchTimer(Clock clock, Uid uid, int type, TimeBase timeBase, Parcel in) {
super(clock, type, timeBase, in);
mUid = uid;
mLastAddedTimeUs = in.readLong();
mLastAddedDurationUs = in.readLong();
mInDischarge = timeBase.isRunning();
}
BatchTimer(Clock clock, Uid uid, int type, TimeBase timeBase) {
super(clock, type, timeBase);
mUid = uid;
mInDischarge = timeBase.isRunning();
}
@Override
public void writeToParcel(Parcel out, long elapsedRealtimeUs) {
super.writeToParcel(out, elapsedRealtimeUs);
out.writeLong(mLastAddedTimeUs);
out.writeLong(mLastAddedDurationUs);
}
@Override
public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
recomputeLastDuration(elapsedRealtimeUs, false);
mInDischarge = false;
super.onTimeStopped(elapsedRealtimeUs, baseUptimeUs, baseRealtimeUs);
}
@Override
public void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
recomputeLastDuration(elapsedRealtimeUs, false);
mInDischarge = true;
// If we are still within the last added duration, then re-added whatever remains.
if (mLastAddedTimeUs == elapsedRealtimeUs) {
mTotalTimeUs += mLastAddedDurationUs;
}
super.onTimeStarted(elapsedRealtimeUs, baseUptimeUs, baseRealtimeUs);
}
@Override
public void logState(Printer pw, String prefix) {
super.logState(pw, prefix);
pw.println(prefix + "mLastAddedTime=" + mLastAddedTimeUs
+ " mLastAddedDuration=" + mLastAddedDurationUs);
}
private long computeOverage(long curTimeUs) {
if (mLastAddedTimeUs > 0) {
return mLastAddedDurationUs - curTimeUs;
}
return 0;
}
private void recomputeLastDuration(long curTimeUs, boolean abort) {
final long overage = computeOverage(curTimeUs);
if (overage > 0) {
// Aborting before the duration ran out -- roll back the remaining
// duration. Only do this if currently discharging; otherwise we didn't
// actually add the time.
if (mInDischarge) {
mTotalTimeUs -= overage;
}
if (abort) {
mLastAddedTimeUs = 0;
} else {
mLastAddedTimeUs = curTimeUs;
mLastAddedDurationUs -= overage;
}
}
}
public void addDuration(BatteryStatsImpl stats, long durationMs) {
addDuration(stats, durationMs, mClock.elapsedRealtime());
}
public void addDuration(BatteryStatsImpl stats, long durationMs, long elapsedRealtimeMs) {
final long nowUs = elapsedRealtimeMs * 1000;
recomputeLastDuration(nowUs, true);
mLastAddedTimeUs = nowUs;
mLastAddedDurationUs = durationMs * 1000;
if (mInDischarge) {
mTotalTimeUs += mLastAddedDurationUs;
mCount++;
}
}
public void abortLastDuration(BatteryStatsImpl stats) {
abortLastDuration(stats, mClock.elapsedRealtime());
}
public void abortLastDuration(BatteryStatsImpl stats, long elapsedRealtimeMs) {
final long nowUs = elapsedRealtimeMs * 1000;
recomputeLastDuration(nowUs, true);
}
@Override
protected int computeCurrentCountLocked() {
return mCount;
}
@Override
protected long computeRunTimeLocked(long curBatteryRealtimeUs, long elapsedRealtimeUs) {
final long overage = computeOverage(elapsedRealtimeUs);
if (overage > 0) {
return mTotalTimeUs = overage;
}
return mTotalTimeUs;
}
@Override
public boolean reset(boolean detachIfReset, long elapsedRealtimeUs) {
recomputeLastDuration(elapsedRealtimeUs, true);
boolean stillActive = mLastAddedTimeUs == elapsedRealtimeUs;
super.reset(!stillActive && detachIfReset, elapsedRealtimeUs);
return !stillActive;
}
}
/**
* A StopwatchTimer that also tracks the total and max individual
* time spent active according to the given timebase. Whereas
* StopwatchTimer apportions the time amongst all in the pool,
* the total and max durations are not apportioned.
*/
public static class DurationTimer extends StopwatchTimer {
/**
* The time (in ms) that the timer was last acquired or the time base
* last (re-)started. Increasing the nesting depth does not reset this time.
*
* -1 if the timer is currently not running or the time base is not running.
*
* If written to a parcel, the start time is reset, as is mNesting in the base class
* StopwatchTimer.
*/
long mStartTimeMs = -1;
/**
* The longest time period (in ms) that the timer has been active. Not pooled.
*/
long mMaxDurationMs;
/**
* The time (in ms) that that the timer has been active since most recent
* stopRunningLocked() or reset(). Not pooled.
*/
long mCurrentDurationMs;
/**
* The total time (in ms) that that the timer has been active since most recent reset()
* prior to the current startRunningLocked. This is the sum of all past currentDurations
* (but not including the present currentDuration) since reset. Not pooled.
*/
long mTotalDurationMs;
public DurationTimer(Clock clock, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
TimeBase timeBase, Parcel in) {
super(clock, uid, type, timerPool, timeBase, in);
mMaxDurationMs = in.readLong();
mTotalDurationMs = in.readLong();
mCurrentDurationMs = in.readLong();
}
public DurationTimer(Clock clock, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
TimeBase timeBase) {
super(clock, uid, type, timerPool, timeBase);
}
@Override
public void writeToParcel(Parcel out, long elapsedRealtimeUs) {
super.writeToParcel(out, elapsedRealtimeUs);
out.writeLong(getMaxDurationMsLocked(elapsedRealtimeUs / 1000));
out.writeLong(mTotalDurationMs);
out.writeLong(getCurrentDurationMsLocked(elapsedRealtimeUs / 1000));
}
/**
* Write the summary to the parcel.
*
* Since the time base is probably meaningless after we come back, reading
* from this will have the effect of stopping the timer. So here all we write
* is the max and total durations.
*/
@Override
public void writeSummaryFromParcelLocked(Parcel out, long elapsedRealtimeUs) {
super.writeSummaryFromParcelLocked(out, elapsedRealtimeUs);
out.writeLong(getMaxDurationMsLocked(elapsedRealtimeUs / 1000));
out.writeLong(getTotalDurationMsLocked(elapsedRealtimeUs / 1000));
}
/**
* Read the summary parcel.
*
* Has the side effect of stopping the timer.
*/
@Override
public void readSummaryFromParcelLocked(Parcel in) {
super.readSummaryFromParcelLocked(in);
mMaxDurationMs = in.readLong();
mTotalDurationMs = in.readLong();
mStartTimeMs = -1;
mCurrentDurationMs = 0;
}
/**
* The TimeBase time started (again).
*
* If the timer is also running, store the start time.
*/
public void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
super.onTimeStarted(elapsedRealtimeUs, baseUptimeUs, baseRealtimeUs);
if (mNesting > 0) {
mStartTimeMs = baseRealtimeUs / 1000;
}
}
/**
* The TimeBase stopped running.
*
* If the timer is running, add the duration into mCurrentDurationMs.
*/
@Override
public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
super.onTimeStopped(elapsedRealtimeUs, baseUptimeUs, baseRealtimeUs);
if (mNesting > 0) {
// baseRealtimeUs has already been converted to the timebase's realtime.
mCurrentDurationMs += (baseRealtimeUs / 1000) - mStartTimeMs;
}
mStartTimeMs = -1;
}
@Override
public void logState(Printer pw, String prefix) {
super.logState(pw, prefix);
}
@Override
public void startRunningLocked(long elapsedRealtimeMs) {
super.startRunningLocked(elapsedRealtimeMs);
if (mNesting == 1 && mTimeBase.isRunning()) {
// Just started
mStartTimeMs = mTimeBase.getRealtime(elapsedRealtimeMs * 1000) / 1000;
}
}
/**
* Decrements the mNesting ref-count on this timer.
*
* If it actually stopped (mNesting went to 0), then possibly update
* mMaxDuration if the current duration was the longest ever.
*/
@Override
public void stopRunningLocked(long elapsedRealtimeMs) {
if (mNesting == 1) {
final long durationMs = getCurrentDurationMsLocked(elapsedRealtimeMs);
mTotalDurationMs += durationMs;
if (durationMs > mMaxDurationMs) {
mMaxDurationMs = durationMs;
}
mStartTimeMs = -1;
mCurrentDurationMs = 0;
}
// super method decrements mNesting, which getCurrentDurationMsLocked relies on,
// so call super.stopRunningLocked after calling getCurrentDurationMsLocked.
super.stopRunningLocked(elapsedRealtimeMs);
}
@Override
public boolean reset(boolean detachIfReset, long elapsedRealtimeUs) {
boolean result = super.reset(detachIfReset, elapsedRealtimeUs);
mMaxDurationMs = 0;
mTotalDurationMs = 0;
mCurrentDurationMs = 0;
if (mNesting > 0) {
mStartTimeMs = mTimeBase.getRealtime(elapsedRealtimeUs) / 1000;
} else {
mStartTimeMs = -1;
}
return result;
}
/**
* Returns the max duration that this timer has ever seen.
*
* Note that this time is NOT split between the timers in the timer group that
* this timer is attached to. It is the TOTAL time.
*/
@Override
public long getMaxDurationMsLocked(long elapsedRealtimeMs) {
if (mNesting > 0) {
final long durationMs = getCurrentDurationMsLocked(elapsedRealtimeMs);
if (durationMs > mMaxDurationMs) {
return durationMs;
}
}
return mMaxDurationMs;
}
/**
* Returns the time since the timer was started.
* Returns 0 if the timer is not currently running.
*
* Note that this time is NOT split between the timers in the timer group that
* this timer is attached to. It is the TOTAL time.
*
* Note that if running timer is parceled and unparceled, this method will return
* current duration value at the time of parceling even though timer may not be
* currently running.
*/
@Override
public long getCurrentDurationMsLocked(long elapsedRealtimeMs) {
long durationMs = mCurrentDurationMs;
if (mNesting > 0 && mTimeBase.isRunning()) {
durationMs += (mTimeBase.getRealtime(elapsedRealtimeMs * 1000) / 1000)
- mStartTimeMs;
}
return durationMs;
}
/**
* Returns the total cumulative duration that this timer has been on since reset().
* If mTimerPool == null, this should be the same
* as getTotalTimeLocked(elapsedRealtimeMs*1000, STATS_SINCE_CHARGED)/1000.
*
* Note that this time is NOT split between the timers in the timer group that
* this timer is attached to. It is the TOTAL time. For this reason, if mTimerPool != null,
* the result will not be equivalent to getTotalTimeLocked.
*/
@Override
public long getTotalDurationMsLocked(long elapsedRealtimeMs) {
return mTotalDurationMs + getCurrentDurationMsLocked(elapsedRealtimeMs);
}
}
/**
* State for keeping track of timing information.
*/
public static class StopwatchTimer extends Timer {
final Uid mUid;
final ArrayList<StopwatchTimer> mTimerPool;
int mNesting;
/**
* The last time at which we updated the timer. If mNesting is > 0,
* subtract this from the current battery time to find the amount of
* time we have been running since we last computed an update.
*/
long mUpdateTimeUs;
/**
* The total time at which the timer was acquired, to determine if it
* was actually held for an interesting duration. If time base was not running when timer
* was acquired, will be -1.
*/
long mAcquireTimeUs = -1;
long mTimeoutUs;
/**
* For partial wake locks, keep track of whether we are in the list
* to consume CPU cycles.
*/
@VisibleForTesting
public boolean mInList;
public StopwatchTimer(Clock clock, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
TimeBase timeBase, Parcel in) {
super(clock, type, timeBase, in);
mUid = uid;
mTimerPool = timerPool;
mUpdateTimeUs = in.readLong();
}
public StopwatchTimer(Clock clock, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
TimeBase timeBase) {
super(clock, type, timeBase);
mUid = uid;
mTimerPool = timerPool;
}
public void setTimeout(long timeoutUs) {
mTimeoutUs = timeoutUs;
}
public void writeToParcel(Parcel out, long elapsedRealtimeUs) {
super.writeToParcel(out, elapsedRealtimeUs);
out.writeLong(mUpdateTimeUs);
}
public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
if (mNesting > 0) {
if (DEBUG && mType < 0) {
Log.v(TAG, "old mUpdateTime=" + mUpdateTimeUs);
}
super.onTimeStopped(elapsedRealtimeUs, baseUptimeUs, baseRealtimeUs);
mUpdateTimeUs = baseRealtimeUs;
if (DEBUG && mType < 0) {
Log.v(TAG, "new mUpdateTime=" + mUpdateTimeUs);
}
}
}
public void logState(Printer pw, String prefix) {
super.logState(pw, prefix);
pw.println(prefix + "mNesting=" + mNesting + " mUpdateTime=" + mUpdateTimeUs
+ " mAcquireTime=" + mAcquireTimeUs);
}
public void startRunningLocked(long elapsedRealtimeMs) {
if (mNesting++ == 0) {
final long batteryRealtimeUs = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);
mUpdateTimeUs = batteryRealtimeUs;
if (mTimerPool != null) {
// Accumulate time to all currently active timers before adding
// this new one to the pool.
refreshTimersLocked(batteryRealtimeUs, mTimerPool, null);
// Add this timer to the active pool
mTimerPool.add(this);
}
if (mTimeBase.isRunning()) {
// Increment the count
mCount++;
mAcquireTimeUs = mTotalTimeUs;
} else {
mAcquireTimeUs = -1;
}
if (DEBUG && mType < 0) {
Log.v(TAG, "start #" + mType + ": mUpdateTime=" + mUpdateTimeUs
+ " mTotalTime=" + mTotalTimeUs + " mCount=" + mCount
+ " mAcquireTime=" + mAcquireTimeUs);
}
}
}
public boolean isRunningLocked() {
return mNesting > 0;
}
public void stopRunningLocked(long elapsedRealtimeMs) {
// Ignore attempt to stop a timer that isn't running
if (mNesting == 0) {
return;
}
if (--mNesting == 0) {
final long batteryRealtimeUs = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);
if (mTimerPool != null) {
// Accumulate time to all active counters, scaled by the total
// active in the pool, before taking this one out of the pool.
refreshTimersLocked(batteryRealtimeUs, mTimerPool, null);
// Remove this timer from the active pool
mTimerPool.remove(this);
} else {
mNesting = 1;
mTotalTimeUs = computeRunTimeLocked(batteryRealtimeUs,
elapsedRealtimeMs * 1000);
mNesting = 0;
}
if (DEBUG && mType < 0) {
Log.v(TAG, "stop #" + mType + ": mUpdateTime=" + mUpdateTimeUs
+ " mTotalTime=" + mTotalTimeUs + " mCount=" + mCount
+ " mAcquireTime=" + mAcquireTimeUs);
}
if (mAcquireTimeUs >= 0 && mTotalTimeUs == mAcquireTimeUs) {
// If there was no change in the time, then discard this
// count. A somewhat cheezy strategy, but hey.
mCount--;
}
}
}
public void stopAllRunningLocked(long elapsedRealtimeMs) {
if (mNesting > 0) {
mNesting = 1;
stopRunningLocked(elapsedRealtimeMs);
}
}
// Update the total time for all other running Timers with the same type as this Timer
// due to a change in timer count
private static long refreshTimersLocked(long batteryRealtimeUs,
final ArrayList<StopwatchTimer> pool, StopwatchTimer self) {
long selfTimeUs = 0;
final int N = pool.size();
for (int i=N-1; i>= 0; i--) {
final StopwatchTimer t = pool.get(i);
long heldTimeUs = batteryRealtimeUs - t.mUpdateTimeUs;
if (heldTimeUs > 0) {
final long myTimeUs = heldTimeUs / N;
if (t == self) {
selfTimeUs = myTimeUs;
}
t.mTotalTimeUs += myTimeUs;
}
t.mUpdateTimeUs = batteryRealtimeUs;
}
return selfTimeUs;
}
@Override
protected long computeRunTimeLocked(long curBatteryRealtimeUs, long elapsedRealtimeUs) {
if (mTimeoutUs > 0 && curBatteryRealtimeUs > mUpdateTimeUs + mTimeoutUs) {
curBatteryRealtimeUs = mUpdateTimeUs + mTimeoutUs;
}
return mTotalTimeUs + (mNesting > 0
? (curBatteryRealtimeUs - mUpdateTimeUs)
/ (mTimerPool != null ? mTimerPool.size() : 1)
: 0);
}
@Override
protected int computeCurrentCountLocked() {
return mCount;
}
@Override
public boolean reset(boolean detachIfReset, long elapsedRealtimeUs) {
boolean canDetach = mNesting <= 0;
super.reset(canDetach && detachIfReset, elapsedRealtimeUs);
if (mNesting > 0) {
mUpdateTimeUs = mTimeBase.getRealtime(elapsedRealtimeUs);
}
// To ensure mCount isn't decreased to -1 if timer is stopped later.
mAcquireTimeUs = -1;
return canDetach;
}
@Override
@UnsupportedAppUsage
public void detach() {
super.detach();
if (mTimerPool != null) {
mTimerPool.remove(this);
}
}
@Override
public void readSummaryFromParcelLocked(Parcel in) {
super.readSummaryFromParcelLocked(in);
mNesting = 0;
}
/**
* Set the mark so that we can query later for the total time the timer has
* accumulated since this point. The timer can be running or not.
*
* @param elapsedRealtimeMs the current elapsed realtime in milliseconds.
*/
public void setMark(long elapsedRealtimeMs) {
final long batteryRealtimeUs = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);
if (mNesting > 0) {
// We are running.
if (mTimerPool != null) {
refreshTimersLocked(batteryRealtimeUs, mTimerPool, this);
} else {
mTotalTimeUs += batteryRealtimeUs - mUpdateTimeUs;
mUpdateTimeUs = batteryRealtimeUs;
}
}
mTimeBeforeMarkUs = mTotalTimeUs;
}
}
/**
* State for keeping track of two DurationTimers with different TimeBases, presumably where one
* TimeBase is effectively a subset of the other.
*/
public static class DualTimer extends DurationTimer {
// This class both is a DurationTimer and also holds a second DurationTimer.
// The main timer (this) typically tracks the total time. It may be pooled (but since it's a
// durationTimer, it also has the unpooled getTotalDurationMsLocked() for
// STATS_SINCE_CHARGED).
// mSubTimer typically tracks only part of the total time, such as background time, as
// determined by a subTimeBase. It is NOT pooled.
private final DurationTimer mSubTimer;
/**
* Creates a DualTimer to hold a main timer (this) and a mSubTimer.
* The main timer (this) is based on the given timeBase and timerPool.
* The mSubTimer is based on the given subTimeBase. The mSubTimer is not pooled, even if
* the main timer is.
*/
public DualTimer(Clock clock, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
TimeBase timeBase, TimeBase subTimeBase, Parcel in) {
super(clock, uid, type, timerPool, timeBase, in);
mSubTimer = new DurationTimer(clock, uid, type, null, subTimeBase, in);
}
/**
* Creates a DualTimer to hold a main timer (this) and a mSubTimer.
* The main timer (this) is based on the given timeBase and timerPool.
* The mSubTimer is based on the given subTimeBase. The mSubTimer is not pooled, even if
* the main timer is.
*/
public DualTimer(Clock clock, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
TimeBase timeBase, TimeBase subTimeBase) {
super(clock, uid, type, timerPool, timeBase);
mSubTimer = new DurationTimer(clock, uid, type, null, subTimeBase);
}
/** Get the secondary timer. */
@Override
public DurationTimer getSubTimer() {
return mSubTimer;
}
@Override
public void startRunningLocked(long elapsedRealtimeMs) {
super.startRunningLocked(elapsedRealtimeMs);
mSubTimer.startRunningLocked(elapsedRealtimeMs);
}
@Override
public void stopRunningLocked(long elapsedRealtimeMs) {
super.stopRunningLocked(elapsedRealtimeMs);
mSubTimer.stopRunningLocked(elapsedRealtimeMs);
}
@Override
public void stopAllRunningLocked(long elapsedRealtimeMs) {
super.stopAllRunningLocked(elapsedRealtimeMs);
mSubTimer.stopAllRunningLocked(elapsedRealtimeMs);
}
@Override
public boolean reset(boolean detachIfReset, long elapsedRealtimeUs) {
boolean active = false;
// Do not detach the subTimer explicitly since that'll be done by DualTimer.detach().
active |= !mSubTimer.reset(false, elapsedRealtimeUs);
active |= !super.reset(detachIfReset, elapsedRealtimeUs);
return !active;
}
@Override
public void detach() {
mSubTimer.detach();
super.detach();
}
@Override
public void writeToParcel(Parcel out, long elapsedRealtimeUs) {
super.writeToParcel(out, elapsedRealtimeUs);
mSubTimer.writeToParcel(out, elapsedRealtimeUs);
}
@Override
public void writeSummaryFromParcelLocked(Parcel out, long elapsedRealtimeUs) {
super.writeSummaryFromParcelLocked(out, elapsedRealtimeUs);
mSubTimer.writeSummaryFromParcelLocked(out, elapsedRealtimeUs);
}
@Override
public void readSummaryFromParcelLocked(Parcel in) {
super.readSummaryFromParcelLocked(in);
mSubTimer.readSummaryFromParcelLocked(in);
}
}
public abstract class OverflowArrayMap<T> {
private static final String OVERFLOW_NAME = "*overflow*";
final int mUid;
final ArrayMap<String, T> mMap = new ArrayMap<>();
T mCurOverflow;
ArrayMap<String, MutableInt> mActiveOverflow;
long mLastOverflowTimeMs;
long mLastOverflowFinishTimeMs;
long mLastClearTimeMs;
long mLastCleanupTimeMs;
public OverflowArrayMap(int uid) {
mUid = uid;
}
public ArrayMap<String, T> getMap() {
return mMap;
}
public void clear() {
mLastClearTimeMs = SystemClock.elapsedRealtime();
mMap.clear();
mCurOverflow = null;
mActiveOverflow = null;
}
public void add(String name, T obj) {
if (name == null) {
name = "";
}
mMap.put(name, obj);
if (OVERFLOW_NAME.equals(name)) {
mCurOverflow = obj;
}
}
public void cleanup(long elapsedRealtimeMs) {
mLastCleanupTimeMs = elapsedRealtimeMs;
if (mActiveOverflow != null) {
if (mActiveOverflow.size() == 0) {
mActiveOverflow = null;
}
}
if (mActiveOverflow == null) {
// There is no currently active overflow, so we should no longer have
// an overflow entry.
if (mMap.containsKey(OVERFLOW_NAME)) {
Slog.wtf(TAG, "Cleaning up with no active overflow, but have overflow entry "
+ mMap.get(OVERFLOW_NAME));
mMap.remove(OVERFLOW_NAME);
}
mCurOverflow = null;
} else {
// There is currently active overflow, so we should still have an overflow entry.
if (mCurOverflow == null || !mMap.containsKey(OVERFLOW_NAME)) {
Slog.wtf(TAG, "Cleaning up with active overflow, but no overflow entry: cur="
+ mCurOverflow + " map=" + mMap.get(OVERFLOW_NAME));
}
}
}
public T startObject(String name, long elapsedRealtimeMs) {
if (name == null) {
name = "";
}
T obj = mMap.get(name);
if (obj != null) {
return obj;
}
// No object exists for the given name, but do we currently have it
// running as part of the overflow?
if (mActiveOverflow != null) {
MutableInt over = mActiveOverflow.get(name);
if (over != null) {
// We are already actively counting this name in the overflow object.
obj = mCurOverflow;
if (obj == null) {
// Shouldn't be here, but we'll try to recover.
Slog.wtf(TAG, "Have active overflow " + name + " but null overflow");
obj = mCurOverflow = instantiateObject();
mMap.put(OVERFLOW_NAME, obj);
}
over.value++;
return obj;
}
}
// No object exists for given name nor in the overflow; we need to make
// a new one.
final int N = mMap.size();
if (N >= MAX_WAKELOCKS_PER_UID) {
// Went over the limit on number of objects to track; this one goes
// in to the overflow.
obj = mCurOverflow;
if (obj == null) {
// Need to start overflow now...
obj = mCurOverflow = instantiateObject();
mMap.put(OVERFLOW_NAME, obj);
}
if (mActiveOverflow == null) {
mActiveOverflow = new ArrayMap<>();
}
mActiveOverflow.put(name, new MutableInt(1));
mLastOverflowTimeMs = elapsedRealtimeMs;
return obj;
}
// Normal case where we just need to make a new object.
obj = instantiateObject();
mMap.put(name, obj);
return obj;
}
public T stopObject(String name, long elapsedRealtimeMs) {
if (name == null) {
name = "";
}
T obj = mMap.get(name);
if (obj != null) {
return obj;
}
// No object exists for the given name, but do we currently have it
// running as part of the overflow?
if (mActiveOverflow != null) {
MutableInt over = mActiveOverflow.get(name);
if (over != null) {
// We are already actively counting this name in the overflow object.
obj = mCurOverflow;
if (obj != null) {
over.value--;
if (over.value <= 0) {
mActiveOverflow.remove(name);
mLastOverflowFinishTimeMs = elapsedRealtimeMs;
}
return obj;
}
}
}
// Huh, they are stopping an active operation but we can't find one!
// That's not good.
StringBuilder sb = new StringBuilder();
sb.append("Unable to find object for ");
sb.append(name);
sb.append(" in uid ");
sb.append(mUid);
sb.append(" mapsize=");
sb.append(mMap.size());
sb.append(" activeoverflow=");
sb.append(mActiveOverflow);
sb.append(" curoverflow=");
sb.append(mCurOverflow);
long now = elapsedRealtimeMs;
if (mLastOverflowTimeMs != 0) {
sb.append(" lastOverflowTime=");
TimeUtils.formatDuration(mLastOverflowTimeMs - now, sb);
}
if (mLastOverflowFinishTimeMs != 0) {
sb.append(" lastOverflowFinishTime=");
TimeUtils.formatDuration(mLastOverflowFinishTimeMs - now, sb);
}
if (mLastClearTimeMs != 0) {
sb.append(" lastClearTime=");
TimeUtils.formatDuration(mLastClearTimeMs - now, sb);
}
if (mLastCleanupTimeMs != 0) {
sb.append(" lastCleanupTime=");
TimeUtils.formatDuration(mLastCleanupTimeMs - now, sb);
}
Slog.wtf(TAG, sb.toString());
return null;
}
public abstract T instantiateObject();
}
public static class ControllerActivityCounterImpl extends ControllerActivityCounter
implements Parcelable {
private final Clock mClock;
private final TimeBase mTimeBase;
private int mNumTxStates;
private int mProcessState;
private TimeMultiStateCounter mIdleTimeMillis;
private final LongSamplingCounter mScanTimeMillis;
private final LongSamplingCounter mSleepTimeMillis;
private TimeMultiStateCounter mRxTimeMillis;
private TimeMultiStateCounter[] mTxTimeMillis;
private final LongSamplingCounter mPowerDrainMaMs;
private final LongSamplingCounter mMonitoredRailChargeConsumedMaMs;
public ControllerActivityCounterImpl(Clock clock, TimeBase timeBase, int numTxStates) {
mClock = clock;
mTimeBase = timeBase;
mNumTxStates = numTxStates;
mScanTimeMillis = new LongSamplingCounter(timeBase);
mSleepTimeMillis = new LongSamplingCounter(timeBase);
mPowerDrainMaMs = new LongSamplingCounter(timeBase);
mMonitoredRailChargeConsumedMaMs = new LongSamplingCounter(timeBase);
}
public ControllerActivityCounterImpl(Clock clock, TimeBase timeBase, int numTxStates,
Parcel in) {
mClock = clock;
mTimeBase = timeBase;
mNumTxStates = numTxStates;
mIdleTimeMillis = readTimeMultiStateCounter(in, timeBase);
mScanTimeMillis = new LongSamplingCounter(timeBase, in);
mSleepTimeMillis = new LongSamplingCounter(timeBase, in);
mRxTimeMillis = readTimeMultiStateCounter(in, timeBase);
mTxTimeMillis = readTimeMultiStateCounters(in, timeBase, numTxStates);
mPowerDrainMaMs = new LongSamplingCounter(timeBase, in);
mMonitoredRailChargeConsumedMaMs = new LongSamplingCounter(timeBase, in);
}
public void readSummaryFromParcel(Parcel in) {
mIdleTimeMillis = readTimeMultiStateCounter(in, mTimeBase);
mScanTimeMillis.readSummaryFromParcelLocked(in);
mSleepTimeMillis.readSummaryFromParcelLocked(in);
mRxTimeMillis = readTimeMultiStateCounter(in, mTimeBase);
mTxTimeMillis = readTimeMultiStateCounters(in, mTimeBase, mNumTxStates);
mPowerDrainMaMs.readSummaryFromParcelLocked(in);
mMonitoredRailChargeConsumedMaMs.readSummaryFromParcelLocked(in);
}
@Override
public int describeContents() {
return 0;
}
public void writeSummaryToParcel(Parcel dest) {
writeTimeMultiStateCounter(dest, mIdleTimeMillis);
mScanTimeMillis.writeSummaryFromParcelLocked(dest);
mSleepTimeMillis.writeSummaryFromParcelLocked(dest);
writeTimeMultiStateCounter(dest, mRxTimeMillis);
writeTimeMultiStateCounters(dest, mTxTimeMillis);
mPowerDrainMaMs.writeSummaryFromParcelLocked(dest);
mMonitoredRailChargeConsumedMaMs.writeSummaryFromParcelLocked(dest);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
writeTimeMultiStateCounter(dest, mIdleTimeMillis);
mScanTimeMillis.writeToParcel(dest);
mSleepTimeMillis.writeToParcel(dest);
writeTimeMultiStateCounter(dest, mRxTimeMillis);
writeTimeMultiStateCounters(dest, mTxTimeMillis);
mPowerDrainMaMs.writeToParcel(dest);
mMonitoredRailChargeConsumedMaMs.writeToParcel(dest);
}
private TimeMultiStateCounter readTimeMultiStateCounter(Parcel in, TimeBase timeBase) {
if (in.readBoolean()) {
return TimeMultiStateCounter.readFromParcel(in, timeBase,
BatteryConsumer.PROCESS_STATE_COUNT, mClock.elapsedRealtime());
}
return null;
}
private void writeTimeMultiStateCounter(Parcel dest, TimeMultiStateCounter counter) {
if (counter != null) {
dest.writeBoolean(true);
counter.writeToParcel(dest);
} else {
dest.writeBoolean(false);
}
}
private TimeMultiStateCounter[] readTimeMultiStateCounters(Parcel in, TimeBase timeBase,
int expectedNumCounters) {
if (in.readBoolean()) {
final int numCounters = in.readInt();
boolean valid = (numCounters == expectedNumCounters);
// Need to read counters out of the Parcel, even if all or some of them are
// invalid.
TimeMultiStateCounter[] counters = new TimeMultiStateCounter[numCounters];
for (int i = 0; i < numCounters; i++) {
final TimeMultiStateCounter counter = TimeMultiStateCounter.readFromParcel(in,
timeBase, BatteryConsumer.PROCESS_STATE_COUNT,
mClock.elapsedRealtime());
if (counter != null) {
counters[i] = counter;
} else {
valid = false;
}
}
if (valid) {
return counters;
}
}
return null;
}
private void writeTimeMultiStateCounters(Parcel dest, TimeMultiStateCounter[] counters) {
if (counters != null) {
dest.writeBoolean(true);
dest.writeInt(counters.length);
for (TimeMultiStateCounter counter : counters) {
counter.writeToParcel(dest);
}
} else {
dest.writeBoolean(false);
}
}
public void reset(boolean detachIfReset, long elapsedRealtimeUs) {
resetIfNotNull(mIdleTimeMillis, detachIfReset, elapsedRealtimeUs);
mScanTimeMillis.reset(detachIfReset, elapsedRealtimeUs);
mSleepTimeMillis.reset(detachIfReset, elapsedRealtimeUs);
resetIfNotNull(mRxTimeMillis, detachIfReset, elapsedRealtimeUs);
resetIfNotNull(mTxTimeMillis, detachIfReset, elapsedRealtimeUs);
mPowerDrainMaMs.reset(detachIfReset, elapsedRealtimeUs);
mMonitoredRailChargeConsumedMaMs.reset(detachIfReset, elapsedRealtimeUs);
}
public void detach() {
detachIfNotNull(mIdleTimeMillis);
mScanTimeMillis.detach();
mSleepTimeMillis.detach();
detachIfNotNull(mRxTimeMillis);
detachIfNotNull(mTxTimeMillis);
mPowerDrainMaMs.detach();
mMonitoredRailChargeConsumedMaMs.detach();
}
/**
* @return a LongSamplingCounter, measuring time spent in the idle state in
* milliseconds.
*/
@Override
public LongCounter getIdleTimeCounter() {
if (mIdleTimeMillis == null) {
return ZERO_LONG_COUNTER;
}
return mIdleTimeMillis;
}
private TimeMultiStateCounter getOrCreateIdleTimeCounter() {
if (mIdleTimeMillis == null) {
mIdleTimeMillis = createTimeMultiStateCounter();
}
return mIdleTimeMillis;
}
/**
* @return a LongSamplingCounter, measuring time spent in the scan state in
* milliseconds.
*/
@Override
public LongSamplingCounter getScanTimeCounter() {
return mScanTimeMillis;
}
/**
* @return a LongSamplingCounter, measuring time spent in the sleep state in
* milliseconds.
*/
@Override
public LongSamplingCounter getSleepTimeCounter() {
return mSleepTimeMillis;
}
/**
* @return a LongSamplingCounter, measuring time spent in the receive state in
* milliseconds.
*/
@Override
public LongCounter getRxTimeCounter() {
if (mRxTimeMillis == null) {
return ZERO_LONG_COUNTER;
}
return mRxTimeMillis;
}
private TimeMultiStateCounter getOrCreateRxTimeCounter() {
if (mRxTimeMillis == null) {
mRxTimeMillis = createTimeMultiStateCounter();
}
return mRxTimeMillis;
}
/**
* @return a LongSamplingCounter[], measuring time spent in various transmit states in
* milliseconds.
*/
@Override
public LongCounter[] getTxTimeCounters() {
if (mTxTimeMillis == null) {
return ZERO_LONG_COUNTER_ARRAY;
}
return mTxTimeMillis;
}
private TimeMultiStateCounter[] getOrCreateTxTimeCounters() {
if (mTxTimeMillis == null) {
mTxTimeMillis = new TimeMultiStateCounter[mNumTxStates];
for (int i = 0; i < mNumTxStates; i++) {
mTxTimeMillis[i] = createTimeMultiStateCounter();
}
}
return mTxTimeMillis;
}
private TimeMultiStateCounter createTimeMultiStateCounter() {
final long timestampMs = mClock.elapsedRealtime();
TimeMultiStateCounter counter = new TimeMultiStateCounter(mTimeBase,
BatteryConsumer.PROCESS_STATE_COUNT, timestampMs);
counter.setState(mapUidProcessStateToBatteryConsumerProcessState(mProcessState),
timestampMs);
counter.update(0, timestampMs);
return counter;
}
/**
* @return a LongSamplingCounter, measuring power use in milli-ampere milliseconds (mAmS).
*/
@Override
public LongSamplingCounter getPowerCounter() {
return mPowerDrainMaMs;
}
/**
* @return a LongSamplingCounter, measuring actual monitored rail energy consumed
* milli-ampere milli-seconds (mAmS).
*/
@Override
public LongSamplingCounter getMonitoredRailChargeConsumedMaMs() {
return mMonitoredRailChargeConsumedMaMs;
}
private void setState(int processState, long elapsedTimeMs) {
mProcessState = processState;
if (mIdleTimeMillis != null) {
mIdleTimeMillis.setState(processState, elapsedTimeMs);
}
if (mRxTimeMillis != null) {
mRxTimeMillis.setState(processState, elapsedTimeMs);
}
if (mTxTimeMillis != null) {
for (int i = 0; i < mTxTimeMillis.length; i++) {
mTxTimeMillis[i].setState(processState, elapsedTimeMs);
}
}
}
}
/** Get Resource Power Manager stats. Create a new one if it doesn't already exist. */
public SamplingTimer getRpmTimerLocked(String name) {
SamplingTimer rpmt = mRpmStats.get(name);
if (rpmt == null) {
rpmt = new SamplingTimer(mClock, mOnBatteryTimeBase);
mRpmStats.put(name, rpmt);
}
return rpmt;
}
/** Get Screen-off Resource Power Manager stats. Create new one if it doesn't already exist. */
public SamplingTimer getScreenOffRpmTimerLocked(String name) {
SamplingTimer rpmt = mScreenOffRpmStats.get(name);
if (rpmt == null) {
rpmt = new SamplingTimer(mClock, mOnBatteryScreenOffTimeBase);
mScreenOffRpmStats.put(name, rpmt);
}
return rpmt;
}
/*
* Get the wakeup reason counter, and create a new one if one
* doesn't already exist.
*/
public SamplingTimer getWakeupReasonTimerLocked(String name) {
SamplingTimer timer = mWakeupReasonStats.get(name);
if (timer == null) {
timer = new SamplingTimer(mClock, mOnBatteryTimeBase);
mWakeupReasonStats.put(name, timer);
}
return timer;
}
/*
* Get the KernelWakelockTimer associated with name, and create a new one if one
* doesn't already exist.
*/
public SamplingTimer getKernelWakelockTimerLocked(String name) {
SamplingTimer kwlt = mKernelWakelockStats.get(name);
if (kwlt == null) {
kwlt = new SamplingTimer(mClock, mOnBatteryScreenOffTimeBase);
mKernelWakelockStats.put(name, kwlt);
}
return kwlt;
}
public SamplingTimer getKernelMemoryTimerLocked(long bucket) {
SamplingTimer kmt = mKernelMemoryStats.get(bucket);
if (kmt == null) {
kmt = new SamplingTimer(mClock, mOnBatteryTimeBase);
mKernelMemoryStats.put(bucket, kmt);
}
return kmt;
}
/**
* Returns the index for the specified tag. If this is the first time the tag is encountered
* while writing the current history buffer, the method returns
* <code>(index | TAG_FIRST_OCCURRENCE_FLAG)</code>
*/
private int writeHistoryTag(HistoryTag tag) {
if (tag.string == null) {
Slog.wtfStack(TAG, "writeHistoryTag called with null name");
}
final int stringLength = tag.string.length();
if (stringLength > MAX_HISTORY_TAG_STRING_LENGTH) {
Slog.e(TAG, "Long battery history tag: " + tag.string);
tag.string = tag.string.substring(0, MAX_HISTORY_TAG_STRING_LENGTH);
}
Integer idxObj = mHistoryTagPool.get(tag);
int idx;
if (idxObj != null) {
idx = idxObj;
if ((idx & TAG_FIRST_OCCURRENCE_FLAG) != 0) {
mHistoryTagPool.put(tag, idx & ~TAG_FIRST_OCCURRENCE_FLAG);
}
return idx;
} else if (mNextHistoryTagIdx < HISTORY_TAG_INDEX_LIMIT) {
idx = mNextHistoryTagIdx;
HistoryTag key = new HistoryTag();
key.setTo(tag);
tag.poolIdx = idx;
mHistoryTagPool.put(key, idx);
mNextHistoryTagIdx++;
mNumHistoryTagChars += stringLength + 1;
if (mHistoryTags != null) {
mHistoryTags.put(idx, key);
}
return idx | TAG_FIRST_OCCURRENCE_FLAG;
} else {
// Tag pool overflow: include the tag itself in the parcel
return HISTORY_TAG_INDEX_LIMIT | TAG_FIRST_OCCURRENCE_FLAG;
}
}
/*
The history delta format uses flags to denote further data in subsequent ints in the parcel.
There is always the first token, which may contain the delta time, or an indicator of
the length of the time (int or long) following this token.
First token: always present,
31 23 15 7 0
â–ˆM|L|K|J|I|H|G|Fâ–ˆE|D|C|B|A|T|T|Tâ–ˆT|T|T|T|T|T|T|Tâ–ˆT|T|T|T|T|T|T|Tâ–ˆ
T: the delta time if it is <= 0x7fffd. Otherwise 0x7fffe indicates an int immediately
follows containing the time, and 0x7ffff indicates a long immediately follows with the
delta time.
A: battery level changed and an int follows with battery data.
B: state changed and an int follows with state change data.
C: state2 has changed and an int follows with state2 change data.
D: wakelock/wakereason has changed and an wakelock/wakereason struct follows.
E: event data has changed and an event struct follows.
F: battery charge in coulombs has changed and an int with the charge follows.
G: state flag denoting that the mobile radio was active.
H: state flag denoting that the wifi radio was active.
I: state flag denoting that a wifi scan occurred.
J: state flag denoting that a wifi full lock was held.
K: state flag denoting that the gps was on.
L: state flag denoting that a wakelock was held.
M: state flag denoting that the cpu was running.
Time int/long: if T in the first token is 0x7ffff or 0x7fffe, then an int or long follows
with the time delta.
Battery level int: if A in the first token is set,
31 23 15 7 0
â–ˆL|L|L|L|L|L|L|Tâ–ˆT|T|T|T|T|T|T|Tâ–ˆT|V|V|V|V|V|V|Vâ–ˆV|V|V|V|V|V|V|Dâ–ˆ
D: indicates that extra history details follow.
V: the battery voltage.
T: the battery temperature.
L: the battery level (out of 100).
State change int: if B in the first token is set,
31 23 15 7 0
â–ˆS|S|S|H|H|H|P|Pâ–ˆF|E|D|C|B| | |Aâ–ˆ | | | | | | | â–ˆ | | | | | | | â–ˆ
A: wifi multicast was on.
B: battery was plugged in.
C: screen was on.
D: phone was scanning for signal.
E: audio was on.
F: a sensor was active.
State2 change int: if C in the first token is set,
31 23 15 7 0
â–ˆM|L|K|J|I|H|H|Gâ–ˆF|E|D|C| | | | â–ˆ | | | | | | | â–ˆ |B|B|B|A|A|A|Aâ–ˆ
A: 4 bits indicating the wifi supplicant state: {@link BatteryStats#WIFI_SUPPL_STATE_NAMES}.
B: 3 bits indicating the wifi signal strength: 0, 1, 2, 3, 4.
C: a bluetooth scan was active.
D: the camera was active.
E: bluetooth was on.
F: a phone call was active.
G: the device was charging.
H: 2 bits indicating the device-idle (doze) state: off, light, full
I: the flashlight was on.
J: wifi was on.
K: wifi was running.
L: video was playing.
M: power save mode was on.
Wakelock/wakereason struct: if D in the first token is set,
TODO(adamlesinski): describe wakelock/wakereason struct.
Event struct: if E in the first token is set,
TODO(adamlesinski): describe the event struct.
History step details struct: if D in the battery level int is set,
TODO(adamlesinski): describe the history step details struct.
Battery charge int: if F in the first token is set, an int representing the battery charge
in coulombs follows.
*/
// Part of initial delta int that specifies the time delta.
static final int DELTA_TIME_MASK = 0x7ffff;
static final int DELTA_TIME_LONG = 0x7ffff; // The delta is a following long
static final int DELTA_TIME_INT = 0x7fffe; // The delta is a following int
static final int DELTA_TIME_ABS = 0x7fffd; // Following is an entire abs update.
// Flag in delta int: a new battery level int follows.
static final int DELTA_BATTERY_LEVEL_FLAG = 0x00080000;
// Flag in delta int: a new full state and battery status int follows.
static final int DELTA_STATE_FLAG = 0x00100000;
// Flag in delta int: a new full state2 int follows.
static final int DELTA_STATE2_FLAG = 0x00200000;
// Flag in delta int: contains a wakelock or wakeReason tag.
static final int DELTA_WAKELOCK_FLAG = 0x00400000;
// Flag in delta int: contains an event description.
static final int DELTA_EVENT_FLAG = 0x00800000;
// Flag in delta int: contains the battery charge count in uAh.
static final int DELTA_BATTERY_CHARGE_FLAG = 0x01000000;
// These upper bits are the frequently changing state bits.
static final int DELTA_STATE_MASK = 0xfe000000;
// Flag in history tag index: indicates that this is the first occurrence of this tag,
// therefore the tag value is written in the parcel
static final int TAG_FIRST_OCCURRENCE_FLAG = 0x8000;
// These are the pieces of battery state that are packed in to the upper bits of
// the state int that have been packed in to the first delta int. They must fit
// in STATE_BATTERY_MASK.
static final int STATE_BATTERY_MASK = 0xff000000;
static final int STATE_BATTERY_STATUS_MASK = 0x00000007;
static final int STATE_BATTERY_STATUS_SHIFT = 29;
static final int STATE_BATTERY_HEALTH_MASK = 0x00000007;
static final int STATE_BATTERY_HEALTH_SHIFT = 26;
static final int STATE_BATTERY_PLUG_MASK = 0x00000003;
static final int STATE_BATTERY_PLUG_SHIFT = 24;
// We use the low bit of the battery state int to indicate that we have full details
// from a battery level change.
static final int BATTERY_DELTA_LEVEL_FLAG = 0x00000001;
@GuardedBy("this")
public void writeHistoryDelta(Parcel dest, HistoryItem cur, HistoryItem last) {
if (last == null || cur.cmd != HistoryItem.CMD_UPDATE) {
dest.writeInt(DELTA_TIME_ABS);
cur.writeToParcel(dest, 0);
return;
}
final long deltaTime = cur.time - last.time;
final int lastBatteryLevelInt = buildBatteryLevelInt(last);
final int lastStateInt = buildStateInt(last);
int deltaTimeToken;
if (deltaTime < 0 || deltaTime > Integer.MAX_VALUE) {
deltaTimeToken = DELTA_TIME_LONG;
} else if (deltaTime >= DELTA_TIME_ABS) {
deltaTimeToken = DELTA_TIME_INT;
} else {
deltaTimeToken = (int)deltaTime;
}
int firstToken = deltaTimeToken | (cur.states&DELTA_STATE_MASK);
final int includeStepDetails = mLastHistoryStepLevel > cur.batteryLevel
? BATTERY_DELTA_LEVEL_FLAG : 0;
final boolean computeStepDetails = includeStepDetails != 0
|| mLastHistoryStepDetails == null;
final int batteryLevelInt = buildBatteryLevelInt(cur) | includeStepDetails;
final boolean batteryLevelIntChanged = batteryLevelInt != lastBatteryLevelInt;
if (batteryLevelIntChanged) {
firstToken |= DELTA_BATTERY_LEVEL_FLAG;
}
final int stateInt = buildStateInt(cur);
final boolean stateIntChanged = stateInt != lastStateInt;
if (stateIntChanged) {
firstToken |= DELTA_STATE_FLAG;
}
final boolean state2IntChanged = cur.states2 != last.states2;
if (state2IntChanged) {
firstToken |= DELTA_STATE2_FLAG;
}
if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
firstToken |= DELTA_WAKELOCK_FLAG;
}
if (cur.eventCode != HistoryItem.EVENT_NONE) {
firstToken |= DELTA_EVENT_FLAG;
}
final boolean batteryChargeChanged = cur.batteryChargeUah != last.batteryChargeUah;
if (batteryChargeChanged) {
firstToken |= DELTA_BATTERY_CHARGE_FLAG;
}
dest.writeInt(firstToken);
if (DEBUG) Slog.i(TAG, "WRITE DELTA: firstToken=0x" + Integer.toHexString(firstToken)
+ " deltaTime=" + deltaTime);
if (deltaTimeToken >= DELTA_TIME_INT) {
if (deltaTimeToken == DELTA_TIME_INT) {
if (DEBUG) Slog.i(TAG, "WRITE DELTA: int deltaTime=" + (int)deltaTime);
dest.writeInt((int)deltaTime);
} else {
if (DEBUG) Slog.i(TAG, "WRITE DELTA: long deltaTime=" + deltaTime);
dest.writeLong(deltaTime);
}
}
if (batteryLevelIntChanged) {
dest.writeInt(batteryLevelInt);
if (DEBUG) Slog.i(TAG, "WRITE DELTA: batteryToken=0x"
+ Integer.toHexString(batteryLevelInt)
+ " batteryLevel=" + cur.batteryLevel
+ " batteryTemp=" + cur.batteryTemperature
+ " batteryVolt=" + (int)cur.batteryVoltage);
}
if (stateIntChanged) {
dest.writeInt(stateInt);
if (DEBUG) Slog.i(TAG, "WRITE DELTA: stateToken=0x"
+ Integer.toHexString(stateInt)
+ " batteryStatus=" + cur.batteryStatus
+ " batteryHealth=" + cur.batteryHealth
+ " batteryPlugType=" + cur.batteryPlugType
+ " states=0x" + Integer.toHexString(cur.states));
}
if (state2IntChanged) {
dest.writeInt(cur.states2);
if (DEBUG) Slog.i(TAG, "WRITE DELTA: states2=0x"
+ Integer.toHexString(cur.states2));
}
if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
int wakeLockIndex;
int wakeReasonIndex;
if (cur.wakelockTag != null) {
wakeLockIndex = writeHistoryTag(cur.wakelockTag);
if (DEBUG) Slog.i(TAG, "WRITE DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx
+ " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string);
} else {
wakeLockIndex = 0xffff;
}
if (cur.wakeReasonTag != null) {
wakeReasonIndex = writeHistoryTag(cur.wakeReasonTag);
if (DEBUG) Slog.i(TAG, "WRITE DELTA: wakeReasonTag=#" + cur.wakeReasonTag.poolIdx
+ " " + cur.wakeReasonTag.uid + ":" + cur.wakeReasonTag.string);
} else {
wakeReasonIndex = 0xffff;
}
dest.writeInt((wakeReasonIndex<<16) | wakeLockIndex);
if (cur.wakelockTag != null && (wakeLockIndex & TAG_FIRST_OCCURRENCE_FLAG) != 0) {
cur.wakelockTag.writeToParcel(dest, 0);
cur.tagsFirstOccurrence = true;
}
if (cur.wakeReasonTag != null && (wakeReasonIndex & TAG_FIRST_OCCURRENCE_FLAG) != 0) {
cur.wakeReasonTag.writeToParcel(dest, 0);
cur.tagsFirstOccurrence = true;
}
}
if (cur.eventCode != HistoryItem.EVENT_NONE) {
final int index = writeHistoryTag(cur.eventTag);
final int codeAndIndex = (cur.eventCode & 0xffff) | (index << 16);
dest.writeInt(codeAndIndex);
if ((index & TAG_FIRST_OCCURRENCE_FLAG) != 0) {
cur.eventTag.writeToParcel(dest, 0);
cur.tagsFirstOccurrence = true;
}
if (DEBUG) Slog.i(TAG, "WRITE DELTA: event=" + cur.eventCode + " tag=#"
+ cur.eventTag.poolIdx + " " + cur.eventTag.uid + ":"
+ cur.eventTag.string);
}
if (computeStepDetails) {
if (mPlatformIdleStateCallback != null) {
mCurHistoryStepDetails.statSubsystemPowerState =
mPlatformIdleStateCallback.getSubsystemLowPowerStats();
if (DEBUG) Slog.i(TAG, "WRITE SubsystemPowerState:" +
mCurHistoryStepDetails.statSubsystemPowerState);
}
computeHistoryStepDetails(mCurHistoryStepDetails, mLastHistoryStepDetails);
if (includeStepDetails != 0) {
mCurHistoryStepDetails.writeToParcel(dest);
}
cur.stepDetails = mCurHistoryStepDetails;
mLastHistoryStepDetails = mCurHistoryStepDetails;
} else {
cur.stepDetails = null;
}
if (mLastHistoryStepLevel < cur.batteryLevel) {
mLastHistoryStepDetails = null;
}
mLastHistoryStepLevel = cur.batteryLevel;
if (batteryChargeChanged) {
if (DEBUG) Slog.i(TAG, "WRITE DELTA: batteryChargeUah=" + cur.batteryChargeUah);
dest.writeInt(cur.batteryChargeUah);
}
dest.writeDouble(cur.modemRailChargeMah);
dest.writeDouble(cur.wifiRailChargeMah);
}
private int buildBatteryLevelInt(HistoryItem h) {
return ((((int)h.batteryLevel)<<25)&0xfe000000)
| ((((int)h.batteryTemperature)<<15)&0x01ff8000)
| ((((int)h.batteryVoltage)<<1)&0x00007ffe);
}
private void readBatteryLevelInt(int batteryLevelInt, HistoryItem out) {
out.batteryLevel = (byte)((batteryLevelInt & 0xfe000000) >>> 25);
out.batteryTemperature = (short)((batteryLevelInt & 0x01ff8000) >>> 15);
out.batteryVoltage = (char)((batteryLevelInt & 0x00007ffe) >>> 1);
}
private int buildStateInt(HistoryItem h) {
int plugType = 0;
if ((h.batteryPlugType&BatteryManager.BATTERY_PLUGGED_AC) != 0) {
plugType = 1;
} else if ((h.batteryPlugType&BatteryManager.BATTERY_PLUGGED_USB) != 0) {
plugType = 2;
} else if ((h.batteryPlugType&BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0) {
plugType = 3;
}
return ((h.batteryStatus&STATE_BATTERY_STATUS_MASK)<<STATE_BATTERY_STATUS_SHIFT)
| ((h.batteryHealth&STATE_BATTERY_HEALTH_MASK)<<STATE_BATTERY_HEALTH_SHIFT)
| ((plugType&STATE_BATTERY_PLUG_MASK)<<STATE_BATTERY_PLUG_SHIFT)
| (h.states&(~STATE_BATTERY_MASK));
}
private void computeHistoryStepDetails(final HistoryStepDetails out,
final HistoryStepDetails last) {
final HistoryStepDetails tmp = last != null ? mTmpHistoryStepDetails : out;
// Perform a CPU update right after we do this collection, so we have started
// collecting good data for the next step.
requestImmediateCpuUpdate();
if (last == null) {
// We are not generating a delta, so all we need to do is reset the stats
// we will later be doing a delta from.
final int NU = mUidStats.size();
for (int i=0; i<NU; i++) {
final BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
uid.mLastStepUserTimeMs = uid.mCurStepUserTimeMs;
uid.mLastStepSystemTimeMs = uid.mCurStepSystemTimeMs;
}
mLastStepCpuUserTimeMs = mCurStepCpuUserTimeMs;
mLastStepCpuSystemTimeMs = mCurStepCpuSystemTimeMs;
mLastStepStatUserTimeMs = mCurStepStatUserTimeMs;
mLastStepStatSystemTimeMs = mCurStepStatSystemTimeMs;
mLastStepStatIOWaitTimeMs = mCurStepStatIOWaitTimeMs;
mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs;
mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs;
mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs;
tmp.clear();
return;
}
if (DEBUG) {
Slog.d(TAG, "Step stats last: user=" + mLastStepCpuUserTimeMs + " sys="
+ mLastStepStatSystemTimeMs + " io=" + mLastStepStatIOWaitTimeMs
+ " irq=" + mLastStepStatIrqTimeMs + " sirq="
+ mLastStepStatSoftIrqTimeMs + " idle=" + mLastStepStatIdleTimeMs);
Slog.d(TAG, "Step stats cur: user=" + mCurStepCpuUserTimeMs + " sys="
+ mCurStepStatSystemTimeMs + " io=" + mCurStepStatIOWaitTimeMs
+ " irq=" + mCurStepStatIrqTimeMs + " sirq="
+ mCurStepStatSoftIrqTimeMs + " idle=" + mCurStepStatIdleTimeMs);
}
out.userTime = (int) (mCurStepCpuUserTimeMs - mLastStepCpuUserTimeMs);
out.systemTime = (int) (mCurStepCpuSystemTimeMs - mLastStepCpuSystemTimeMs);
out.statUserTime = (int) (mCurStepStatUserTimeMs - mLastStepStatUserTimeMs);
out.statSystemTime = (int) (mCurStepStatSystemTimeMs - mLastStepStatSystemTimeMs);
out.statIOWaitTime = (int) (mCurStepStatIOWaitTimeMs - mLastStepStatIOWaitTimeMs);
out.statIrqTime = (int) (mCurStepStatIrqTimeMs - mLastStepStatIrqTimeMs);
out.statSoftIrqTime = (int) (mCurStepStatSoftIrqTimeMs - mLastStepStatSoftIrqTimeMs);
out.statIdlTime = (int) (mCurStepStatIdleTimeMs - mLastStepStatIdleTimeMs);
out.appCpuUid1 = out.appCpuUid2 = out.appCpuUid3 = -1;
out.appCpuUTime1 = out.appCpuUTime2 = out.appCpuUTime3 = 0;
out.appCpuSTime1 = out.appCpuSTime2 = out.appCpuSTime3 = 0;
final int NU = mUidStats.size();
for (int i=0; i<NU; i++) {
final BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
final int totalUTimeMs = (int) (uid.mCurStepUserTimeMs - uid.mLastStepUserTimeMs);
final int totalSTimeMs = (int) (uid.mCurStepSystemTimeMs - uid.mLastStepSystemTimeMs);
final int totalTimeMs = totalUTimeMs + totalSTimeMs;
uid.mLastStepUserTimeMs = uid.mCurStepUserTimeMs;
uid.mLastStepSystemTimeMs = uid.mCurStepSystemTimeMs;
if (totalTimeMs <= (out.appCpuUTime3 + out.appCpuSTime3)) {
continue;
}
if (totalTimeMs <= (out.appCpuUTime2 + out.appCpuSTime2)) {
out.appCpuUid3 = uid.mUid;
out.appCpuUTime3 = totalUTimeMs;
out.appCpuSTime3 = totalSTimeMs;
} else {
out.appCpuUid3 = out.appCpuUid2;
out.appCpuUTime3 = out.appCpuUTime2;
out.appCpuSTime3 = out.appCpuSTime2;
if (totalTimeMs <= (out.appCpuUTime1 + out.appCpuSTime1)) {
out.appCpuUid2 = uid.mUid;
out.appCpuUTime2 = totalUTimeMs;
out.appCpuSTime2 = totalSTimeMs;
} else {
out.appCpuUid2 = out.appCpuUid1;
out.appCpuUTime2 = out.appCpuUTime1;
out.appCpuSTime2 = out.appCpuSTime1;
out.appCpuUid1 = uid.mUid;
out.appCpuUTime1 = totalUTimeMs;
out.appCpuSTime1 = totalSTimeMs;
}
}
}
mLastStepCpuUserTimeMs = mCurStepCpuUserTimeMs;
mLastStepCpuSystemTimeMs = mCurStepCpuSystemTimeMs;
mLastStepStatUserTimeMs = mCurStepStatUserTimeMs;
mLastStepStatSystemTimeMs = mCurStepStatSystemTimeMs;
mLastStepStatIOWaitTimeMs = mCurStepStatIOWaitTimeMs;
mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs;
mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs;
mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs;
}
@GuardedBy("this")
@Override
public void commitCurrentHistoryBatchLocked() {
mHistoryLastWritten.cmd = HistoryItem.CMD_NULL;
}
@GuardedBy("this")
public void createFakeHistoryEvents(long numEvents) {
final long elapsedRealtimeMs = mClock.elapsedRealtime();
final long uptimeMs = mClock.uptimeMillis();
for(long i = 0; i < numEvents; i++) {
noteLongPartialWakelockStart("name1", "historyName1", 1000,
elapsedRealtimeMs, uptimeMs);
noteLongPartialWakelockFinish("name1", "historyName1", 1000,
elapsedRealtimeMs, uptimeMs);
}
}
@GuardedBy("this")
void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
if (!mHaveBatteryLevel || !mRecordingHistory) {
return;
}
final long timeDiffMs = (mHistoryBaseTimeMs + elapsedRealtimeMs) - mHistoryLastWritten.time;
final int diffStates = mHistoryLastWritten.states^(cur.states&mActiveHistoryStates);
final int diffStates2 = mHistoryLastWritten.states2^(cur.states2&mActiveHistoryStates2);
final int lastDiffStates = mHistoryLastWritten.states^mHistoryLastLastWritten.states;
final int lastDiffStates2 = mHistoryLastWritten.states2^mHistoryLastLastWritten.states2;
if (DEBUG) {
Slog.i(TAG, "ADD: tdelta=" + timeDiffMs + " diff="
+ Integer.toHexString(diffStates) + " lastDiff="
+ Integer.toHexString(lastDiffStates) + " diff2="
+ Integer.toHexString(diffStates2) + " lastDiff2="
+ Integer.toHexString(lastDiffStates2));
}
if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE
&& timeDiffMs < 1000 && (diffStates & lastDiffStates) == 0
&& (diffStates2&lastDiffStates2) == 0
&& (!mHistoryLastWritten.tagsFirstOccurrence && !cur.tagsFirstOccurrence)
&& (mHistoryLastWritten.wakelockTag == null || cur.wakelockTag == null)
&& (mHistoryLastWritten.wakeReasonTag == null || cur.wakeReasonTag == null)
&& mHistoryLastWritten.stepDetails == null
&& (mHistoryLastWritten.eventCode == HistoryItem.EVENT_NONE
|| cur.eventCode == HistoryItem.EVENT_NONE)
&& mHistoryLastWritten.batteryLevel == cur.batteryLevel
&& mHistoryLastWritten.batteryStatus == cur.batteryStatus
&& mHistoryLastWritten.batteryHealth == cur.batteryHealth
&& mHistoryLastWritten.batteryPlugType == cur.batteryPlugType
&& mHistoryLastWritten.batteryTemperature == cur.batteryTemperature
&& mHistoryLastWritten.batteryVoltage == cur.batteryVoltage) {
// We can merge this new change in with the last one. Merging is
// allowed as long as only the states have changed, and within those states
// as long as no bit has changed both between now and the last entry, as
// well as the last entry and the one before it (so we capture any toggles).
if (DEBUG) Slog.i(TAG, "ADD: rewinding back to " + mHistoryBufferLastPos);
mHistoryBuffer.setDataSize(mHistoryBufferLastPos);
mHistoryBuffer.setDataPosition(mHistoryBufferLastPos);
mHistoryBufferLastPos = -1;
elapsedRealtimeMs = mHistoryLastWritten.time - mHistoryBaseTimeMs;
// If the last written history had a wakelock tag, we need to retain it.
// Note that the condition above made sure that we aren't in a case where
// both it and the current history item have a wakelock tag.
if (mHistoryLastWritten.wakelockTag != null) {
cur.wakelockTag = cur.localWakelockTag;
cur.wakelockTag.setTo(mHistoryLastWritten.wakelockTag);
}
// If the last written history had a wake reason tag, we need to retain it.
// Note that the condition above made sure that we aren't in a case where
// both it and the current history item have a wakelock tag.
if (mHistoryLastWritten.wakeReasonTag != null) {
cur.wakeReasonTag = cur.localWakeReasonTag;
cur.wakeReasonTag.setTo(mHistoryLastWritten.wakeReasonTag);
}
// If the last written history had an event, we need to retain it.
// Note that the condition above made sure that we aren't in a case where
// both it and the current history item have an event.
if (mHistoryLastWritten.eventCode != HistoryItem.EVENT_NONE) {
cur.eventCode = mHistoryLastWritten.eventCode;
cur.eventTag = cur.localEventTag;
cur.eventTag.setTo(mHistoryLastWritten.eventTag);
}
mHistoryLastWritten.setTo(mHistoryLastLastWritten);
}
final int dataSize = mHistoryBuffer.dataSize();
if (dataSize >= mConstants.MAX_HISTORY_BUFFER) {
//open a new history file.
final long start = SystemClock.uptimeMillis();
writeHistoryLocked(true);
if (DEBUG) {
Slog.d(TAG, "addHistoryBufferLocked writeHistoryLocked takes ms:"
+ (SystemClock.uptimeMillis() - start));
}
mBatteryStatsHistory.startNextFile();
mHistoryBuffer.setDataSize(0);
mHistoryBuffer.setDataPosition(0);
mHistoryBuffer.setDataCapacity(mConstants.MAX_HISTORY_BUFFER / 2);
mHistoryBufferLastPos = -1;
mHistoryLastWritten.clear();
mHistoryLastLastWritten.clear();
// Mark every entry in the pool with a flag indicating that the tag
// has not yet been encountered while writing the current history buffer.
for (Map.Entry<HistoryTag, Integer> entry: mHistoryTagPool.entrySet()) {
entry.setValue(entry.getValue() | TAG_FIRST_OCCURRENCE_FLAG);
}
// Make a copy of mHistoryCur.
HistoryItem copy = new HistoryItem();
copy.setTo(cur);
// startRecordingHistory will reset mHistoryCur.
startRecordingHistory(elapsedRealtimeMs, uptimeMs, false);
// Add the copy into history buffer.
addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, copy);
return;
}
if (dataSize == 0) {
// The history is currently empty; we need it to start with a time stamp.
cur.currentTime = mClock.currentTimeMillis();
addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_RESET, cur);
}
addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
}
@GuardedBy("this")
private void addHistoryBufferLocked(long elapsedRealtimeMs, byte cmd, HistoryItem cur) {
if (mBatteryStatsHistoryIterator != null) {
throw new IllegalStateException("Can't do this while iterating history!");
}
mHistoryBufferLastPos = mHistoryBuffer.dataPosition();
mHistoryLastLastWritten.setTo(mHistoryLastWritten);
final boolean hasTags = mHistoryLastWritten.tagsFirstOccurrence || cur.tagsFirstOccurrence;
mHistoryLastWritten.setTo(mHistoryBaseTimeMs + elapsedRealtimeMs, cmd, cur);
mHistoryLastWritten.tagsFirstOccurrence = hasTags;
mHistoryLastWritten.states &= mActiveHistoryStates;
mHistoryLastWritten.states2 &= mActiveHistoryStates2;
writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten);
mLastHistoryElapsedRealtimeMs = elapsedRealtimeMs;
cur.wakelockTag = null;
cur.wakeReasonTag = null;
cur.eventCode = HistoryItem.EVENT_NONE;
cur.eventTag = null;
cur.tagsFirstOccurrence = false;
if (DEBUG_HISTORY) Slog.i(TAG, "Writing history buffer: was " + mHistoryBufferLastPos
+ " now " + mHistoryBuffer.dataPosition()
+ " size is now " + mHistoryBuffer.dataSize());
}
int mChangedStates = 0;
int mChangedStates2 = 0;
@GuardedBy("this")
void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mTrackRunningHistoryElapsedRealtimeMs != 0) {
final long diffElapsedMs = elapsedRealtimeMs - mTrackRunningHistoryElapsedRealtimeMs;
final long diffUptimeMs = uptimeMs - mTrackRunningHistoryUptimeMs;
if (diffUptimeMs < (diffElapsedMs - 20)) {
final long wakeElapsedTimeMs = elapsedRealtimeMs - (diffElapsedMs - diffUptimeMs);
mHistoryAddTmp.setTo(mHistoryLastWritten);
mHistoryAddTmp.wakelockTag = null;
mHistoryAddTmp.wakeReasonTag = null;
mHistoryAddTmp.eventCode = HistoryItem.EVENT_NONE;
mHistoryAddTmp.states &= ~HistoryItem.STATE_CPU_RUNNING_FLAG;
addHistoryRecordInnerLocked(wakeElapsedTimeMs, uptimeMs, mHistoryAddTmp);
}
}
mHistoryCur.states |= HistoryItem.STATE_CPU_RUNNING_FLAG;
mTrackRunningHistoryElapsedRealtimeMs = elapsedRealtimeMs;
mTrackRunningHistoryUptimeMs = uptimeMs;
addHistoryRecordInnerLocked(elapsedRealtimeMs, uptimeMs, mHistoryCur);
}
@GuardedBy("this")
void addHistoryRecordInnerLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, cur);
}
@GuardedBy("this")
public void addHistoryEventLocked(long elapsedRealtimeMs, long uptimeMs, int code,
String name, int uid) {
mHistoryCur.eventCode = code;
mHistoryCur.eventTag = mHistoryCur.localEventTag;
mHistoryCur.eventTag.string = name;
mHistoryCur.eventTag.uid = uid;
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
@GuardedBy("this")
void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd, HistoryItem cur) {
HistoryItem rec = mHistoryCache;
if (rec != null) {
mHistoryCache = rec.next;
} else {
rec = new HistoryItem();
}
rec.setTo(mHistoryBaseTimeMs + elapsedRealtimeMs, cmd, cur);
addHistoryRecordLocked(rec);
}
@GuardedBy("this")
void addHistoryRecordLocked(HistoryItem rec) {
mNumHistoryItems++;
rec.next = null;
mHistoryLastEnd = mHistoryEnd;
if (mHistoryEnd != null) {
mHistoryEnd.next = rec;
mHistoryEnd = rec;
} else {
mHistory = mHistoryEnd = rec;
}
}
@GuardedBy("this")
void clearHistoryLocked() {
if (DEBUG_HISTORY) Slog.i(TAG, "********** CLEARING HISTORY!");
mHistoryBaseTimeMs = 0;
mLastHistoryElapsedRealtimeMs = 0;
mTrackRunningHistoryElapsedRealtimeMs = 0;
mTrackRunningHistoryUptimeMs = 0;
mHistoryBuffer.setDataSize(0);
mHistoryBuffer.setDataPosition(0);
mHistoryBuffer.setDataCapacity(mConstants.MAX_HISTORY_BUFFER / 2);
mHistoryLastLastWritten.clear();
mHistoryLastWritten.clear();
mHistoryTagPool.clear();
mNextHistoryTagIdx = 0;
mNumHistoryTagChars = 0;
mHistoryBufferLastPos = -1;
mActiveHistoryStates = 0xffffffff;
mActiveHistoryStates2 = 0xffffffff;
}
@GuardedBy("this")
public void updateTimeBasesLocked(boolean unplugged, int screenState, long uptimeUs,
long realtimeUs) {
final boolean screenOff = !Display.isOnState(screenState);
final boolean updateOnBatteryTimeBase = unplugged != mOnBatteryTimeBase.isRunning();
final boolean updateOnBatteryScreenOffTimeBase =
(unplugged && screenOff) != mOnBatteryScreenOffTimeBase.isRunning();
if (updateOnBatteryScreenOffTimeBase || updateOnBatteryTimeBase) {
if (updateOnBatteryScreenOffTimeBase) {
updateKernelWakelocksLocked(realtimeUs);
updateBatteryPropertiesLocked();
}
// This if{} is only necessary due to SCREEN_OFF_RPM_STATS_ENABLED, which exists because
// updateRpmStatsLocked is too slow to run each screen change. When the speed is
// improved, remove the surrounding if{}.
if (SCREEN_OFF_RPM_STATS_ENABLED || updateOnBatteryTimeBase) {
// if either OnBattery or OnBatteryScreenOfftimebase changes.
updateRpmStatsLocked(realtimeUs);
}
if (DEBUG_ENERGY_CPU) {
Slog.d(TAG, "Updating cpu time because screen is now "
+ Display.stateToString(screenState)
+ " and battery is " + (unplugged ? "on" : "off"));
}
mOnBatteryTimeBase.setRunning(unplugged, uptimeUs, realtimeUs);
if (updateOnBatteryTimeBase) {
for (int i = mUidStats.size() - 1; i >= 0; --i) {
mUidStats.valueAt(i).updateOnBatteryBgTimeBase(uptimeUs, realtimeUs);
}
}
if (updateOnBatteryScreenOffTimeBase) {
mOnBatteryScreenOffTimeBase.setRunning(unplugged && screenOff,
uptimeUs, realtimeUs);
for (int i = mUidStats.size() - 1; i >= 0; --i) {
mUidStats.valueAt(i).updateOnBatteryScreenOffBgTimeBase(uptimeUs, realtimeUs);
}
}
}
}
@GuardedBy("this")
private void updateBatteryPropertiesLocked() {
try {
IBatteryPropertiesRegistrar registrar = IBatteryPropertiesRegistrar.Stub.asInterface(
ServiceManager.getService("batteryproperties"));
if (registrar != null) {
registrar.scheduleUpdate();
}
} catch (RemoteException e) {
// Ignore.
}
}
@GuardedBy("this")
public void addIsolatedUidLocked(int isolatedUid, int appUid) {
addIsolatedUidLocked(isolatedUid, appUid,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
@SuppressWarnings("GuardedBy") // errorprone false positive on u.addIsolatedUid
public void addIsolatedUidLocked(int isolatedUid, int appUid,
long elapsedRealtimeMs, long uptimeMs) {
mIsolatedUids.put(isolatedUid, appUid);
mIsolatedUidRefCounts.put(isolatedUid, 1);
final Uid u = getUidStatsLocked(appUid, elapsedRealtimeMs, uptimeMs);
u.addIsolatedUid(isolatedUid);
}
/**
* Schedules a read of the latest cpu times before removing the isolated UID.
* @see #removeIsolatedUidLocked(int, int, int)
*/
public void scheduleRemoveIsolatedUidLocked(int isolatedUid, int appUid) {
int curUid = mIsolatedUids.get(isolatedUid, -1);
if (curUid == appUid) {
if (mExternalSync != null) {
mExternalSync.scheduleCpuSyncDueToRemovedUid(isolatedUid);
}
}
}
/**
* Isolated uid should only be removed after all wakelocks associated with the uid are stopped
* and the cpu time-in-state has been read one last time for the uid.
*
* @see #scheduleRemoveIsolatedUidLocked(int, int)
*
* @return true if the isolated uid is actually removed.
*/
@GuardedBy("this")
public boolean maybeRemoveIsolatedUidLocked(int isolatedUid, long elapsedRealtimeMs,
long uptimeMs) {
final int refCount = mIsolatedUidRefCounts.get(isolatedUid, 0) - 1;
if (refCount > 0) {
// Isolated uid is still being tracked
mIsolatedUidRefCounts.put(isolatedUid, refCount);
return false;
}
final int idx = mIsolatedUids.indexOfKey(isolatedUid);
if (idx >= 0) {
final int ownerUid = mIsolatedUids.valueAt(idx);
final Uid u = getUidStatsLocked(ownerUid, elapsedRealtimeMs, uptimeMs);
u.removeIsolatedUid(isolatedUid);
mIsolatedUids.removeAt(idx);
mIsolatedUidRefCounts.delete(isolatedUid);
} else {
Slog.w(TAG, "Attempted to remove untracked isolated uid (" + isolatedUid + ")");
}
mPendingRemovedUids.add(new UidToRemove(isolatedUid, elapsedRealtimeMs));
return true;
}
/**
* Increment the ref count for an isolated uid.
* call #maybeRemoveIsolatedUidLocked to decrement.
*/
public void incrementIsolatedUidRefCount(int uid) {
final int refCount = mIsolatedUidRefCounts.get(uid, 0);
if (refCount <= 0) {
// Uid is not mapped or referenced
Slog.w(TAG,
"Attempted to increment ref counted of untracked isolated uid (" + uid + ")");
return;
}
mIsolatedUidRefCounts.put(uid, refCount + 1);
}
private int mapUid(int uid) {
if (Process.isSdkSandboxUid(uid)) {
return Process.getAppUidForSdkSandboxUid(uid);
}
return mapIsolatedUid(uid);
}
private int mapIsolatedUid(int uid) {
return mIsolatedUids.get(/*key=*/uid, /*valueIfKeyNotFound=*/uid);
}
@GuardedBy("this")
public void noteEventLocked(int code, String name, int uid) {
noteEventLocked(code, name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteEventLocked(int code, String name, int uid,
long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (!mActiveEvents.updateState(code, name, uid, 0)) {
return;
}
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, code, name, uid);
}
@GuardedBy("this")
public void noteCurrentTimeChangedLocked() {
final long currentTime = mClock.currentTimeMillis();
final long elapsedRealtime = mClock.elapsedRealtime();
final long uptime = mClock.uptimeMillis();
noteCurrentTimeChangedLocked(currentTime, elapsedRealtime, uptime);
}
@GuardedBy("this")
public void noteCurrentTimeChangedLocked(long currentTimeMs,
long elapsedRealtimeMs, long uptimeMs) {
recordCurrentTimeChangeLocked(currentTimeMs, elapsedRealtimeMs, uptimeMs);
}
@GuardedBy("this")
public void noteProcessStartLocked(String name, int uid) {
noteProcessStartLocked(name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteProcessStartLocked(String name, int uid,
long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (isOnBattery()) {
Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs);
u.getProcessStatsLocked(name).incStartsLocked();
}
if (!mActiveEvents.updateState(HistoryItem.EVENT_PROC_START, name, uid, 0)) {
return;
}
if (!mRecordAllHistory) {
return;
}
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_PROC_START, name, uid);
}
@GuardedBy("this")
public void noteProcessCrashLocked(String name, int uid) {
noteProcessCrashLocked(name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteProcessCrashLocked(String name, int uid,
long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (isOnBattery()) {
Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs);
u.getProcessStatsLocked(name).incNumCrashesLocked();
}
}
@GuardedBy("this")
public void noteProcessAnrLocked(String name, int uid) {
noteProcessAnrLocked(name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteProcessAnrLocked(String name, int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (isOnBattery()) {
Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs);
u.getProcessStatsLocked(name).incNumAnrsLocked();
}
}
@GuardedBy("this")
public void noteUidProcessStateLocked(int uid, int state) {
noteUidProcessStateLocked(uid, state, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
@SuppressWarnings("GuardedBy") // errorprone false positive on u.updateUidProcessStateLocked
public void noteUidProcessStateLocked(int uid, int state,
long elapsedRealtimeMs, long uptimeMs) {
int parentUid = mapUid(uid);
if (uid != parentUid) {
if (Process.isIsolated(uid)) {
// Isolated UIDs process state is already rolled up into parent, so no need to track
// Otherwise the parent's process state will get downgraded incorrectly
return;
}
}
// TODO(b/155216561): It is possible for isolated uids to be in a higher
// state than its parent uid. We should track the highest state within the union of host
// and isolated uids rather than only the parent uid.
FrameworkStatsLog.write(FrameworkStatsLog.UID_PROCESS_STATE_CHANGED, uid,
ActivityManager.processStateAmToProto(state));
getUidStatsLocked(parentUid, elapsedRealtimeMs, uptimeMs)
.updateUidProcessStateLocked(state, elapsedRealtimeMs, uptimeMs);
}
@GuardedBy("this")
public void noteProcessFinishLocked(String name, int uid) {
noteProcessFinishLocked(name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteProcessFinishLocked(String name, int uid,
long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (!mActiveEvents.updateState(HistoryItem.EVENT_PROC_FINISH, name, uid, 0)) {
return;
}
if (!mRecordAllHistory) {
return;
}
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_PROC_FINISH,
name, uid);
}
@GuardedBy("this")
public void noteSyncStartLocked(String name, int uid) {
noteSyncStartLocked(name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteSyncStartLocked(String name, int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteStartSyncLocked(name, elapsedRealtimeMs);
if (!mActiveEvents.updateState(HistoryItem.EVENT_SYNC_START, name, uid, 0)) {
return;
}
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_SYNC_START, name, uid);
}
@GuardedBy("this")
public void noteSyncFinishLocked(String name, int uid) {
noteSyncFinishLocked(name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteSyncFinishLocked(String name, int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteStopSyncLocked(name, elapsedRealtimeMs);
if (!mActiveEvents.updateState(HistoryItem.EVENT_SYNC_FINISH, name, uid, 0)) {
return;
}
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_SYNC_FINISH,
name, uid);
}
@GuardedBy("this")
public void noteJobStartLocked(String name, int uid) {
noteJobStartLocked(name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteJobStartLocked(String name, int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteStartJobLocked(name, elapsedRealtimeMs);
if (!mActiveEvents.updateState(HistoryItem.EVENT_JOB_START, name, uid, 0)) {
return;
}
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_JOB_START, name, uid);
}
@GuardedBy("this")
public void noteJobFinishLocked(String name, int uid, int stopReason) {
noteJobFinishLocked(name, uid, stopReason,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteJobFinishLocked(String name, int uid, int stopReason,
long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteStopJobLocked(name, elapsedRealtimeMs, stopReason);
if (!mActiveEvents.updateState(HistoryItem.EVENT_JOB_FINISH, name, uid, 0)) {
return;
}
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_JOB_FINISH, name, uid);
}
@GuardedBy("this")
public void noteJobsDeferredLocked(int uid, int numDeferred, long sinceLast) {
noteJobsDeferredLocked(uid, numDeferred, sinceLast,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteJobsDeferredLocked(int uid, int numDeferred, long sinceLast,
long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteJobsDeferredLocked(numDeferred, sinceLast);
}
@GuardedBy("this")
public void noteAlarmStartLocked(String name, WorkSource workSource, int uid) {
noteAlarmStartLocked(name, workSource, uid,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteAlarmStartLocked(String name, WorkSource workSource, int uid,
long elapsedRealtimeMs, long uptimeMs) {
noteAlarmStartOrFinishLocked(HistoryItem.EVENT_ALARM_START, name, workSource, uid,
elapsedRealtimeMs, uptimeMs);
}
@GuardedBy("this")
public void noteAlarmFinishLocked(String name, WorkSource workSource, int uid) {
noteAlarmFinishLocked(name, workSource, uid,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteAlarmFinishLocked(String name, WorkSource workSource, int uid,
long elapsedRealtimeMs, long uptimeMs) {
noteAlarmStartOrFinishLocked(HistoryItem.EVENT_ALARM_FINISH, name, workSource, uid,
elapsedRealtimeMs, uptimeMs);
}
@GuardedBy("this")
private void noteAlarmStartOrFinishLocked(int historyItem, String name, WorkSource workSource,
int uid, long elapsedRealtimeMs, long uptimeMs) {
if (!mRecordAllHistory) {
return;
}
if (workSource != null) {
for (int i = 0; i < workSource.size(); ++i) {
uid = mapUid(workSource.getUid(i));
if (mActiveEvents.updateState(historyItem, name, uid, 0)) {
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, historyItem, name, uid);
}
}
List<WorkChain> workChains = workSource.getWorkChains();
if (workChains != null) {
for (int i = 0; i < workChains.size(); ++i) {
uid = mapUid(workChains.get(i).getAttributionUid());
if (mActiveEvents.updateState(historyItem, name, uid, 0)) {
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, historyItem, name, uid);
}
}
}
} else {
uid = mapUid(uid);
if (mActiveEvents.updateState(historyItem, name, uid, 0)) {
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, historyItem, name, uid);
}
}
}
@GuardedBy("this")
public void noteWakupAlarmLocked(String packageName, int uid, WorkSource workSource,
String tag) {
noteWakupAlarmLocked(packageName, uid, workSource, tag,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteWakupAlarmLocked(String packageName, int uid, WorkSource workSource,
String tag, long elapsedRealtimeMs, long uptimeMs) {
if (workSource != null) {
for (int i = 0; i < workSource.size(); ++i) {
uid = workSource.getUid(i);
final String workSourceName = workSource.getPackageName(i);
if (isOnBattery()) {
BatteryStatsImpl.Uid.Pkg pkg = getPackageStatsLocked(uid,
workSourceName != null ? workSourceName : packageName,
elapsedRealtimeMs, uptimeMs);
pkg.noteWakeupAlarmLocked(tag);
}
}
List<WorkChain> workChains = workSource.getWorkChains();
if (workChains != null) {
for (int i = 0; i < workChains.size(); ++i) {
final WorkChain wc = workChains.get(i);
uid = wc.getAttributionUid();
if (isOnBattery()) {
BatteryStatsImpl.Uid.Pkg pkg = getPackageStatsLocked(uid, packageName,
elapsedRealtimeMs, uptimeMs);
pkg.noteWakeupAlarmLocked(tag);
}
}
}
} else {
if (isOnBattery()) {
BatteryStatsImpl.Uid.Pkg pkg = getPackageStatsLocked(uid, packageName,
elapsedRealtimeMs, uptimeMs);
pkg.noteWakeupAlarmLocked(tag);
}
}
}
private void requestWakelockCpuUpdate() {
mExternalSync.scheduleCpuSyncDueToWakelockChange(DELAY_UPDATE_WAKELOCKS);
}
private void requestImmediateCpuUpdate() {
mExternalSync.scheduleCpuSyncDueToWakelockChange(0 /* delayMillis */);
}
@GuardedBy("this")
public void setRecordAllHistoryLocked(boolean enabled) {
mRecordAllHistory = enabled;
if (!enabled) {
// Clear out any existing state.
mActiveEvents.removeEvents(HistoryItem.EVENT_WAKE_LOCK);
mActiveEvents.removeEvents(HistoryItem.EVENT_ALARM);
// Record the currently running processes as stopping, now that we are no
// longer tracking them.
HashMap<String, SparseIntArray> active = mActiveEvents.getStateForEvent(
HistoryItem.EVENT_PROC);
if (active != null) {
long mSecRealtime = mClock.elapsedRealtime();
final long mSecUptime = mClock.uptimeMillis();
for (HashMap.Entry<String, SparseIntArray> ent : active.entrySet()) {
SparseIntArray uids = ent.getValue();
for (int j=0; j<uids.size(); j++) {
addHistoryEventLocked(mSecRealtime, mSecUptime,
HistoryItem.EVENT_PROC_FINISH, ent.getKey(), uids.keyAt(j));
}
}
}
} else {
// Record the currently running processes as starting, now that we are tracking them.
HashMap<String, SparseIntArray> active = mActiveEvents.getStateForEvent(
HistoryItem.EVENT_PROC);
if (active != null) {
long mSecRealtime = mClock.elapsedRealtime();
final long mSecUptime = mClock.uptimeMillis();
for (HashMap.Entry<String, SparseIntArray> ent : active.entrySet()) {
SparseIntArray uids = ent.getValue();
for (int j=0; j<uids.size(); j++) {
addHistoryEventLocked(mSecRealtime, mSecUptime,
HistoryItem.EVENT_PROC_START, ent.getKey(), uids.keyAt(j));
}
}
}
}
}
public void setNoAutoReset(boolean enabled) {
mNoAutoReset = enabled;
}
@GuardedBy("this")
public void setPretendScreenOff(boolean pretendScreenOff) {
if (mPretendScreenOff != pretendScreenOff) {
mPretendScreenOff = pretendScreenOff;
final int primaryScreenState = mPerDisplayBatteryStats[0].screenState;
noteScreenStateLocked(0, primaryScreenState,
mClock.elapsedRealtime(), mClock.uptimeMillis(),
mClock.currentTimeMillis());
}
}
private String mInitialAcquireWakeName;
private int mInitialAcquireWakeUid = -1;
@GuardedBy("this")
public void noteStartWakeLocked(int uid, int pid, WorkChain wc, String name, String historyName,
int type, boolean unimportantForLogging) {
noteStartWakeLocked(uid, pid, wc, name, historyName, type, unimportantForLogging,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteStartWakeLocked(int uid, int pid, WorkChain wc, String name, String historyName,
int type, boolean unimportantForLogging, long elapsedRealtimeMs, long uptimeMs) {
final int mappedUid = mapUid(uid);
if (type == WAKE_TYPE_PARTIAL) {
// Only care about partial wake locks, since full wake locks
// will be canceled when the user puts the screen to sleep.
aggregateLastWakeupUptimeLocked(elapsedRealtimeMs, uptimeMs);
if (historyName == null) {
historyName = name;
}
if (mRecordAllHistory) {
if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_START, historyName,
mappedUid, 0)) {
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs,
HistoryItem.EVENT_WAKE_LOCK_START, historyName, mappedUid);
}
}
if (mWakeLockNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_WAKE_LOCK_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
+ Integer.toHexString(mHistoryCur.states));
mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
mHistoryCur.wakelockTag.string = mInitialAcquireWakeName = historyName;
mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = mappedUid;
mWakeLockImportant = !unimportantForLogging;
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
} else if (!mWakeLockImportant && !unimportantForLogging
&& mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE) {
if (mHistoryLastWritten.wakelockTag != null) {
// We'll try to update the last tag.
mHistoryLastWritten.wakelockTag = null;
mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
mHistoryCur.wakelockTag.string = mInitialAcquireWakeName = historyName;
mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = mappedUid;
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
mWakeLockImportant = true;
}
mWakeLockNesting++;
}
if (mappedUid >= 0) {
if (mappedUid != uid) {
// Prevent the isolated uid mapping from being removed while the wakelock is
// being held.
incrementIsolatedUidRefCount(uid);
}
if (mOnBatteryScreenOffTimeBase.isRunning()) {
// We only update the cpu time when a wake lock is acquired if the screen is off.
// If the screen is on, we don't distribute the power amongst partial wakelocks.
if (DEBUG_ENERGY_CPU) {
Slog.d(TAG, "Updating cpu time because of +wake_lock");
}
requestWakelockCpuUpdate();
}
getUidStatsLocked(mappedUid, elapsedRealtimeMs, uptimeMs)
.noteStartWakeLocked(pid, name, type, elapsedRealtimeMs);
if (wc != null) {
FrameworkStatsLog.write(FrameworkStatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(),
wc.getTags(), getPowerManagerWakeLockLevel(type), name,
FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
} else {
FrameworkStatsLog.write_non_chained(FrameworkStatsLog.WAKELOCK_STATE_CHANGED,
mapIsolatedUid(uid), null, getPowerManagerWakeLockLevel(type), name,
FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
}
}
}
@GuardedBy("this")
public void noteStopWakeLocked(int uid, int pid, WorkChain wc, String name, String historyName,
int type) {
noteStopWakeLocked(uid, pid, wc, name, historyName, type,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteStopWakeLocked(int uid, int pid, WorkChain wc, String name, String historyName,
int type, long elapsedRealtimeMs, long uptimeMs) {
final int mappedUid = mapUid(uid);
if (type == WAKE_TYPE_PARTIAL) {
mWakeLockNesting--;
if (mRecordAllHistory) {
if (historyName == null) {
historyName = name;
}
if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_FINISH, historyName,
mappedUid, 0)) {
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs,
HistoryItem.EVENT_WAKE_LOCK_FINISH, historyName, mappedUid);
}
}
if (mWakeLockNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: "
+ Integer.toHexString(mHistoryCur.states));
mInitialAcquireWakeName = null;
mInitialAcquireWakeUid = -1;
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
}
if (mappedUid >= 0) {
if (mOnBatteryScreenOffTimeBase.isRunning()) {
if (DEBUG_ENERGY_CPU) {
Slog.d(TAG, "Updating cpu time because of -wake_lock");
}
requestWakelockCpuUpdate();
}
getUidStatsLocked(mappedUid, elapsedRealtimeMs, uptimeMs)
.noteStopWakeLocked(pid, name, type, elapsedRealtimeMs);
if (wc != null) {
FrameworkStatsLog.write(FrameworkStatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(),
wc.getTags(), getPowerManagerWakeLockLevel(type), name,
FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
} else {
FrameworkStatsLog.write_non_chained(FrameworkStatsLog.WAKELOCK_STATE_CHANGED,
mapIsolatedUid(uid), null, getPowerManagerWakeLockLevel(type), name,
FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
}
if (mappedUid != uid) {
// Decrement the ref count for the isolated uid and delete the mapping if uneeded.
maybeRemoveIsolatedUidLocked(uid, elapsedRealtimeMs, uptimeMs);
}
}
}
/**
* Converts BatteryStats wakelock types back into PowerManager wakelock levels.
* This is the inverse map of Notifier.getBatteryStatsWakeLockMonitorType().
* These are estimations, since batterystats loses some of the original data.
* TODO: Delete this. Instead, FrameworkStatsLog.write should be called from
* PowerManager's Notifier.
*/
private int getPowerManagerWakeLockLevel(int battertStatsWakelockType) {
switch (battertStatsWakelockType) {
// PowerManager.PARTIAL_WAKE_LOCK or PROXIMITY_SCREEN_OFF_WAKE_LOCK
case BatteryStats.WAKE_TYPE_PARTIAL:
return PowerManager.PARTIAL_WAKE_LOCK;
// PowerManager.SCREEN_DIM_WAKE_LOCK or SCREEN_BRIGHT_WAKE_LOCK
case BatteryStats.WAKE_TYPE_FULL:
return PowerManager.FULL_WAKE_LOCK;
case BatteryStats.WAKE_TYPE_DRAW:
return PowerManager.DRAW_WAKE_LOCK;
// It appears that nothing can ever make a Window and PowerManager lacks an equivalent.
case BatteryStats.WAKE_TYPE_WINDOW:
Slog.e(TAG, "Illegal window wakelock type observed in batterystats.");
return -1;
default:
Slog.e(TAG, "Illegal wakelock type in batterystats: " + battertStatsWakelockType);
return -1;
}
}
@GuardedBy("this")
public void noteStartWakeFromSourceLocked(WorkSource ws, int pid, String name,
String historyName, int type, boolean unimportantForLogging) {
noteStartWakeFromSourceLocked(ws, pid, name, historyName, type, unimportantForLogging,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteStartWakeFromSourceLocked(WorkSource ws, int pid, String name,
String historyName, int type, boolean unimportantForLogging,
long elapsedRealtimeMs, long uptimeMs) {
final int N = ws.size();
for (int i=0; i<N; i++) {
noteStartWakeLocked(ws.getUid(i), pid, null, name, historyName, type,
unimportantForLogging, elapsedRealtimeMs, uptimeMs);
}
List<WorkChain> wcs = ws.getWorkChains();
if (wcs != null) {
for (int i = 0; i < wcs.size(); ++i) {
final WorkChain wc = wcs.get(i);
noteStartWakeLocked(wc.getAttributionUid(), pid, wc, name, historyName, type,
unimportantForLogging, elapsedRealtimeMs, uptimeMs);
}
}
}
@GuardedBy("this")
public void noteChangeWakelockFromSourceLocked(WorkSource ws, int pid, String name,
String historyName, int type, WorkSource newWs, int newPid, String newName,
String newHistoryName, int newType, boolean newUnimportantForLogging) {
noteChangeWakelockFromSourceLocked(ws, pid, name, historyName, type, newWs, newPid,
newName, newHistoryName, newType, newUnimportantForLogging,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteChangeWakelockFromSourceLocked(WorkSource ws, int pid, String name,
String historyName, int type, WorkSource newWs, int newPid, String newName,
String newHistoryName, int newType, boolean newUnimportantForLogging,
long elapsedRealtimeMs, long uptimeMs) {
List<WorkChain>[] wcs = WorkSource.diffChains(ws, newWs);
// For correct semantics, we start the need worksources first, so that we won't
// make inappropriate history items as if all wake locks went away and new ones
// appeared. This is okay because tracking of wake locks allows nesting.
//
// First the starts :
final int NN = newWs.size();
for (int i=0; i<NN; i++) {
noteStartWakeLocked(newWs.getUid(i), newPid, null, newName, newHistoryName, newType,
newUnimportantForLogging, elapsedRealtimeMs, uptimeMs);
}
if (wcs != null) {
List<WorkChain> newChains = wcs[0];
if (newChains != null) {
for (int i = 0; i < newChains.size(); ++i) {
final WorkChain newChain = newChains.get(i);
noteStartWakeLocked(newChain.getAttributionUid(), newPid, newChain, newName,
newHistoryName, newType, newUnimportantForLogging, elapsedRealtimeMs,
uptimeMs);
}
}
}
// Then the stops :
final int NO = ws.size();
for (int i=0; i<NO; i++) {
noteStopWakeLocked(ws.getUid(i), pid, null, name, historyName, type, elapsedRealtimeMs,
uptimeMs);
}
if (wcs != null) {
List<WorkChain> goneChains = wcs[1];
if (goneChains != null) {
for (int i = 0; i < goneChains.size(); ++i) {
final WorkChain goneChain = goneChains.get(i);
noteStopWakeLocked(goneChain.getAttributionUid(), pid, goneChain, name,
historyName, type, elapsedRealtimeMs, uptimeMs);
}
}
}
}
@GuardedBy("this")
public void noteStopWakeFromSourceLocked(WorkSource ws, int pid, String name,
String historyName, int type) {
noteStopWakeFromSourceLocked(ws, pid, name, historyName, type,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteStopWakeFromSourceLocked(WorkSource ws, int pid, String name,
String historyName, int type, long elapsedRealtimeMs, long uptimeMs) {
final int N = ws.size();
for (int i=0; i<N; i++) {
noteStopWakeLocked(ws.getUid(i), pid, null, name, historyName, type, elapsedRealtimeMs,
uptimeMs);
}
List<WorkChain> wcs = ws.getWorkChains();
if (wcs != null) {
for (int i = 0; i < wcs.size(); ++i) {
final WorkChain wc = wcs.get(i);
noteStopWakeLocked(wc.getAttributionUid(), pid, wc, name, historyName, type,
elapsedRealtimeMs, uptimeMs);
}
}
}
@GuardedBy("this")
public void noteLongPartialWakelockStart(String name, String historyName, int uid) {
noteLongPartialWakelockStart(name, historyName, uid,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteLongPartialWakelockStart(String name, String historyName, int uid,
long elapsedRealtimeMs, long uptimeMs) {
noteLongPartialWakeLockStartInternal(name, historyName, uid, elapsedRealtimeMs, uptimeMs);
}
@GuardedBy("this")
public void noteLongPartialWakelockStartFromSource(String name, String historyName,
WorkSource workSource) {
noteLongPartialWakelockStartFromSource(name, historyName, workSource,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteLongPartialWakelockStartFromSource(String name, String historyName,
WorkSource workSource, long elapsedRealtimeMs, long uptimeMs) {
final int N = workSource.size();
for (int i = 0; i < N; ++i) {
final int uid = mapUid(workSource.getUid(i));
noteLongPartialWakeLockStartInternal(name, historyName, uid,
elapsedRealtimeMs, uptimeMs);
}
final List<WorkChain> workChains = workSource.getWorkChains();
if (workChains != null) {
for (int i = 0; i < workChains.size(); ++i) {
final WorkChain workChain = workChains.get(i);
final int uid = workChain.getAttributionUid();
noteLongPartialWakeLockStartInternal(name, historyName, uid,
elapsedRealtimeMs, uptimeMs);
}
}
}
@GuardedBy("this")
private void noteLongPartialWakeLockStartInternal(String name, String historyName, int uid,
long elapsedRealtimeMs, long uptimeMs) {
final int mappedUid = mapUid(uid);
if (historyName == null) {
historyName = name;
}
if (!mActiveEvents.updateState(HistoryItem.EVENT_LONG_WAKE_LOCK_START, historyName,
mappedUid, 0)) {
return;
}
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_LONG_WAKE_LOCK_START,
historyName, mappedUid);
if (mappedUid != uid) {
// Prevent the isolated uid mapping from being removed while the wakelock is
// being held.
incrementIsolatedUidRefCount(uid);
}
}
@GuardedBy("this")
public void noteLongPartialWakelockFinish(String name, String historyName, int uid) {
noteLongPartialWakelockFinish(name, historyName, uid,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteLongPartialWakelockFinish(String name, String historyName, int uid,
long elapsedRealtimeMs, long uptimeMs) {
noteLongPartialWakeLockFinishInternal(name, historyName, uid, elapsedRealtimeMs, uptimeMs);
}
@GuardedBy("this")
public void noteLongPartialWakelockFinishFromSource(String name, String historyName,
WorkSource workSource) {
noteLongPartialWakelockFinishFromSource(name, historyName, workSource,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteLongPartialWakelockFinishFromSource(String name, String historyName,
WorkSource workSource, long elapsedRealtimeMs, long uptimeMs) {
final int N = workSource.size();
for (int i = 0; i < N; ++i) {
final int uid = mapUid(workSource.getUid(i));
noteLongPartialWakeLockFinishInternal(name, historyName, uid,
elapsedRealtimeMs, uptimeMs);
}
final List<WorkChain> workChains = workSource.getWorkChains();
if (workChains != null) {
for (int i = 0; i < workChains.size(); ++i) {
final WorkChain workChain = workChains.get(i);
final int uid = workChain.getAttributionUid();
noteLongPartialWakeLockFinishInternal(name, historyName, uid,
elapsedRealtimeMs, uptimeMs);
}
}
}
@GuardedBy("this")
private void noteLongPartialWakeLockFinishInternal(String name, String historyName, int uid,
long elapsedRealtimeMs, long uptimeMs) {
final int mappedUid = mapUid(uid);
if (historyName == null) {
historyName = name;
}
if (!mActiveEvents.updateState(HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH, historyName,
mappedUid, 0)) {
return;
}
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH,
historyName, mappedUid);
if (mappedUid != uid) {
// Decrement the ref count for the isolated uid and delete the mapping if uneeded.
maybeRemoveIsolatedUidLocked(uid, elapsedRealtimeMs, uptimeMs);
}
}
@GuardedBy("this")
void aggregateLastWakeupUptimeLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mLastWakeupReason != null) {
long deltaUptimeMs = uptimeMs - mLastWakeupUptimeMs;
SamplingTimer timer = getWakeupReasonTimerLocked(mLastWakeupReason);
timer.add(deltaUptimeMs * 1000, 1, elapsedRealtimeMs); // time in in microseconds
FrameworkStatsLog.write(FrameworkStatsLog.KERNEL_WAKEUP_REPORTED, mLastWakeupReason,
/* duration_usec */ deltaUptimeMs * 1000);
mLastWakeupReason = null;
}
}
@GuardedBy("this")
public void noteWakeupReasonLocked(String reason) {
noteWakeupReasonLocked(reason, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteWakeupReasonLocked(String reason, long elapsedRealtimeMs, long uptimeMs) {
if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason \"" + reason +"\": "
+ Integer.toHexString(mHistoryCur.states));
aggregateLastWakeupUptimeLocked(elapsedRealtimeMs, uptimeMs);
mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
mHistoryCur.wakeReasonTag.string = reason;
mHistoryCur.wakeReasonTag.uid = 0;
mLastWakeupReason = reason;
mLastWakeupUptimeMs = uptimeMs;
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
@GuardedBy("this")
public boolean startAddingCpuLocked() {
mExternalSync.cancelCpuSyncDueToWakelockChange();
return mOnBatteryInternal;
}
@GuardedBy("this")
public void finishAddingCpuLocked(int totalUTimeMs, int totalSTimeMs, int statUserTimeMs,
int statSystemTimeMs, int statIOWaitTimeMs, int statIrqTimeMs,
int statSoftIrqTimeMs, int statIdleTimeMs) {
if (DEBUG) {
Slog.d(TAG, "Adding cpu: tuser=" + totalUTimeMs + " tsys=" + totalSTimeMs
+ " user=" + statUserTimeMs + " sys=" + statSystemTimeMs
+ " io=" + statIOWaitTimeMs + " irq=" + statIrqTimeMs
+ " sirq=" + statSoftIrqTimeMs + " idle=" + statIdleTimeMs);
}
mCurStepCpuUserTimeMs += totalUTimeMs;
mCurStepCpuSystemTimeMs += totalSTimeMs;
mCurStepStatUserTimeMs += statUserTimeMs;
mCurStepStatSystemTimeMs += statSystemTimeMs;
mCurStepStatIOWaitTimeMs += statIOWaitTimeMs;
mCurStepStatIrqTimeMs += statIrqTimeMs;
mCurStepStatSoftIrqTimeMs += statSoftIrqTimeMs;
mCurStepStatIdleTimeMs += statIdleTimeMs;
}
public void noteProcessDiedLocked(int uid, int pid) {
uid = mapUid(uid);
Uid u = mUidStats.get(uid);
if (u != null) {
u.mPids.remove(pid);
}
}
public long getProcessWakeTime(int uid, int pid, long realtimeMs) {
uid = mapUid(uid);
Uid u = mUidStats.get(uid);
if (u != null) {
Uid.Pid p = u.mPids.get(pid);
if (p != null) {
return p.mWakeSumMs + (p.mWakeNesting > 0 ? (realtimeMs - p.mWakeStartMs) : 0);
}
}
return 0;
}
public void reportExcessiveCpuLocked(int uid, String proc, long overTimeMs, long usedTimeMs) {
uid = mapUid(uid);
Uid u = mUidStats.get(uid);
if (u != null) {
u.reportExcessiveCpuLocked(proc, overTimeMs, usedTimeMs);
}
}
int mSensorNesting;
@GuardedBy("this")
public void noteStartSensorLocked(int uid, int sensor) {
noteStartSensorLocked(uid, sensor, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteStartSensorLocked(int uid, int sensor, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mSensorNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_SENSOR_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Start sensor to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
mSensorNesting++;
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteStartSensor(sensor, elapsedRealtimeMs);
}
@GuardedBy("this")
public void noteStopSensorLocked(int uid, int sensor) {
noteStopSensorLocked(uid, sensor, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteStopSensorLocked(int uid, int sensor, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
mSensorNesting--;
if (mSensorNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_SENSOR_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Stop sensor to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteStopSensor(sensor, elapsedRealtimeMs);
}
int mGpsNesting;
@GuardedBy("this")
public void noteGpsChangedLocked(WorkSource oldWs, WorkSource newWs) {
noteGpsChangedLocked(oldWs, newWs, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteGpsChangedLocked(WorkSource oldWs, WorkSource newWs,
long elapsedRealtimeMs, long uptimeMs) {
for (int i = 0; i < newWs.size(); ++i) {
noteStartGpsLocked(newWs.getUid(i), null, elapsedRealtimeMs, uptimeMs);
}
for (int i = 0; i < oldWs.size(); ++i) {
noteStopGpsLocked((oldWs.getUid(i)), null, elapsedRealtimeMs, uptimeMs);
}
List<WorkChain>[] wcs = WorkSource.diffChains(oldWs, newWs);
if (wcs != null) {
if (wcs[0] != null) {
final List<WorkChain> newChains = wcs[0];
for (int i = 0; i < newChains.size(); ++i) {
noteStartGpsLocked(-1, newChains.get(i), elapsedRealtimeMs, uptimeMs);
}
}
if (wcs[1] != null) {
final List<WorkChain> goneChains = wcs[1];
for (int i = 0; i < goneChains.size(); ++i) {
noteStopGpsLocked(-1, goneChains.get(i), elapsedRealtimeMs, uptimeMs);
}
}
}
}
@GuardedBy("this")
private void noteStartGpsLocked(int uid, WorkChain workChain,
long elapsedRealtimeMs, long uptimeMs) {
if (workChain != null) {
uid = workChain.getAttributionUid();
}
final int mappedUid = mapUid(uid);
if (mGpsNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_GPS_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Start GPS to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
mGpsNesting++;
if (workChain == null) {
FrameworkStatsLog.write_non_chained(FrameworkStatsLog.GPS_SCAN_STATE_CHANGED,
mapIsolatedUid(uid), null, FrameworkStatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON);
} else {
FrameworkStatsLog.write(FrameworkStatsLog.GPS_SCAN_STATE_CHANGED,
workChain.getUids(), workChain.getTags(),
FrameworkStatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON);
}
getUidStatsLocked(mappedUid, elapsedRealtimeMs, uptimeMs).noteStartGps(elapsedRealtimeMs);
}
@GuardedBy("this")
private void noteStopGpsLocked(int uid, WorkChain workChain,
long elapsedRealtimeMs, long uptimeMs) {
if (workChain != null) {
uid = workChain.getAttributionUid();
}
final int mappedUid = mapUid(uid);
mGpsNesting--;
if (mGpsNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_GPS_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Stop GPS to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
stopAllGpsSignalQualityTimersLocked(-1, elapsedRealtimeMs);
mGpsSignalQualityBin = -1;
}
if (workChain == null) {
FrameworkStatsLog.write_non_chained(FrameworkStatsLog.GPS_SCAN_STATE_CHANGED,
mapIsolatedUid(uid), null,
FrameworkStatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF);
} else {
FrameworkStatsLog.write(FrameworkStatsLog.GPS_SCAN_STATE_CHANGED, workChain.getUids(),
workChain.getTags(), FrameworkStatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF);
}
getUidStatsLocked(mappedUid, elapsedRealtimeMs, uptimeMs).noteStopGps(elapsedRealtimeMs);
}
@GuardedBy("this")
public void noteGpsSignalQualityLocked(int signalLevel) {
noteGpsSignalQualityLocked(signalLevel, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteGpsSignalQualityLocked(int signalLevel, long elapsedRealtimeMs, long uptimeMs) {
if (mGpsNesting == 0) {
return;
}
if (signalLevel < 0 || signalLevel >= mGpsSignalQualityTimer.length) {
stopAllGpsSignalQualityTimersLocked(-1, elapsedRealtimeMs);
return;
}
if (mGpsSignalQualityBin != signalLevel) {
if (mGpsSignalQualityBin >= 0) {
mGpsSignalQualityTimer[mGpsSignalQualityBin].stopRunningLocked(elapsedRealtimeMs);
}
if(!mGpsSignalQualityTimer[signalLevel].isRunningLocked()) {
mGpsSignalQualityTimer[signalLevel].startRunningLocked(elapsedRealtimeMs);
}
mHistoryCur.states2 = (mHistoryCur.states2&~HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK)
| (signalLevel << HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT);
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mGpsSignalQualityBin = signalLevel;
}
return;
}
@GuardedBy("this")
public void noteScreenStateLocked(int display, int state) {
noteScreenStateLocked(display, state, mClock.elapsedRealtime(), mClock.uptimeMillis(),
mClock.currentTimeMillis());
}
@GuardedBy("this")
public void noteScreenStateLocked(int display, int displayState,
long elapsedRealtimeMs, long uptimeMs, long currentTimeMs) {
// Battery stats relies on there being 4 states. To accommodate this, new states beyond the
// original 4 are mapped to one of the originals.
if (displayState > MAX_TRACKED_SCREEN_STATE) {
if (Display.isOnState(displayState)) {
displayState = Display.STATE_ON;
} else if (Display.isDozeState(displayState)) {
if (Display.isSuspendedState(displayState)) {
displayState = Display.STATE_DOZE_SUSPEND;
} else {
displayState = Display.STATE_DOZE;
}
} else if (Display.isOffState(displayState)) {
displayState = Display.STATE_OFF;
} else {
Slog.wtf(TAG, "Unknown screen state (not mapped): " + displayState);
displayState = Display.STATE_UNKNOWN;
}
}
// As of this point, displayState should be mapped to one of:
// - Display.STATE_ON,
// - Display.STATE_DOZE
// - Display.STATE_DOZE_SUSPEND
// - Display.STATE_OFF
// - Display.STATE_UNKNOWN
int state;
int overallBin = mScreenBrightnessBin;
int externalUpdateFlag = 0;
boolean shouldScheduleSync = false;
final int numDisplay = mPerDisplayBatteryStats.length;
if (display < 0 || display >= numDisplay) {
Slog.wtf(TAG, "Unexpected note screen state for display " + display + " (only "
+ mPerDisplayBatteryStats.length + " displays exist...)");
return;
}
final DisplayBatteryStats displayStats = mPerDisplayBatteryStats[display];
final int oldDisplayState = displayStats.screenState;
if (oldDisplayState == displayState) {
// Nothing changed
state = mScreenState;
} else {
displayStats.screenState = displayState;
// Stop timer for previous display state.
switch (oldDisplayState) {
case Display.STATE_ON:
displayStats.screenOnTimer.stopRunningLocked(elapsedRealtimeMs);
final int bin = displayStats.screenBrightnessBin;
if (bin >= 0) {
displayStats.screenBrightnessTimers[bin].stopRunningLocked(
elapsedRealtimeMs);
}
overallBin = evaluateOverallScreenBrightnessBinLocked();
shouldScheduleSync = true;
break;
case Display.STATE_DOZE:
// Transition from doze to doze suspend can be ignored.
if (displayState == Display.STATE_DOZE_SUSPEND) break;
displayStats.screenDozeTimer.stopRunningLocked(elapsedRealtimeMs);
shouldScheduleSync = true;
break;
case Display.STATE_DOZE_SUSPEND:
// Transition from doze suspend to doze can be ignored.
if (displayState == Display.STATE_DOZE) break;
displayStats.screenDozeTimer.stopRunningLocked(elapsedRealtimeMs);
shouldScheduleSync = true;
break;
case Display.STATE_OFF: // fallthrough
case Display.STATE_UNKNOWN:
// Not tracked by timers.
break;
default:
Slog.wtf(TAG,
"Attempted to stop timer for unexpected display state " + display);
}
// Start timer for new display state.
switch (displayState) {
case Display.STATE_ON:
displayStats.screenOnTimer.startRunningLocked(elapsedRealtimeMs);
final int bin = displayStats.screenBrightnessBin;
if (bin >= 0) {
displayStats.screenBrightnessTimers[bin].startRunningLocked(
elapsedRealtimeMs);
}
overallBin = evaluateOverallScreenBrightnessBinLocked();
shouldScheduleSync = true;
break;
case Display.STATE_DOZE:
// Transition from doze suspend to doze can be ignored.
if (oldDisplayState == Display.STATE_DOZE_SUSPEND) break;
displayStats.screenDozeTimer.startRunningLocked(elapsedRealtimeMs);
shouldScheduleSync = true;
break;
case Display.STATE_DOZE_SUSPEND:
// Transition from doze to doze suspend can be ignored.
if (oldDisplayState == Display.STATE_DOZE) break;
displayStats.screenDozeTimer.startRunningLocked(elapsedRealtimeMs);
shouldScheduleSync = true;
break;
case Display.STATE_OFF: // fallthrough
case Display.STATE_UNKNOWN:
// Not tracked by timers.
break;
default:
Slog.wtf(TAG,
"Attempted to start timer for unexpected display state " + displayState
+ " for display " + display);
}
if (shouldScheduleSync
&& mGlobalMeasuredEnergyStats != null
&& mGlobalMeasuredEnergyStats.isStandardBucketSupported(
MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON)) {
// Display measured energy stats is available. Prepare to schedule an
// external sync.
externalUpdateFlag |= ExternalStatsSync.UPDATE_DISPLAY;
}
// Reevaluate most important display screen state.
state = Display.STATE_UNKNOWN;
for (int i = 0; i < numDisplay; i++) {
final int tempState = mPerDisplayBatteryStats[i].screenState;
if (tempState == Display.STATE_ON
|| state == Display.STATE_ON) {
state = Display.STATE_ON;
} else if (tempState == Display.STATE_DOZE
|| state == Display.STATE_DOZE) {
state = Display.STATE_DOZE;
} else if (tempState == Display.STATE_DOZE_SUSPEND
|| state == Display.STATE_DOZE_SUSPEND) {
state = Display.STATE_DOZE_SUSPEND;
} else if (tempState == Display.STATE_OFF
|| state == Display.STATE_OFF) {
state = Display.STATE_OFF;
}
}
}
final boolean batteryRunning = mOnBatteryTimeBase.isRunning();
final boolean batteryScreenOffRunning = mOnBatteryScreenOffTimeBase.isRunning();
state = mPretendScreenOff ? Display.STATE_OFF : state;
if (mScreenState != state) {
recordDailyStatsIfNeededLocked(true, currentTimeMs);
final int oldState = mScreenState;
mScreenState = state;
if (DEBUG) Slog.v(TAG, "Screen state: oldState=" + Display.stateToString(oldState)
+ ", newState=" + Display.stateToString(state));
if (state != Display.STATE_UNKNOWN) {
int stepState = state-1;
if ((stepState & STEP_LEVEL_MODE_SCREEN_STATE) == stepState) {
mModStepMode |= (mCurStepMode & STEP_LEVEL_MODE_SCREEN_STATE) ^ stepState;
mCurStepMode = (mCurStepMode & ~STEP_LEVEL_MODE_SCREEN_STATE) | stepState;
} else {
Slog.wtf(TAG, "Unexpected screen state: " + state);
}
}
boolean updateHistory = false;
if (Display.isDozeState(state) && !Display.isDozeState(oldState)) {
mHistoryCur.states |= HistoryItem.STATE_SCREEN_DOZE_FLAG;
mScreenDozeTimer.startRunningLocked(elapsedRealtimeMs);
updateHistory = true;
} else if (Display.isDozeState(oldState) && !Display.isDozeState(state)) {
mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_DOZE_FLAG;
mScreenDozeTimer.stopRunningLocked(elapsedRealtimeMs);
updateHistory = true;
}
if (Display.isOnState(state)) {
mHistoryCur.states |= HistoryItem.STATE_SCREEN_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Screen on to: "
+ Integer.toHexString(mHistoryCur.states));
mScreenOnTimer.startRunningLocked(elapsedRealtimeMs);
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin]
.startRunningLocked(elapsedRealtimeMs);
}
updateHistory = true;
} else if (Display.isOnState(oldState)) {
mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Screen off to: "
+ Integer.toHexString(mHistoryCur.states));
mScreenOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin]
.stopRunningLocked(elapsedRealtimeMs);
}
updateHistory = true;
}
if (updateHistory) {
if (DEBUG_HISTORY) Slog.v(TAG, "Screen state to: "
+ Display.stateToString(state));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
// Per screen state Cpu stats needed. Prepare to schedule an external sync.
externalUpdateFlag |= ExternalStatsSync.UPDATE_CPU;
shouldScheduleSync = true;
if (Display.isOnState(state)) {
updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), state,
uptimeMs * 1000, elapsedRealtimeMs * 1000);
// Fake a wake lock, so we consider the device waked as long as the screen is on.
noteStartWakeLocked(-1, -1, null, "screen", null, WAKE_TYPE_PARTIAL, false,
elapsedRealtimeMs, uptimeMs);
} else if (Display.isOnState(oldState)) {
noteStopWakeLocked(-1, -1, null, "screen", "screen", WAKE_TYPE_PARTIAL,
elapsedRealtimeMs, uptimeMs);
updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), state,
uptimeMs * 1000, elapsedRealtimeMs * 1000);
}
// Update discharge amounts.
if (mOnBatteryInternal) {
updateDischargeScreenLevelsLocked(oldState, state);
}
}
// Changing display states might have changed the screen used to determine the overall
// brightness.
maybeUpdateOverallScreenBrightness(overallBin, elapsedRealtimeMs, uptimeMs);
if (shouldScheduleSync) {
final int numDisplays = mPerDisplayBatteryStats.length;
final int[] displayStates = new int[numDisplays];
for (int i = 0; i < numDisplays; i++) {
displayStates[i] = mPerDisplayBatteryStats[i].screenState;
}
mExternalSync.scheduleSyncDueToScreenStateChange(externalUpdateFlag,
batteryRunning, batteryScreenOffRunning, state, displayStates);
}
}
@UnsupportedAppUsage
@GuardedBy("this")
public void noteScreenBrightnessLocked(int brightness) {
noteScreenBrightnessLocked(0, brightness);
}
/**
* Note screen brightness change for a display.
*/
@GuardedBy("this")
public void noteScreenBrightnessLocked(int display, int brightness) {
noteScreenBrightnessLocked(display, brightness, mClock.elapsedRealtime(),
mClock.uptimeMillis());
}
/**
* Note screen brightness change for a display.
*/
@GuardedBy("this")
public void noteScreenBrightnessLocked(int display, int brightness, long elapsedRealtimeMs,
long uptimeMs) {
// Bin the brightness.
int bin = brightness / (256/NUM_SCREEN_BRIGHTNESS_BINS);
if (bin < 0) bin = 0;
else if (bin >= NUM_SCREEN_BRIGHTNESS_BINS) bin = NUM_SCREEN_BRIGHTNESS_BINS-1;
final int overallBin;
final int numDisplays = mPerDisplayBatteryStats.length;
if (display < 0 || display >= numDisplays) {
Slog.wtf(TAG, "Unexpected note screen brightness for display " + display + " (only "
+ mPerDisplayBatteryStats.length + " displays exist...)");
return;
}
final DisplayBatteryStats displayStats = mPerDisplayBatteryStats[display];
final int oldBin = displayStats.screenBrightnessBin;
if (oldBin == bin) {
// Nothing changed
overallBin = mScreenBrightnessBin;
} else {
displayStats.screenBrightnessBin = bin;
if (displayStats.screenState == Display.STATE_ON) {
if (oldBin >= 0) {
displayStats.screenBrightnessTimers[oldBin].stopRunningLocked(
elapsedRealtimeMs);
}
displayStats.screenBrightnessTimers[bin].startRunningLocked(
elapsedRealtimeMs);
}
overallBin = evaluateOverallScreenBrightnessBinLocked();
}
maybeUpdateOverallScreenBrightness(overallBin, elapsedRealtimeMs, uptimeMs);
}
@GuardedBy("this")
private int evaluateOverallScreenBrightnessBinLocked() {
int overallBin = -1;
final int numDisplays = getDisplayCount();
for (int display = 0; display < numDisplays; display++) {
final int displayBrightnessBin;
if (mPerDisplayBatteryStats[display].screenState == Display.STATE_ON) {
displayBrightnessBin = mPerDisplayBatteryStats[display].screenBrightnessBin;
} else {
displayBrightnessBin = -1;
}
if (displayBrightnessBin > overallBin) {
overallBin = displayBrightnessBin;
}
}
return overallBin;
}
@GuardedBy("this")
private void maybeUpdateOverallScreenBrightness(int overallBin, long elapsedRealtimeMs,
long uptimeMs) {
if (mScreenBrightnessBin != overallBin) {
if (overallBin >= 0) {
mHistoryCur.states = (mHistoryCur.states & ~HistoryItem.STATE_BRIGHTNESS_MASK)
| (overallBin << HistoryItem.STATE_BRIGHTNESS_SHIFT);
if (DEBUG_HISTORY) {
Slog.v(TAG, "Screen brightness " + overallBin + " to: "
+ Integer.toHexString(mHistoryCur.states));
}
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
if (mScreenState == Display.STATE_ON) {
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin]
.stopRunningLocked(elapsedRealtimeMs);
}
if (overallBin >= 0) {
mScreenBrightnessTimer[overallBin]
.startRunningLocked(elapsedRealtimeMs);
}
}
mScreenBrightnessBin = overallBin;
}
}
@UnsupportedAppUsage
@GuardedBy("this")
public void noteUserActivityLocked(int uid, int event) {
noteUserActivityLocked(uid, event, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteUserActivityLocked(int uid, int event, long elapsedRealtimeMs, long uptimeMs) {
if (mOnBatteryInternal) {
uid = mapUid(uid);
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs).noteUserActivityLocked(event);
}
}
@GuardedBy("this")
public void noteWakeUpLocked(String reason, int reasonUid) {
noteWakeUpLocked(reason, reasonUid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteWakeUpLocked(String reason, int reasonUid,
long elapsedRealtimeMs, long uptimeMs) {
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_SCREEN_WAKE_UP,
reason, reasonUid);
}
@GuardedBy("this")
public void noteInteractiveLocked(boolean interactive) {
noteInteractiveLocked(interactive, mClock.elapsedRealtime());
}
@GuardedBy("this")
public void noteInteractiveLocked(boolean interactive, long elapsedRealtimeMs) {
if (mInteractive != interactive) {
mInteractive = interactive;
if (DEBUG) Slog.v(TAG, "Interactive: " + interactive);
if (interactive) {
mInteractiveTimer.startRunningLocked(elapsedRealtimeMs);
} else {
mInteractiveTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
}
@GuardedBy("this")
public void noteConnectivityChangedLocked(int type, String extra) {
noteConnectivityChangedLocked(type, extra,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteConnectivityChangedLocked(int type, String extra,
long elapsedRealtimeMs, long uptimeMs) {
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_CONNECTIVITY_CHANGED,
extra, type);
mNumConnectivityChange++;
}
@GuardedBy("this")
private void noteMobileRadioApWakeupLocked(final long elapsedRealtimeMillis,
final long uptimeMillis, int uid) {
uid = mapUid(uid);
addHistoryEventLocked(elapsedRealtimeMillis, uptimeMillis, HistoryItem.EVENT_WAKEUP_AP, "",
uid);
getUidStatsLocked(uid, elapsedRealtimeMillis, uptimeMillis).noteMobileRadioApWakeupLocked();
}
/**
* Updates the radio power state and returns true if an external stats collection should occur.
*/
@GuardedBy("this")
public boolean noteMobileRadioPowerStateLocked(int powerState, long timestampNs, int uid) {
return noteMobileRadioPowerStateLocked(powerState, timestampNs, uid,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public boolean noteMobileRadioPowerStateLocked(int powerState, long timestampNs, int uid,
long elapsedRealtimeMs, long uptimeMs) {
if (mMobileRadioPowerState != powerState) {
long realElapsedRealtimeMs;
final boolean active = isActiveRadioPowerState(powerState);
if (active) {
if (uid > 0) {
noteMobileRadioApWakeupLocked(elapsedRealtimeMs, uptimeMs, uid);
}
mMobileRadioActiveStartTimeMs = realElapsedRealtimeMs = timestampNs / (1000 * 1000);
mHistoryCur.states |= HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
} else {
realElapsedRealtimeMs = timestampNs / (1000*1000);
long lastUpdateTimeMs = mMobileRadioActiveStartTimeMs;
if (realElapsedRealtimeMs < lastUpdateTimeMs) {
Slog.wtf(TAG, "Data connection inactive timestamp " + realElapsedRealtimeMs
+ " is before start time " + lastUpdateTimeMs);
realElapsedRealtimeMs = elapsedRealtimeMs;
} else if (realElapsedRealtimeMs < elapsedRealtimeMs) {
mMobileRadioActiveAdjustedTime.addCountLocked(elapsedRealtimeMs
- realElapsedRealtimeMs);
}
mHistoryCur.states &= ~HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
}
if (DEBUG_HISTORY) Slog.v(TAG, "Mobile network active " + active + " to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mMobileRadioPowerState = powerState;
// Inform current RatBatteryStats that the modem active state might have changed.
getRatBatteryStatsLocked(mActiveRat).noteActive(active, elapsedRealtimeMs);
if (active) {
mMobileRadioActiveTimer.startRunningLocked(elapsedRealtimeMs);
mMobileRadioActivePerAppTimer.startRunningLocked(elapsedRealtimeMs);
} else {
mMobileRadioActiveTimer.stopRunningLocked(realElapsedRealtimeMs);
mMobileRadioActivePerAppTimer.stopRunningLocked(realElapsedRealtimeMs);
// Tell the caller to collect radio network/power stats.
return true;
}
}
return false;
}
private static boolean isActiveRadioPowerState(int powerState) {
return powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM
|| powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
}
@GuardedBy("this")
public void notePowerSaveModeLocked(boolean enabled) {
notePowerSaveModeLocked(enabled, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
/**
* Toggles the power save mode state.
*/
@GuardedBy("this")
public void notePowerSaveModeLockedInit(boolean enabled, long elapsedRealtimeMs,
long uptimeMs) {
if (mPowerSaveModeEnabled != enabled) {
notePowerSaveModeLocked(enabled, elapsedRealtimeMs, uptimeMs);
} else {
// Log an initial value for BATTERY_SAVER_MODE_STATE_CHANGED in order to
// allow the atom to read all future state changes.
FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED,
enabled
? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
: FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
}
}
@GuardedBy("this")
public void notePowerSaveModeLocked(boolean enabled, long elapsedRealtimeMs, long uptimeMs) {
if (mPowerSaveModeEnabled != enabled) {
int stepState = enabled ? STEP_LEVEL_MODE_POWER_SAVE : 0;
mModStepMode |= (mCurStepMode&STEP_LEVEL_MODE_POWER_SAVE) ^ stepState;
mCurStepMode = (mCurStepMode&~STEP_LEVEL_MODE_POWER_SAVE) | stepState;
mPowerSaveModeEnabled = enabled;
if (enabled) {
mHistoryCur.states2 |= HistoryItem.STATE2_POWER_SAVE_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Power save mode enabled to: "
+ Integer.toHexString(mHistoryCur.states2));
mPowerSaveModeEnabledTimer.startRunningLocked(elapsedRealtimeMs);
} else {
mHistoryCur.states2 &= ~HistoryItem.STATE2_POWER_SAVE_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Power save mode disabled to: "
+ Integer.toHexString(mHistoryCur.states2));
mPowerSaveModeEnabledTimer.stopRunningLocked(elapsedRealtimeMs);
}
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED,
enabled
? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
: FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
}
}
@GuardedBy("this")
public void noteDeviceIdleModeLocked(final int mode, String activeReason, int activeUid) {
noteDeviceIdleModeLocked(mode, activeReason, activeUid,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteDeviceIdleModeLocked(final int mode, String activeReason, int activeUid,
long elapsedRealtimeMs, long uptimeMs) {
boolean nowIdling = mode == DEVICE_IDLE_MODE_DEEP;
if (mDeviceIdling && !nowIdling && activeReason == null) {
// We don't go out of general idling mode until explicitly taken out of
// device idle through going active or significant motion.
nowIdling = true;
}
boolean nowLightIdling = mode == DEVICE_IDLE_MODE_LIGHT;
if (mDeviceLightIdling && !nowLightIdling && !nowIdling && activeReason == null) {
// We don't go out of general light idling mode until explicitly taken out of
// device idle through going active or significant motion.
nowLightIdling = true;
}
if (activeReason != null && (mDeviceIdling || mDeviceLightIdling)) {
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_ACTIVE,
activeReason, activeUid);
}
if (mDeviceIdling != nowIdling || mDeviceLightIdling != nowLightIdling) {
int statsmode;
if (nowIdling) statsmode = DEVICE_IDLE_MODE_DEEP;
else if (nowLightIdling) statsmode = DEVICE_IDLE_MODE_LIGHT;
else statsmode = DEVICE_IDLE_MODE_OFF;
FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_IDLING_MODE_STATE_CHANGED, statsmode);
}
if (mDeviceIdling != nowIdling) {
mDeviceIdling = nowIdling;
int stepState = nowIdling ? STEP_LEVEL_MODE_DEVICE_IDLE : 0;
mModStepMode |= (mCurStepMode&STEP_LEVEL_MODE_DEVICE_IDLE) ^ stepState;
mCurStepMode = (mCurStepMode&~STEP_LEVEL_MODE_DEVICE_IDLE) | stepState;
if (nowIdling) {
mDeviceIdlingTimer.startRunningLocked(elapsedRealtimeMs);
} else {
mDeviceIdlingTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
if (mDeviceLightIdling != nowLightIdling) {
mDeviceLightIdling = nowLightIdling;
if (nowLightIdling) {
mDeviceLightIdlingTimer.startRunningLocked(elapsedRealtimeMs);
} else {
mDeviceLightIdlingTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
if (mDeviceIdleMode != mode) {
mHistoryCur.states2 = (mHistoryCur.states2 & ~HistoryItem.STATE2_DEVICE_IDLE_MASK)
| (mode << HistoryItem.STATE2_DEVICE_IDLE_SHIFT);
if (DEBUG_HISTORY) Slog.v(TAG, "Device idle mode changed to: "
+ Integer.toHexString(mHistoryCur.states2));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
long lastDuration = elapsedRealtimeMs - mLastIdleTimeStartMs;
mLastIdleTimeStartMs = elapsedRealtimeMs;
if (mDeviceIdleMode == DEVICE_IDLE_MODE_LIGHT) {
if (lastDuration > mLongestLightIdleTimeMs) {
mLongestLightIdleTimeMs = lastDuration;
}
mDeviceIdleModeLightTimer.stopRunningLocked(elapsedRealtimeMs);
} else if (mDeviceIdleMode == DEVICE_IDLE_MODE_DEEP) {
if (lastDuration > mLongestFullIdleTimeMs) {
mLongestFullIdleTimeMs = lastDuration;
}
mDeviceIdleModeFullTimer.stopRunningLocked(elapsedRealtimeMs);
}
if (mode == DEVICE_IDLE_MODE_LIGHT) {
mDeviceIdleModeLightTimer.startRunningLocked(elapsedRealtimeMs);
} else if (mode == DEVICE_IDLE_MODE_DEEP) {
mDeviceIdleModeFullTimer.startRunningLocked(elapsedRealtimeMs);
}
mDeviceIdleMode = mode;
FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mode);
}
}
@GuardedBy("this")
public void notePackageInstalledLocked(String pkgName, long versionCode) {
notePackageInstalledLocked(pkgName, versionCode,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void notePackageInstalledLocked(String pkgName, long versionCode,
long elapsedRealtimeMs, long uptimeMs) {
// XXX need to figure out what to do with long version codes.
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_PACKAGE_INSTALLED,
pkgName, (int)versionCode);
PackageChange pc = new PackageChange();
pc.mPackageName = pkgName;
pc.mUpdate = true;
pc.mVersionCode = versionCode;
addPackageChange(pc);
}
@GuardedBy("this")
public void notePackageUninstalledLocked(String pkgName) {
notePackageUninstalledLocked(pkgName, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void notePackageUninstalledLocked(String pkgName,
long elapsedRealtimeMs, long uptimeMs) {
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs,
HistoryItem.EVENT_PACKAGE_UNINSTALLED, pkgName, 0);
PackageChange pc = new PackageChange();
pc.mPackageName = pkgName;
pc.mUpdate = true;
addPackageChange(pc);
}
private void addPackageChange(PackageChange pc) {
if (mDailyPackageChanges == null) {
mDailyPackageChanges = new ArrayList<>();
}
mDailyPackageChanges.add(pc);
}
@GuardedBy("this")
void stopAllGpsSignalQualityTimersLocked(int except) {
stopAllGpsSignalQualityTimersLocked(except, mClock.elapsedRealtime());
}
@GuardedBy("this")
void stopAllGpsSignalQualityTimersLocked(int except, long elapsedRealtimeMs) {
for (int i = 0; i < mGpsSignalQualityTimer.length; i++) {
if (i == except) {
continue;
}
while (mGpsSignalQualityTimer[i].isRunningLocked()) {
mGpsSignalQualityTimer[i].stopRunningLocked(elapsedRealtimeMs);
}
}
}
@UnsupportedAppUsage
@GuardedBy("this")
public void notePhoneOnLocked() {
notePhoneOnLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void notePhoneOnLocked(long elapsedRealtimeMs, long uptimeMs) {
if (!mPhoneOn) {
mHistoryCur.states2 |= HistoryItem.STATE2_PHONE_IN_CALL_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Phone on to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mPhoneOn = true;
mPhoneOnTimer.startRunningLocked(elapsedRealtimeMs);
}
}
@UnsupportedAppUsage
@GuardedBy("this")
public void notePhoneOffLocked() {
notePhoneOffLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void notePhoneOffLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mPhoneOn) {
mHistoryCur.states2 &= ~HistoryItem.STATE2_PHONE_IN_CALL_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Phone off to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mPhoneOn = false;
mPhoneOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
@GuardedBy("this")
private void registerUsbStateReceiver(Context context) {
final IntentFilter usbStateFilter = new IntentFilter();
usbStateFilter.addAction(UsbManager.ACTION_USB_STATE);
context.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final boolean state = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
synchronized (BatteryStatsImpl.this) {
noteUsbConnectionStateLocked(state, mClock.elapsedRealtime(),
mClock.uptimeMillis());
}
}
}, usbStateFilter);
synchronized (this) {
if (mUsbDataState == USB_DATA_UNKNOWN) {
final Intent usbState = context.registerReceiver(null, usbStateFilter);
final boolean initState = usbState != null && usbState.getBooleanExtra(
UsbManager.USB_CONNECTED, false);
noteUsbConnectionStateLocked(initState, mClock.elapsedRealtime(),
mClock.uptimeMillis());
}
}
}
@GuardedBy("this")
private void noteUsbConnectionStateLocked(boolean connected, long elapsedRealtimeMs,
long uptimeMs) {
int newState = connected ? USB_DATA_CONNECTED : USB_DATA_DISCONNECTED;
if (mUsbDataState != newState) {
mUsbDataState = newState;
if (connected) {
mHistoryCur.states2 |= HistoryItem.STATE2_USB_DATA_LINK_FLAG;
} else {
mHistoryCur.states2 &= ~HistoryItem.STATE2_USB_DATA_LINK_FLAG;
}
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
}
@GuardedBy("this")
void stopAllPhoneSignalStrengthTimersLocked(int except, long elapsedRealtimeMs) {
for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
if (i == except) {
continue;
}
while (mPhoneSignalStrengthsTimer[i].isRunningLocked()) {
mPhoneSignalStrengthsTimer[i].stopRunningLocked(elapsedRealtimeMs);
}
}
}
private int fixPhoneServiceState(int state, int signalBin) {
if (mPhoneSimStateRaw == TelephonyManager.SIM_STATE_ABSENT) {
// In this case we will always be STATE_OUT_OF_SERVICE, so need
// to infer that we are scanning from other data.
if (state == ServiceState.STATE_OUT_OF_SERVICE
&& signalBin > CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
state = ServiceState.STATE_IN_SERVICE;
}
}
return state;
}
@GuardedBy("this")
private void updateAllPhoneStateLocked(int state, int simState, int strengthBin,
long elapsedRealtimeMs, long uptimeMs) {
boolean scanning = false;
boolean newHistory = false;
mPhoneServiceStateRaw = state;
mPhoneSimStateRaw = simState;
mPhoneSignalStrengthBinRaw = strengthBin;
if (simState == TelephonyManager.SIM_STATE_ABSENT) {
// In this case we will always be STATE_OUT_OF_SERVICE, so need
// to infer that we are scanning from other data.
if (state == ServiceState.STATE_OUT_OF_SERVICE
&& strengthBin > CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
state = ServiceState.STATE_IN_SERVICE;
}
}
// If the phone is powered off, stop all timers.
if (state == ServiceState.STATE_POWER_OFF) {
strengthBin = -1;
// If we are in service, make sure the correct signal string timer is running.
} else if (state == ServiceState.STATE_IN_SERVICE) {
// Bin will be changed below.
// If we're out of service, we are in the lowest signal strength
// bin and have the scanning bit set.
} else if (state == ServiceState.STATE_OUT_OF_SERVICE) {
scanning = true;
strengthBin = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
if (!mPhoneSignalScanningTimer.isRunningLocked()) {
mHistoryCur.states |= HistoryItem.STATE_PHONE_SCANNING_FLAG;
newHistory = true;
if (DEBUG_HISTORY) Slog.v(TAG, "Phone started scanning to: "
+ Integer.toHexString(mHistoryCur.states));
mPhoneSignalScanningTimer.startRunningLocked(elapsedRealtimeMs);
FrameworkStatsLog.write(FrameworkStatsLog.PHONE_SERVICE_STATE_CHANGED, state,
simState, strengthBin);
}
}
if (!scanning) {
// If we are no longer scanning, then stop the scanning timer.
if (mPhoneSignalScanningTimer.isRunningLocked()) {
mHistoryCur.states &= ~HistoryItem.STATE_PHONE_SCANNING_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Phone stopped scanning to: "
+ Integer.toHexString(mHistoryCur.states));
newHistory = true;
mPhoneSignalScanningTimer.stopRunningLocked(elapsedRealtimeMs);
FrameworkStatsLog.write(FrameworkStatsLog.PHONE_SERVICE_STATE_CHANGED, state,
simState, strengthBin);
}
}
if (mPhoneServiceState != state) {
mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_PHONE_STATE_MASK)
| (state << HistoryItem.STATE_PHONE_STATE_SHIFT);
if (DEBUG_HISTORY) Slog.v(TAG, "Phone state " + state + " to: "
+ Integer.toHexString(mHistoryCur.states));
newHistory = true;
mPhoneServiceState = state;
}
if (mPhoneSignalStrengthBin != strengthBin) {
if (mPhoneSignalStrengthBin >= 0) {
mPhoneSignalStrengthsTimer[mPhoneSignalStrengthBin].stopRunningLocked(
elapsedRealtimeMs);
}
if (strengthBin >= 0) {
if (!mPhoneSignalStrengthsTimer[strengthBin].isRunningLocked()) {
mPhoneSignalStrengthsTimer[strengthBin].startRunningLocked(elapsedRealtimeMs);
}
mHistoryCur.states =
(mHistoryCur.states & ~HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_MASK)
| (strengthBin << HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_SHIFT);
if (DEBUG_HISTORY) Slog.v(TAG, "Signal strength " + strengthBin + " to: "
+ Integer.toHexString(mHistoryCur.states));
newHistory = true;
FrameworkStatsLog.write(
FrameworkStatsLog.PHONE_SIGNAL_STRENGTH_CHANGED, strengthBin);
} else {
stopAllPhoneSignalStrengthTimersLocked(-1, elapsedRealtimeMs);
}
mPhoneSignalStrengthBin = strengthBin;
}
if (newHistory) {
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
}
/**
* Telephony stack updates the phone state.
* @param state phone state from ServiceState.getState()
*/
@GuardedBy("this")
public void notePhoneStateLocked(int state, int simState) {
notePhoneStateLocked(state, simState, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void notePhoneStateLocked(int state, int simState,
long elapsedRealtimeMs, long uptimeMs) {
updateAllPhoneStateLocked(state, simState, mPhoneSignalStrengthBinRaw,
elapsedRealtimeMs, uptimeMs);
}
@UnsupportedAppUsage
@GuardedBy("this")
public void notePhoneSignalStrengthLocked(SignalStrength signalStrength) {
notePhoneSignalStrengthLocked(signalStrength,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void notePhoneSignalStrengthLocked(SignalStrength signalStrength,
long elapsedRealtimeMs, long uptimeMs) {
final int overallSignalStrength = signalStrength.getLevel();
final SparseIntArray perRatSignalStrength = new SparseIntArray(
BatteryStats.RADIO_ACCESS_TECHNOLOGY_COUNT);
// Extract signal strength level for each RAT.
final List<CellSignalStrength> cellSignalStrengths =
signalStrength.getCellSignalStrengths();
final int size = cellSignalStrengths.size();
for (int i = 0; i < size; i++) {
CellSignalStrength cellSignalStrength = cellSignalStrengths.get(i);
// Map each CellSignalStrength to a BatteryStats.RadioAccessTechnology
final int ratType;
final int level;
if (cellSignalStrength instanceof CellSignalStrengthNr) {
ratType = RADIO_ACCESS_TECHNOLOGY_NR;
level = cellSignalStrength.getLevel();
} else if (cellSignalStrength instanceof CellSignalStrengthLte) {
ratType = RADIO_ACCESS_TECHNOLOGY_LTE;
level = cellSignalStrength.getLevel();
} else {
ratType = RADIO_ACCESS_TECHNOLOGY_OTHER;
level = cellSignalStrength.getLevel();
}
// According to SignalStrength#getCellSignalStrengths(), multiple of the same
// cellSignalStrength can be present. Just take the highest level one for each RAT.
if (perRatSignalStrength.get(ratType, -1) < level) {
perRatSignalStrength.put(ratType, level);
}
}
notePhoneSignalStrengthLocked(overallSignalStrength, perRatSignalStrength,
elapsedRealtimeMs, uptimeMs);
}
/**
* Note phone signal strength change, including per RAT signal strength.
*
* @param signalStrength overall signal strength {@see SignalStrength#getLevel()}
* @param perRatSignalStrength signal strength of available RATs
*/
@GuardedBy("this")
public void notePhoneSignalStrengthLocked(int signalStrength,
SparseIntArray perRatSignalStrength) {
notePhoneSignalStrengthLocked(signalStrength, perRatSignalStrength,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
/**
* Note phone signal strength change, including per RAT signal strength.
*
* @param signalStrength overall signal strength {@see SignalStrength#getLevel()}
* @param perRatSignalStrength signal strength of available RATs
*/
@GuardedBy("this")
public void notePhoneSignalStrengthLocked(int signalStrength,
SparseIntArray perRatSignalStrength,
long elapsedRealtimeMs, long uptimeMs) {
// Note each RAT's signal strength.
final int size = perRatSignalStrength.size();
for (int i = 0; i < size; i++) {
final int rat = perRatSignalStrength.keyAt(i);
final int ratSignalStrength = perRatSignalStrength.valueAt(i);
getRatBatteryStatsLocked(rat).noteSignalStrength(ratSignalStrength, elapsedRealtimeMs);
}
updateAllPhoneStateLocked(mPhoneServiceStateRaw, mPhoneSimStateRaw, signalStrength,
elapsedRealtimeMs, uptimeMs);
}
@UnsupportedAppUsage
@GuardedBy("this")
public void notePhoneDataConnectionStateLocked(@NetworkType int dataType, boolean hasData,
@RegState int serviceType, @ServiceState.FrequencyRange int nrFrequency) {
notePhoneDataConnectionStateLocked(dataType, hasData, serviceType, nrFrequency,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void notePhoneDataConnectionStateLocked(@NetworkType int dataType, boolean hasData,
@RegState int serviceType, @ServiceState.FrequencyRange int nrFrequency,
long elapsedRealtimeMs, long uptimeMs) {
// BatteryStats uses 0 to represent no network type.
// Telephony does not have a concept of no network type, and uses 0 to represent unknown.
// Unknown is included in DATA_CONNECTION_OTHER.
int bin = DATA_CONNECTION_OUT_OF_SERVICE;
if (hasData) {
if (dataType > 0 && dataType <= TelephonyManager.getAllNetworkTypes().length) {
bin = dataType;
} else {
switch (serviceType) {
case ServiceState.STATE_OUT_OF_SERVICE:
bin = DATA_CONNECTION_OUT_OF_SERVICE;
break;
case ServiceState.STATE_EMERGENCY_ONLY:
bin = DATA_CONNECTION_EMERGENCY_SERVICE;
break;
default:
bin = DATA_CONNECTION_OTHER;
break;
}
}
}
final int newRat = mapNetworkTypeToRadioAccessTechnology(bin);
if (newRat == RADIO_ACCESS_TECHNOLOGY_NR) {
// Note possible frequency change for the NR RAT.
getRatBatteryStatsLocked(newRat).noteFrequencyRange(nrFrequency, elapsedRealtimeMs);
}
if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData);
if (mPhoneDataConnectionType != bin) {
mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_DATA_CONNECTION_MASK)
| (bin << HistoryItem.STATE_DATA_CONNECTION_SHIFT);
if (DEBUG_HISTORY) Slog.v(TAG, "Data connection " + bin + " to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
if (mPhoneDataConnectionType >= 0) {
mPhoneDataConnectionsTimer[mPhoneDataConnectionType].stopRunningLocked(
elapsedRealtimeMs);
}
mPhoneDataConnectionType = bin;
mPhoneDataConnectionsTimer[bin].startRunningLocked(elapsedRealtimeMs);
if (mActiveRat != newRat) {
getRatBatteryStatsLocked(mActiveRat).noteActive(false, elapsedRealtimeMs);
mActiveRat = newRat;
}
final boolean modemActive = mMobileRadioActiveTimer.isRunningLocked();
getRatBatteryStatsLocked(newRat).noteActive(modemActive, elapsedRealtimeMs);
}
}
@RadioAccessTechnology
private static int mapNetworkTypeToRadioAccessTechnology(@NetworkType int dataType) {
switch (dataType) {
case TelephonyManager.NETWORK_TYPE_NR:
return RADIO_ACCESS_TECHNOLOGY_NR;
case TelephonyManager.NETWORK_TYPE_LTE:
return RADIO_ACCESS_TECHNOLOGY_LTE;
case TelephonyManager.NETWORK_TYPE_UNKNOWN: //fallthrough
case TelephonyManager.NETWORK_TYPE_GPRS: //fallthrough
case TelephonyManager.NETWORK_TYPE_EDGE: //fallthrough
case TelephonyManager.NETWORK_TYPE_UMTS: //fallthrough
case TelephonyManager.NETWORK_TYPE_CDMA: //fallthrough
case TelephonyManager.NETWORK_TYPE_EVDO_0: //fallthrough
case TelephonyManager.NETWORK_TYPE_EVDO_A: //fallthrough
case TelephonyManager.NETWORK_TYPE_1xRTT: //fallthrough
case TelephonyManager.NETWORK_TYPE_HSDPA: //fallthrough
case TelephonyManager.NETWORK_TYPE_HSUPA: //fallthrough
case TelephonyManager.NETWORK_TYPE_HSPA: //fallthrough
case TelephonyManager.NETWORK_TYPE_IDEN: //fallthrough
case TelephonyManager.NETWORK_TYPE_EVDO_B: //fallthrough
case TelephonyManager.NETWORK_TYPE_EHRPD: //fallthrough
case TelephonyManager.NETWORK_TYPE_HSPAP: //fallthrough
case TelephonyManager.NETWORK_TYPE_GSM: //fallthrough
case TelephonyManager.NETWORK_TYPE_TD_SCDMA: //fallthrough
case TelephonyManager.NETWORK_TYPE_IWLAN: //fallthrough
return RADIO_ACCESS_TECHNOLOGY_OTHER;
default:
Slog.w(TAG, "Unhandled NetworkType (" + dataType + "), mapping to OTHER");
return RADIO_ACCESS_TECHNOLOGY_OTHER;
}
}
@RadioAccessTechnology
private static int mapRadioAccessNetworkTypeToRadioAccessTechnology(
@AccessNetworkConstants.RadioAccessNetworkType int dataType) {
switch (dataType) {
case AccessNetworkConstants.AccessNetworkType.NGRAN:
return RADIO_ACCESS_TECHNOLOGY_NR;
case AccessNetworkConstants.AccessNetworkType.EUTRAN:
return RADIO_ACCESS_TECHNOLOGY_LTE;
case AccessNetworkConstants.AccessNetworkType.UNKNOWN: //fallthrough
case AccessNetworkConstants.AccessNetworkType.GERAN: //fallthrough
case AccessNetworkConstants.AccessNetworkType.UTRAN: //fallthrough
case AccessNetworkConstants.AccessNetworkType.CDMA2000: //fallthrough
case AccessNetworkConstants.AccessNetworkType.IWLAN:
return RADIO_ACCESS_TECHNOLOGY_OTHER;
default:
Slog.w(TAG,
"Unhandled RadioAccessNetworkType (" + dataType + "), mapping to OTHER");
return RADIO_ACCESS_TECHNOLOGY_OTHER;
}
}
@GuardedBy("this")
public void noteWifiOnLocked() {
noteWifiOnLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteWifiOnLocked(long elapsedRealtimeMs, long uptimeMs) {
if (!mWifiOn) {
mHistoryCur.states2 |= HistoryItem.STATE2_WIFI_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI on to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mWifiOn = true;
mWifiOnTimer.startRunningLocked(elapsedRealtimeMs);
scheduleSyncExternalStatsLocked("wifi-off", ExternalStatsSync.UPDATE_WIFI);
}
}
@GuardedBy("this")
public void noteWifiOffLocked() {
noteWifiOffLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteWifiOffLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mWifiOn) {
mHistoryCur.states2 &= ~HistoryItem.STATE2_WIFI_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI off to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mWifiOn = false;
mWifiOnTimer.stopRunningLocked(elapsedRealtimeMs);
scheduleSyncExternalStatsLocked("wifi-on", ExternalStatsSync.UPDATE_WIFI);
}
}
@UnsupportedAppUsage
@GuardedBy("this")
public void noteAudioOnLocked(int uid) {
noteAudioOnLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteAudioOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mAudioOnNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_AUDIO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Audio on to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mAudioOnTimer.startRunningLocked(elapsedRealtimeMs);
}
mAudioOnNesting++;
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteAudioTurnedOnLocked(elapsedRealtimeMs);
}
@UnsupportedAppUsage
@GuardedBy("this")
public void noteAudioOffLocked(int uid) {
noteAudioOffLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteAudioOffLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
if (mAudioOnNesting == 0) {
return;
}
uid = mapUid(uid);
if (--mAudioOnNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_AUDIO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Audio off to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mAudioOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteAudioTurnedOffLocked(elapsedRealtimeMs);
}
@UnsupportedAppUsage
@GuardedBy("this")
public void noteVideoOnLocked(int uid) {
noteVideoOnLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteVideoOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mVideoOnNesting == 0) {
mHistoryCur.states2 |= HistoryItem.STATE2_VIDEO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Video on to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mVideoOnTimer.startRunningLocked(elapsedRealtimeMs);
}
mVideoOnNesting++;
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteVideoTurnedOnLocked(elapsedRealtimeMs);
}
@UnsupportedAppUsage
@GuardedBy("this")
public void noteVideoOffLocked(int uid) {
noteVideoOffLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteVideoOffLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
if (mVideoOnNesting == 0) {
return;
}
uid = mapUid(uid);
if (--mVideoOnNesting == 0) {
mHistoryCur.states2 &= ~HistoryItem.STATE2_VIDEO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mVideoOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteVideoTurnedOffLocked(elapsedRealtimeMs);
}
@GuardedBy("this")
public void noteResetAudioLocked() {
noteResetAudioLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteResetAudioLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mAudioOnNesting > 0) {
mAudioOnNesting = 0;
mHistoryCur.states &= ~HistoryItem.STATE_AUDIO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Audio off to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mAudioOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
for (int i=0; i<mUidStats.size(); i++) {
BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
uid.noteResetAudioLocked(elapsedRealtimeMs);
}
}
}
@GuardedBy("this")
public void noteResetVideoLocked() {
noteResetVideoLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteResetVideoLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mVideoOnNesting > 0) {
mVideoOnNesting = 0;
mHistoryCur.states2 &= ~HistoryItem.STATE2_VIDEO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mVideoOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
for (int i=0; i<mUidStats.size(); i++) {
BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
uid.noteResetVideoLocked(elapsedRealtimeMs);
}
}
}
@GuardedBy("this")
public void noteActivityResumedLocked(int uid) {
noteActivityResumedLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteActivityResumedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteActivityResumedLocked(elapsedRealtimeMs);
}
@GuardedBy("this")
public void noteActivityPausedLocked(int uid) {
noteActivityPausedLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteActivityPausedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteActivityPausedLocked(elapsedRealtimeMs);
}
@GuardedBy("this")
public void noteVibratorOnLocked(int uid, long durationMillis) {
noteVibratorOnLocked(uid, durationMillis,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteVibratorOnLocked(int uid, long durationMillis,
long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteVibratorOnLocked(durationMillis, elapsedRealtimeMs);
}
@GuardedBy("this")
public void noteVibratorOffLocked(int uid) {
noteVibratorOffLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteVibratorOffLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteVibratorOffLocked(elapsedRealtimeMs);
}
@GuardedBy("this")
public void noteFlashlightOnLocked(int uid) {
noteFlashlightOnLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteFlashlightOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mFlashlightOnNesting++ == 0) {
mHistoryCur.states2 |= HistoryItem.STATE2_FLASHLIGHT_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Flashlight on to: "
+ Integer.toHexString(mHistoryCur.states2));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mFlashlightOnTimer.startRunningLocked(elapsedRealtimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteFlashlightTurnedOnLocked(elapsedRealtimeMs);
}
@GuardedBy("this")
public void noteFlashlightOffLocked(int uid) {
noteFlashlightOffLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteFlashlightOffLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
if (mFlashlightOnNesting == 0) {
return;
}
uid = mapUid(uid);
if (--mFlashlightOnNesting == 0) {
mHistoryCur.states2 &= ~HistoryItem.STATE2_FLASHLIGHT_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Flashlight off to: "
+ Integer.toHexString(mHistoryCur.states2));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mFlashlightOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteFlashlightTurnedOffLocked(elapsedRealtimeMs);
}
@GuardedBy("this")
public void noteCameraOnLocked(int uid) {
noteCameraOnLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteCameraOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mCameraOnNesting++ == 0) {
mHistoryCur.states2 |= HistoryItem.STATE2_CAMERA_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Camera on to: "
+ Integer.toHexString(mHistoryCur.states2));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mCameraOnTimer.startRunningLocked(elapsedRealtimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteCameraTurnedOnLocked(elapsedRealtimeMs);
}
@GuardedBy("this")
public void noteCameraOffLocked(int uid) {
noteCameraOffLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteCameraOffLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
if (mCameraOnNesting == 0) {
return;
}
uid = mapUid(uid);
if (--mCameraOnNesting == 0) {
mHistoryCur.states2 &= ~HistoryItem.STATE2_CAMERA_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Camera off to: "
+ Integer.toHexString(mHistoryCur.states2));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mCameraOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteCameraTurnedOffLocked(elapsedRealtimeMs);
}
@GuardedBy("this")
public void noteResetCameraLocked() {
noteResetCameraLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteResetCameraLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mCameraOnNesting > 0) {
mCameraOnNesting = 0;
mHistoryCur.states2 &= ~HistoryItem.STATE2_CAMERA_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Camera off to: "
+ Integer.toHexString(mHistoryCur.states2));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mCameraOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
for (int i=0; i<mUidStats.size(); i++) {
BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
uid.noteResetCameraLocked(elapsedRealtimeMs);
}
}
}
@GuardedBy("this")
public void noteResetFlashlightLocked() {
noteResetFlashlightLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteResetFlashlightLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mFlashlightOnNesting > 0) {
mFlashlightOnNesting = 0;
mHistoryCur.states2 &= ~HistoryItem.STATE2_FLASHLIGHT_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Flashlight off to: "
+ Integer.toHexString(mHistoryCur.states2));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mFlashlightOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
for (int i=0; i<mUidStats.size(); i++) {
BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
uid.noteResetFlashlightLocked(elapsedRealtimeMs);
}
}
}
@GuardedBy("this")
private void noteBluetoothScanStartedLocked(WorkChain workChain, int uid,
boolean isUnoptimized, long elapsedRealtimeMs, long uptimeMs) {
if (workChain != null) {
uid = workChain.getAttributionUid();
}
uid = mapUid(uid);
if (mBluetoothScanNesting == 0) {
mHistoryCur.states2 |= HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "BLE scan started for: "
+ Integer.toHexString(mHistoryCur.states2));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mBluetoothScanTimer.startRunningLocked(elapsedRealtimeMs);
}
mBluetoothScanNesting++;
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteBluetoothScanStartedLocked(elapsedRealtimeMs, isUnoptimized);
}
@GuardedBy("this")
public void noteBluetoothScanStartedFromSourceLocked(WorkSource ws, boolean isUnoptimized) {
noteBluetoothScanStartedFromSourceLocked(ws, isUnoptimized,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteBluetoothScanStartedFromSourceLocked(WorkSource ws, boolean isUnoptimized,
long elapsedRealtimeMs, long uptimeMs) {
final int N = ws.size();
for (int i = 0; i < N; i++) {
noteBluetoothScanStartedLocked(null, ws.getUid(i), isUnoptimized,
elapsedRealtimeMs, uptimeMs);
}
final List<WorkChain> workChains = ws.getWorkChains();
if (workChains != null) {
for (int i = 0; i < workChains.size(); ++i) {
noteBluetoothScanStartedLocked(workChains.get(i), -1, isUnoptimized,
elapsedRealtimeMs, uptimeMs);
}
}
}
@GuardedBy("this")
private void noteBluetoothScanStoppedLocked(WorkChain workChain, int uid,
boolean isUnoptimized, long elapsedRealtimeMs, long uptimeMs) {
if (workChain != null) {
uid = workChain.getAttributionUid();
}
uid = mapUid(uid);
mBluetoothScanNesting--;
if (mBluetoothScanNesting == 0) {
mHistoryCur.states2 &= ~HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "BLE scan stopped for: "
+ Integer.toHexString(mHistoryCur.states2));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mBluetoothScanTimer.stopRunningLocked(elapsedRealtimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteBluetoothScanStoppedLocked(elapsedRealtimeMs, isUnoptimized);
}
@GuardedBy("this")
public void noteBluetoothScanStoppedFromSourceLocked(WorkSource ws, boolean isUnoptimized) {
noteBluetoothScanStoppedFromSourceLocked(ws, isUnoptimized,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteBluetoothScanStoppedFromSourceLocked(WorkSource ws, boolean isUnoptimized,
long elapsedRealtimeMs, long uptimeMs) {
final int N = ws.size();
for (int i = 0; i < N; i++) {
noteBluetoothScanStoppedLocked(null, ws.getUid(i), isUnoptimized,
elapsedRealtimeMs, uptimeMs);
}
final List<WorkChain> workChains = ws.getWorkChains();
if (workChains != null) {
for (int i = 0; i < workChains.size(); ++i) {
noteBluetoothScanStoppedLocked(workChains.get(i), -1, isUnoptimized,
elapsedRealtimeMs, uptimeMs);
}
}
}
@GuardedBy("this")
public void noteResetBluetoothScanLocked() {
noteResetBluetoothScanLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteResetBluetoothScanLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mBluetoothScanNesting > 0) {
mBluetoothScanNesting = 0;
mHistoryCur.states2 &= ~HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "BLE can stopped for: "
+ Integer.toHexString(mHistoryCur.states2));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mBluetoothScanTimer.stopAllRunningLocked(elapsedRealtimeMs);
for (int i=0; i<mUidStats.size(); i++) {
BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
uid.noteResetBluetoothScanLocked(elapsedRealtimeMs);
}
}
}
@GuardedBy("this")
public void noteBluetoothScanResultsFromSourceLocked(WorkSource ws, int numNewResults) {
noteBluetoothScanResultsFromSourceLocked(ws, numNewResults,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteBluetoothScanResultsFromSourceLocked(WorkSource ws, int numNewResults,
long elapsedRealtimeMs, long uptimeMs) {
final int N = ws.size();
for (int i = 0; i < N; i++) {
int uid = mapUid(ws.getUid(i));
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteBluetoothScanResultsLocked(numNewResults);
}
final List<WorkChain> workChains = ws.getWorkChains();
if (workChains != null) {
for (int i = 0; i < workChains.size(); ++i) {
final WorkChain wc = workChains.get(i);
int uid = mapUid(wc.getAttributionUid());
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteBluetoothScanResultsLocked(numNewResults);
}
}
}
@GuardedBy("this")
private void noteWifiRadioApWakeupLocked(final long elapsedRealtimeMillis,
final long uptimeMillis, int uid) {
uid = mapUid(uid);
addHistoryEventLocked(elapsedRealtimeMillis, uptimeMillis, HistoryItem.EVENT_WAKEUP_AP, "",
uid);
getUidStatsLocked(uid, elapsedRealtimeMillis, uptimeMillis).noteWifiRadioApWakeupLocked();
}
@GuardedBy("this")
public void noteWifiRadioPowerState(int powerState, long timestampNs, int uid) {
noteWifiRadioPowerState(powerState, timestampNs, uid,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteWifiRadioPowerState(int powerState, long timestampNs, int uid,
long elapsedRealtimeMs, long uptimeMs) {
if (mWifiRadioPowerState != powerState) {
final boolean active =
powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM
|| powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
if (active) {
if (uid > 0) {
noteWifiRadioApWakeupLocked(elapsedRealtimeMs, uptimeMs, uid);
}
mHistoryCur.states |= HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG;
mWifiActiveTimer.startRunningLocked(elapsedRealtimeMs);
} else {
mHistoryCur.states &= ~HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG;
mWifiActiveTimer.stopRunningLocked(timestampNs / (1000 * 1000));
}
if (DEBUG_HISTORY) Slog.v(TAG, "Wifi network active " + active + " to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mWifiRadioPowerState = powerState;
}
}
@GuardedBy("this")
public void noteWifiRunningLocked(WorkSource ws) {
noteWifiRunningLocked(ws, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteWifiRunningLocked(WorkSource ws, long elapsedRealtimeMs, long uptimeMs) {
if (!mGlobalWifiRunning) {
mHistoryCur.states2 |= HistoryItem.STATE2_WIFI_RUNNING_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI running to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mGlobalWifiRunning = true;
mGlobalWifiRunningTimer.startRunningLocked(elapsedRealtimeMs);
int N = ws.size();
for (int i=0; i<N; i++) {
int uid = mapUid(ws.getUid(i));
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteWifiRunningLocked(elapsedRealtimeMs);
}
List<WorkChain> workChains = ws.getWorkChains();
if (workChains != null) {
for (int i = 0; i < workChains.size(); ++i) {
int uid = mapUid(workChains.get(i).getAttributionUid());
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteWifiRunningLocked(elapsedRealtimeMs);
}
}
scheduleSyncExternalStatsLocked("wifi-running", ExternalStatsSync.UPDATE_WIFI);
} else {
Log.w(TAG, "noteWifiRunningLocked -- called while WIFI running");
}
}
@GuardedBy("this")
public void noteWifiRunningChangedLocked(WorkSource oldWs, WorkSource newWs) {
noteWifiRunningChangedLocked(oldWs, newWs,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteWifiRunningChangedLocked(WorkSource oldWs, WorkSource newWs,
long elapsedRealtimeMs, long uptimeMs) {
if (mGlobalWifiRunning) {
int N = oldWs.size();
for (int i=0; i<N; i++) {
int uid = mapUid(oldWs.getUid(i));
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteWifiStoppedLocked(elapsedRealtimeMs);
}
List<WorkChain> workChains = oldWs.getWorkChains();
if (workChains != null) {
for (int i = 0; i < workChains.size(); ++i) {
int uid = mapUid(workChains.get(i).getAttributionUid());
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteWifiStoppedLocked(elapsedRealtimeMs);
}
}
N = newWs.size();
for (int i=0; i<N; i++) {
int uid = mapUid(newWs.getUid(i));
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteWifiRunningLocked(elapsedRealtimeMs);
}
workChains = newWs.getWorkChains();
if (workChains != null) {
for (int i = 0; i < workChains.size(); ++i) {
int uid = mapUid(workChains.get(i).getAttributionUid());
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteWifiRunningLocked(elapsedRealtimeMs);
}
}
} else {
Log.w(TAG, "noteWifiRunningChangedLocked -- called while WIFI not running");
}
}
@GuardedBy("this")
public void noteWifiStoppedLocked(WorkSource ws) {
noteWifiStoppedLocked(ws, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteWifiStoppedLocked(WorkSource ws, long elapsedRealtimeMs, long uptimeMs) {
if (mGlobalWifiRunning) {
mHistoryCur.states2 &= ~HistoryItem.STATE2_WIFI_RUNNING_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI stopped to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mGlobalWifiRunning = false;
mGlobalWifiRunningTimer.stopRunningLocked(elapsedRealtimeMs);
int N = ws.size();
for (int i=0; i<N; i++) {
int uid = mapUid(ws.getUid(i));
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteWifiStoppedLocked(elapsedRealtimeMs);
}
List<WorkChain> workChains = ws.getWorkChains();
if (workChains != null) {
for (int i = 0; i < workChains.size(); ++i) {
int uid = mapUid(workChains.get(i).getAttributionUid());
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteWifiStoppedLocked(elapsedRealtimeMs);
}
}
scheduleSyncExternalStatsLocked("wifi-stopped", ExternalStatsSync.UPDATE_WIFI);
} else {
Log.w(TAG, "noteWifiStoppedLocked -- called while WIFI not running");
}
}
@GuardedBy("this")
public void noteWifiStateLocked(int wifiState, String accessPoint) {
noteWifiStateLocked(wifiState, accessPoint, mClock.elapsedRealtime());
}
@GuardedBy("this")
public void noteWifiStateLocked(int wifiState, String accessPoint, long elapsedRealtimeMs) {
if (DEBUG) Log.i(TAG, "WiFi state -> " + wifiState);
if (mWifiState != wifiState) {
if (mWifiState >= 0) {
mWifiStateTimer[mWifiState].stopRunningLocked(elapsedRealtimeMs);
}
mWifiState = wifiState;
mWifiStateTimer[wifiState].startRunningLocked(elapsedRealtimeMs);
scheduleSyncExternalStatsLocked("wifi-state", ExternalStatsSync.UPDATE_WIFI);
}
}
@GuardedBy("this")
public void noteWifiSupplicantStateChangedLocked(int supplState, boolean failedAuth) {
noteWifiSupplicantStateChangedLocked(supplState, failedAuth,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteWifiSupplicantStateChangedLocked(int supplState, boolean failedAuth,
long elapsedRealtimeMs, long uptimeMs) {
if (DEBUG) Log.i(TAG, "WiFi suppl state -> " + supplState);
if (mWifiSupplState != supplState) {
if (mWifiSupplState >= 0) {
mWifiSupplStateTimer[mWifiSupplState].stopRunningLocked(elapsedRealtimeMs);
}
mWifiSupplState = supplState;
mWifiSupplStateTimer[supplState].startRunningLocked(elapsedRealtimeMs);
mHistoryCur.states2 =
(mHistoryCur.states2&~HistoryItem.STATE2_WIFI_SUPPL_STATE_MASK)
| (supplState << HistoryItem.STATE2_WIFI_SUPPL_STATE_SHIFT);
if (DEBUG_HISTORY) Slog.v(TAG, "Wifi suppl state " + supplState + " to: "
+ Integer.toHexString(mHistoryCur.states2));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
}
@GuardedBy("this")
void stopAllWifiSignalStrengthTimersLocked(int except, long elapsedRealtimeMs) {
for (int i = 0; i < NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
if (i == except) {
continue;
}
while (mWifiSignalStrengthsTimer[i].isRunningLocked()) {
mWifiSignalStrengthsTimer[i].stopRunningLocked(elapsedRealtimeMs);
}
}
}
@GuardedBy("this")
public void noteWifiRssiChangedLocked(int newRssi) {
noteWifiRssiChangedLocked(newRssi, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteWifiRssiChangedLocked(int newRssi, long elapsedRealtimeMs, long uptimeMs) {
int strengthBin = WifiManager.calculateSignalLevel(newRssi, NUM_WIFI_SIGNAL_STRENGTH_BINS);
if (DEBUG) Log.i(TAG, "WiFi rssi -> " + newRssi + " bin=" + strengthBin);
if (mWifiSignalStrengthBin != strengthBin) {
if (mWifiSignalStrengthBin >= 0) {
mWifiSignalStrengthsTimer[mWifiSignalStrengthBin].stopRunningLocked(
elapsedRealtimeMs);
}
if (strengthBin >= 0) {
if (!mWifiSignalStrengthsTimer[strengthBin].isRunningLocked()) {
mWifiSignalStrengthsTimer[strengthBin].startRunningLocked(elapsedRealtimeMs);
}
mHistoryCur.states2 =
(mHistoryCur.states2&~HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK)
| (strengthBin << HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_SHIFT);
if (DEBUG_HISTORY) Slog.v(TAG, "Wifi signal strength " + strengthBin + " to: "
+ Integer.toHexString(mHistoryCur.states2));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
} else {
stopAllWifiSignalStrengthTimersLocked(-1, elapsedRealtimeMs);
}
mWifiSignalStrengthBin = strengthBin;
}
}
int mWifiFullLockNesting = 0;
@UnsupportedAppUsage
@GuardedBy("this")
public void noteFullWifiLockAcquiredLocked(int uid) {
noteFullWifiLockAcquiredLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteFullWifiLockAcquiredLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
if (mWifiFullLockNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_WIFI_FULL_LOCK_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock on to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
mWifiFullLockNesting++;
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteFullWifiLockAcquiredLocked(elapsedRealtimeMs);
}
@UnsupportedAppUsage
@GuardedBy("this")
public void noteFullWifiLockReleasedLocked(int uid) {
noteFullWifiLockReleasedLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteFullWifiLockReleasedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
mWifiFullLockNesting--;
if (mWifiFullLockNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_WIFI_FULL_LOCK_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock off to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteFullWifiLockReleasedLocked(elapsedRealtimeMs);
}
int mWifiScanNesting = 0;
@GuardedBy("this")
public void noteWifiScanStartedLocked(int uid) {
noteWifiScanStartedLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteWifiScanStartedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
if (mWifiScanNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_WIFI_SCAN_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan started for: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
mWifiScanNesting++;
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteWifiScanStartedLocked(elapsedRealtimeMs);
}
@GuardedBy("this")
public void noteWifiScanStoppedLocked(int uid) {
noteWifiScanStoppedLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteWifiScanStoppedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
mWifiScanNesting--;
if (mWifiScanNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_WIFI_SCAN_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan stopped for: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteWifiScanStoppedLocked(elapsedRealtimeMs);
}
public void noteWifiBatchedScanStartedLocked(int uid, int csph) {
noteWifiBatchedScanStartedLocked(uid, csph,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteWifiBatchedScanStartedLocked(int uid, int csph,
long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteWifiBatchedScanStartedLocked(csph, elapsedRealtimeMs);
}
public void noteWifiBatchedScanStoppedLocked(int uid) {
noteWifiBatchedScanStoppedLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteWifiBatchedScanStoppedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteWifiBatchedScanStoppedLocked(elapsedRealtimeMs);
}
int mWifiMulticastNesting = 0;
@GuardedBy("this")
@UnsupportedAppUsage
public void noteWifiMulticastEnabledLocked(int uid) {
noteWifiMulticastEnabledLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteWifiMulticastEnabledLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mWifiMulticastNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast on to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
// Start Wifi Multicast overall timer
if (!mWifiMulticastWakelockTimer.isRunningLocked()) {
if (DEBUG_HISTORY) Slog.v(TAG, "WiFi Multicast Overall Timer Started");
mWifiMulticastWakelockTimer.startRunningLocked(elapsedRealtimeMs);
}
}
mWifiMulticastNesting++;
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteWifiMulticastEnabledLocked(elapsedRealtimeMs);
}
@UnsupportedAppUsage
@GuardedBy("this")
public void noteWifiMulticastDisabledLocked(int uid) {
noteWifiMulticastDisabledLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteWifiMulticastDisabledLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
mWifiMulticastNesting--;
if (mWifiMulticastNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast off to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
// Stop Wifi Multicast overall timer
if (mWifiMulticastWakelockTimer.isRunningLocked()) {
if (DEBUG_HISTORY) Slog.v(TAG, "Multicast Overall Timer Stopped");
mWifiMulticastWakelockTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteWifiMulticastDisabledLocked(elapsedRealtimeMs);
}
@GuardedBy("this")
public void noteFullWifiLockAcquiredFromSourceLocked(WorkSource ws) {
noteFullWifiLockAcquiredFromSourceLocked(ws,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteFullWifiLockAcquiredFromSourceLocked(WorkSource ws,
long elapsedRealtimeMs, long uptimeMs) {
int N = ws.size();
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.getUid(i));
noteFullWifiLockAcquiredLocked(uid, elapsedRealtimeMs, uptimeMs);
}
final List<WorkChain> workChains = ws.getWorkChains();
if (workChains != null) {
for (int i = 0; i < workChains.size(); ++i) {
final WorkChain workChain = workChains.get(i);
final int uid = mapUid(workChain.getAttributionUid());
noteFullWifiLockAcquiredLocked(uid, elapsedRealtimeMs, uptimeMs);
}
}
}
@GuardedBy("this")
public void noteFullWifiLockReleasedFromSourceLocked(WorkSource ws) {
noteFullWifiLockReleasedFromSourceLocked(ws,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteFullWifiLockReleasedFromSourceLocked(WorkSource ws,
long elapsedRealtimeMs, long uptimeMs) {
int N = ws.size();
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.getUid(i));
noteFullWifiLockReleasedLocked(uid, elapsedRealtimeMs, uptimeMs);
}
final List<WorkChain> workChains = ws.getWorkChains();
if (workChains != null) {
for (int i = 0; i < workChains.size(); ++i) {
final WorkChain workChain = workChains.get(i);
final int uid = mapUid(workChain.getAttributionUid());
noteFullWifiLockReleasedLocked(uid, elapsedRealtimeMs, uptimeMs);
}
}
}
@GuardedBy("this")
public void noteWifiScanStartedFromSourceLocked(WorkSource ws) {
noteWifiScanStartedFromSourceLocked(ws, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteWifiScanStartedFromSourceLocked(WorkSource ws,
long elapsedRealtimeMs, long uptimeMs) {
int N = ws.size();
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.getUid(i));
noteWifiScanStartedLocked(uid, elapsedRealtimeMs, uptimeMs);
}
final List<WorkChain> workChains = ws.getWorkChains();
if (workChains != null) {
for (int i = 0; i < workChains.size(); ++i) {
final WorkChain workChain = workChains.get(i);
final int uid = mapUid(workChain.getAttributionUid());
noteWifiScanStartedLocked(uid, elapsedRealtimeMs, uptimeMs);
}
}
}
@GuardedBy("this")
public void noteWifiScanStoppedFromSourceLocked(WorkSource ws) {
noteWifiScanStoppedFromSourceLocked(ws, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteWifiScanStoppedFromSourceLocked(WorkSource ws,
long elapsedRealtimeMs, long uptimeMs) {
int N = ws.size();
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.getUid(i));
noteWifiScanStoppedLocked(uid, elapsedRealtimeMs, uptimeMs);
}
final List<WorkChain> workChains = ws.getWorkChains();
if (workChains != null) {
for (int i = 0; i < workChains.size(); ++i) {
final WorkChain workChain = workChains.get(i);
final int uid = mapUid(workChain.getAttributionUid());
noteWifiScanStoppedLocked(uid, elapsedRealtimeMs, uptimeMs);
}
}
}
@GuardedBy("this")
public void noteWifiBatchedScanStartedFromSourceLocked(WorkSource ws, int csph) {
noteWifiBatchedScanStartedFromSourceLocked(ws, csph,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteWifiBatchedScanStartedFromSourceLocked(WorkSource ws, int csph,
long elapsedRealtimeMs, long uptimeMs) {
int N = ws.size();
for (int i=0; i<N; i++) {
noteWifiBatchedScanStartedLocked(ws.getUid(i), csph, elapsedRealtimeMs, uptimeMs);
}
final List<WorkChain> workChains = ws.getWorkChains();
if (workChains != null) {
for (int i = 0; i < workChains.size(); ++i) {
noteWifiBatchedScanStartedLocked(workChains.get(i).getAttributionUid(), csph,
elapsedRealtimeMs, uptimeMs);
}
}
}
@GuardedBy("this")
public void noteWifiBatchedScanStoppedFromSourceLocked(WorkSource ws) {
noteWifiBatchedScanStoppedFromSourceLocked(ws,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@GuardedBy("this")
public void noteWifiBatchedScanStoppedFromSourceLocked(WorkSource ws,
long elapsedRealtimeMs, long uptimeMs) {
int N = ws.size();
for (int i=0; i<N; i++) {
noteWifiBatchedScanStoppedLocked(ws.getUid(i), elapsedRealtimeMs, uptimeMs);
}
final List<WorkChain> workChains = ws.getWorkChains();
if (workChains != null) {
for (int i = 0; i < workChains.size(); ++i) {
noteWifiBatchedScanStoppedLocked(workChains.get(i).getAttributionUid(),
elapsedRealtimeMs, uptimeMs);
}
}
}
private static String[] includeInStringArray(String[] array, String str) {
if (ArrayUtils.indexOf(array, str) >= 0) {
return array;
}
String[] newArray = new String[array.length+1];
System.arraycopy(array, 0, newArray, 0, array.length);
newArray[array.length] = str;
return newArray;
}
private static String[] excludeFromStringArray(String[] array, String str) {
int index = ArrayUtils.indexOf(array, str);
if (index >= 0) {
String[] newArray = new String[array.length-1];
if (index > 0) {
System.arraycopy(array, 0, newArray, 0, index);
}
if (index < array.length-1) {
System.arraycopy(array, index+1, newArray, index, array.length-index-1);
}
return newArray;
}
return array;
}
/** @hide */
public void noteNetworkInterfaceForTransports(String iface, int[] transportTypes) {
if (TextUtils.isEmpty(iface)) return;
final int displayTransport = NetworkCapabilitiesUtils.getDisplayTransport(transportTypes);
synchronized (mModemNetworkLock) {
if (displayTransport == TRANSPORT_CELLULAR) {
mModemIfaces = includeInStringArray(mModemIfaces, iface);
if (DEBUG) Slog.d(TAG, "Note mobile iface " + iface + ": " + mModemIfaces);
} else {
mModemIfaces = excludeFromStringArray(mModemIfaces, iface);
if (DEBUG) Slog.d(TAG, "Note non-mobile iface " + iface + ": " + mModemIfaces);
}
}
synchronized (mWifiNetworkLock) {
if (displayTransport == TRANSPORT_WIFI) {
mWifiIfaces = includeInStringArray(mWifiIfaces, iface);
if (DEBUG) Slog.d(TAG, "Note wifi iface " + iface + ": " + mWifiIfaces);
} else {
mWifiIfaces = excludeFromStringArray(mWifiIfaces, iface);
if (DEBUG) Slog.d(TAG, "Note non-wifi iface " + iface + ": " + mWifiIfaces);
}
}
}
/**
* Records timing data related to an incoming Binder call in order to attribute
* the power consumption to the calling app.
*/
public void noteBinderCallStats(int workSourceUid, long incrementalCallCount,
Collection<BinderCallsStats.CallStat> callStats) {
noteBinderCallStats(workSourceUid, incrementalCallCount, callStats,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteBinderCallStats(int workSourceUid, long incrementalCallCount,
Collection<BinderCallsStats.CallStat> callStats,
long elapsedRealtimeMs, long uptimeMs) {
synchronized (this) {
getUidStatsLocked(workSourceUid, elapsedRealtimeMs, uptimeMs)
.noteBinderCallStatsLocked(incrementalCallCount, callStats);
}
}
/**
* Takes note of native IDs of threads taking incoming binder calls. The CPU time
* of these threads is attributed to the apps making those binder calls.
*/
public void noteBinderThreadNativeIds(int[] binderThreadNativeTids) {
mSystemServerCpuThreadReader.setBinderThreadNativeTids(binderThreadNativeTids);
}
/**
* Estimates the proportion of system server CPU activity handling incoming binder calls
* that can be attributed to each app
*/
@VisibleForTesting
public void updateSystemServiceCallStats() {
// Start off by computing the average duration of recorded binder calls,
// regardless of which binder or transaction. We will use this as a fallback
// for calls that were not sampled at all.
int totalRecordedCallCount = 0;
long totalRecordedCallTimeMicros = 0;
for (int i = 0; i < mUidStats.size(); i++) {
Uid uid = mUidStats.valueAt(i);
ArraySet<BinderCallStats> binderCallStats = uid.mBinderCallStats;
for (int j = binderCallStats.size() - 1; j >= 0; j--) {
BinderCallStats stats = binderCallStats.valueAt(j);
totalRecordedCallCount += stats.recordedCallCount;
totalRecordedCallTimeMicros += stats.recordedCpuTimeMicros;
}
}
long totalSystemServiceTimeMicros = 0;
// For every UID, use recorded durations of sampled binder calls to estimate
// the total time the system server spent handling requests from this UID.
for (int i = 0; i < mUidStats.size(); i++) {
Uid uid = mUidStats.valueAt(i);
long totalTimeForUidUs = 0;
int totalCallCountForUid = 0;
ArraySet<BinderCallStats> binderCallStats = uid.mBinderCallStats;
for (int j = binderCallStats.size() - 1; j >= 0; j--) {
BinderCallStats stats = binderCallStats.valueAt(j);
totalCallCountForUid += stats.callCount;
if (stats.recordedCallCount > 0) {
totalTimeForUidUs +=
stats.callCount * stats.recordedCpuTimeMicros / stats.recordedCallCount;
} else if (totalRecordedCallCount > 0) {
totalTimeForUidUs +=
stats.callCount * totalRecordedCallTimeMicros / totalRecordedCallCount;
}
}
if (totalCallCountForUid < uid.mBinderCallCount && totalRecordedCallCount > 0) {
// Estimate remaining calls, which were not tracked because of binder call
// stats sampling
totalTimeForUidUs +=
(uid.mBinderCallCount - totalCallCountForUid) * totalRecordedCallTimeMicros
/ totalRecordedCallCount;
}
uid.mSystemServiceTimeUs = totalTimeForUidUs;
totalSystemServiceTimeMicros += totalTimeForUidUs;
}
for (int i = 0; i < mUidStats.size(); i++) {
Uid uid = mUidStats.valueAt(i);
if (totalSystemServiceTimeMicros > 0) {
uid.mProportionalSystemServiceUsage =
(double) uid.mSystemServiceTimeUs / totalSystemServiceTimeMicros;
} else {
uid.mProportionalSystemServiceUsage = 0;
}
}
}
public String[] getWifiIfaces() {
synchronized (mWifiNetworkLock) {
return mWifiIfaces;
}
}
public String[] getMobileIfaces() {
synchronized (mModemNetworkLock) {
return mModemIfaces;
}
}
@UnsupportedAppUsage
@Override public long getScreenOnTime(long elapsedRealtimeUs, int which) {
return mScreenOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override public int getScreenOnCount(int which) {
return mScreenOnTimer.getCountLocked(which);
}
@Override public long getScreenDozeTime(long elapsedRealtimeUs, int which) {
return mScreenDozeTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override public int getScreenDozeCount(int which) {
return mScreenDozeTimer.getCountLocked(which);
}
@UnsupportedAppUsage
@Override public long getScreenBrightnessTime(int brightnessBin,
long elapsedRealtimeUs, int which) {
return mScreenBrightnessTimer[brightnessBin].getTotalTimeLocked(
elapsedRealtimeUs, which);
}
@Override public Timer getScreenBrightnessTimer(int brightnessBin) {
return mScreenBrightnessTimer[brightnessBin];
}
@Override
public int getDisplayCount() {
return mPerDisplayBatteryStats.length;
}
@Override
public long getDisplayScreenOnTime(int display, long elapsedRealtimeUs) {
return mPerDisplayBatteryStats[display].screenOnTimer.getTotalTimeLocked(elapsedRealtimeUs,
STATS_SINCE_CHARGED);
}
@Override
public long getDisplayScreenDozeTime(int display, long elapsedRealtimeUs) {
return mPerDisplayBatteryStats[display].screenDozeTimer.getTotalTimeLocked(
elapsedRealtimeUs, STATS_SINCE_CHARGED);
}
@Override
public long getDisplayScreenBrightnessTime(int display, int brightnessBin,
long elapsedRealtimeUs) {
final DisplayBatteryStats displayStats = mPerDisplayBatteryStats[display];
return displayStats.screenBrightnessTimers[brightnessBin].getTotalTimeLocked(
elapsedRealtimeUs, STATS_SINCE_CHARGED);
}
@Override public long getInteractiveTime(long elapsedRealtimeUs, int which) {
return mInteractiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override public long getPowerSaveModeEnabledTime(long elapsedRealtimeUs, int which) {
return mPowerSaveModeEnabledTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override public int getPowerSaveModeEnabledCount(int which) {
return mPowerSaveModeEnabledTimer.getCountLocked(which);
}
@Override public long getDeviceIdleModeTime(int mode, long elapsedRealtimeUs,
int which) {
switch (mode) {
case DEVICE_IDLE_MODE_LIGHT:
return mDeviceIdleModeLightTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
case DEVICE_IDLE_MODE_DEEP:
return mDeviceIdleModeFullTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
return 0;
}
@Override public int getDeviceIdleModeCount(int mode, int which) {
switch (mode) {
case DEVICE_IDLE_MODE_LIGHT:
return mDeviceIdleModeLightTimer.getCountLocked(which);
case DEVICE_IDLE_MODE_DEEP:
return mDeviceIdleModeFullTimer.getCountLocked(which);
}
return 0;
}
@Override public long getLongestDeviceIdleModeTime(int mode) {
switch (mode) {
case DEVICE_IDLE_MODE_LIGHT:
return mLongestLightIdleTimeMs;
case DEVICE_IDLE_MODE_DEEP:
return mLongestFullIdleTimeMs;
}
return 0;
}
@Override public long getDeviceIdlingTime(int mode, long elapsedRealtimeUs, int which) {
switch (mode) {
case DEVICE_IDLE_MODE_LIGHT:
return mDeviceLightIdlingTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
case DEVICE_IDLE_MODE_DEEP:
return mDeviceIdlingTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
return 0;
}
@Override public int getDeviceIdlingCount(int mode, int which) {
switch (mode) {
case DEVICE_IDLE_MODE_LIGHT:
return mDeviceLightIdlingTimer.getCountLocked(which);
case DEVICE_IDLE_MODE_DEEP:
return mDeviceIdlingTimer.getCountLocked(which);
}
return 0;
}
@Override public int getNumConnectivityChange(int which) {
return mNumConnectivityChange;
}
@Override public long getGpsSignalQualityTime(int strengthBin,
long elapsedRealtimeUs, int which) {
if (strengthBin < 0 || strengthBin >= mGpsSignalQualityTimer.length) {
return 0;
}
return mGpsSignalQualityTimer[strengthBin].getTotalTimeLocked(
elapsedRealtimeUs, which);
}
@Override public long getGpsBatteryDrainMaMs() {
final double opVolt = mPowerProfile.getAveragePower(
PowerProfile.POWER_GPS_OPERATING_VOLTAGE) / 1000.0;
if (opVolt == 0) {
return 0;
}
double energyUsedMaMs = 0.0;
final int which = STATS_SINCE_CHARGED;
final long rawRealtimeUs = SystemClock.elapsedRealtime() * 1000;
for(int i=0; i < mGpsSignalQualityTimer.length; i++) {
energyUsedMaMs
+= mPowerProfile.getAveragePower(PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED, i)
* (getGpsSignalQualityTime(i, rawRealtimeUs, which) / 1000);
}
return (long) energyUsedMaMs;
}
@UnsupportedAppUsage
@Override public long getPhoneOnTime(long elapsedRealtimeUs, int which) {
return mPhoneOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override public int getPhoneOnCount(int which) {
return mPhoneOnTimer.getCountLocked(which);
}
@UnsupportedAppUsage
@Override public long getPhoneSignalStrengthTime(int strengthBin,
long elapsedRealtimeUs, int which) {
return mPhoneSignalStrengthsTimer[strengthBin].getTotalTimeLocked(
elapsedRealtimeUs, which);
}
@UnsupportedAppUsage
@Override public long getPhoneSignalScanningTime(
long elapsedRealtimeUs, int which) {
return mPhoneSignalScanningTimer.getTotalTimeLocked(
elapsedRealtimeUs, which);
}
@Override public Timer getPhoneSignalScanningTimer() {
return mPhoneSignalScanningTimer;
}
@UnsupportedAppUsage
@Override public int getPhoneSignalStrengthCount(int strengthBin, int which) {
return mPhoneSignalStrengthsTimer[strengthBin].getCountLocked(which);
}
@Override public Timer getPhoneSignalStrengthTimer(int strengthBin) {
return mPhoneSignalStrengthsTimer[strengthBin];
}
@UnsupportedAppUsage
@Override public long getPhoneDataConnectionTime(int dataType,
long elapsedRealtimeUs, int which) {
return mPhoneDataConnectionsTimer[dataType].getTotalTimeLocked(
elapsedRealtimeUs, which);
}
@UnsupportedAppUsage
@Override public int getPhoneDataConnectionCount(int dataType, int which) {
return mPhoneDataConnectionsTimer[dataType].getCountLocked(which);
}
@Override public Timer getPhoneDataConnectionTimer(int dataType) {
return mPhoneDataConnectionsTimer[dataType];
}
@Override public long getActiveRadioDurationMs(@RadioAccessTechnology int rat,
@ServiceState.FrequencyRange int frequencyRange, int signalStrength,
long elapsedRealtimeMs) {
final RadioAccessTechnologyBatteryStats stats = mPerRatBatteryStats[rat];
if (stats == null) return 0L;
final int freqCount = stats.perStateTimers.length;
if (frequencyRange < 0 || frequencyRange >= freqCount) return 0L;
final StopwatchTimer[] strengthTimers = stats.perStateTimers[frequencyRange];
final int strengthCount = strengthTimers.length;
if (signalStrength < 0 || signalStrength >= strengthCount) return 0L;
return stats.perStateTimers[frequencyRange][signalStrength].getTotalTimeLocked(
elapsedRealtimeMs * 1000, STATS_SINCE_CHARGED) / 1000;
}
@Override
public long getActiveTxRadioDurationMs(@RadioAccessTechnology int rat,
@ServiceState.FrequencyRange int frequencyRange, int signalStrength,
long elapsedRealtimeMs) {
final RadioAccessTechnologyBatteryStats stats = mPerRatBatteryStats[rat];
if (stats == null) return DURATION_UNAVAILABLE;
final LongSamplingCounter counter = stats.getTxDurationCounter(frequencyRange,
signalStrength, false);
if (counter == null) return DURATION_UNAVAILABLE;
return counter.getCountLocked(STATS_SINCE_CHARGED);
}
@Override
public long getActiveRxRadioDurationMs(@RadioAccessTechnology int rat,
@ServiceState.FrequencyRange int frequencyRange, long elapsedRealtimeMs) {
final RadioAccessTechnologyBatteryStats stats = mPerRatBatteryStats[rat];
if (stats == null) return DURATION_UNAVAILABLE;
final LongSamplingCounter counter = stats.getRxDurationCounter(frequencyRange, false);
if (counter == null) return DURATION_UNAVAILABLE;
return counter.getCountLocked(STATS_SINCE_CHARGED);
}
@UnsupportedAppUsage
@Override public long getMobileRadioActiveTime(long elapsedRealtimeUs, int which) {
return mMobileRadioActiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override public int getMobileRadioActiveCount(int which) {
return mMobileRadioActiveTimer.getCountLocked(which);
}
@Override public long getMobileRadioActiveAdjustedTime(int which) {
return mMobileRadioActiveAdjustedTime.getCountLocked(which);
}
@Override public long getMobileRadioActiveUnknownTime(int which) {
return mMobileRadioActiveUnknownTime.getCountLocked(which);
}
@Override public int getMobileRadioActiveUnknownCount(int which) {
return (int)mMobileRadioActiveUnknownCount.getCountLocked(which);
}
@Override public long getWifiMulticastWakelockTime(
long elapsedRealtimeUs, int which) {
return mWifiMulticastWakelockTimer.getTotalTimeLocked(
elapsedRealtimeUs, which);
}
@Override public int getWifiMulticastWakelockCount(int which) {
return mWifiMulticastWakelockTimer.getCountLocked(which);
}
@UnsupportedAppUsage
@Override public long getWifiOnTime(long elapsedRealtimeUs, int which) {
return mWifiOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override public long getWifiActiveTime(long elapsedRealtimeUs, int which) {
return mWifiActiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@UnsupportedAppUsage
@Override public long getGlobalWifiRunningTime(long elapsedRealtimeUs, int which) {
return mGlobalWifiRunningTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override public long getWifiStateTime(int wifiState,
long elapsedRealtimeUs, int which) {
return mWifiStateTimer[wifiState].getTotalTimeLocked(
elapsedRealtimeUs, which);
}
@Override public int getWifiStateCount(int wifiState, int which) {
return mWifiStateTimer[wifiState].getCountLocked(which);
}
@Override public Timer getWifiStateTimer(int wifiState) {
return mWifiStateTimer[wifiState];
}
@Override public long getWifiSupplStateTime(int state,
long elapsedRealtimeUs, int which) {
return mWifiSupplStateTimer[state].getTotalTimeLocked(
elapsedRealtimeUs, which);
}
@Override public int getWifiSupplStateCount(int state, int which) {
return mWifiSupplStateTimer[state].getCountLocked(which);
}
@Override public Timer getWifiSupplStateTimer(int state) {
return mWifiSupplStateTimer[state];
}
@Override public long getWifiSignalStrengthTime(int strengthBin,
long elapsedRealtimeUs, int which) {
return mWifiSignalStrengthsTimer[strengthBin].getTotalTimeLocked(
elapsedRealtimeUs, which);
}
@Override public int getWifiSignalStrengthCount(int strengthBin, int which) {
return mWifiSignalStrengthsTimer[strengthBin].getCountLocked(which);
}
@Override public Timer getWifiSignalStrengthTimer(int strengthBin) {
return mWifiSignalStrengthsTimer[strengthBin];
}
@Override
public ControllerActivityCounter getBluetoothControllerActivity() {
return mBluetoothActivity;
}
@Override
public ControllerActivityCounter getWifiControllerActivity() {
return mWifiActivity;
}
@Override
public ControllerActivityCounter getModemControllerActivity() {
return mModemActivity;
}
@Override
public boolean hasBluetoothActivityReporting() {
return mHasBluetoothReporting;
}
@Override
public boolean hasWifiActivityReporting() {
return mHasWifiReporting;
}
@Override
public boolean hasModemActivityReporting() {
return mHasModemReporting;
}
@Override
public long getFlashlightOnTime(long elapsedRealtimeUs, int which) {
return mFlashlightOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override
public long getFlashlightOnCount(int which) {
return mFlashlightOnTimer.getCountLocked(which);
}
@Override
public long getCameraOnTime(long elapsedRealtimeUs, int which) {
return mCameraOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override
public long getBluetoothScanTime(long elapsedRealtimeUs, int which) {
return mBluetoothScanTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override
@UnsupportedAppUsage
public long getNetworkActivityBytes(int type, int which) {
if (type >= 0 && type < mNetworkByteActivityCounters.length) {
return mNetworkByteActivityCounters[type].getCountLocked(which);
} else {
return 0;
}
}
@Override
public long getNetworkActivityPackets(int type, int which) {
if (type >= 0 && type < mNetworkPacketActivityCounters.length) {
return mNetworkPacketActivityCounters[type].getCountLocked(which);
} else {
return 0;
}
}
@GuardedBy("this")
@Override
public long getBluetoothMeasuredBatteryConsumptionUC() {
return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH);
}
@GuardedBy("this")
@Override
public long getCpuMeasuredBatteryConsumptionUC() {
return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU);
}
@GuardedBy("this")
@Override
public long getGnssMeasuredBatteryConsumptionUC() {
return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_GNSS);
}
@GuardedBy("this")
@Override
public long getMobileRadioMeasuredBatteryConsumptionUC() {
return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO);
}
@GuardedBy("this")
@Override
public long getScreenOnMeasuredBatteryConsumptionUC() {
return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON);
}
@GuardedBy("this")
@Override
public long getScreenDozeMeasuredBatteryConsumptionUC() {
return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_DOZE);
}
@GuardedBy("this")
@Override
public long getWifiMeasuredBatteryConsumptionUC() {
return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_WIFI);
}
/**
* Returns the consumption (in microcoulombs) that the given standard power bucket consumed.
* Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable
*
* @param bucket standard power bucket of interest
* @return charge (in microcoulombs) used for this power bucket
*/
@GuardedBy("this")
private long getPowerBucketConsumptionUC(@StandardPowerBucket int bucket) {
if (mGlobalMeasuredEnergyStats == null) {
return POWER_DATA_UNAVAILABLE;
}
return mGlobalMeasuredEnergyStats.getAccumulatedStandardBucketCharge(bucket);
}
@GuardedBy("this")
@Override
public @Nullable long[] getCustomConsumerMeasuredBatteryConsumptionUC() {
if (mGlobalMeasuredEnergyStats == null) {
return null;
}
return mGlobalMeasuredEnergyStats.getAccumulatedCustomBucketCharges();
}
/**
* Returns the names of custom power components.
*/
@GuardedBy("this")
@Override
public @NonNull String[] getCustomEnergyConsumerNames() {
if (mMeasuredEnergyStatsConfig == null) {
return new String[0];
}
final String[] names = mMeasuredEnergyStatsConfig.getCustomBucketNames();
for (int i = 0; i < names.length; i++) {
if (TextUtils.isEmpty(names[i])) {
names[i] = "CUSTOM_" + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + i;
}
}
return names;
}
@GuardedBy("this")
@Override public long getStartClockTime() {
final long currentTimeMs = mClock.currentTimeMillis();
if ((currentTimeMs > MILLISECONDS_IN_YEAR
&& mStartClockTimeMs < (currentTimeMs - MILLISECONDS_IN_YEAR))
|| (mStartClockTimeMs > currentTimeMs)) {
// If the start clock time has changed by more than a year, then presumably
// the previous time was completely bogus. So we are going to figure out a
// new time based on how much time has elapsed since we started counting.
recordCurrentTimeChangeLocked(currentTimeMs, mClock.elapsedRealtime(),
mClock.uptimeMillis());
return currentTimeMs - (mClock.elapsedRealtime() - (mRealtimeStartUs / 1000));
}
return mStartClockTimeMs;
}
@Override public String getStartPlatformVersion() {
return mStartPlatformVersion;
}
@Override public String getEndPlatformVersion() {
return mEndPlatformVersion;
}
@Override public int getParcelVersion() {
return VERSION;
}
@Override public boolean getIsOnBattery() {
return mOnBattery;
}
@Override public long getStatsStartRealtime() {
return mRealtimeStartUs;
}
@UnsupportedAppUsage
@Override public SparseArray<? extends BatteryStats.Uid> getUidStats() {
return mUidStats;
}
private static <T extends TimeBaseObs> boolean resetIfNotNull(T t, boolean detachIfReset,
long elapsedRealtimeUs) {
if (t != null) {
return t.reset(detachIfReset, elapsedRealtimeUs);
}
return true;
}
private static <T extends TimeBaseObs> boolean resetIfNotNull(T[] t, boolean detachIfReset,
long elapsedRealtimeUs) {
if (t != null) {
boolean ret = true;
for (int i = 0; i < t.length; i++) {
ret &= resetIfNotNull(t[i], detachIfReset, elapsedRealtimeUs);
}
return ret;
}
return true;
}
private static <T extends TimeBaseObs> boolean resetIfNotNull(T[][] t, boolean detachIfReset,
long elapsedRealtimeUs) {
if (t != null) {
boolean ret = true;
for (int i = 0; i < t.length; i++) {
ret &= resetIfNotNull(t[i], detachIfReset, elapsedRealtimeUs);
}
return ret;
}
return true;
}
private static boolean resetIfNotNull(ControllerActivityCounterImpl counter,
boolean detachIfReset, long elapsedRealtimeUs) {
if (counter != null) {
counter.reset(detachIfReset, elapsedRealtimeUs);
}
return true;
}
private static <T extends TimeBaseObs> void detachIfNotNull(T t) {
if (t != null) {
t.detach();
}
}
private static <T extends TimeBaseObs> void detachIfNotNull(T[] t) {
if (t != null) {
for (int i = 0; i < t.length; i++) {
detachIfNotNull(t[i]);
}
}
}
private static <T extends TimeBaseObs> void detachIfNotNull(T[][] t) {
if (t != null) {
for (int i = 0; i < t.length; i++) {
detachIfNotNull(t[i]);
}
}
}
private static void detachIfNotNull(ControllerActivityCounterImpl counter) {
if (counter != null) {
counter.detach();
}
}
/**
* Accumulates stats for a specific binder transaction.
*/
@VisibleForTesting
protected static class BinderCallStats {
static final Comparator<BinderCallStats> COMPARATOR =
Comparator.comparing(BinderCallStats::getClassName)
.thenComparing(BinderCallStats::getMethodName);
public Class<? extends Binder> binderClass;
public int transactionCode;
public String methodName;
public long callCount;
public long recordedCallCount;
public long recordedCpuTimeMicros;
@Override
public int hashCode() {
return binderClass.hashCode() * 31 + transactionCode;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof BinderCallStats)) {
return false;
}
BinderCallStats bcsk = (BinderCallStats) obj;
return binderClass.equals(bcsk.binderClass) && transactionCode == bcsk.transactionCode;
}
public String getClassName() {
return binderClass.getName();
}
public String getMethodName() {
return methodName;
}
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public void ensureMethodName(BinderTransactionNameResolver resolver) {
if (methodName == null) {
methodName = resolver.getMethodName(binderClass, transactionCode);
}
}
@Override
public String toString() {
return "BinderCallStats{"
+ binderClass
+ " transaction=" + transactionCode
+ " callCount=" + callCount
+ " recordedCallCount=" + recordedCallCount
+ " recorderCpuTimeMicros=" + recordedCpuTimeMicros
+ "}";
}
}
/**
* The statistics associated with a particular uid.
*/
public static class Uid extends BatteryStats.Uid {
/**
* BatteryStatsImpl that we are associated with.
*/
protected BatteryStatsImpl mBsi;
final int mUid;
/** TimeBase for when uid is in background and device is on battery. */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public final TimeBase mOnBatteryBackgroundTimeBase;
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public final TimeBase mOnBatteryScreenOffBackgroundTimeBase;
boolean mWifiRunning;
StopwatchTimer mWifiRunningTimer;
boolean mFullWifiLockOut;
StopwatchTimer mFullWifiLockTimer;
boolean mWifiScanStarted;
DualTimer mWifiScanTimer;
static final int NO_BATCHED_SCAN_STARTED = -1;
int mWifiBatchedScanBinStarted = NO_BATCHED_SCAN_STARTED;
StopwatchTimer[] mWifiBatchedScanTimer;
int mWifiMulticastWakelockCount;
StopwatchTimer mWifiMulticastTimer;
StopwatchTimer mAudioTurnedOnTimer;
StopwatchTimer mVideoTurnedOnTimer;
StopwatchTimer mFlashlightTurnedOnTimer;
StopwatchTimer mCameraTurnedOnTimer;
StopwatchTimer mForegroundActivityTimer;
StopwatchTimer mForegroundServiceTimer;
/** Total time spent by the uid holding any partial wakelocks. */
DualTimer mAggregatedPartialWakelockTimer;
DualTimer mBluetoothScanTimer;
DualTimer mBluetoothUnoptimizedScanTimer;
Counter mBluetoothScanResultCounter;
Counter mBluetoothScanResultBgCounter;
int mProcessState = Uid.PROCESS_STATE_NONEXISTENT;
StopwatchTimer[] mProcessStateTimer;
boolean mInForegroundService = false;
BatchTimer mVibratorOnTimer;
Counter[] mUserActivityCounters;
LongSamplingCounter[] mNetworkByteActivityCounters;
LongSamplingCounter[] mNetworkPacketActivityCounters;
TimeMultiStateCounter mMobileRadioActiveTime;
LongSamplingCounter mMobileRadioActiveCount;
/**
* How many times this UID woke up the Application Processor due to a Mobile radio packet.
*/
private LongSamplingCounter mMobileRadioApWakeupCount;
/**
* How many times this UID woke up the Application Processor due to a Wifi packet.
*/
private LongSamplingCounter mWifiRadioApWakeupCount;
/**
* The amount of time this uid has kept the WiFi controller in idle, tx, and rx mode.
* Can be null if the UID has had no such activity.
*/
private ControllerActivityCounterImpl mWifiControllerActivity;
/**
* The amount of time this uid has kept the Bluetooth controller in idle, tx, and rx mode.
* Can be null if the UID has had no such activity.
*/
private ControllerActivityCounterImpl mBluetoothControllerActivity;
/**
* The amount of time this uid has kept the Modem controller in idle, tx, and rx mode.
* Can be null if the UID has had no such activity.
*/
private ControllerActivityCounterImpl mModemControllerActivity;
/**
* The CPU times we had at the last history details update.
*/
long mLastStepUserTimeMs;
long mLastStepSystemTimeMs;
long mCurStepUserTimeMs;
long mCurStepSystemTimeMs;
LongSamplingCounter mUserCpuTime;
LongSamplingCounter mSystemCpuTime;
LongSamplingCounter[][] mCpuClusterSpeedTimesUs;
TimeMultiStateCounter mCpuActiveTimeMs;
LongSamplingCounterArray mCpuFreqTimeMs;
LongSamplingCounterArray mScreenOffCpuFreqTimeMs;
LongSamplingCounterArray mCpuClusterTimesMs;
TimeInFreqMultiStateCounter mProcStateTimeMs;
TimeInFreqMultiStateCounter mProcStateScreenOffTimeMs;
SparseArray<ChildUid> mChildUids;
/**
* The statistics we have collected for this uid's wake locks.
*/
final OverflowArrayMap<Wakelock> mWakelockStats;
/**
* The statistics we have collected for this uid's syncs.
*/
final OverflowArrayMap<DualTimer> mSyncStats;
/**
* The statistics we have collected for this uid's jobs.
*/
final OverflowArrayMap<DualTimer> mJobStats;
/**
* Count of the jobs that have completed and the reasons why they completed.
*/
final ArrayMap<String, SparseIntArray> mJobCompletions = new ArrayMap<>();
/**
* Count of app launch events that had associated deferred job counts or info about
* last time a job was run.
*/
Counter mJobsDeferredEventCount;
/**
* Count of deferred jobs that were pending when the app was launched or brought to
* the foreground through a user interaction.
*/
Counter mJobsDeferredCount;
/**
* Sum of time since the last time a job was run for this app before it was launched.
*/
LongSamplingCounter mJobsFreshnessTimeMs;
/**
* Array of counts of instances where the time since the last job was run for the app
* fell within one of the thresholds in {@link #JOB_FRESHNESS_BUCKETS}.
*/
final Counter[] mJobsFreshnessBuckets;
/**
* The statistics we have collected for this uid's sensor activations.
*/
final SparseArray<Sensor> mSensorStats = new SparseArray<>();
/**
* The statistics we have collected for this uid's processes.
*/
final ArrayMap<String, Proc> mProcessStats = new ArrayMap<>();
/**
* The statistics we have collected for this uid's processes.
*/
final ArrayMap<String, Pkg> mPackageStats = new ArrayMap<>();
/**
* The transient wake stats we have collected for this uid's pids.
*/
final SparseArray<Pid> mPids = new SparseArray<>();
/**
* Grand total of system server binder calls made by this uid.
*/
private long mBinderCallCount;
/**
* Detailed information about system server binder calls made by this uid.
*/
private final ArraySet<BinderCallStats> mBinderCallStats = new ArraySet<>();
/**
* Measured charge consumption by this uid while on battery.
* Its '<b>custom</b> power buckets' correspond to the
* {@link android.hardware.power.stats.EnergyConsumer.ordinal}s of (custom) energy consumer
* type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
*
* Will be null if energy consumer data is completely unavailable (in which case
* {@link #mGlobalMeasuredEnergyStats} will also be null) or if the power usage by this uid
* is 0 for every bucket.
*/
private MeasuredEnergyStats mUidMeasuredEnergyStats;
/**
* Estimated total time spent by the system server handling requests from this uid.
*/
private long mSystemServiceTimeUs;
/**
* Estimated proportion of system server binder call CPU cost for this uid.
*/
private double mProportionalSystemServiceUsage;
public Uid(BatteryStatsImpl bsi, int uid) {
this(bsi, uid, bsi.mClock.elapsedRealtime(), bsi.mClock.uptimeMillis());
}
public Uid(BatteryStatsImpl bsi, int uid, long elapsedRealtimeMs, long uptimeMs) {
mBsi = bsi;
mUid = uid;
/* Observer list of TimeBase object in Uid is short */
mOnBatteryBackgroundTimeBase = new TimeBase(false);
mOnBatteryBackgroundTimeBase.init(uptimeMs * 1000, elapsedRealtimeMs * 1000);
/* Observer list of TimeBase object in Uid is short */
mOnBatteryScreenOffBackgroundTimeBase = new TimeBase(false);
mOnBatteryScreenOffBackgroundTimeBase.init(uptimeMs * 1000, elapsedRealtimeMs * 1000);
mUserCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
mSystemCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
mCpuClusterTimesMs = new LongSamplingCounterArray(mBsi.mOnBatteryTimeBase);
mWakelockStats = mBsi.new OverflowArrayMap<Wakelock>(uid) {
@Override public Wakelock instantiateObject() {
return new Wakelock(mBsi, Uid.this);
}
};
mSyncStats = mBsi.new OverflowArrayMap<DualTimer>(uid) {
@Override public DualTimer instantiateObject() {
return new DualTimer(mBsi.mClock, Uid.this, SYNC, null,
mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase);
}
};
mJobStats = mBsi.new OverflowArrayMap<DualTimer>(uid) {
@Override public DualTimer instantiateObject() {
return new DualTimer(mBsi.mClock, Uid.this, JOB, null,
mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase);
}
};
mWifiRunningTimer = new StopwatchTimer(mBsi.mClock, this, WIFI_RUNNING,
mBsi.mWifiRunningTimers, mBsi.mOnBatteryTimeBase);
mFullWifiLockTimer = new StopwatchTimer(mBsi.mClock, this, FULL_WIFI_LOCK,
mBsi.mFullWifiLockTimers, mBsi.mOnBatteryTimeBase);
mWifiScanTimer = new DualTimer(mBsi.mClock, this, WIFI_SCAN,
mBsi.mWifiScanTimers, mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase);
mWifiBatchedScanTimer = new StopwatchTimer[NUM_WIFI_BATCHED_SCAN_BINS];
mWifiMulticastTimer = new StopwatchTimer(mBsi.mClock, this, WIFI_MULTICAST_ENABLED,
mBsi.mWifiMulticastTimers, mBsi.mOnBatteryTimeBase);
mProcessStateTimer = new StopwatchTimer[NUM_PROCESS_STATE];
mJobsDeferredEventCount = new Counter(mBsi.mOnBatteryTimeBase);
mJobsDeferredCount = new Counter(mBsi.mOnBatteryTimeBase);
mJobsFreshnessTimeMs = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
mJobsFreshnessBuckets = new Counter[JOB_FRESHNESS_BUCKETS.length];
}
@GuardedBy("mBsi")
@VisibleForTesting
public void setProcessStateForTest(int procState, long elapsedTimeMs) {
mProcessState = procState;
getProcStateTimeCounter(elapsedTimeMs).setState(procState, elapsedTimeMs);
getProcStateScreenOffTimeCounter(elapsedTimeMs).setState(procState, elapsedTimeMs);
final int batteryConsumerProcessState =
mapUidProcessStateToBatteryConsumerProcessState(procState);
getCpuActiveTimeCounter().setState(batteryConsumerProcessState, elapsedTimeMs);
getMobileRadioActiveTimeCounter().setState(batteryConsumerProcessState, elapsedTimeMs);
final ControllerActivityCounterImpl wifiControllerActivity =
getWifiControllerActivity();
if (wifiControllerActivity != null) {
wifiControllerActivity.setState(batteryConsumerProcessState, elapsedTimeMs);
}
final ControllerActivityCounterImpl bluetoothControllerActivity =
getBluetoothControllerActivity();
if (bluetoothControllerActivity != null) {
bluetoothControllerActivity.setState(batteryConsumerProcessState, elapsedTimeMs);
}
final MeasuredEnergyStats energyStats =
getOrCreateMeasuredEnergyStatsIfSupportedLocked();
if (energyStats != null) {
energyStats.setState(batteryConsumerProcessState, elapsedTimeMs);
}
}
@Override
public long[] getCpuFreqTimes(int which) {
return nullIfAllZeros(mCpuFreqTimeMs, which);
}
@Override
public long[] getScreenOffCpuFreqTimes(int which) {
return nullIfAllZeros(mScreenOffCpuFreqTimeMs, which);
}
private TimeMultiStateCounter getCpuActiveTimeCounter() {
if (mCpuActiveTimeMs == null) {
final long timestampMs = mBsi.mClock.elapsedRealtime();
mCpuActiveTimeMs = new TimeMultiStateCounter(mBsi.mOnBatteryTimeBase,
BatteryConsumer.PROCESS_STATE_COUNT, timestampMs);
mCpuActiveTimeMs.setState(
mapUidProcessStateToBatteryConsumerProcessState(mProcessState),
timestampMs);
}
return mCpuActiveTimeMs;
}
@Override
public long getCpuActiveTime() {
if (mCpuActiveTimeMs == null) {
return 0;
}
long activeTime = 0;
for (int procState = 0; procState < BatteryConsumer.PROCESS_STATE_COUNT; procState++) {
activeTime += mCpuActiveTimeMs.getCountForProcessState(procState);
}
return activeTime;
}
@Override
public long getCpuActiveTime(int procState) {
if (mCpuActiveTimeMs == null
|| procState < 0 || procState >= BatteryConsumer.PROCESS_STATE_COUNT) {
return 0;
}
return mCpuActiveTimeMs.getCountForProcessState(procState);
}
@Override
public long[] getCpuClusterTimes() {
return nullIfAllZeros(mCpuClusterTimesMs, STATS_SINCE_CHARGED);
}
@GuardedBy("mBsi")
@Override
public boolean getCpuFreqTimes(long[] timesInFreqMs, int procState) {
if (procState < 0 || procState >= NUM_PROCESS_STATE) {
return false;
}
if (mProcStateTimeMs == null) {
return false;
}
if (!mBsi.mPerProcStateCpuTimesAvailable) {
mProcStateTimeMs = null;
return false;
}
return mProcStateTimeMs.getCountsLocked(timesInFreqMs, procState);
}
@GuardedBy("mBsi")
@Override
public boolean getScreenOffCpuFreqTimes(long[] timesInFreqMs, int procState) {
if (procState < 0 || procState >= NUM_PROCESS_STATE) {
return false;
}
if (mProcStateScreenOffTimeMs == null) {
return false;
}
if (!mBsi.mPerProcStateCpuTimesAvailable) {
mProcStateScreenOffTimeMs = null;
return false;
}
return mProcStateScreenOffTimeMs.getCountsLocked(timesInFreqMs, procState);
}
public long getBinderCallCount() {
return mBinderCallCount;
}
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
public ArraySet<BinderCallStats> getBinderCallStats() {
return mBinderCallStats;
}
@Override
public double getProportionalSystemServiceUsage() {
return mProportionalSystemServiceUsage;
}
@GuardedBy("mBsi")
public void addIsolatedUid(int isolatedUid) {
if (mChildUids == null) {
mChildUids = new SparseArray<>();
} else if (mChildUids.indexOfKey(isolatedUid) >= 0) {
return;
}
mChildUids.put(isolatedUid, new ChildUid());
}
public void removeIsolatedUid(int isolatedUid) {
final int idx = mChildUids == null ? -1 : mChildUids.indexOfKey(isolatedUid);
if (idx < 0) {
return;
}
mChildUids.remove(idx);
}
@GuardedBy("mBsi")
ChildUid getChildUid(int childUid) {
return mChildUids == null ? null : mChildUids.get(childUid);
}
private long[] nullIfAllZeros(LongSamplingCounterArray cpuTimesMs, int which) {
if (cpuTimesMs == null) {
return null;
}
final long[] counts = cpuTimesMs.getCountsLocked(which);
if (counts == null) {
return null;
}
// Return counts only if at least one of the elements is non-zero.
for (int i = counts.length - 1; i >= 0; --i) {
if (counts[i] != 0) {
return counts;
}
}
return null;
}
@GuardedBy("mBsi")
private void ensureMultiStateCounters(long timestampMs) {
if (mProcStateTimeMs != null) {
return;
}
mProcStateTimeMs =
new TimeInFreqMultiStateCounter(mBsi.mOnBatteryTimeBase,
PROC_STATE_TIME_COUNTER_STATE_COUNT, mBsi.getCpuFreqCount(),
timestampMs);
mProcStateScreenOffTimeMs =
new TimeInFreqMultiStateCounter(mBsi.mOnBatteryScreenOffTimeBase,
PROC_STATE_TIME_COUNTER_STATE_COUNT, mBsi.getCpuFreqCount(),
timestampMs);
}
@GuardedBy("mBsi")
private TimeInFreqMultiStateCounter getProcStateTimeCounter(long timestampMs) {
ensureMultiStateCounters(timestampMs);
return mProcStateTimeMs;
}
@GuardedBy("mBsi")
private TimeInFreqMultiStateCounter getProcStateScreenOffTimeCounter(long timestampMs) {
ensureMultiStateCounters(timestampMs);
return mProcStateScreenOffTimeMs;
}
@Override
public Timer getAggregatedPartialWakelockTimer() {
return mAggregatedPartialWakelockTimer;
}
@Override
@UnsupportedAppUsage
public ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> getWakelockStats() {
return mWakelockStats.getMap();
}
@Override
public Timer getMulticastWakelockStats() {
return mWifiMulticastTimer;
}
@Override
public ArrayMap<String, ? extends BatteryStats.Timer> getSyncStats() {
return mSyncStats.getMap();
}
@Override
public ArrayMap<String, ? extends BatteryStats.Timer> getJobStats() {
return mJobStats.getMap();
}
@Override
public ArrayMap<String, SparseIntArray> getJobCompletionStats() {
return mJobCompletions;
}
@Override
@UnsupportedAppUsage
public SparseArray<? extends BatteryStats.Uid.Sensor> getSensorStats() {
return mSensorStats;
}
@Override
@UnsupportedAppUsage
public ArrayMap<String, ? extends BatteryStats.Uid.Proc> getProcessStats() {
return mProcessStats;
}
@Override
public ArrayMap<String, ? extends BatteryStats.Uid.Pkg> getPackageStats() {
return mPackageStats;
}
@Override
@UnsupportedAppUsage
public int getUid() {
return mUid;
}
@Override
public void noteWifiRunningLocked(long elapsedRealtimeMs) {
if (!mWifiRunning) {
mWifiRunning = true;
if (mWifiRunningTimer == null) {
mWifiRunningTimer = new StopwatchTimer(mBsi.mClock, Uid.this, WIFI_RUNNING,
mBsi.mWifiRunningTimers, mBsi.mOnBatteryTimeBase);
}
mWifiRunningTimer.startRunningLocked(elapsedRealtimeMs);
}
}
@Override
public void noteWifiStoppedLocked(long elapsedRealtimeMs) {
if (mWifiRunning) {
mWifiRunning = false;
mWifiRunningTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
@Override
public void noteFullWifiLockAcquiredLocked(long elapsedRealtimeMs) {
if (!mFullWifiLockOut) {
mFullWifiLockOut = true;
if (mFullWifiLockTimer == null) {
mFullWifiLockTimer = new StopwatchTimer(mBsi.mClock, Uid.this, FULL_WIFI_LOCK,
mBsi.mFullWifiLockTimers, mBsi.mOnBatteryTimeBase);
}
mFullWifiLockTimer.startRunningLocked(elapsedRealtimeMs);
}
}
@Override
public void noteFullWifiLockReleasedLocked(long elapsedRealtimeMs) {
if (mFullWifiLockOut) {
mFullWifiLockOut = false;
mFullWifiLockTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
@Override
public void noteWifiScanStartedLocked(long elapsedRealtimeMs) {
if (!mWifiScanStarted) {
mWifiScanStarted = true;
if (mWifiScanTimer == null) {
mWifiScanTimer = new DualTimer(mBsi.mClock, Uid.this, WIFI_SCAN,
mBsi.mWifiScanTimers, mBsi.mOnBatteryTimeBase,
mOnBatteryBackgroundTimeBase);
}
mWifiScanTimer.startRunningLocked(elapsedRealtimeMs);
}
}
@Override
public void noteWifiScanStoppedLocked(long elapsedRealtimeMs) {
if (mWifiScanStarted) {
mWifiScanStarted = false;
mWifiScanTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
@Override
public void noteWifiBatchedScanStartedLocked(int csph, long elapsedRealtimeMs) {
int bin = 0;
while (csph > 8 && bin < NUM_WIFI_BATCHED_SCAN_BINS-1) {
csph = csph >> 3;
bin++;
}
if (mWifiBatchedScanBinStarted == bin) return;
if (mWifiBatchedScanBinStarted != NO_BATCHED_SCAN_STARTED) {
mWifiBatchedScanTimer[mWifiBatchedScanBinStarted].
stopRunningLocked(elapsedRealtimeMs);
}
mWifiBatchedScanBinStarted = bin;
if (mWifiBatchedScanTimer[bin] == null) {
makeWifiBatchedScanBin(bin, null);
}
mWifiBatchedScanTimer[bin].startRunningLocked(elapsedRealtimeMs);
}
@Override
public void noteWifiBatchedScanStoppedLocked(long elapsedRealtimeMs) {
if (mWifiBatchedScanBinStarted != NO_BATCHED_SCAN_STARTED) {
mWifiBatchedScanTimer[mWifiBatchedScanBinStarted].
stopRunningLocked(elapsedRealtimeMs);
mWifiBatchedScanBinStarted = NO_BATCHED_SCAN_STARTED;
}
}
@Override
public void noteWifiMulticastEnabledLocked(long elapsedRealtimeMs) {
if (mWifiMulticastWakelockCount == 0) {
if (mWifiMulticastTimer == null) {
mWifiMulticastTimer = new StopwatchTimer(mBsi.mClock, Uid.this,
WIFI_MULTICAST_ENABLED, mBsi.mWifiMulticastTimers, mBsi.mOnBatteryTimeBase);
}
mWifiMulticastTimer.startRunningLocked(elapsedRealtimeMs);
}
mWifiMulticastWakelockCount++;
}
@Override
public void noteWifiMulticastDisabledLocked(long elapsedRealtimeMs) {
if (mWifiMulticastWakelockCount == 0) {
return;
}
mWifiMulticastWakelockCount--;
if (mWifiMulticastWakelockCount == 0) {
mWifiMulticastTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
@Override
public ControllerActivityCounterImpl getWifiControllerActivity() {
return mWifiControllerActivity;
}
@Override
public ControllerActivityCounterImpl getBluetoothControllerActivity() {
return mBluetoothControllerActivity;
}
@Override
public ControllerActivityCounter getModemControllerActivity() {
return mModemControllerActivity;
}
public ControllerActivityCounterImpl getOrCreateWifiControllerActivityLocked() {
if (mWifiControllerActivity == null) {
mWifiControllerActivity = new ControllerActivityCounterImpl(mBsi.mClock,
mBsi.mOnBatteryTimeBase, NUM_WIFI_TX_LEVELS);
}
return mWifiControllerActivity;
}
public ControllerActivityCounterImpl getOrCreateBluetoothControllerActivityLocked() {
if (mBluetoothControllerActivity == null) {
mBluetoothControllerActivity = new ControllerActivityCounterImpl(mBsi.mClock,
mBsi.mOnBatteryTimeBase, NUM_BT_TX_LEVELS);
}
return mBluetoothControllerActivity;
}
public ControllerActivityCounterImpl getOrCreateModemControllerActivityLocked() {
if (mModemControllerActivity == null) {
mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mClock,
mBsi.mOnBatteryTimeBase, ModemActivityInfo.getNumTxPowerLevels());
}
return mModemControllerActivity;
}
@GuardedBy("mBsi")
private MeasuredEnergyStats getOrCreateMeasuredEnergyStatsLocked() {
if (mUidMeasuredEnergyStats == null) {
mUidMeasuredEnergyStats = new MeasuredEnergyStats(mBsi.mMeasuredEnergyStatsConfig);
}
return mUidMeasuredEnergyStats;
}
@GuardedBy("mBsi")
private MeasuredEnergyStats getOrCreateMeasuredEnergyStatsIfSupportedLocked() {
if (mUidMeasuredEnergyStats == null && mBsi.mMeasuredEnergyStatsConfig != null) {
mUidMeasuredEnergyStats = new MeasuredEnergyStats(mBsi.mMeasuredEnergyStatsConfig);
}
return mUidMeasuredEnergyStats;
}
/** Adds the given charge to the given standard power bucket for this uid. */
@GuardedBy("mBsi")
private void addChargeToStandardBucketLocked(long chargeDeltaUC,
@StandardPowerBucket int powerBucket, long timestampMs) {
final MeasuredEnergyStats measuredEnergyStats =
getOrCreateMeasuredEnergyStatsLocked();
measuredEnergyStats.updateStandardBucket(powerBucket, chargeDeltaUC, timestampMs);
}
/** Adds the given charge to the given custom power bucket for this uid. */
@GuardedBy("mBsi")
private void addChargeToCustomBucketLocked(long chargeDeltaUC, int powerBucket) {
getOrCreateMeasuredEnergyStatsLocked().updateCustomBucket(powerBucket, chargeDeltaUC,
mBsi.mClock.elapsedRealtime());
}
/**
* Returns the battery consumption (in microcoulomb) of this uid for a standard power bucket
* of interest.
* @param bucket standard power bucket of interest
* @return consumption (in microcolombs) used by this uid for this power bucket
*/
@GuardedBy("mBsi")
public long getMeasuredBatteryConsumptionUC(@StandardPowerBucket int bucket) {
if (mBsi.mGlobalMeasuredEnergyStats == null
|| !mBsi.mGlobalMeasuredEnergyStats.isStandardBucketSupported(bucket)) {
return POWER_DATA_UNAVAILABLE;
}
if (mUidMeasuredEnergyStats == null) {
return 0L; // It is supported, but was never filled, so it must be 0
}
return mUidMeasuredEnergyStats.getAccumulatedStandardBucketCharge(bucket);
}
/**
* Returns the battery consumption (in microcoulombs) of this uid for a standard power
* bucket and a process state, such as Uid.PROCESS_STATE_TOP.
*/
@GuardedBy("mBsi")
public long getMeasuredBatteryConsumptionUC(@StandardPowerBucket int bucket,
int processState) {
if (mBsi.mGlobalMeasuredEnergyStats == null
|| !mBsi.mGlobalMeasuredEnergyStats.isStandardBucketSupported(bucket)) {
return POWER_DATA_UNAVAILABLE;
}
if (mUidMeasuredEnergyStats == null) {
return 0L; // It is supported, but was never filled, so it must be 0
}
return mUidMeasuredEnergyStats.getAccumulatedStandardBucketCharge(bucket, processState);
}
@GuardedBy("mBsi")
@Override
public long[] getCustomConsumerMeasuredBatteryConsumptionUC() {
if (mBsi.mGlobalMeasuredEnergyStats == null) {
return null;
}
if (mUidMeasuredEnergyStats == null) {
// Custom buckets may exist. But all values for this uid are 0 so we report all 0s.
return new long[mBsi.mGlobalMeasuredEnergyStats.getNumberCustomPowerBuckets()];
}
return mUidMeasuredEnergyStats.getAccumulatedCustomBucketCharges();
}
@GuardedBy("mBsi")
@Override
public long getBluetoothMeasuredBatteryConsumptionUC() {
return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH);
}
@GuardedBy("mBsi")
@Override
public long getBluetoothMeasuredBatteryConsumptionUC(
@BatteryConsumer.ProcessState int processState) {
return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH,
processState);
}
@GuardedBy("mBsi")
@Override
public long getCpuMeasuredBatteryConsumptionUC() {
return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU);
}
@GuardedBy("mBsi")
@Override
public long getCpuMeasuredBatteryConsumptionUC(
@BatteryConsumer.ProcessState int processState) {
return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU,
processState);
}
@GuardedBy("mBsi")
@Override
public long getGnssMeasuredBatteryConsumptionUC() {
return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_GNSS);
}
@GuardedBy("mBsi")
@Override
public long getMobileRadioMeasuredBatteryConsumptionUC() {
return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO);
}
@GuardedBy("mBsi")
@Override
public long getMobileRadioMeasuredBatteryConsumptionUC(int processState) {
return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO,
processState);
}
@GuardedBy("mBsi")
@Override
public long getScreenOnMeasuredBatteryConsumptionUC() {
return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON);
}
@GuardedBy("mBsi")
@Override
public long getWifiMeasuredBatteryConsumptionUC() {
return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_WIFI);
}
@GuardedBy("mBsi")
@Override
public long getWifiMeasuredBatteryConsumptionUC(int processState) {
return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_WIFI,
processState);
}
/**
* Gets the minimum of the uid's foreground activity time and its PROCESS_STATE_TOP time
* since last marked. Also sets the mark time for both these timers.
*
* @see CpuPowerCalculator
*
* @param doCalc if true, then calculate the minimum; else don't bother and return 0. Either
* way, the mark is set.
*/
private long markProcessForegroundTimeUs(long elapsedRealtimeMs,
boolean doCalc) {
long fgTimeUs = 0;
final StopwatchTimer fgTimer = mForegroundActivityTimer;
if (fgTimer != null) {
if (doCalc) fgTimeUs = fgTimer.getTimeSinceMarkLocked(elapsedRealtimeMs * 1000);
fgTimer.setMark(elapsedRealtimeMs);
}
long topTimeUs = 0;
final StopwatchTimer topTimer = mProcessStateTimer[PROCESS_STATE_TOP];
if (topTimer != null) {
if (doCalc) topTimeUs = topTimer.getTimeSinceMarkLocked(elapsedRealtimeMs * 1000);
topTimer.setMark(elapsedRealtimeMs);
}
// Return the min of the two
return (topTimeUs < fgTimeUs) ? topTimeUs : fgTimeUs;
}
/**
* Gets the uid's time spent using the GNSS since last marked. Also sets the mark time for
* the GNSS timer.
*/
private long markGnssTimeUs(long elapsedRealtimeMs) {
final Sensor sensor = mSensorStats.get(Sensor.GPS);
if (sensor == null) {
return 0;
}
final StopwatchTimer timer = sensor.mTimer;
if (timer == null) {
return 0;
}
final long gnssTimeUs = timer.getTimeSinceMarkLocked(elapsedRealtimeMs * 1000);
timer.setMark(elapsedRealtimeMs);
return gnssTimeUs;
}
public StopwatchTimer createAudioTurnedOnTimerLocked() {
if (mAudioTurnedOnTimer == null) {
mAudioTurnedOnTimer = new StopwatchTimer(mBsi.mClock, Uid.this, AUDIO_TURNED_ON,
mBsi.mAudioTurnedOnTimers, mBsi.mOnBatteryTimeBase);
}
return mAudioTurnedOnTimer;
}
public void noteAudioTurnedOnLocked(long elapsedRealtimeMs) {
createAudioTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
}
public void noteAudioTurnedOffLocked(long elapsedRealtimeMs) {
if (mAudioTurnedOnTimer != null) {
mAudioTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
public void noteResetAudioLocked(long elapsedRealtimeMs) {
if (mAudioTurnedOnTimer != null) {
mAudioTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
}
}
public StopwatchTimer createVideoTurnedOnTimerLocked() {
if (mVideoTurnedOnTimer == null) {
mVideoTurnedOnTimer = new StopwatchTimer(mBsi.mClock, Uid.this, VIDEO_TURNED_ON,
mBsi.mVideoTurnedOnTimers, mBsi.mOnBatteryTimeBase);
}
return mVideoTurnedOnTimer;
}
public void noteVideoTurnedOnLocked(long elapsedRealtimeMs) {
createVideoTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
}
public void noteVideoTurnedOffLocked(long elapsedRealtimeMs) {
if (mVideoTurnedOnTimer != null) {
mVideoTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
public void noteResetVideoLocked(long elapsedRealtimeMs) {
if (mVideoTurnedOnTimer != null) {
mVideoTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
}
}
public StopwatchTimer createFlashlightTurnedOnTimerLocked() {
if (mFlashlightTurnedOnTimer == null) {
mFlashlightTurnedOnTimer = new StopwatchTimer(mBsi.mClock, Uid.this,
FLASHLIGHT_TURNED_ON, mBsi.mFlashlightTurnedOnTimers, mBsi.mOnBatteryTimeBase);
}
return mFlashlightTurnedOnTimer;
}
public void noteFlashlightTurnedOnLocked(long elapsedRealtimeMs) {
createFlashlightTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
}
public void noteFlashlightTurnedOffLocked(long elapsedRealtimeMs) {
if (mFlashlightTurnedOnTimer != null) {
mFlashlightTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
public void noteResetFlashlightLocked(long elapsedRealtimeMs) {
if (mFlashlightTurnedOnTimer != null) {
mFlashlightTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
}
}
public StopwatchTimer createCameraTurnedOnTimerLocked() {
if (mCameraTurnedOnTimer == null) {
mCameraTurnedOnTimer = new StopwatchTimer(mBsi.mClock, Uid.this, CAMERA_TURNED_ON,
mBsi.mCameraTurnedOnTimers, mBsi.mOnBatteryTimeBase);
}
return mCameraTurnedOnTimer;
}
public void noteCameraTurnedOnLocked(long elapsedRealtimeMs) {
createCameraTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
}
public void noteCameraTurnedOffLocked(long elapsedRealtimeMs) {
if (mCameraTurnedOnTimer != null) {
mCameraTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
public void noteResetCameraLocked(long elapsedRealtimeMs) {
if (mCameraTurnedOnTimer != null) {
mCameraTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
}
}
public StopwatchTimer createForegroundActivityTimerLocked() {
if (mForegroundActivityTimer == null) {
mForegroundActivityTimer = new StopwatchTimer(mBsi.mClock, Uid.this,
FOREGROUND_ACTIVITY, null, mBsi.mOnBatteryTimeBase);
}
return mForegroundActivityTimer;
}
public StopwatchTimer createForegroundServiceTimerLocked() {
if (mForegroundServiceTimer == null) {
mForegroundServiceTimer = new StopwatchTimer(mBsi.mClock, Uid.this,
FOREGROUND_SERVICE, null, mBsi.mOnBatteryTimeBase);
}
return mForegroundServiceTimer;
}
public DualTimer createAggregatedPartialWakelockTimerLocked() {
if (mAggregatedPartialWakelockTimer == null) {
mAggregatedPartialWakelockTimer = new DualTimer(mBsi.mClock, this,
AGGREGATED_WAKE_TYPE_PARTIAL, null,
mBsi.mOnBatteryScreenOffTimeBase, mOnBatteryScreenOffBackgroundTimeBase);
}
return mAggregatedPartialWakelockTimer;
}
public DualTimer createBluetoothScanTimerLocked() {
if (mBluetoothScanTimer == null) {
mBluetoothScanTimer = new DualTimer(mBsi.mClock, Uid.this, BLUETOOTH_SCAN_ON,
mBsi.mBluetoothScanOnTimers, mBsi.mOnBatteryTimeBase,
mOnBatteryBackgroundTimeBase);
}
return mBluetoothScanTimer;
}
public DualTimer createBluetoothUnoptimizedScanTimerLocked() {
if (mBluetoothUnoptimizedScanTimer == null) {
mBluetoothUnoptimizedScanTimer = new DualTimer(mBsi.mClock, Uid.this,
BLUETOOTH_UNOPTIMIZED_SCAN_ON, null,
mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase);
}
return mBluetoothUnoptimizedScanTimer;
}
public void noteBluetoothScanStartedLocked(long elapsedRealtimeMs,
boolean isUnoptimized) {
createBluetoothScanTimerLocked().startRunningLocked(elapsedRealtimeMs);
if (isUnoptimized) {
createBluetoothUnoptimizedScanTimerLocked().startRunningLocked(elapsedRealtimeMs);
}
}
public void noteBluetoothScanStoppedLocked(long elapsedRealtimeMs, boolean isUnoptimized) {
if (mBluetoothScanTimer != null) {
mBluetoothScanTimer.stopRunningLocked(elapsedRealtimeMs);
}
if (isUnoptimized && mBluetoothUnoptimizedScanTimer != null) {
mBluetoothUnoptimizedScanTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
public void noteResetBluetoothScanLocked(long elapsedRealtimeMs) {
if (mBluetoothScanTimer != null) {
mBluetoothScanTimer.stopAllRunningLocked(elapsedRealtimeMs);
}
if (mBluetoothUnoptimizedScanTimer != null) {
mBluetoothUnoptimizedScanTimer.stopAllRunningLocked(elapsedRealtimeMs);
}
}
public Counter createBluetoothScanResultCounterLocked() {
if (mBluetoothScanResultCounter == null) {
mBluetoothScanResultCounter = new Counter(mBsi.mOnBatteryTimeBase);
}
return mBluetoothScanResultCounter;
}
public Counter createBluetoothScanResultBgCounterLocked() {
if (mBluetoothScanResultBgCounter == null) {
mBluetoothScanResultBgCounter = new Counter(mOnBatteryBackgroundTimeBase);
}
return mBluetoothScanResultBgCounter;
}
public void noteBluetoothScanResultsLocked(int numNewResults) {
createBluetoothScanResultCounterLocked().addAtomic(numNewResults);
// Uses background timebase, so the count will only be incremented if uid in background.
createBluetoothScanResultBgCounterLocked().addAtomic(numNewResults);
}
@Override
public void noteActivityResumedLocked(long elapsedRealtimeMs) {
// We always start, since we want multiple foreground PIDs to nest
createForegroundActivityTimerLocked().startRunningLocked(elapsedRealtimeMs);
}
@Override
public void noteActivityPausedLocked(long elapsedRealtimeMs) {
if (mForegroundActivityTimer != null) {
mForegroundActivityTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
public void noteForegroundServiceResumedLocked(long elapsedRealtimeMs) {
createForegroundServiceTimerLocked().startRunningLocked(elapsedRealtimeMs);
}
public void noteForegroundServicePausedLocked(long elapsedRealtimeMs) {
if (mForegroundServiceTimer != null) {
mForegroundServiceTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
public BatchTimer createVibratorOnTimerLocked() {
if (mVibratorOnTimer == null) {
mVibratorOnTimer = new BatchTimer(mBsi.mClock, Uid.this, VIBRATOR_ON,
mBsi.mOnBatteryTimeBase);
}
return mVibratorOnTimer;
}
public void noteVibratorOnLocked(long durationMillis, long elapsedRealtimeMs) {
createVibratorOnTimerLocked().addDuration(mBsi, durationMillis, elapsedRealtimeMs);
}
public void noteVibratorOffLocked(long elapsedRealtimeMs) {
if (mVibratorOnTimer != null) {
mVibratorOnTimer.abortLastDuration(mBsi, elapsedRealtimeMs);
}
}
@Override
@UnsupportedAppUsage
public long getWifiRunningTime(long elapsedRealtimeUs, int which) {
if (mWifiRunningTimer == null) {
return 0;
}
return mWifiRunningTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override
public long getFullWifiLockTime(long elapsedRealtimeUs, int which) {
if (mFullWifiLockTimer == null) {
return 0;
}
return mFullWifiLockTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override
@UnsupportedAppUsage
public long getWifiScanTime(long elapsedRealtimeUs, int which) {
if (mWifiScanTimer == null) {
return 0;
}
return mWifiScanTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override
public int getWifiScanCount(int which) {
if (mWifiScanTimer == null) {
return 0;
}
return mWifiScanTimer.getCountLocked(which);
}
@Override
public Timer getWifiScanTimer() {
return mWifiScanTimer;
}
@Override
public int getWifiScanBackgroundCount(int which) {
if (mWifiScanTimer == null || mWifiScanTimer.getSubTimer() == null) {
return 0;
}
return mWifiScanTimer.getSubTimer().getCountLocked(which);
}
@Override
public long getWifiScanActualTime(final long elapsedRealtimeUs) {
if (mWifiScanTimer == null) {
return 0;
}
final long elapsedRealtimeMs = (elapsedRealtimeUs + 500) / 1000;
return mWifiScanTimer.getTotalDurationMsLocked(elapsedRealtimeMs) * 1000;
}
@Override
public long getWifiScanBackgroundTime(final long elapsedRealtimeUs) {
if (mWifiScanTimer == null || mWifiScanTimer.getSubTimer() == null) {
return 0;
}
final long elapsedRealtimeMs = (elapsedRealtimeUs + 500) / 1000;
return mWifiScanTimer.getSubTimer().getTotalDurationMsLocked(elapsedRealtimeMs) * 1000;
}
@Override
public Timer getWifiScanBackgroundTimer() {
if (mWifiScanTimer == null) {
return null;
}
return mWifiScanTimer.getSubTimer();
}
@Override
public long getWifiBatchedScanTime(int csphBin, long elapsedRealtimeUs, int which) {
if (csphBin < 0 || csphBin >= NUM_WIFI_BATCHED_SCAN_BINS) return 0;
if (mWifiBatchedScanTimer[csphBin] == null) {
return 0;
}
return mWifiBatchedScanTimer[csphBin].getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override
public int getWifiBatchedScanCount(int csphBin, int which) {
if (csphBin < 0 || csphBin >= NUM_WIFI_BATCHED_SCAN_BINS) return 0;
if (mWifiBatchedScanTimer[csphBin] == null) {
return 0;
}
return mWifiBatchedScanTimer[csphBin].getCountLocked(which);
}
@Override
public long getWifiMulticastTime(long elapsedRealtimeUs, int which) {
if (mWifiMulticastTimer == null) {
return 0;
}
return mWifiMulticastTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override
public Timer getAudioTurnedOnTimer() {
return mAudioTurnedOnTimer;
}
@Override
public Timer getVideoTurnedOnTimer() {
return mVideoTurnedOnTimer;
}
@Override
public Timer getFlashlightTurnedOnTimer() {
return mFlashlightTurnedOnTimer;
}
@Override
public Timer getCameraTurnedOnTimer() {
return mCameraTurnedOnTimer;
}
@Override
public Timer getForegroundActivityTimer() {
return mForegroundActivityTimer;
}
@Override
public Timer getForegroundServiceTimer() {
return mForegroundServiceTimer;
}
@Override
public Timer getBluetoothScanTimer() {
return mBluetoothScanTimer;
}
@Override
public Timer getBluetoothScanBackgroundTimer() {
if (mBluetoothScanTimer == null) {
return null;
}
return mBluetoothScanTimer.getSubTimer();
}
@Override
public Timer getBluetoothUnoptimizedScanTimer() {
return mBluetoothUnoptimizedScanTimer;
}
@Override
public Timer getBluetoothUnoptimizedScanBackgroundTimer() {
if (mBluetoothUnoptimizedScanTimer == null) {
return null;
}
return mBluetoothUnoptimizedScanTimer.getSubTimer();
}
@Override
public Counter getBluetoothScanResultCounter() {
return mBluetoothScanResultCounter;
}
@Override
public Counter getBluetoothScanResultBgCounter() {
return mBluetoothScanResultBgCounter;
}
void makeProcessState(int i, Parcel in) {
if (i < 0 || i >= NUM_PROCESS_STATE) return;
detachIfNotNull(mProcessStateTimer[i]);
if (in == null) {
mProcessStateTimer[i] = new StopwatchTimer(mBsi.mClock, this, PROCESS_STATE, null,
mBsi.mOnBatteryTimeBase);
} else {
mProcessStateTimer[i] = new StopwatchTimer(mBsi.mClock, this, PROCESS_STATE, null,
mBsi.mOnBatteryTimeBase, in);
}
}
@Override
public long getProcessStateTime(int state, long elapsedRealtimeUs, int which) {
if (state < 0 || state >= NUM_PROCESS_STATE) return 0;
if (mProcessStateTimer[state] == null) {
return 0;
}
return mProcessStateTimer[state].getTotalTimeLocked(elapsedRealtimeUs, which);
}
@Override
public Timer getProcessStateTimer(int state) {
if (state < 0 || state >= NUM_PROCESS_STATE) return null;
return mProcessStateTimer[state];
}
@Override
public Timer getVibratorOnTimer() {
return mVibratorOnTimer;
}
@Override
public void noteUserActivityLocked(int type) {
if (mUserActivityCounters == null) {
initUserActivityLocked();
}
if (type >= 0 && type < NUM_USER_ACTIVITY_TYPES) {
mUserActivityCounters[type].stepAtomic();
} else {
Slog.w(TAG, "Unknown user activity type " + type + " was specified.",
new Throwable());
}
}
@Override
public boolean hasUserActivity() {
return mUserActivityCounters != null;
}
@Override
public int getUserActivityCount(int type, int which) {
if (mUserActivityCounters == null) {
return 0;
}
return mUserActivityCounters[type].getCountLocked(which);
}
void makeWifiBatchedScanBin(int i, Parcel in) {
if (i < 0 || i >= NUM_WIFI_BATCHED_SCAN_BINS) return;
ArrayList<StopwatchTimer> collected = mBsi.mWifiBatchedScanTimers.get(i);
if (collected == null) {
collected = new ArrayList<StopwatchTimer>();
mBsi.mWifiBatchedScanTimers.put(i, collected);
}
detachIfNotNull(mWifiBatchedScanTimer[i]);
if (in == null) {
mWifiBatchedScanTimer[i] = new StopwatchTimer(mBsi.mClock, this, WIFI_BATCHED_SCAN,
collected, mBsi.mOnBatteryTimeBase);
} else {
mWifiBatchedScanTimer[i] = new StopwatchTimer(mBsi.mClock, this, WIFI_BATCHED_SCAN,
collected, mBsi.mOnBatteryTimeBase, in);
}
}
void initUserActivityLocked() {
detachIfNotNull(mUserActivityCounters);
mUserActivityCounters = new Counter[NUM_USER_ACTIVITY_TYPES];
for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
mUserActivityCounters[i] = new Counter(mBsi.mOnBatteryTimeBase);
}
}
void noteNetworkActivityLocked(int type, long deltaBytes, long deltaPackets) {
ensureNetworkActivityLocked();
if (type >= 0 && type < NUM_NETWORK_ACTIVITY_TYPES) {
mNetworkByteActivityCounters[type].addCountLocked(deltaBytes);
mNetworkPacketActivityCounters[type].addCountLocked(deltaPackets);
} else {
Slog.w(TAG, "Unknown network activity type " + type + " was specified.",
new Throwable());
}
}
void noteMobileRadioActiveTimeLocked(long batteryUptimeDeltaUs, long elapsedTimeMs) {
ensureNetworkActivityLocked();
getMobileRadioActiveTimeCounter().increment(batteryUptimeDeltaUs, elapsedTimeMs);
mMobileRadioActiveCount.addCountLocked(1);
}
private TimeMultiStateCounter getMobileRadioActiveTimeCounter() {
if (mMobileRadioActiveTime == null) {
final long timestampMs = mBsi.mClock.elapsedRealtime();
mMobileRadioActiveTime = new TimeMultiStateCounter(
mBsi.mOnBatteryTimeBase, BatteryConsumer.PROCESS_STATE_COUNT, timestampMs);
mMobileRadioActiveTime.setState(
mapUidProcessStateToBatteryConsumerProcessState(mProcessState),
timestampMs);
mMobileRadioActiveTime.update(0, timestampMs);
}
return mMobileRadioActiveTime;
}
@Override
public boolean hasNetworkActivity() {
return mNetworkByteActivityCounters != null;
}
@Override
public long getNetworkActivityBytes(int type, int which) {
if (mNetworkByteActivityCounters != null && type >= 0
&& type < mNetworkByteActivityCounters.length) {
return mNetworkByteActivityCounters[type].getCountLocked(which);
} else {
return 0;
}
}
@Override
public long getNetworkActivityPackets(int type, int which) {
if (mNetworkPacketActivityCounters != null && type >= 0
&& type < mNetworkPacketActivityCounters.length) {
return mNetworkPacketActivityCounters[type].getCountLocked(which);
} else {
return 0;
}
}
@Override
public long getMobileRadioActiveTime(int which) {
return getMobileRadioActiveTimeInProcessState(BatteryConsumer.PROCESS_STATE_ANY);
}
@Override
public long getMobileRadioActiveTimeInProcessState(
@BatteryConsumer.ProcessState int processState) {
if (mMobileRadioActiveTime == null) {
return 0;
}
if (processState == BatteryConsumer.PROCESS_STATE_ANY) {
return mMobileRadioActiveTime.getTotalCountLocked();
} else {
return mMobileRadioActiveTime.getCountForProcessState(processState);
}
}
@Override
public int getMobileRadioActiveCount(int which) {
return mMobileRadioActiveCount != null
? (int)mMobileRadioActiveCount.getCountLocked(which) : 0;
}
@Override
public long getUserCpuTimeUs(int which) {
return mUserCpuTime.getCountLocked(which);
}
@Override
public long getSystemCpuTimeUs(int which) {
return mSystemCpuTime.getCountLocked(which);
}
@Override
public long getTimeAtCpuSpeed(int cluster, int step, int which) {
if (mCpuClusterSpeedTimesUs != null) {
if (cluster >= 0 && cluster < mCpuClusterSpeedTimesUs.length) {
final LongSamplingCounter[] cpuSpeedTimesUs = mCpuClusterSpeedTimesUs[cluster];
if (cpuSpeedTimesUs != null) {
if (step >= 0 && step < cpuSpeedTimesUs.length) {
final LongSamplingCounter c = cpuSpeedTimesUs[step];
if (c != null) {
return c.getCountLocked(which);
}
}
}
}
}
return 0;
}
public void noteMobileRadioApWakeupLocked() {
if (mMobileRadioApWakeupCount == null) {
mMobileRadioApWakeupCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
}
mMobileRadioApWakeupCount.addCountLocked(1);
}
@Override
public long getMobileRadioApWakeupCount(int which) {
if (mMobileRadioApWakeupCount != null) {
return mMobileRadioApWakeupCount.getCountLocked(which);
}
return 0;
}
public void noteWifiRadioApWakeupLocked() {
if (mWifiRadioApWakeupCount == null) {
mWifiRadioApWakeupCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
}
mWifiRadioApWakeupCount.addCountLocked(1);
}
@Override
public long getWifiRadioApWakeupCount(int which) {
if (mWifiRadioApWakeupCount != null) {
return mWifiRadioApWakeupCount.getCountLocked(which);
}
return 0;
}
@Override
public void getDeferredJobsCheckinLineLocked(StringBuilder sb, int which) {
sb.setLength(0);
final int deferredEventCount = mJobsDeferredEventCount.getCountLocked(which);
if (deferredEventCount == 0) {
return;
}
final int deferredCount = mJobsDeferredCount.getCountLocked(which);
final long totalLatency = mJobsFreshnessTimeMs.getCountLocked(which);
sb.append(deferredEventCount); sb.append(',');
sb.append(deferredCount); sb.append(',');
sb.append(totalLatency);
for (int i = 0; i < JOB_FRESHNESS_BUCKETS.length; i++) {
if (mJobsFreshnessBuckets[i] == null) {
sb.append(",0");
} else {
sb.append(",");
sb.append(mJobsFreshnessBuckets[i].getCountLocked(which));
}
}
}
@Override
public void getDeferredJobsLineLocked(StringBuilder sb, int which) {
sb.setLength(0);
final int deferredEventCount = mJobsDeferredEventCount.getCountLocked(which);
if (deferredEventCount == 0) {
return;
}
final int deferredCount = mJobsDeferredCount.getCountLocked(which);
final long totalLatency = mJobsFreshnessTimeMs.getCountLocked(which);
sb.append("times="); sb.append(deferredEventCount); sb.append(", ");
sb.append("count="); sb.append(deferredCount); sb.append(", ");
sb.append("totalLatencyMs="); sb.append(totalLatency); sb.append(", ");
for (int i = 0; i < JOB_FRESHNESS_BUCKETS.length; i++) {
sb.append("<"); sb.append(JOB_FRESHNESS_BUCKETS[i]); sb.append("ms=");
if (mJobsFreshnessBuckets[i] == null) {
sb.append("0");
} else {
sb.append(mJobsFreshnessBuckets[i].getCountLocked(which));
}
sb.append(" ");
}
}
void ensureNetworkActivityLocked() {
if (mNetworkByteActivityCounters != null) {
return;
}
mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
mNetworkPacketActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
mNetworkByteActivityCounters[i] = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
mNetworkPacketActivityCounters[i] = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
}
mMobileRadioActiveCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
}
/**
* Clear all stats for this uid. Returns true if the uid is completely
* inactive so can be dropped.
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public boolean reset(long uptimeUs, long realtimeUs, int resetReason) {
boolean active = false;
mOnBatteryBackgroundTimeBase.init(uptimeUs, realtimeUs);
mOnBatteryScreenOffBackgroundTimeBase.init(uptimeUs, realtimeUs);
if (mWifiRunningTimer != null) {
active |= !mWifiRunningTimer.reset(false, realtimeUs);
active |= mWifiRunning;
}
if (mFullWifiLockTimer != null) {
active |= !mFullWifiLockTimer.reset(false, realtimeUs);
active |= mFullWifiLockOut;
}
if (mWifiScanTimer != null) {
active |= !mWifiScanTimer.reset(false, realtimeUs);
active |= mWifiScanStarted;
}
if (mWifiBatchedScanTimer != null) {
for (int i = 0; i < NUM_WIFI_BATCHED_SCAN_BINS; i++) {
if (mWifiBatchedScanTimer[i] != null) {
active |= !mWifiBatchedScanTimer[i].reset(false, realtimeUs);
}
}
active |= (mWifiBatchedScanBinStarted != NO_BATCHED_SCAN_STARTED);
}
if (mWifiMulticastTimer != null) {
active |= !mWifiMulticastTimer.reset(false, realtimeUs);
active |= (mWifiMulticastWakelockCount > 0);
}
active |= !resetIfNotNull(mAudioTurnedOnTimer, false, realtimeUs);
active |= !resetIfNotNull(mVideoTurnedOnTimer, false, realtimeUs);
active |= !resetIfNotNull(mFlashlightTurnedOnTimer, false, realtimeUs);
active |= !resetIfNotNull(mCameraTurnedOnTimer, false, realtimeUs);
active |= !resetIfNotNull(mForegroundActivityTimer, false, realtimeUs);
active |= !resetIfNotNull(mForegroundServiceTimer, false, realtimeUs);
active |= !resetIfNotNull(mAggregatedPartialWakelockTimer, false, realtimeUs);
active |= !resetIfNotNull(mBluetoothScanTimer, false, realtimeUs);
active |= !resetIfNotNull(mBluetoothUnoptimizedScanTimer, false, realtimeUs);
resetIfNotNull(mBluetoothScanResultCounter, false, realtimeUs);
resetIfNotNull(mBluetoothScanResultBgCounter, false, realtimeUs);
if (mProcessStateTimer != null) {
for (int i = 0; i < NUM_PROCESS_STATE; i++) {
active |= !resetIfNotNull(mProcessStateTimer[i], false, realtimeUs);
}
active |= (mProcessState != Uid.PROCESS_STATE_NONEXISTENT);
}
if (mVibratorOnTimer != null) {
if (mVibratorOnTimer.reset(false, realtimeUs)) {
mVibratorOnTimer.detach();
mVibratorOnTimer = null;
} else {
active = true;
}
}
resetIfNotNull(mUserActivityCounters, false, realtimeUs);
resetIfNotNull(mNetworkByteActivityCounters, false, realtimeUs);
resetIfNotNull(mNetworkPacketActivityCounters, false, realtimeUs);
resetIfNotNull(mMobileRadioActiveTime, false, realtimeUs);
resetIfNotNull(mMobileRadioActiveCount, false, realtimeUs);
resetIfNotNull(mWifiControllerActivity, false, realtimeUs);
resetIfNotNull(mBluetoothControllerActivity, false, realtimeUs);
resetIfNotNull(mModemControllerActivity, false, realtimeUs);
if (resetReason == RESET_REASON_MEASURED_ENERGY_BUCKETS_CHANGE) {
mUidMeasuredEnergyStats = null;
} else {
MeasuredEnergyStats.resetIfNotNull(mUidMeasuredEnergyStats);
}
resetIfNotNull(mUserCpuTime, false, realtimeUs);
resetIfNotNull(mSystemCpuTime, false, realtimeUs);
resetIfNotNull(mCpuClusterSpeedTimesUs, false, realtimeUs);
resetIfNotNull(mCpuFreqTimeMs, false, realtimeUs /* unused */);
resetIfNotNull(mScreenOffCpuFreqTimeMs, false, realtimeUs /* unused */);
resetIfNotNull(mCpuActiveTimeMs, false, realtimeUs /* unused */);
resetIfNotNull(mCpuClusterTimesMs, false, realtimeUs /* unused */);
resetIfNotNull(mProcStateTimeMs, false, realtimeUs /* unused */);
resetIfNotNull(mProcStateScreenOffTimeMs, false, realtimeUs /* unused */);
resetIfNotNull(mMobileRadioApWakeupCount, false, realtimeUs);
resetIfNotNull(mWifiRadioApWakeupCount, false, realtimeUs);
final ArrayMap<String, Wakelock> wakeStats = mWakelockStats.getMap();
for (int iw=wakeStats.size()-1; iw>=0; iw--) {
Wakelock wl = wakeStats.valueAt(iw);
if (wl.reset(realtimeUs)) {
wakeStats.removeAt(iw);
} else {
active = true;
}
}
final long realtimeMs = realtimeUs / 1000;
mWakelockStats.cleanup(realtimeMs);
final ArrayMap<String, DualTimer> syncStats = mSyncStats.getMap();
for (int is=syncStats.size()-1; is>=0; is--) {
DualTimer timer = syncStats.valueAt(is);
if (timer.reset(false, realtimeUs)) {
syncStats.removeAt(is);
timer.detach();
} else {
active = true;
}
}
mSyncStats.cleanup(realtimeMs);
final ArrayMap<String, DualTimer> jobStats = mJobStats.getMap();
for (int ij=jobStats.size()-1; ij>=0; ij--) {
DualTimer timer = jobStats.valueAt(ij);
if (timer.reset(false, realtimeUs)) {
jobStats.removeAt(ij);
timer.detach();
} else {
active = true;
}
}
mJobStats.cleanup(realtimeMs);
mJobCompletions.clear();
resetIfNotNull(mJobsDeferredEventCount, false, realtimeUs);
resetIfNotNull(mJobsDeferredCount, false, realtimeUs);
resetIfNotNull(mJobsFreshnessTimeMs, false, realtimeUs /* unused */);
resetIfNotNull(mJobsFreshnessBuckets, false, realtimeUs);
for (int ise = mSensorStats.size() - 1; ise >= 0; ise--) {
Sensor s = mSensorStats.valueAt(ise);
if (s.reset(realtimeUs)) {
mSensorStats.removeAt(ise);
} else {
active = true;
}
}
for (int ip = mProcessStats.size() - 1; ip >= 0; ip--) {
Proc proc = mProcessStats.valueAt(ip);
proc.detach();
}
mProcessStats.clear();
for (int i = mPids.size() - 1; i >= 0; i--) {
Pid pid = mPids.valueAt(i);
if (pid.mWakeNesting > 0) {
active = true;
} else {
mPids.removeAt(i);
}
}
for(int i = mPackageStats.size() - 1; i >= 0; i--) {
Pkg p = mPackageStats.valueAt(i);
p.detach();
}
mPackageStats.clear();
mBinderCallCount = 0;
mBinderCallStats.clear();
mProportionalSystemServiceUsage = 0;
mLastStepUserTimeMs = mLastStepSystemTimeMs = 0;
mCurStepUserTimeMs = mCurStepSystemTimeMs = 0;
return !active;
}
/**
* This method MUST be called whenever the Uid object is destructed, otherwise it is a
* memory leak in {@link TimeBase#mObservers} list.
* Typically the Uid object is destructed when it is removed from
* {@link BatteryStatsImpl#mUidStats}
*/
void detachFromTimeBase() {
detachIfNotNull(mWifiRunningTimer);
detachIfNotNull(mFullWifiLockTimer);
detachIfNotNull(mWifiScanTimer);
detachIfNotNull(mWifiBatchedScanTimer);
detachIfNotNull(mWifiMulticastTimer);
detachIfNotNull(mAudioTurnedOnTimer);
detachIfNotNull(mVideoTurnedOnTimer);
detachIfNotNull(mFlashlightTurnedOnTimer);
detachIfNotNull(mCameraTurnedOnTimer);
detachIfNotNull(mForegroundActivityTimer);
detachIfNotNull(mForegroundServiceTimer);
detachIfNotNull(mAggregatedPartialWakelockTimer);
detachIfNotNull(mBluetoothScanTimer);
detachIfNotNull(mBluetoothUnoptimizedScanTimer);
detachIfNotNull(mBluetoothScanResultCounter);
detachIfNotNull(mBluetoothScanResultBgCounter);
detachIfNotNull(mProcessStateTimer);
detachIfNotNull(mVibratorOnTimer);
detachIfNotNull(mUserActivityCounters);
detachIfNotNull(mNetworkByteActivityCounters);
detachIfNotNull(mNetworkPacketActivityCounters);
detachIfNotNull(mMobileRadioActiveTime);
detachIfNotNull(mMobileRadioActiveCount);
detachIfNotNull(mMobileRadioApWakeupCount);
detachIfNotNull(mWifiRadioApWakeupCount);
detachIfNotNull(mWifiControllerActivity);
detachIfNotNull(mBluetoothControllerActivity);
detachIfNotNull(mModemControllerActivity);
mPids.clear();
detachIfNotNull(mUserCpuTime);
detachIfNotNull(mSystemCpuTime);
detachIfNotNull(mCpuClusterSpeedTimesUs);
detachIfNotNull(mCpuActiveTimeMs);
detachIfNotNull(mCpuFreqTimeMs);
detachIfNotNull(mScreenOffCpuFreqTimeMs);
detachIfNotNull(mCpuClusterTimesMs);
detachIfNotNull(mProcStateTimeMs);
detachIfNotNull(mProcStateScreenOffTimeMs);
final ArrayMap<String, Wakelock> wakeStats = mWakelockStats.getMap();
for (int iw = wakeStats.size() - 1; iw >= 0; iw--) {
Wakelock wl = wakeStats.valueAt(iw);
wl.detachFromTimeBase();
}
final ArrayMap<String, DualTimer> syncStats = mSyncStats.getMap();
for (int is = syncStats.size() - 1; is >= 0; is--) {
DualTimer timer = syncStats.valueAt(is);
detachIfNotNull(timer);
}
final ArrayMap<String, DualTimer> jobStats = mJobStats.getMap();
for (int ij = jobStats.size() - 1; ij >= 0; ij--) {
DualTimer timer = jobStats.valueAt(ij);
detachIfNotNull(timer);
}
detachIfNotNull(mJobsDeferredEventCount);
detachIfNotNull(mJobsDeferredCount);
detachIfNotNull(mJobsFreshnessTimeMs);
detachIfNotNull(mJobsFreshnessBuckets);
for (int ise = mSensorStats.size() - 1; ise >= 0; ise--) {
Sensor s = mSensorStats.valueAt(ise);
s.detachFromTimeBase();
}
for (int ip= mProcessStats.size() - 1; ip >= 0; ip--) {
Proc proc = mProcessStats.valueAt(ip);
proc.detach();
}
mProcessStats.clear();
for(int i = mPackageStats.size() - 1; i >= 0; i--) {
Pkg p = mPackageStats.valueAt(i);
p.detach();
}
mPackageStats.clear();
}
void writeJobCompletionsToParcelLocked(Parcel out) {
int NJC = mJobCompletions.size();
out.writeInt(NJC);
for (int ijc=0; ijc<NJC; ijc++) {
out.writeString(mJobCompletions.keyAt(ijc));
SparseIntArray types = mJobCompletions.valueAt(ijc);
int NT = types.size();
out.writeInt(NT);
for (int it=0; it<NT; it++) {
out.writeInt(types.keyAt(it));
out.writeInt(types.valueAt(it));
}
}
}
void writeToParcelLocked(Parcel out, long uptimeUs, long elapsedRealtimeUs) {
mOnBatteryBackgroundTimeBase.writeToParcel(out, uptimeUs, elapsedRealtimeUs);
mOnBatteryScreenOffBackgroundTimeBase.writeToParcel(out, uptimeUs, elapsedRealtimeUs);
final ArrayMap<String, Wakelock> wakeStats = mWakelockStats.getMap();
int NW = wakeStats.size();
out.writeInt(NW);
for (int iw=0; iw<NW; iw++) {
out.writeString(wakeStats.keyAt(iw));
Uid.Wakelock wakelock = wakeStats.valueAt(iw);
wakelock.writeToParcelLocked(out, elapsedRealtimeUs);
}
final ArrayMap<String, DualTimer> syncStats = mSyncStats.getMap();
int NS = syncStats.size();
out.writeInt(NS);
for (int is=0; is<NS; is++) {
out.writeString(syncStats.keyAt(is));
DualTimer timer = syncStats.valueAt(is);
Timer.writeTimerToParcel(out, timer, elapsedRealtimeUs);
}
final ArrayMap<String, DualTimer> jobStats = mJobStats.getMap();
int NJ = jobStats.size();
out.writeInt(NJ);
for (int ij=0; ij<NJ; ij++) {
out.writeString(jobStats.keyAt(ij));
DualTimer timer = jobStats.valueAt(ij);
Timer.writeTimerToParcel(out, timer, elapsedRealtimeUs);
}
writeJobCompletionsToParcelLocked(out);
mJobsDeferredEventCount.writeToParcel(out);
mJobsDeferredCount.writeToParcel(out);
mJobsFreshnessTimeMs.writeToParcel(out);
for (int i = 0; i < JOB_FRESHNESS_BUCKETS.length; i++) {
Counter.writeCounterToParcel(out, mJobsFreshnessBuckets[i]);
}
int NSE = mSensorStats.size();
out.writeInt(NSE);
for (int ise=0; ise<NSE; ise++) {
out.writeInt(mSensorStats.keyAt(ise));
Uid.Sensor sensor = mSensorStats.valueAt(ise);
sensor.writeToParcelLocked(out, elapsedRealtimeUs);
}
int NP = mProcessStats.size();
out.writeInt(NP);
for (int ip=0; ip<NP; ip++) {
out.writeString(mProcessStats.keyAt(ip));
Uid.Proc proc = mProcessStats.valueAt(ip);
proc.writeToParcelLocked(out);
}
out.writeInt(mPackageStats.size());
for (Map.Entry<String, Uid.Pkg> pkgEntry : mPackageStats.entrySet()) {
out.writeString(pkgEntry.getKey());
Uid.Pkg pkg = pkgEntry.getValue();
pkg.writeToParcelLocked(out);
}
if (mWifiRunningTimer != null) {
out.writeInt(1);
mWifiRunningTimer.writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
if (mFullWifiLockTimer != null) {
out.writeInt(1);
mFullWifiLockTimer.writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
if (mWifiScanTimer != null) {
out.writeInt(1);
mWifiScanTimer.writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
for (int i = 0; i < NUM_WIFI_BATCHED_SCAN_BINS; i++) {
if (mWifiBatchedScanTimer[i] != null) {
out.writeInt(1);
mWifiBatchedScanTimer[i].writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
}
if (mWifiMulticastTimer != null) {
out.writeInt(1);
mWifiMulticastTimer.writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
if (mAudioTurnedOnTimer != null) {
out.writeInt(1);
mAudioTurnedOnTimer.writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
if (mVideoTurnedOnTimer != null) {
out.writeInt(1);
mVideoTurnedOnTimer.writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
if (mFlashlightTurnedOnTimer != null) {
out.writeInt(1);
mFlashlightTurnedOnTimer.writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
if (mCameraTurnedOnTimer != null) {
out.writeInt(1);
mCameraTurnedOnTimer.writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
if (mForegroundActivityTimer != null) {
out.writeInt(1);
mForegroundActivityTimer.writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
if (mForegroundServiceTimer != null) {
out.writeInt(1);
mForegroundServiceTimer.writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
if (mAggregatedPartialWakelockTimer != null) {
out.writeInt(1);
mAggregatedPartialWakelockTimer.writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
if (mBluetoothScanTimer != null) {
out.writeInt(1);
mBluetoothScanTimer.writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
if (mBluetoothUnoptimizedScanTimer != null) {
out.writeInt(1);
mBluetoothUnoptimizedScanTimer.writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
if (mBluetoothScanResultCounter != null) {
out.writeInt(1);
mBluetoothScanResultCounter.writeToParcel(out);
} else {
out.writeInt(0);
}
if (mBluetoothScanResultBgCounter != null) {
out.writeInt(1);
mBluetoothScanResultBgCounter.writeToParcel(out);
} else {
out.writeInt(0);
}
for (int i = 0; i < NUM_PROCESS_STATE; i++) {
if (mProcessStateTimer[i] != null) {
out.writeInt(1);
mProcessStateTimer[i].writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
}
if (mVibratorOnTimer != null) {
out.writeInt(1);
mVibratorOnTimer.writeToParcel(out, elapsedRealtimeUs);
} else {
out.writeInt(0);
}
if (mUserActivityCounters != null) {
out.writeInt(1);
for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
mUserActivityCounters[i].writeToParcel(out);
}
} else {
out.writeInt(0);
}
if (mNetworkByteActivityCounters != null) {
out.writeInt(1);
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
mNetworkByteActivityCounters[i].writeToParcel(out);
mNetworkPacketActivityCounters[i].writeToParcel(out);
}
if (mMobileRadioActiveTime != null) {
out.writeBoolean(true);
mMobileRadioActiveTime.writeToParcel(out);
} else {
out.writeBoolean(false);
}
mMobileRadioActiveCount.writeToParcel(out);
} else {
out.writeInt(0);
}
if (mWifiControllerActivity != null) {
out.writeInt(1);
mWifiControllerActivity.writeToParcel(out, 0);
} else {
out.writeInt(0);
}
if (mBluetoothControllerActivity != null) {
out.writeInt(1);
mBluetoothControllerActivity.writeToParcel(out, 0);
} else {
out.writeInt(0);
}
if (mModemControllerActivity != null) {
out.writeInt(1);
mModemControllerActivity.writeToParcel(out, 0);
} else {
out.writeInt(0);
}
if (mUidMeasuredEnergyStats != null) {
out.writeInt(1);
mUidMeasuredEnergyStats.writeToParcel(out);
} else {
out.writeInt(0);
}
mUserCpuTime.writeToParcel(out);
mSystemCpuTime.writeToParcel(out);
mBsi.writeCpuSpeedCountersToParcel(out, mCpuClusterSpeedTimesUs);
LongSamplingCounterArray.writeToParcel(out, mCpuFreqTimeMs);
LongSamplingCounterArray.writeToParcel(out, mScreenOffCpuFreqTimeMs);
if (mCpuActiveTimeMs != null) {
out.writeInt(mCpuActiveTimeMs.getStateCount());
mCpuActiveTimeMs.writeToParcel(out);
} else {
out.writeInt(0);
}
mCpuClusterTimesMs.writeToParcel(out);
if (mProcStateTimeMs != null) {
out.writeInt(mProcStateTimeMs.getStateCount());
mProcStateTimeMs.writeToParcel(out);
} else {
out.writeInt(0);
}
if (mProcStateScreenOffTimeMs != null) {
out.writeInt(mProcStateScreenOffTimeMs.getStateCount());
mProcStateScreenOffTimeMs.writeToParcel(out);
} else {
out.writeInt(0);
}
if (mMobileRadioApWakeupCount != null) {
out.writeInt(1);
mMobileRadioApWakeupCount.writeToParcel(out);
} else {
out.writeInt(0);
}
if (mWifiRadioApWakeupCount != null) {
out.writeInt(1);
mWifiRadioApWakeupCount.writeToParcel(out);
} else {
out.writeInt(0);
}
out.writeDouble(mProportionalSystemServiceUsage);
}
void readJobCompletionsFromParcelLocked(Parcel in) {
int numJobCompletions = in.readInt();
mJobCompletions.clear();
for (int j = 0; j < numJobCompletions; j++) {
String jobName = in.readString();
int numTypes = in.readInt();
if (numTypes > 0) {
SparseIntArray types = new SparseIntArray();
for (int k = 0; k < numTypes; k++) {
int type = in.readInt();
int count = in.readInt();
types.put(type, count);
}
mJobCompletions.put(jobName, types);
}
}
}
@GuardedBy("mBsi")
void readFromParcelLocked(TimeBase timeBase, TimeBase screenOffTimeBase, Parcel in) {
final long timestampMs = mBsi.mClock.elapsedRealtime();
mOnBatteryBackgroundTimeBase.readFromParcel(in);
mOnBatteryScreenOffBackgroundTimeBase.readFromParcel(in);
int numWakelocks = in.readInt();
mWakelockStats.clear();
for (int j = 0; j < numWakelocks; j++) {
String wakelockName = in.readString();
Uid.Wakelock wakelock = new Wakelock(mBsi, this);
wakelock.readFromParcelLocked(
timeBase, screenOffTimeBase, mOnBatteryScreenOffBackgroundTimeBase, in);
mWakelockStats.add(wakelockName, wakelock);
}
int numSyncs = in.readInt();
mSyncStats.clear();
for (int j = 0; j < numSyncs; j++) {
String syncName = in.readString();
if (in.readInt() != 0) {
mSyncStats.add(syncName, new DualTimer(mBsi.mClock, Uid.this, SYNC, null,
mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase, in));
}
}
int numJobs = in.readInt();
mJobStats.clear();
for (int j = 0; j < numJobs; j++) {
String jobName = in.readString();
if (in.readInt() != 0) {
mJobStats.add(jobName, new DualTimer(mBsi.mClock, Uid.this, JOB, null,
mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase, in));
}
}
readJobCompletionsFromParcelLocked(in);
mJobsDeferredEventCount = new Counter(mBsi.mOnBatteryTimeBase, in);
mJobsDeferredCount = new Counter(mBsi.mOnBatteryTimeBase, in);
mJobsFreshnessTimeMs = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
for (int i = 0; i < JOB_FRESHNESS_BUCKETS.length; i++) {
mJobsFreshnessBuckets[i] = Counter.readCounterFromParcel(mBsi.mOnBatteryTimeBase,
in);
}
int numSensors = in.readInt();
mSensorStats.clear();
for (int k = 0; k < numSensors; k++) {
int sensorNumber = in.readInt();
Uid.Sensor sensor = new Sensor(mBsi, this, sensorNumber);
sensor.readFromParcelLocked(mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase,
in);
mSensorStats.put(sensorNumber, sensor);
}
int numProcs = in.readInt();
mProcessStats.clear();
for (int k = 0; k < numProcs; k++) {
String processName = in.readString();
Uid.Proc proc = new Proc(mBsi, processName);
proc.readFromParcelLocked(in);
mProcessStats.put(processName, proc);
}
int numPkgs = in.readInt();
mPackageStats.clear();
for (int l = 0; l < numPkgs; l++) {
String packageName = in.readString();
Uid.Pkg pkg = new Pkg(mBsi);
pkg.readFromParcelLocked(in);
mPackageStats.put(packageName, pkg);
}
mWifiRunning = false;
if (in.readInt() != 0) {
mWifiRunningTimer = new StopwatchTimer(mBsi.mClock, Uid.this, WIFI_RUNNING,
mBsi.mWifiRunningTimers, mBsi.mOnBatteryTimeBase, in);
} else {
mWifiRunningTimer = null;
}
mFullWifiLockOut = false;
if (in.readInt() != 0) {
mFullWifiLockTimer = new StopwatchTimer(mBsi.mClock, Uid.this, FULL_WIFI_LOCK,
mBsi.mFullWifiLockTimers, mBsi.mOnBatteryTimeBase, in);
} else {
mFullWifiLockTimer = null;
}
mWifiScanStarted = false;
if (in.readInt() != 0) {
mWifiScanTimer = new DualTimer(mBsi.mClock, Uid.this, WIFI_SCAN,
mBsi.mWifiScanTimers, mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase,
in);
} else {
mWifiScanTimer = null;
}
mWifiBatchedScanBinStarted = NO_BATCHED_SCAN_STARTED;
for (int i = 0; i < NUM_WIFI_BATCHED_SCAN_BINS; i++) {
if (in.readInt() != 0) {
makeWifiBatchedScanBin(i, in);
} else {
mWifiBatchedScanTimer[i] = null;
}
}
mWifiMulticastWakelockCount = 0;
if (in.readInt() != 0) {
mWifiMulticastTimer = new StopwatchTimer(mBsi.mClock, Uid.this,
WIFI_MULTICAST_ENABLED,
mBsi.mWifiMulticastTimers, mBsi.mOnBatteryTimeBase, in);
} else {
mWifiMulticastTimer = null;
}
if (in.readInt() != 0) {
mAudioTurnedOnTimer = new StopwatchTimer(mBsi.mClock, Uid.this, AUDIO_TURNED_ON,
mBsi.mAudioTurnedOnTimers, mBsi.mOnBatteryTimeBase, in);
} else {
mAudioTurnedOnTimer = null;
}
if (in.readInt() != 0) {
mVideoTurnedOnTimer = new StopwatchTimer(mBsi.mClock, Uid.this, VIDEO_TURNED_ON,
mBsi.mVideoTurnedOnTimers, mBsi.mOnBatteryTimeBase, in);
} else {
mVideoTurnedOnTimer = null;
}
if (in.readInt() != 0) {
mFlashlightTurnedOnTimer = new StopwatchTimer(mBsi.mClock, Uid.this,
FLASHLIGHT_TURNED_ON, mBsi.mFlashlightTurnedOnTimers, mBsi.mOnBatteryTimeBase, in);
} else {
mFlashlightTurnedOnTimer = null;
}
if (in.readInt() != 0) {
mCameraTurnedOnTimer = new StopwatchTimer(mBsi.mClock, Uid.this, CAMERA_TURNED_ON,
mBsi.mCameraTurnedOnTimers, mBsi.mOnBatteryTimeBase, in);
} else {
mCameraTurnedOnTimer = null;
}
if (in.readInt() != 0) {
mForegroundActivityTimer = new StopwatchTimer(mBsi.mClock, Uid.this,
FOREGROUND_ACTIVITY, null, mBsi.mOnBatteryTimeBase, in);
} else {
mForegroundActivityTimer = null;
}
if (in.readInt() != 0) {
mForegroundServiceTimer = new StopwatchTimer(mBsi.mClock, Uid.this,
FOREGROUND_SERVICE, null, mBsi.mOnBatteryTimeBase, in);
} else {
mForegroundServiceTimer = null;
}
if (in.readInt() != 0) {
mAggregatedPartialWakelockTimer = new DualTimer(mBsi.mClock, this,
AGGREGATED_WAKE_TYPE_PARTIAL, null,
mBsi.mOnBatteryScreenOffTimeBase, mOnBatteryScreenOffBackgroundTimeBase,
in);
} else {
mAggregatedPartialWakelockTimer = null;
}
if (in.readInt() != 0) {
mBluetoothScanTimer = new DualTimer(mBsi.mClock, Uid.this, BLUETOOTH_SCAN_ON,
mBsi.mBluetoothScanOnTimers, mBsi.mOnBatteryTimeBase,
mOnBatteryBackgroundTimeBase, in);
} else {
mBluetoothScanTimer = null;
}
if (in.readInt() != 0) {
mBluetoothUnoptimizedScanTimer = new DualTimer(mBsi.mClock, Uid.this,
BLUETOOTH_UNOPTIMIZED_SCAN_ON, null,
mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase, in);
} else {
mBluetoothUnoptimizedScanTimer = null;
}
if (in.readInt() != 0) {
mBluetoothScanResultCounter = new Counter(mBsi.mOnBatteryTimeBase, in);
} else {
mBluetoothScanResultCounter = null;
}
if (in.readInt() != 0) {
mBluetoothScanResultBgCounter = new Counter(mOnBatteryBackgroundTimeBase, in);
} else {
mBluetoothScanResultBgCounter = null;
}
mProcessState = Uid.PROCESS_STATE_NONEXISTENT;
for (int i = 0; i < NUM_PROCESS_STATE; i++) {
if (in.readInt() != 0) {
makeProcessState(i, in);
} else {
mProcessStateTimer[i] = null;
}
}
if (in.readInt() != 0) {
mVibratorOnTimer = new BatchTimer(mBsi.mClock, Uid.this, VIBRATOR_ON,
mBsi.mOnBatteryTimeBase, in);
} else {
mVibratorOnTimer = null;
}
if (in.readInt() != 0) {
mUserActivityCounters = new Counter[NUM_USER_ACTIVITY_TYPES];
for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
mUserActivityCounters[i] = new Counter(mBsi.mOnBatteryTimeBase, in);
}
} else {
mUserActivityCounters = null;
}
if (in.readInt() != 0) {
mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
mNetworkPacketActivityCounters
= new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
mNetworkByteActivityCounters[i]
= new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
mNetworkPacketActivityCounters[i]
= new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
}
if (in.readBoolean()) {
mMobileRadioActiveTime = TimeMultiStateCounter.readFromParcel(in,
mBsi.mOnBatteryTimeBase, BatteryConsumer.PROCESS_STATE_COUNT,
timestampMs);
}
mMobileRadioActiveCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
} else {
mNetworkByteActivityCounters = null;
mNetworkPacketActivityCounters = null;
}
if (in.readInt() != 0) {
mWifiControllerActivity = new ControllerActivityCounterImpl(mBsi.mClock,
mBsi.mOnBatteryTimeBase, NUM_WIFI_TX_LEVELS, in);
} else {
mWifiControllerActivity = null;
}
if (in.readInt() != 0) {
mBluetoothControllerActivity = new ControllerActivityCounterImpl(mBsi.mClock,
mBsi.mOnBatteryTimeBase, NUM_BT_TX_LEVELS, in);
} else {
mBluetoothControllerActivity = null;
}
if (in.readInt() != 0) {
mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mClock,
mBsi.mOnBatteryTimeBase, ModemActivityInfo.getNumTxPowerLevels(), in);
} else {
mModemControllerActivity = null;
}
if (in.readInt() != 0) {
mUidMeasuredEnergyStats = new MeasuredEnergyStats(mBsi.mMeasuredEnergyStatsConfig,
in);
}
mUserCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
mSystemCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
mCpuClusterSpeedTimesUs = mBsi.readCpuSpeedCountersFromParcel(in);
mCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel(in, mBsi.mOnBatteryTimeBase);
mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel(
in, mBsi.mOnBatteryScreenOffTimeBase);
int stateCount = in.readInt();
if (stateCount != 0) {
mCpuActiveTimeMs = TimeMultiStateCounter.readFromParcel(in,
mBsi.mOnBatteryTimeBase, BatteryConsumer.PROCESS_STATE_COUNT,
timestampMs);
}
mCpuClusterTimesMs = new LongSamplingCounterArray(mBsi.mOnBatteryTimeBase, in);
stateCount = in.readInt();
if (stateCount != 0) {
mProcStateTimeMs = TimeInFreqMultiStateCounter.readFromParcel(in,
mBsi.mOnBatteryTimeBase, PROC_STATE_TIME_COUNTER_STATE_COUNT,
mBsi.getCpuFreqCount(), mBsi.mClock.elapsedRealtime());
} else {
mProcStateTimeMs = null;
}
stateCount = in.readInt();
if (stateCount != 0) {
mProcStateScreenOffTimeMs = TimeInFreqMultiStateCounter.readFromParcel(in,
mBsi.mOnBatteryScreenOffTimeBase, PROC_STATE_TIME_COUNTER_STATE_COUNT,
mBsi.getCpuFreqCount(), mBsi.mClock.elapsedRealtime());
} else {
mProcStateScreenOffTimeMs = null;
}
if (in.readInt() != 0) {
mMobileRadioApWakeupCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
} else {
mMobileRadioApWakeupCount = null;
}
if (in.readInt() != 0) {
mWifiRadioApWakeupCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
} else {
mWifiRadioApWakeupCount = null;
}
mProportionalSystemServiceUsage = in.readDouble();
}
public void noteJobsDeferredLocked(int numDeferred, long sinceLast) {
mJobsDeferredEventCount.addAtomic(1);
mJobsDeferredCount.addAtomic(numDeferred);
if (sinceLast != 0) {
// Add the total time, which can be divided by the event count to get an average
mJobsFreshnessTimeMs.addCountLocked(sinceLast);
// Also keep track of how many times there were in these different buckets.
for (int i = 0; i < JOB_FRESHNESS_BUCKETS.length; i++) {
if (sinceLast < JOB_FRESHNESS_BUCKETS[i]) {
if (mJobsFreshnessBuckets[i] == null) {
mJobsFreshnessBuckets[i] = new Counter(
mBsi.mOnBatteryTimeBase);
}
mJobsFreshnessBuckets[i].addAtomic(1);
break;
}
}
}
}
// Reusable object used as a key to lookup values in mBinderCallStats
private static BinderCallStats sTempBinderCallStats = new BinderCallStats();
/**
* Notes incoming binder call stats associated with this work source UID.
*/
public void noteBinderCallStatsLocked(long incrementalCallCount,
Collection<BinderCallsStats.CallStat> callStats) {
if (DEBUG) {
Slog.d(TAG, "noteBinderCalls() workSourceUid = [" + mUid + "], "
+ " incrementalCallCount: " + incrementalCallCount + " callStats = ["
+ new ArrayList<>(callStats) + "]");
}
mBinderCallCount += incrementalCallCount;
for (BinderCallsStats.CallStat stat : callStats) {
BinderCallStats bcs;
sTempBinderCallStats.binderClass = stat.binderClass;
sTempBinderCallStats.transactionCode = stat.transactionCode;
int index = mBinderCallStats.indexOf(sTempBinderCallStats);
if (index >= 0) {
bcs = mBinderCallStats.valueAt(index);
} else {
bcs = new BinderCallStats();
bcs.binderClass = stat.binderClass;
bcs.transactionCode = stat.transactionCode;
mBinderCallStats.add(bcs);
}
bcs.callCount += stat.incrementalCallCount;
bcs.recordedCallCount = stat.recordedCallCount;
bcs.recordedCpuTimeMicros = stat.cpuTimeMicros;
}
}
/**
* The statistics associated with a particular wake lock.
*/
public static class Wakelock extends BatteryStats.Uid.Wakelock {
/**
* BatteryStatsImpl that we are associated with.
*/
protected BatteryStatsImpl mBsi;
/**
* BatteryStatsImpl that we are associated with.
*/
protected Uid mUid;
/**
* How long (in ms) this uid has been keeping the device partially awake.
* Tracks both the total time and the time while the app was in the background.
*/
DualTimer mTimerPartial;
/**
* How long (in ms) this uid has been keeping the device fully awake.
*/
StopwatchTimer mTimerFull;
/**
* How long (in ms) this uid has had a window keeping the device awake.
*/
StopwatchTimer mTimerWindow;
/**
* How long (in ms) this uid has had a draw wake lock.
*/
StopwatchTimer mTimerDraw;
public Wakelock(BatteryStatsImpl bsi, Uid uid) {
mBsi = bsi;
mUid = uid;
}
/**
* Reads a possibly null Timer from a Parcel. The timer is associated with the
* proper timer pool from the given BatteryStatsImpl object.
*
* @param in the Parcel to be read from.
* return a new Timer, or null.
*/
private StopwatchTimer readStopwatchTimerFromParcel(int type,
ArrayList<StopwatchTimer> pool, TimeBase timeBase, Parcel in) {
if (in.readInt() == 0) {
return null;
}
return new StopwatchTimer(mBsi.mClock, mUid, type, pool, timeBase, in);
}
/**
* Reads a possibly null Timer from a Parcel. The timer is associated with the
* proper timer pool from the given BatteryStatsImpl object.
*
* @param in the Parcel to be read from.
* return a new Timer, or null.
*/
private DualTimer readDualTimerFromParcel(int type, ArrayList<StopwatchTimer> pool,
TimeBase timeBase, TimeBase bgTimeBase, Parcel in) {
if (in.readInt() == 0) {
return null;
}
return new DualTimer(mBsi.mClock, mUid, type, pool, timeBase, bgTimeBase, in);
}
boolean reset(long elapsedRealtimeUs) {
boolean wlactive = false;
wlactive |= !resetIfNotNull(mTimerFull, false, elapsedRealtimeUs);
wlactive |= !resetIfNotNull(mTimerPartial, false, elapsedRealtimeUs);
wlactive |= !resetIfNotNull(mTimerWindow, false, elapsedRealtimeUs);
wlactive |= !resetIfNotNull(mTimerDraw, false, elapsedRealtimeUs);
if (!wlactive) {
detachIfNotNull(mTimerFull);
mTimerFull = null;
detachIfNotNull(mTimerPartial);
mTimerPartial = null;
detachIfNotNull(mTimerWindow);
mTimerWindow = null;
detachIfNotNull(mTimerDraw);
mTimerDraw = null;
}
return !wlactive;
}
void readFromParcelLocked(TimeBase timeBase, TimeBase screenOffTimeBase,
TimeBase screenOffBgTimeBase, Parcel in) {
mTimerPartial = readDualTimerFromParcel(WAKE_TYPE_PARTIAL,
mBsi.mPartialTimers, screenOffTimeBase, screenOffBgTimeBase, in);
mTimerFull = readStopwatchTimerFromParcel(WAKE_TYPE_FULL,
mBsi.mFullTimers, timeBase, in);
mTimerWindow = readStopwatchTimerFromParcel(WAKE_TYPE_WINDOW,
mBsi.mWindowTimers, timeBase, in);
mTimerDraw = readStopwatchTimerFromParcel(WAKE_TYPE_DRAW,
mBsi.mDrawTimers, timeBase, in);
}
void writeToParcelLocked(Parcel out, long elapsedRealtimeUs) {
Timer.writeTimerToParcel(out, mTimerPartial, elapsedRealtimeUs);
Timer.writeTimerToParcel(out, mTimerFull, elapsedRealtimeUs);
Timer.writeTimerToParcel(out, mTimerWindow, elapsedRealtimeUs);
Timer.writeTimerToParcel(out, mTimerDraw, elapsedRealtimeUs);
}
@Override
@UnsupportedAppUsage
public Timer getWakeTime(int type) {
switch (type) {
case WAKE_TYPE_FULL: return mTimerFull;
case WAKE_TYPE_PARTIAL: return mTimerPartial;
case WAKE_TYPE_WINDOW: return mTimerWindow;
case WAKE_TYPE_DRAW: return mTimerDraw;
default: throw new IllegalArgumentException("type = " + type);
}
}
public void detachFromTimeBase() {
detachIfNotNull(mTimerPartial);
detachIfNotNull(mTimerFull);
detachIfNotNull(mTimerWindow);
detachIfNotNull(mTimerDraw);
}
}
public static class Sensor extends BatteryStats.Uid.Sensor {
/**
* BatteryStatsImpl that we are associated with.
*/
protected BatteryStatsImpl mBsi;
/**
* Uid that we are associated with.
*/
protected Uid mUid;
final int mHandle;
DualTimer mTimer;
public Sensor(BatteryStatsImpl bsi, Uid uid, int handle) {
mBsi = bsi;
mUid = uid;
mHandle = handle;
}
private DualTimer readTimersFromParcel(
TimeBase timeBase, TimeBase bgTimeBase, Parcel in) {
if (in.readInt() == 0) {
return null;
}
ArrayList<StopwatchTimer> pool = mBsi.mSensorTimers.get(mHandle);
if (pool == null) {
pool = new ArrayList<StopwatchTimer>();
mBsi.mSensorTimers.put(mHandle, pool);
}
return new DualTimer(mBsi.mClock, mUid, 0, pool, timeBase, bgTimeBase, in);
}
boolean reset(long elapsedRealtimeUs) {
if (mTimer.reset(true, elapsedRealtimeUs)) {
mTimer = null;
return true;
}
return false;
}
void readFromParcelLocked(TimeBase timeBase, TimeBase bgTimeBase, Parcel in) {
mTimer = readTimersFromParcel(timeBase, bgTimeBase, in);
}
void writeToParcelLocked(Parcel out, long elapsedRealtimeUs) {
Timer.writeTimerToParcel(out, mTimer, elapsedRealtimeUs);
}
@Override
@UnsupportedAppUsage
public Timer getSensorTime() {
return mTimer;
}
@Override
public Timer getSensorBackgroundTime() {
if (mTimer == null) {
return null;
}
return mTimer.getSubTimer();
}
@Override
@UnsupportedAppUsage
public int getHandle() {
return mHandle;
}
public void detachFromTimeBase() {
detachIfNotNull(mTimer);
}
}
/**
* The statistics associated with a particular process.
*/
public static class Proc extends BatteryStats.Uid.Proc implements TimeBaseObs {
/**
* BatteryStatsImpl that we are associated with.
*/
protected BatteryStatsImpl mBsi;
/**
* The name of this process.
*/
final String mName;
/**
* Remains true until removed from the stats.
*/
boolean mActive = true;
/**
* Total time (in ms) spent executing in user code.
*/
long mUserTimeMs;
/**
* Total time (in ms) spent executing in kernel code.
*/
long mSystemTimeMs;
/**
* Amount of time (in ms) the process was running in the foreground.
*/
long mForegroundTimeMs;
/**
* Number of times the process has been started.
*/
int mStarts;
/**
* Number of times the process has crashed.
*/
int mNumCrashes;
/**
* Number of times the process has had an ANR.
*/
int mNumAnrs;
ArrayList<ExcessivePower> mExcessivePower;
public Proc(BatteryStatsImpl bsi, String name) {
mBsi = bsi;
mName = name;
mBsi.mOnBatteryTimeBase.add(this);
}
public void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs,
long baseRealtimeUs) {
}
public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs,
long baseRealtimeUs) {
}
@Override
public boolean reset(boolean detachIfReset, long elapsedRealtimeUs) {
if (detachIfReset) {
this.detach();
}
return true;
}
@Override
public void detach() {
mActive = false;
mBsi.mOnBatteryTimeBase.remove(this);
}
public int countExcessivePowers() {
return mExcessivePower != null ? mExcessivePower.size() : 0;
}
public ExcessivePower getExcessivePower(int i) {
if (mExcessivePower != null) {
return mExcessivePower.get(i);
}
return null;
}
public void addExcessiveCpu(long overTimeMs, long usedTimeMs) {
if (mExcessivePower == null) {
mExcessivePower = new ArrayList<ExcessivePower>();
}
ExcessivePower ew = new ExcessivePower();
ew.type = ExcessivePower.TYPE_CPU;
ew.overTime = overTimeMs;
ew.usedTime = usedTimeMs;
mExcessivePower.add(ew);
}
void writeExcessivePowerToParcelLocked(Parcel out) {
if (mExcessivePower == null) {
out.writeInt(0);
return;
}
final int N = mExcessivePower.size();
out.writeInt(N);
for (int i=0; i<N; i++) {
ExcessivePower ew = mExcessivePower.get(i);
out.writeInt(ew.type);
out.writeLong(ew.overTime);
out.writeLong(ew.usedTime);
}
}
void readExcessivePowerFromParcelLocked(Parcel in) {
final int N = in.readInt();
if (N == 0) {
mExcessivePower = null;
return;
}
if (N > 10000) {
throw new ParcelFormatException(
"File corrupt: too many excessive power entries " + N);
}
mExcessivePower = new ArrayList<>();
for (int i=0; i<N; i++) {
ExcessivePower ew = new ExcessivePower();
ew.type = in.readInt();
ew.overTime = in.readLong();
ew.usedTime = in.readLong();
mExcessivePower.add(ew);
}
}
void writeToParcelLocked(Parcel out) {
out.writeLong(mUserTimeMs);
out.writeLong(mSystemTimeMs);
out.writeLong(mForegroundTimeMs);
out.writeInt(mStarts);
out.writeInt(mNumCrashes);
out.writeInt(mNumAnrs);
writeExcessivePowerToParcelLocked(out);
}
void readFromParcelLocked(Parcel in) {
mUserTimeMs = in.readLong();
mSystemTimeMs = in.readLong();
mForegroundTimeMs = in.readLong();
mStarts = in.readInt();
mNumCrashes = in.readInt();
mNumAnrs = in.readInt();
readExcessivePowerFromParcelLocked(in);
}
@UnsupportedAppUsage
public void addCpuTimeLocked(int utimeMs, int stimeMs) {
addCpuTimeLocked(utimeMs, stimeMs, mBsi.mOnBatteryTimeBase.isRunning());
}
public void addCpuTimeLocked(int utimeMs, int stimeMs, boolean isRunning) {
if (isRunning) {
mUserTimeMs += utimeMs;
mSystemTimeMs += stimeMs;
}
}
@UnsupportedAppUsage
public void addForegroundTimeLocked(long ttimeMs) {
mForegroundTimeMs += ttimeMs;
}
@UnsupportedAppUsage
public void incStartsLocked() {
mStarts++;
}
public void incNumCrashesLocked() {
mNumCrashes++;
}
public void incNumAnrsLocked() {
mNumAnrs++;
}
@Override
public boolean isActive() {
return mActive;
}
@Override
@UnsupportedAppUsage
public long getUserTime(int which) {
return mUserTimeMs;
}
@Override
@UnsupportedAppUsage
public long getSystemTime(int which) {
return mSystemTimeMs;
}
@Override
@UnsupportedAppUsage
public long getForegroundTime(int which) {
return mForegroundTimeMs;
}
@Override
@UnsupportedAppUsage
public int getStarts(int which) {
return mStarts;
}
@Override
public int getNumCrashes(int which) {
return mNumCrashes;
}
@Override
public int getNumAnrs(int which) {
return mNumAnrs;
}
}
/**
* The statistics associated with a particular package.
*/
public static class Pkg extends BatteryStats.Uid.Pkg implements TimeBaseObs {
/**
* BatteryStatsImpl that we are associated with.
*/
protected BatteryStatsImpl mBsi;
/**
* Number of times wakeup alarms have occurred for this app.
* On screen-off timebase starting in report v25.
*/
ArrayMap<String, Counter> mWakeupAlarms = new ArrayMap<>();
/**
* The statics we have collected for this package's services.
*/
final ArrayMap<String, Serv> mServiceStats = new ArrayMap<>();
public Pkg(BatteryStatsImpl bsi) {
mBsi = bsi;
mBsi.mOnBatteryScreenOffTimeBase.add(this);
}
public void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs,
long baseRealtimeUs) {
}
public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs,
long baseRealtimeUs) {
}
@Override
public boolean reset(boolean detachIfReset, long elapsedRealtimeUs) {
if (detachIfReset) {
this.detach();
}
return true;
}
@Override
public void detach() {
mBsi.mOnBatteryScreenOffTimeBase.remove(this);
for (int j = mWakeupAlarms.size() - 1; j >= 0; j--) {
detachIfNotNull(mWakeupAlarms.valueAt(j));
}
for (int j = mServiceStats.size() - 1; j >= 0; j--) {
detachIfNotNull(mServiceStats.valueAt(j));
}
}
void readFromParcelLocked(Parcel in) {
int numWA = in.readInt();
mWakeupAlarms.clear();
for (int i=0; i<numWA; i++) {
String tag = in.readString();
mWakeupAlarms.put(tag, new Counter(mBsi.mOnBatteryScreenOffTimeBase, in));
}
int numServs = in.readInt();
mServiceStats.clear();
for (int m = 0; m < numServs; m++) {
String serviceName = in.readString();
Uid.Pkg.Serv serv = new Serv(mBsi);
mServiceStats.put(serviceName, serv);
serv.readFromParcelLocked(in);
}
}
void writeToParcelLocked(Parcel out) {
int numWA = mWakeupAlarms.size();
out.writeInt(numWA);
for (int i=0; i<numWA; i++) {
out.writeString(mWakeupAlarms.keyAt(i));
mWakeupAlarms.valueAt(i).writeToParcel(out);
}
final int NS = mServiceStats.size();
out.writeInt(NS);
for (int i=0; i<NS; i++) {
out.writeString(mServiceStats.keyAt(i));
Uid.Pkg.Serv serv = mServiceStats.valueAt(i);
serv.writeToParcelLocked(out);
}
}
@Override
public ArrayMap<String, ? extends BatteryStats.Counter> getWakeupAlarmStats() {
return mWakeupAlarms;
}
public void noteWakeupAlarmLocked(String tag) {
Counter c = mWakeupAlarms.get(tag);
if (c == null) {
c = new Counter(mBsi.mOnBatteryScreenOffTimeBase);
mWakeupAlarms.put(tag, c);
}
c.stepAtomic();
}
@Override
public ArrayMap<String, ? extends BatteryStats.Uid.Pkg.Serv> getServiceStats() {
return mServiceStats;
}
/**
* The statistics associated with a particular service.
*/
public static class Serv extends BatteryStats.Uid.Pkg.Serv implements TimeBaseObs {
/**
* BatteryStatsImpl that we are associated with.
*/
protected BatteryStatsImpl mBsi;
/**
* The android package in which this service resides.
*/
protected Pkg mPkg;
/**
* Total time (ms in battery uptime) the service has been left started.
*/
protected long mStartTimeMs;
/**
* If service has been started and not yet stopped, this is
* when it was started.
*/
protected long mRunningSinceMs;
/**
* True if we are currently running.
*/
protected boolean mRunning;
/**
* Total number of times startService() has been called.
*/
protected int mStarts;
/**
* Total time (ms in battery uptime) the service has been left launched.
*/
protected long mLaunchedTimeMs;
/**
* If service has been launched and not yet exited, this is
* when it was launched (ms in battery uptime).
*/
protected long mLaunchedSinceMs;
/**
* True if we are currently launched.
*/
protected boolean mLaunched;
/**
* Total number times the service has been launched.
*/
protected int mLaunches;
/**
* Construct a Serv. Also adds it to the on-battery time base as a listener.
*/
public Serv(BatteryStatsImpl bsi) {
mBsi = bsi;
mBsi.mOnBatteryTimeBase.add(this);
}
public void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs,
long baseRealtimeUs) {
}
public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs,
long baseRealtimeUs) {
}
@Override
public boolean reset(boolean detachIfReset, long elapsedRealtimeUs) {
if (detachIfReset) {
this.detach();
}
return true;
}
/**
* Remove this Serv as a listener from the time base.
Ms*/
@Override
public void detach() {
mBsi.mOnBatteryTimeBase.remove(this);
}
public void readFromParcelLocked(Parcel in) {
mStartTimeMs = in.readLong();
mRunningSinceMs = in.readLong();
mRunning = in.readInt() != 0;
mStarts = in.readInt();
mLaunchedTimeMs = in.readLong();
mLaunchedSinceMs = in.readLong();
mLaunched = in.readInt() != 0;
mLaunches = in.readInt();
}
public void writeToParcelLocked(Parcel out) {
out.writeLong(mStartTimeMs);
out.writeLong(mRunningSinceMs);
out.writeInt(mRunning ? 1 : 0);
out.writeInt(mStarts);
out.writeLong(mLaunchedTimeMs);
out.writeLong(mLaunchedSinceMs);
out.writeInt(mLaunched ? 1 : 0);
out.writeInt(mLaunches);
}
public long getLaunchTimeToNowLocked(long batteryUptimeMs) {
if (!mLaunched) return mLaunchedTimeMs;
return mLaunchedTimeMs + batteryUptimeMs - mLaunchedSinceMs;
}
public long getStartTimeToNowLocked(long batteryUptimeMs) {
if (!mRunning) return mStartTimeMs;
return mStartTimeMs + batteryUptimeMs - mRunningSinceMs;
}
@UnsupportedAppUsage
public void startLaunchedLocked() {
startLaunchedLocked(mBsi.mClock.uptimeMillis());
}
public void startLaunchedLocked(long uptimeMs) {
if (!mLaunched) {
mLaunches++;
mLaunchedSinceMs = mBsi.getBatteryUptimeLocked(uptimeMs) / 1000;
mLaunched = true;
}
}
@UnsupportedAppUsage
public void stopLaunchedLocked() {
stopLaunchedLocked(mBsi.mClock.uptimeMillis());
}
public void stopLaunchedLocked(long uptimeMs) {
if (mLaunched) {
long timeMs = mBsi.getBatteryUptimeLocked(uptimeMs) / 1000
- mLaunchedSinceMs;
if (timeMs > 0) {
mLaunchedTimeMs += timeMs;
} else {
mLaunches--;
}
mLaunched = false;
}
}
@UnsupportedAppUsage
public void startRunningLocked() {
startRunningLocked(mBsi.mClock.uptimeMillis());
}
public void startRunningLocked(long uptimeMs) {
if (!mRunning) {
mStarts++;
mRunningSinceMs = mBsi.getBatteryUptimeLocked(uptimeMs) / 1000;
mRunning = true;
}
}
@UnsupportedAppUsage
public void stopRunningLocked() {
stopRunningLocked(mBsi.mClock.uptimeMillis());
}
public void stopRunningLocked(long uptimeMs) {
if (mRunning) {
long timeMs = mBsi.getBatteryUptimeLocked(uptimeMs) / 1000
- mRunningSinceMs;
if (timeMs > 0) {
mStartTimeMs += timeMs;
} else {
mStarts--;
}
mRunning = false;
}
}
@UnsupportedAppUsage
public BatteryStatsImpl getBatteryStats() {
return mBsi;
}
@Override
public int getLaunches(int which) {
return mLaunches;
}
@Override
public long getStartTime(long now, int which) {
return getStartTimeToNowLocked(now);
}
@Override
public int getStarts(int which) {
return mStarts;
}
}
final Serv newServiceStatsLocked() {
return new Serv(mBsi);
}
}
private class ChildUid {
public final TimeMultiStateCounter cpuActiveCounter;
public final LongArrayMultiStateCounter cpuTimeInFreqCounter;
ChildUid() {
final long timestampMs = mBsi.mClock.elapsedRealtime();
cpuActiveCounter =
new TimeMultiStateCounter(mBsi.mOnBatteryTimeBase, 1, timestampMs);
cpuActiveCounter.setState(0, timestampMs);
if (mBsi.trackPerProcStateCpuTimes()) {
final int cpuFreqCount = mBsi.getCpuFreqCount();
cpuTimeInFreqCounter = new LongArrayMultiStateCounter(1, cpuFreqCount);
// Set initial values to all 0. This is a child UID and we want to include
// the entirety of its CPU time-in-freq stats into the parent's stats.
cpuTimeInFreqCounter.updateValues(
new LongArrayMultiStateCounter.LongArrayContainer(cpuFreqCount),
timestampMs);
} else {
cpuTimeInFreqCounter = null;
}
}
}
/**
* Retrieve the statistics object for a particular process, creating
* if needed.
*/
public Proc getProcessStatsLocked(String name) {
Proc ps = mProcessStats.get(name);
if (ps == null) {
ps = new Proc(mBsi, name);
mProcessStats.put(name, ps);
}
return ps;
}
@GuardedBy("mBsi")
public void updateUidProcessStateLocked(int procState,
long elapsedRealtimeMs, long uptimeMs) {
int uidRunningState;
// Make special note of Foreground Services
final boolean userAwareService =
(ActivityManager.isForegroundService(procState));
uidRunningState = BatteryStats.mapToInternalProcessState(procState);
if (mProcessState == uidRunningState && userAwareService == mInForegroundService) {
return;
}
if (mProcessState != uidRunningState) {
if (mProcessState != Uid.PROCESS_STATE_NONEXISTENT) {
mProcessStateTimer[mProcessState].stopRunningLocked(elapsedRealtimeMs);
}
if (uidRunningState != Uid.PROCESS_STATE_NONEXISTENT) {
if (mProcessStateTimer[uidRunningState] == null) {
makeProcessState(uidRunningState, null);
}
mProcessStateTimer[uidRunningState].startRunningLocked(elapsedRealtimeMs);
}
if (mBsi.trackPerProcStateCpuTimes()) {
mBsi.updateProcStateCpuTimesLocked(mUid, elapsedRealtimeMs);
LongArrayMultiStateCounter onBatteryCounter =
getProcStateTimeCounter(elapsedRealtimeMs).getCounter();
LongArrayMultiStateCounter onBatteryScreenOffCounter =
getProcStateScreenOffTimeCounter(elapsedRealtimeMs).getCounter();
onBatteryCounter.setState(uidRunningState, elapsedRealtimeMs);
onBatteryScreenOffCounter.setState(uidRunningState, elapsedRealtimeMs);
}
final int prevBatteryConsumerProcessState =
mapUidProcessStateToBatteryConsumerProcessState(mProcessState);
mProcessState = uidRunningState;
updateOnBatteryBgTimeBase(uptimeMs * 1000, elapsedRealtimeMs * 1000);
updateOnBatteryScreenOffBgTimeBase(uptimeMs * 1000, elapsedRealtimeMs * 1000);
final int batteryConsumerProcessState =
mapUidProcessStateToBatteryConsumerProcessState(uidRunningState);
getCpuActiveTimeCounter().setState(batteryConsumerProcessState, elapsedRealtimeMs);
getMobileRadioActiveTimeCounter()
.setState(batteryConsumerProcessState, elapsedRealtimeMs);
final ControllerActivityCounterImpl wifiControllerActivity =
getWifiControllerActivity();
if (wifiControllerActivity != null) {
wifiControllerActivity.setState(batteryConsumerProcessState, elapsedRealtimeMs);
}
final ControllerActivityCounterImpl bluetoothControllerActivity =
getBluetoothControllerActivity();
if (bluetoothControllerActivity != null) {
bluetoothControllerActivity.setState(batteryConsumerProcessState,
elapsedRealtimeMs);
}
final MeasuredEnergyStats energyStats =
getOrCreateMeasuredEnergyStatsIfSupportedLocked();
if (energyStats != null) {
energyStats.setState(batteryConsumerProcessState, elapsedRealtimeMs);
}
maybeScheduleExternalStatsSync(prevBatteryConsumerProcessState,
batteryConsumerProcessState);
}
if (userAwareService != mInForegroundService) {
if (userAwareService) {
noteForegroundServiceResumedLocked(elapsedRealtimeMs);
} else {
noteForegroundServicePausedLocked(elapsedRealtimeMs);
}
mInForegroundService = userAwareService;
}
}
@GuardedBy("mBsi")
private void maybeScheduleExternalStatsSync(
@BatteryConsumer.ProcessState int oldProcessState,
@BatteryConsumer.ProcessState int newProcessState) {
if (oldProcessState == newProcessState) {
return;
}
// Transitions between BACKGROUND and such non-foreground states like cached
// or nonexistent do not warrant doing a sync. If some of the stats for those
// proc states bleed into the PROCESS_STATE_BACKGROUND, that's ok.
if ((oldProcessState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED
&& newProcessState == BatteryConsumer.PROCESS_STATE_BACKGROUND)
|| (oldProcessState == BatteryConsumer.PROCESS_STATE_BACKGROUND
&& newProcessState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED)) {
return;
}
int flags = ExternalStatsSync.UPDATE_ON_PROC_STATE_CHANGE;
// Skip querying for inactive radio, where power usage is probably negligible.
if (!BatteryStatsImpl.isActiveRadioPowerState(mBsi.mMobileRadioPowerState)) {
flags &= ~ExternalStatsSync.UPDATE_RADIO;
}
mBsi.mExternalSync.scheduleSyncDueToProcessStateChange(flags,
mBsi.mConstants.PROC_STATE_CHANGE_COLLECTION_DELAY_MS);
}
/** Whether to consider Uid to be in the background for background timebase purposes. */
public boolean isInBackground() {
// Note that PROCESS_STATE_CACHED and Uid.PROCESS_STATE_NONEXISTENT is
// also considered to be 'background' for our purposes, because it's not foreground.
return mProcessState >= PROCESS_STATE_BACKGROUND;
}
public boolean updateOnBatteryBgTimeBase(long uptimeUs, long realtimeUs) {
boolean on = mBsi.mOnBatteryTimeBase.isRunning() && isInBackground();
return mOnBatteryBackgroundTimeBase.setRunning(on, uptimeUs, realtimeUs);
}
public boolean updateOnBatteryScreenOffBgTimeBase(long uptimeUs, long realtimeUs) {
boolean on = mBsi.mOnBatteryScreenOffTimeBase.isRunning() && isInBackground();
return mOnBatteryScreenOffBackgroundTimeBase.setRunning(on, uptimeUs, realtimeUs);
}
public SparseArray<? extends Pid> getPidStats() {
return mPids;
}
public Pid getPidStatsLocked(int pid) {
Pid p = mPids.get(pid);
if (p == null) {
p = new Pid();
mPids.put(pid, p);
}
return p;
}
/**
* Retrieve the statistics object for a particular service, creating
* if needed.
*/
public Pkg getPackageStatsLocked(String name) {
Pkg ps = mPackageStats.get(name);
if (ps == null) {
ps = new Pkg(mBsi);
mPackageStats.put(name, ps);
}
return ps;
}
/**
* Retrieve the statistics object for a particular service, creating
* if needed.
*/
public Pkg.Serv getServiceStatsLocked(String pkg, String serv) {
Pkg ps = getPackageStatsLocked(pkg);
Pkg.Serv ss = ps.mServiceStats.get(serv);
if (ss == null) {
ss = ps.newServiceStatsLocked();
ps.mServiceStats.put(serv, ss);
}
return ss;
}
public void readSyncSummaryFromParcelLocked(String name, Parcel in) {
DualTimer timer = mSyncStats.instantiateObject();
timer.readSummaryFromParcelLocked(in);
mSyncStats.add(name, timer);
}
public void readJobSummaryFromParcelLocked(String name, Parcel in) {
DualTimer timer = mJobStats.instantiateObject();
timer.readSummaryFromParcelLocked(in);
mJobStats.add(name, timer);
}
public void readWakeSummaryFromParcelLocked(String wlName, Parcel in) {
Wakelock wl = new Wakelock(mBsi, this);
mWakelockStats.add(wlName, wl);
if (in.readInt() != 0) {
getWakelockTimerLocked(wl, WAKE_TYPE_FULL).readSummaryFromParcelLocked(in);
}
if (in.readInt() != 0) {
getWakelockTimerLocked(wl, WAKE_TYPE_PARTIAL).readSummaryFromParcelLocked(in);
}
if (in.readInt() != 0) {
getWakelockTimerLocked(wl, WAKE_TYPE_WINDOW).readSummaryFromParcelLocked(in);
}
if (in.readInt() != 0) {
getWakelockTimerLocked(wl, WAKE_TYPE_DRAW).readSummaryFromParcelLocked(in);
}
}
public DualTimer getSensorTimerLocked(int sensor, boolean create) {
Sensor se = mSensorStats.get(sensor);
if (se == null) {
if (!create) {
return null;
}
se = new Sensor(mBsi, this, sensor);
mSensorStats.put(sensor, se);
}
DualTimer t = se.mTimer;
if (t != null) {
return t;
}
ArrayList<StopwatchTimer> timers = mBsi.mSensorTimers.get(sensor);
if (timers == null) {
timers = new ArrayList<StopwatchTimer>();
mBsi.mSensorTimers.put(sensor, timers);
}
t = new DualTimer(mBsi.mClock, this, BatteryStats.SENSOR, timers,
mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase);
se.mTimer = t;
return t;
}
public void noteStartSyncLocked(String name, long elapsedRealtimeMs) {
DualTimer t = mSyncStats.startObject(name, elapsedRealtimeMs);
if (t != null) {
t.startRunningLocked(elapsedRealtimeMs);
}
}
public void noteStopSyncLocked(String name, long elapsedRealtimeMs) {
DualTimer t = mSyncStats.stopObject(name, elapsedRealtimeMs);
if (t != null) {
t.stopRunningLocked(elapsedRealtimeMs);
}
}
public void noteStartJobLocked(String name, long elapsedRealtimeMs) {
DualTimer t = mJobStats.startObject(name, elapsedRealtimeMs);
if (t != null) {
t.startRunningLocked(elapsedRealtimeMs);
}
}
public void noteStopJobLocked(String name, long elapsedRealtimeMs, int stopReason) {
DualTimer t = mJobStats.stopObject(name, elapsedRealtimeMs);
if (t != null) {
t.stopRunningLocked(elapsedRealtimeMs);
}
if (mBsi.mOnBatteryTimeBase.isRunning()) {
SparseIntArray types = mJobCompletions.get(name);
if (types == null) {
types = new SparseIntArray();
mJobCompletions.put(name, types);
}
int last = types.get(stopReason, 0);
types.put(stopReason, last + 1);
}
}
public StopwatchTimer getWakelockTimerLocked(Wakelock wl, int type) {
if (wl == null) {
return null;
}
switch (type) {
case WAKE_TYPE_PARTIAL: {
DualTimer t = wl.mTimerPartial;
if (t == null) {
t = new DualTimer(mBsi.mClock, this, WAKE_TYPE_PARTIAL,
mBsi.mPartialTimers, mBsi.mOnBatteryScreenOffTimeBase,
mOnBatteryScreenOffBackgroundTimeBase);
wl.mTimerPartial = t;
}
return t;
}
case WAKE_TYPE_FULL: {
StopwatchTimer t = wl.mTimerFull;
if (t == null) {
t = new StopwatchTimer(mBsi.mClock, this, WAKE_TYPE_FULL,
mBsi.mFullTimers, mBsi.mOnBatteryTimeBase);
wl.mTimerFull = t;
}
return t;
}
case WAKE_TYPE_WINDOW: {
StopwatchTimer t = wl.mTimerWindow;
if (t == null) {
t = new StopwatchTimer(mBsi.mClock, this, WAKE_TYPE_WINDOW,
mBsi.mWindowTimers, mBsi.mOnBatteryTimeBase);
wl.mTimerWindow = t;
}
return t;
}
case WAKE_TYPE_DRAW: {
StopwatchTimer t = wl.mTimerDraw;
if (t == null) {
t = new StopwatchTimer(mBsi.mClock, this, WAKE_TYPE_DRAW,
mBsi.mDrawTimers, mBsi.mOnBatteryTimeBase);
wl.mTimerDraw = t;
}
return t;
}
default:
throw new IllegalArgumentException("type=" + type);
}
}
public void noteStartWakeLocked(int pid, String name, int type, long elapsedRealtimeMs) {
Wakelock wl = mWakelockStats.startObject(name, elapsedRealtimeMs);
if (wl != null) {
getWakelockTimerLocked(wl, type).startRunningLocked(elapsedRealtimeMs);
}
if (type == WAKE_TYPE_PARTIAL) {
createAggregatedPartialWakelockTimerLocked().startRunningLocked(elapsedRealtimeMs);
if (pid >= 0) {
Pid p = getPidStatsLocked(pid);
if (p.mWakeNesting++ == 0) {
p.mWakeStartMs = elapsedRealtimeMs;
}
}
}
}
public void noteStopWakeLocked(int pid, String name, int type, long elapsedRealtimeMs) {
Wakelock wl = mWakelockStats.stopObject(name, elapsedRealtimeMs);
if (wl != null) {
StopwatchTimer wlt = getWakelockTimerLocked(wl, type);
wlt.stopRunningLocked(elapsedRealtimeMs);
}
if (type == WAKE_TYPE_PARTIAL) {
if (mAggregatedPartialWakelockTimer != null) {
mAggregatedPartialWakelockTimer.stopRunningLocked(elapsedRealtimeMs);
}
if (pid >= 0) {
Pid p = mPids.get(pid);
if (p != null && p.mWakeNesting > 0) {
if (p.mWakeNesting-- == 1) {
p.mWakeSumMs += elapsedRealtimeMs - p.mWakeStartMs;
p.mWakeStartMs = 0;
}
}
}
}
}
public void reportExcessiveCpuLocked(String proc, long overTimeMs, long usedTimeMs) {
Proc p = getProcessStatsLocked(proc);
if (p != null) {
p.addExcessiveCpu(overTimeMs, usedTimeMs);
}
}
public void noteStartSensor(int sensor, long elapsedRealtimeMs) {
DualTimer t = getSensorTimerLocked(sensor, /* create= */ true);
t.startRunningLocked(elapsedRealtimeMs);
}
public void noteStopSensor(int sensor, long elapsedRealtimeMs) {
// Don't create a timer if one doesn't already exist
DualTimer t = getSensorTimerLocked(sensor, false);
if (t != null) {
t.stopRunningLocked(elapsedRealtimeMs);
}
}
public void noteStartGps(long elapsedRealtimeMs) {
noteStartSensor(Sensor.GPS, elapsedRealtimeMs);
}
public void noteStopGps(long elapsedRealtimeMs) {
noteStopSensor(Sensor.GPS, elapsedRealtimeMs);
}
public BatteryStatsImpl getBatteryStats() {
return mBsi;
}
}
@GuardedBy("this")
@Override
public long[] getCpuFreqs() {
if (!mCpuFreqsInitialized) {
mCpuFreqs = mCpuUidFreqTimeReader.readFreqs(mPowerProfile);
mCpuFreqsInitialized = true;
}
return mCpuFreqs;
}
@GuardedBy("this")
@Override
public int getCpuFreqCount() {
final long[] cpuFreqs = getCpuFreqs();
return cpuFreqs != null ? cpuFreqs.length : 0;
}
@GuardedBy("this")
private LongArrayMultiStateCounter.LongArrayContainer getCpuTimeInFreqContainer() {
if (mTmpCpuTimeInFreq == null) {
mTmpCpuTimeInFreq =
new LongArrayMultiStateCounter.LongArrayContainer(getCpuFreqCount());
}
return mTmpCpuTimeInFreq;
}
public BatteryStatsImpl(File systemDir, Handler handler, PlatformIdleStateCallback cb,
MeasuredEnergyRetriever energyStatsCb, UserInfoProvider userInfoProvider) {
this(Clock.SYSTEM_CLOCK, systemDir, handler, cb, energyStatsCb, userInfoProvider);
}
private BatteryStatsImpl(Clock clock, File systemDir, Handler handler,
PlatformIdleStateCallback cb, MeasuredEnergyRetriever energyStatsCb,
UserInfoProvider userInfoProvider) {
init(clock);
if (systemDir == null) {
mStatsFile = null;
mBatteryStatsHistory = new BatteryStatsHistory(mHistoryBuffer);
} else {
mStatsFile = new AtomicFile(new File(systemDir, "batterystats.bin"));
mBatteryStatsHistory = new BatteryStatsHistory(this, systemDir, mHistoryBuffer);
}
mCheckinFile = new AtomicFile(new File(systemDir, "batterystats-checkin.bin"));
mDailyFile = new AtomicFile(new File(systemDir, "batterystats-daily.xml"));
mHandler = new MyHandler(handler.getLooper());
mConstants = new Constants(mHandler);
mStartCount++;
initTimersAndCounters();
mOnBattery = mOnBatteryInternal = false;
long uptimeUs = mClock.uptimeMillis() * 1000;
long realtimeUs = mClock.elapsedRealtime() * 1000;
initTimes(uptimeUs, realtimeUs);
mStartPlatformVersion = mEndPlatformVersion = Build.ID;
initDischarge(realtimeUs);
clearHistoryLocked();
updateDailyDeadlineLocked();
mPlatformIdleStateCallback = cb;
mMeasuredEnergyRetriever = energyStatsCb;
mUserInfoProvider = userInfoProvider;
// Notify statsd that the system is initially not in doze.
mDeviceIdleMode = DEVICE_IDLE_MODE_OFF;
FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mDeviceIdleMode);
}
@VisibleForTesting
protected void initTimersAndCounters() {
mScreenOnTimer = new StopwatchTimer(mClock, null, -1, null, mOnBatteryTimeBase);
mScreenDozeTimer = new StopwatchTimer(mClock, null, -1, null, mOnBatteryTimeBase);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i] = new StopwatchTimer(mClock, null, -100 - i, null,
mOnBatteryTimeBase);
}
mPerDisplayBatteryStats = new DisplayBatteryStats[1];
mPerDisplayBatteryStats[0] = new DisplayBatteryStats(mClock, mOnBatteryTimeBase);
mInteractiveTimer = new StopwatchTimer(mClock, null, -10, null, mOnBatteryTimeBase);
mPowerSaveModeEnabledTimer = new StopwatchTimer(mClock, null, -2, null,
mOnBatteryTimeBase);
mDeviceIdleModeLightTimer = new StopwatchTimer(mClock, null, -11, null,
mOnBatteryTimeBase);
mDeviceIdleModeFullTimer = new StopwatchTimer(mClock, null, -14, null, mOnBatteryTimeBase);
mDeviceLightIdlingTimer = new StopwatchTimer(mClock, null, -15, null, mOnBatteryTimeBase);
mDeviceIdlingTimer = new StopwatchTimer(mClock, null, -12, null, mOnBatteryTimeBase);
mPhoneOnTimer = new StopwatchTimer(mClock, null, -3, null, mOnBatteryTimeBase);
for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(mClock, null, -200 - i, null,
mOnBatteryTimeBase);
}
mPhoneSignalScanningTimer = new StopwatchTimer(mClock, null, -200 + 1, null,
mOnBatteryTimeBase);
for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
mPhoneDataConnectionsTimer[i] = new StopwatchTimer(mClock, null, -300 - i, null,
mOnBatteryTimeBase);
}
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
mNetworkByteActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase);
mNetworkPacketActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase);
}
mWifiActivity = new ControllerActivityCounterImpl(mClock, mOnBatteryTimeBase,
NUM_WIFI_TX_LEVELS);
mBluetoothActivity = new ControllerActivityCounterImpl(mClock, mOnBatteryTimeBase,
NUM_BT_TX_LEVELS);
mModemActivity = new ControllerActivityCounterImpl(mClock, mOnBatteryTimeBase,
ModemActivityInfo.getNumTxPowerLevels());
mMobileRadioActiveTimer = new StopwatchTimer(mClock, null, -400, null, mOnBatteryTimeBase);
mMobileRadioActivePerAppTimer = new StopwatchTimer(mClock, null, -401, null,
mOnBatteryTimeBase);
mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase);
mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase);
mMobileRadioActiveUnknownCount = new LongSamplingCounter(mOnBatteryTimeBase);
mWifiMulticastWakelockTimer = new StopwatchTimer(mClock, null,
WIFI_AGGREGATE_MULTICAST_ENABLED, null, mOnBatteryTimeBase);
mWifiOnTimer = new StopwatchTimer(mClock, null, -4, null, mOnBatteryTimeBase);
mGlobalWifiRunningTimer = new StopwatchTimer(mClock, null, -5, null, mOnBatteryTimeBase);
for (int i=0; i<NUM_WIFI_STATES; i++) {
mWifiStateTimer[i] = new StopwatchTimer(mClock, null, -600 - i, null,
mOnBatteryTimeBase);
}
for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
mWifiSupplStateTimer[i] = new StopwatchTimer(mClock, null, -700 - i, null,
mOnBatteryTimeBase);
}
for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
mWifiSignalStrengthsTimer[i] = new StopwatchTimer(mClock, null, -800 - i, null,
mOnBatteryTimeBase);
}
mWifiActiveTimer = new StopwatchTimer(mClock, null, -900, null, mOnBatteryTimeBase);
for (int i=0; i< mGpsSignalQualityTimer.length; i++) {
mGpsSignalQualityTimer[i] = new StopwatchTimer(mClock, null, -1000 - i, null,
mOnBatteryTimeBase);
}
mAudioOnTimer = new StopwatchTimer(mClock, null, -7, null, mOnBatteryTimeBase);
mVideoOnTimer = new StopwatchTimer(mClock, null, -8, null, mOnBatteryTimeBase);
mFlashlightOnTimer = new StopwatchTimer(mClock, null, -9, null, mOnBatteryTimeBase);
mCameraOnTimer = new StopwatchTimer(mClock, null, -13, null, mOnBatteryTimeBase);
mBluetoothScanTimer = new StopwatchTimer(mClock, null, -14, null, mOnBatteryTimeBase);
mDischargeScreenOffCounter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase);
mDischargeScreenDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
mDischargeLightDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
mDischargeDeepDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
mDischargeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
mDischargeStartLevel = 0;
mDischargeUnplugLevel = 0;
mDischargePlugLevel = -1;
mDischargeCurrentLevel = 0;
mCurrentBatteryLevel = 0;
}
@UnsupportedAppUsage
public BatteryStatsImpl(Parcel p) {
this(Clock.SYSTEM_CLOCK, p);
}
public BatteryStatsImpl(Clock clock, Parcel p) {
init(clock);
mStatsFile = null;
mCheckinFile = null;
mDailyFile = null;
mHandler = null;
mExternalSync = null;
mConstants = new Constants(mHandler);
clearHistoryLocked();
mBatteryStatsHistory = new BatteryStatsHistory(mHistoryBuffer);
readFromParcel(p);
mPlatformIdleStateCallback = null;
mMeasuredEnergyRetriever = null;
}
public void setPowerProfileLocked(PowerProfile profile) {
mPowerProfile = profile;
// We need to initialize the KernelCpuSpeedReaders to read from
// the first cpu of each core. Once we have the PowerProfile, we have access to this
// information.
final int numClusters = mPowerProfile.getNumCpuClusters();
mKernelCpuSpeedReaders = new KernelCpuSpeedReader[numClusters];
int firstCpuOfCluster = 0;
for (int i = 0; i < numClusters; i++) {
final int numSpeedSteps = mPowerProfile.getNumSpeedStepsInCpuCluster(i);
mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster,
numSpeedSteps);
firstCpuOfCluster += mPowerProfile.getNumCoresInCpuCluster(i);
}
if (mEstimatedBatteryCapacityMah == -1) {
// Initialize the estimated battery capacity to a known preset one.
mEstimatedBatteryCapacityMah = (int) mPowerProfile.getBatteryCapacity();
}
setDisplayCountLocked(mPowerProfile.getNumDisplays());
}
PowerProfile getPowerProfile() {
return mPowerProfile;
}
/**
* Starts tracking CPU time-in-state for threads of the system server process,
* keeping a separate account of threads receiving incoming binder calls.
*/
public void startTrackingSystemServerCpuTime() {
mSystemServerCpuThreadReader.startTrackingThreadCpuTime();
}
public SystemServiceCpuThreadTimes getSystemServiceCpuThreadTimes() {
return mSystemServerCpuThreadReader.readAbsolute();
}
public void setCallback(BatteryCallback cb) {
mCallback = cb;
}
public void setRadioScanningTimeoutLocked(long timeoutUs) {
if (mPhoneSignalScanningTimer != null) {
mPhoneSignalScanningTimer.setTimeout(timeoutUs);
}
}
public void setExternalStatsSyncLocked(ExternalStatsSync sync) {
mExternalSync = sync;
}
/**
* Initialize and set multi display timers and states.
*/
public void setDisplayCountLocked(int numDisplays) {
mPerDisplayBatteryStats = new DisplayBatteryStats[numDisplays];
for (int i = 0; i < numDisplays; i++) {
mPerDisplayBatteryStats[i] = new DisplayBatteryStats(mClock, mOnBatteryTimeBase);
}
}
public void updateDailyDeadlineLocked() {
// Get the current time.
long currentTimeMs = mDailyStartTimeMs = mClock.currentTimeMillis();
Calendar calDeadline = Calendar.getInstance();
calDeadline.setTimeInMillis(currentTimeMs);
// Move time up to the next day, ranging from 1am to 3pm.
calDeadline.set(Calendar.DAY_OF_YEAR, calDeadline.get(Calendar.DAY_OF_YEAR) + 1);
calDeadline.set(Calendar.MILLISECOND, 0);
calDeadline.set(Calendar.SECOND, 0);
calDeadline.set(Calendar.MINUTE, 0);
calDeadline.set(Calendar.HOUR_OF_DAY, 1);
mNextMinDailyDeadlineMs = calDeadline.getTimeInMillis();
calDeadline.set(Calendar.HOUR_OF_DAY, 3);
mNextMaxDailyDeadlineMs = calDeadline.getTimeInMillis();
}
public void recordDailyStatsIfNeededLocked(boolean settled, long currentTimeMs) {
if (currentTimeMs >= mNextMaxDailyDeadlineMs) {
recordDailyStatsLocked();
} else if (settled && currentTimeMs >= mNextMinDailyDeadlineMs) {
recordDailyStatsLocked();
} else if (currentTimeMs < (mDailyStartTimeMs - (1000 * 60 * 60 * 24))) {
recordDailyStatsLocked();
}
}
public void recordDailyStatsLocked() {
DailyItem item = new DailyItem();
item.mStartTime = mDailyStartTimeMs;
item.mEndTime = mClock.currentTimeMillis();
boolean hasData = false;
if (mDailyDischargeStepTracker.mNumStepDurations > 0) {
hasData = true;
item.mDischargeSteps = new LevelStepTracker(
mDailyDischargeStepTracker.mNumStepDurations,
mDailyDischargeStepTracker.mStepDurations);
}
if (mDailyChargeStepTracker.mNumStepDurations > 0) {
hasData = true;
item.mChargeSteps = new LevelStepTracker(
mDailyChargeStepTracker.mNumStepDurations,
mDailyChargeStepTracker.mStepDurations);
}
if (mDailyPackageChanges != null) {
hasData = true;
item.mPackageChanges = mDailyPackageChanges;
mDailyPackageChanges = null;
}
mDailyDischargeStepTracker.init();
mDailyChargeStepTracker.init();
updateDailyDeadlineLocked();
if (hasData) {
final long startTimeMs = SystemClock.uptimeMillis();
mDailyItems.add(item);
while (mDailyItems.size() > MAX_DAILY_ITEMS) {
mDailyItems.remove(0);
}
final ByteArrayOutputStream memStream = new ByteArrayOutputStream();
try {
TypedXmlSerializer out = Xml.resolveSerializer(memStream);
writeDailyItemsLocked(out);
final long initialTimeMs = SystemClock.uptimeMillis() - startTimeMs;
BackgroundThread.getHandler().post(new Runnable() {
@Override
public void run() {
synchronized (mCheckinFile) {
final long startTimeMs2 = SystemClock.uptimeMillis();
FileOutputStream stream = null;
try {
stream = mDailyFile.startWrite();
memStream.writeTo(stream);
stream.flush();
mDailyFile.finishWrite(stream);
com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
"batterystats-daily",
initialTimeMs + SystemClock.uptimeMillis() - startTimeMs2);
} catch (IOException e) {
Slog.w("BatteryStats",
"Error writing battery daily items", e);
mDailyFile.failWrite(stream);
}
}
}
});
} catch (IOException e) {
}
}
}
private void writeDailyItemsLocked(TypedXmlSerializer out) throws IOException {
StringBuilder sb = new StringBuilder(64);
out.startDocument(null, true);
out.startTag(null, "daily-items");
for (int i=0; i<mDailyItems.size(); i++) {
final DailyItem dit = mDailyItems.get(i);
out.startTag(null, "item");
out.attributeLong(null, "start", dit.mStartTime);
out.attributeLong(null, "end", dit.mEndTime);
writeDailyLevelSteps(out, "dis", dit.mDischargeSteps, sb);
writeDailyLevelSteps(out, "chg", dit.mChargeSteps, sb);
if (dit.mPackageChanges != null) {
for (int j=0; j<dit.mPackageChanges.size(); j++) {
PackageChange pc = dit.mPackageChanges.get(j);
if (pc.mUpdate) {
out.startTag(null, "upd");
out.attribute(null, "pkg", pc.mPackageName);
out.attributeLong(null, "ver", pc.mVersionCode);
out.endTag(null, "upd");
} else {
out.startTag(null, "rem");
out.attribute(null, "pkg", pc.mPackageName);
out.endTag(null, "rem");
}
}
}
out.endTag(null, "item");
}
out.endTag(null, "daily-items");
out.endDocument();
}
private void writeDailyLevelSteps(TypedXmlSerializer out, String tag, LevelStepTracker steps,
StringBuilder tmpBuilder) throws IOException {
if (steps != null) {
out.startTag(null, tag);
out.attributeInt(null, "n", steps.mNumStepDurations);
for (int i=0; i<steps.mNumStepDurations; i++) {
out.startTag(null, "s");
tmpBuilder.setLength(0);
steps.encodeEntryAt(i, tmpBuilder);
out.attribute(null, "v", tmpBuilder.toString());
out.endTag(null, "s");
}
out.endTag(null, tag);
}
}
@GuardedBy("this")
public void readDailyStatsLocked() {
Slog.d(TAG, "Reading daily items from " + mDailyFile.getBaseFile());
mDailyItems.clear();
FileInputStream stream;
try {
stream = mDailyFile.openRead();
} catch (FileNotFoundException e) {
return;
}
try {
TypedXmlPullParser parser = Xml.resolvePullParser(stream);
readDailyItemsLocked(parser);
} catch (IOException e) {
} finally {
try {
stream.close();
} catch (IOException e) {
}
}
}
private void readDailyItemsLocked(TypedXmlPullParser parser) {
try {
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
;
}
if (type != XmlPullParser.START_TAG) {
throw new IllegalStateException("no start tag found");
}
int outerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("item")) {
readDailyItemTagLocked(parser);
} else {
Slog.w(TAG, "Unknown element under <daily-items>: "
+ parser.getName());
XmlUtils.skipCurrentTag(parser);
}
}
} catch (IllegalStateException e) {
Slog.w(TAG, "Failed parsing daily " + e);
} catch (NullPointerException e) {
Slog.w(TAG, "Failed parsing daily " + e);
} catch (NumberFormatException e) {
Slog.w(TAG, "Failed parsing daily " + e);
} catch (XmlPullParserException e) {
Slog.w(TAG, "Failed parsing daily " + e);
} catch (IOException e) {
Slog.w(TAG, "Failed parsing daily " + e);
} catch (IndexOutOfBoundsException e) {
Slog.w(TAG, "Failed parsing daily " + e);
}
}
void readDailyItemTagLocked(TypedXmlPullParser parser) throws NumberFormatException,
XmlPullParserException, IOException {
DailyItem dit = new DailyItem();
dit.mStartTime = parser.getAttributeLong(null, "start", 0);
dit.mEndTime = parser.getAttributeLong(null, "end", 0);
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("dis")) {
readDailyItemTagDetailsLocked(parser, dit, false, "dis");
} else if (tagName.equals("chg")) {
readDailyItemTagDetailsLocked(parser, dit, true, "chg");
} else if (tagName.equals("upd")) {
if (dit.mPackageChanges == null) {
dit.mPackageChanges = new ArrayList<>();
}
PackageChange pc = new PackageChange();
pc.mUpdate = true;
pc.mPackageName = parser.getAttributeValue(null, "pkg");
pc.mVersionCode = parser.getAttributeLong(null, "ver", 0);
dit.mPackageChanges.add(pc);
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("rem")) {
if (dit.mPackageChanges == null) {
dit.mPackageChanges = new ArrayList<>();
}
PackageChange pc = new PackageChange();
pc.mUpdate = false;
pc.mPackageName = parser.getAttributeValue(null, "pkg");
dit.mPackageChanges.add(pc);
XmlUtils.skipCurrentTag(parser);
} else {
Slog.w(TAG, "Unknown element under <item>: "
+ parser.getName());
XmlUtils.skipCurrentTag(parser);
}
}
mDailyItems.add(dit);
}
void readDailyItemTagDetailsLocked(TypedXmlPullParser parser, DailyItem dit, boolean isCharge,
String tag)
throws NumberFormatException, XmlPullParserException, IOException {
final int num = parser.getAttributeInt(null, "n", -1);
if (num == -1) {
Slog.w(TAG, "Missing 'n' attribute at " + parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
return;
}
LevelStepTracker steps = new LevelStepTracker(num);
if (isCharge) {
dit.mChargeSteps = steps;
} else {
dit.mDischargeSteps = steps;
}
int i = 0;
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if ("s".equals(tagName)) {
if (i < num) {
String valueAttr = parser.getAttributeValue(null, "v");
if (valueAttr != null) {
steps.decodeEntryAt(i, valueAttr);
i++;
}
}
} else {
Slog.w(TAG, "Unknown element under <" + tag + ">: "
+ parser.getName());
XmlUtils.skipCurrentTag(parser);
}
}
steps.mNumStepDurations = i;
}
@Override
public DailyItem getDailyItemLocked(int daysAgo) {
int index = mDailyItems.size()-1-daysAgo;
return index >= 0 ? mDailyItems.get(index) : null;
}
@Override
public long getCurrentDailyStartTime() {
return mDailyStartTimeMs;
}
@Override
public long getNextMinDailyDeadline() {
return mNextMinDailyDeadlineMs;
}
@Override
public long getNextMaxDailyDeadline() {
return mNextMaxDailyDeadlineMs;
}
@GuardedBy("this")
public int getHistoryTotalSize() {
return mConstants.MAX_HISTORY_BUFFER * mConstants.MAX_HISTORY_FILES;
}
public int getHistoryUsedSize() {
return mBatteryStatsHistory.getHistoryUsedSize();
}
@Override
@UnsupportedAppUsage
public boolean startIteratingHistoryLocked() {
mBatteryStatsHistoryIterator = createBatteryStatsHistoryIterator();
return true;
}
/**
* Creates an iterator for battery stats history.
*/
@VisibleForTesting
public BatteryStatsHistoryIterator createBatteryStatsHistoryIterator() {
return new BatteryStatsHistoryIterator(mBatteryStatsHistory);
}
@Override
public int getHistoryStringPoolSize() {
return mHistoryTagPool.size();
}
@Override
public int getHistoryStringPoolBytes() {
return mNumHistoryTagChars;
}
@Override
public String getHistoryTagPoolString(int index) {
ensureHistoryTagArray();
HistoryTag historyTag = mHistoryTags.get(index);
return historyTag != null ? historyTag.string : null;
}
@Override
public int getHistoryTagPoolUid(int index) {
ensureHistoryTagArray();
HistoryTag historyTag = mHistoryTags.get(index);
return historyTag != null ? historyTag.uid : Process.INVALID_UID;
}
private void ensureHistoryTagArray() {
if (mHistoryTags != null) {
return;
}
mHistoryTags = new SparseArray<>(mHistoryTagPool.size());
for (Map.Entry<HistoryTag, Integer> entry: mHistoryTagPool.entrySet()) {
mHistoryTags.put(entry.getValue() & ~TAG_FIRST_OCCURRENCE_FLAG, entry.getKey());
}
}
@Override
@UnsupportedAppUsage
public boolean getNextHistoryLocked(HistoryItem out) {
return mBatteryStatsHistoryIterator.next(out);
}
@Override
public void finishIteratingHistoryLocked() {
mBatteryStatsHistoryIterator = null;
}
@Override
public long getHistoryBaseTime() {
return mHistoryBaseTimeMs;
}
@Override
public int getStartCount() {
return mStartCount;
}
@UnsupportedAppUsage
public boolean isOnBattery() {
return mOnBattery;
}
public boolean isCharging() {
return mCharging;
}
void initTimes(long uptimeUs, long realtimeUs) {
mStartClockTimeMs = mClock.currentTimeMillis();
mOnBatteryTimeBase.init(uptimeUs, realtimeUs);
mOnBatteryScreenOffTimeBase.init(uptimeUs, realtimeUs);
mRealtimeUs = 0;
mUptimeUs = 0;
mRealtimeStartUs = realtimeUs;
mUptimeStartUs = uptimeUs;
}
void initDischarge(long elapsedRealtimeUs) {
mLowDischargeAmountSinceCharge = 0;
mHighDischargeAmountSinceCharge = 0;
mDischargeAmountScreenOn = 0;
mDischargeAmountScreenOnSinceCharge = 0;
mDischargeAmountScreenOff = 0;
mDischargeAmountScreenOffSinceCharge = 0;
mDischargeAmountScreenDoze = 0;
mDischargeAmountScreenDozeSinceCharge = 0;
mDischargeStepTracker.init();
mChargeStepTracker.init();
mDischargeScreenOffCounter.reset(false, elapsedRealtimeUs);
mDischargeScreenDozeCounter.reset(false, elapsedRealtimeUs);
mDischargeLightDozeCounter.reset(false, elapsedRealtimeUs);
mDischargeDeepDozeCounter.reset(false, elapsedRealtimeUs);
mDischargeCounter.reset(false, elapsedRealtimeUs);
}
public void setBatteryResetListener(BatteryResetListener batteryResetListener) {
mBatteryResetListener = batteryResetListener;
}
@GuardedBy("this")
public void resetAllStatsCmdLocked() {
final long mSecUptime = mClock.uptimeMillis();
long uptimeUs = mSecUptime * 1000;
long mSecRealtime = mClock.elapsedRealtime();
long realtimeUs = mSecRealtime * 1000;
resetAllStatsLocked(mSecUptime, mSecRealtime, RESET_REASON_ADB_COMMAND);
mDischargeStartLevel = mHistoryCur.batteryLevel;
pullPendingStateUpdatesLocked();
addHistoryRecordLocked(mSecRealtime, mSecUptime);
mDischargeCurrentLevel = mDischargeUnplugLevel = mDischargePlugLevel
= mCurrentBatteryLevel = mHistoryCur.batteryLevel;
mOnBatteryTimeBase.reset(uptimeUs, realtimeUs);
mOnBatteryScreenOffTimeBase.reset(uptimeUs, realtimeUs);
if ((mHistoryCur.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) == 0) {
if (Display.isOnState(mScreenState)) {
mDischargeScreenOnUnplugLevel = mHistoryCur.batteryLevel;
mDischargeScreenDozeUnplugLevel = 0;
mDischargeScreenOffUnplugLevel = 0;
} else if (Display.isDozeState(mScreenState)) {
mDischargeScreenOnUnplugLevel = 0;
mDischargeScreenDozeUnplugLevel = mHistoryCur.batteryLevel;
mDischargeScreenOffUnplugLevel = 0;
} else {
mDischargeScreenOnUnplugLevel = 0;
mDischargeScreenDozeUnplugLevel = 0;
mDischargeScreenOffUnplugLevel = mHistoryCur.batteryLevel;
}
mDischargeAmountScreenOn = 0;
mDischargeAmountScreenOff = 0;
mDischargeAmountScreenDoze = 0;
}
initActiveHistoryEventsLocked(mSecRealtime, mSecUptime);
}
@GuardedBy("this")
private void resetAllStatsLocked(long uptimeMillis, long elapsedRealtimeMillis,
int resetReason) {
if (mBatteryResetListener != null) {
mBatteryResetListener.prepareForBatteryStatsReset(resetReason);
}
final long uptimeUs = uptimeMillis * 1000;
final long elapsedRealtimeUs = elapsedRealtimeMillis * 1000;
mStartCount = 0;
initTimes(uptimeUs, elapsedRealtimeUs);
mScreenOnTimer.reset(false, elapsedRealtimeUs);
mScreenDozeTimer.reset(false, elapsedRealtimeUs);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].reset(false, elapsedRealtimeUs);
}
final int numDisplays = mPerDisplayBatteryStats.length;
for (int i = 0; i < numDisplays; i++) {
mPerDisplayBatteryStats[i].reset(elapsedRealtimeUs);
}
if (mPowerProfile != null) {
mEstimatedBatteryCapacityMah = (int) mPowerProfile.getBatteryCapacity();
} else {
mEstimatedBatteryCapacityMah = -1;
}
mLastLearnedBatteryCapacityUah = -1;
mMinLearnedBatteryCapacityUah = -1;
mMaxLearnedBatteryCapacityUah = -1;
mInteractiveTimer.reset(false, elapsedRealtimeUs);
mPowerSaveModeEnabledTimer.reset(false, elapsedRealtimeUs);
mLastIdleTimeStartMs = elapsedRealtimeMillis;
mLongestLightIdleTimeMs = 0;
mLongestFullIdleTimeMs = 0;
mDeviceIdleModeLightTimer.reset(false, elapsedRealtimeUs);
mDeviceIdleModeFullTimer.reset(false, elapsedRealtimeUs);
mDeviceLightIdlingTimer.reset(false, elapsedRealtimeUs);
mDeviceIdlingTimer.reset(false, elapsedRealtimeUs);
mPhoneOnTimer.reset(false, elapsedRealtimeUs);
mAudioOnTimer.reset(false, elapsedRealtimeUs);
mVideoOnTimer.reset(false, elapsedRealtimeUs);
mFlashlightOnTimer.reset(false, elapsedRealtimeUs);
mCameraOnTimer.reset(false, elapsedRealtimeUs);
mBluetoothScanTimer.reset(false, elapsedRealtimeUs);
for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
mPhoneSignalStrengthsTimer[i].reset(false, elapsedRealtimeUs);
}
mPhoneSignalScanningTimer.reset(false, elapsedRealtimeUs);
for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
mPhoneDataConnectionsTimer[i].reset(false, elapsedRealtimeUs);
}
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
mNetworkByteActivityCounters[i].reset(false, elapsedRealtimeUs);
mNetworkPacketActivityCounters[i].reset(false, elapsedRealtimeUs);
}
for (int i = 0; i < RADIO_ACCESS_TECHNOLOGY_COUNT; i++) {
final RadioAccessTechnologyBatteryStats stats = mPerRatBatteryStats[i];
if (stats == null) continue;
stats.reset(elapsedRealtimeUs);
}
mMobileRadioActiveTimer.reset(false, elapsedRealtimeUs);
mMobileRadioActivePerAppTimer.reset(false, elapsedRealtimeUs);
mMobileRadioActiveAdjustedTime.reset(false, elapsedRealtimeUs);
mMobileRadioActiveUnknownTime.reset(false, elapsedRealtimeUs);
mMobileRadioActiveUnknownCount.reset(false, elapsedRealtimeUs);
mWifiOnTimer.reset(false, elapsedRealtimeUs);
mGlobalWifiRunningTimer.reset(false, elapsedRealtimeUs);
for (int i=0; i<NUM_WIFI_STATES; i++) {
mWifiStateTimer[i].reset(false, elapsedRealtimeUs);
}
for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
mWifiSupplStateTimer[i].reset(false, elapsedRealtimeUs);
}
for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
mWifiSignalStrengthsTimer[i].reset(false, elapsedRealtimeUs);
}
mWifiMulticastWakelockTimer.reset(false, elapsedRealtimeUs);
mWifiActiveTimer.reset(false, elapsedRealtimeUs);
mWifiActivity.reset(false, elapsedRealtimeUs);
for (int i=0; i< mGpsSignalQualityTimer.length; i++) {
mGpsSignalQualityTimer[i].reset(false, elapsedRealtimeUs);
}
mBluetoothActivity.reset(false, elapsedRealtimeUs);
mModemActivity.reset(false, elapsedRealtimeUs);
mNumConnectivityChange = 0;
for (int i=0; i<mUidStats.size(); i++) {
if (mUidStats.valueAt(i).reset(uptimeUs, elapsedRealtimeUs, resetReason)) {
mUidStats.valueAt(i).detachFromTimeBase();
mUidStats.remove(mUidStats.keyAt(i));
i--;
}
}
if (mRpmStats.size() > 0) {
for (SamplingTimer timer : mRpmStats.values()) {
mOnBatteryTimeBase.remove(timer);
}
mRpmStats.clear();
}
if (mScreenOffRpmStats.size() > 0) {
for (SamplingTimer timer : mScreenOffRpmStats.values()) {
mOnBatteryScreenOffTimeBase.remove(timer);
}
mScreenOffRpmStats.clear();
}
if (mKernelWakelockStats.size() > 0) {
for (SamplingTimer timer : mKernelWakelockStats.values()) {
mOnBatteryScreenOffTimeBase.remove(timer);
}
mKernelWakelockStats.clear();
}
if (mKernelMemoryStats.size() > 0) {
for (int i = 0; i < mKernelMemoryStats.size(); i++) {
mOnBatteryTimeBase.remove(mKernelMemoryStats.valueAt(i));
}
mKernelMemoryStats.clear();
}
if (mWakeupReasonStats.size() > 0) {
for (SamplingTimer timer : mWakeupReasonStats.values()) {
mOnBatteryTimeBase.remove(timer);
}
mWakeupReasonStats.clear();
}
mTmpRailStats.reset();
MeasuredEnergyStats.resetIfNotNull(mGlobalMeasuredEnergyStats);
resetIfNotNull(mBinderThreadCpuTimesUs, false, elapsedRealtimeUs);
mLastHistoryStepDetails = null;
mLastStepCpuUserTimeMs = mLastStepCpuSystemTimeMs = 0;
mCurStepCpuUserTimeMs = mCurStepCpuSystemTimeMs = 0;
mLastStepCpuUserTimeMs = mCurStepCpuUserTimeMs = 0;
mLastStepCpuSystemTimeMs = mCurStepCpuSystemTimeMs = 0;
mLastStepStatUserTimeMs = mCurStepStatUserTimeMs = 0;
mLastStepStatSystemTimeMs = mCurStepStatSystemTimeMs = 0;
mLastStepStatIOWaitTimeMs = mCurStepStatIOWaitTimeMs = 0;
mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs = 0;
mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs = 0;
mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs = 0;
mNumAllUidCpuTimeReads = 0;
mNumUidsRemoved = 0;
initDischarge(elapsedRealtimeUs);
clearHistoryLocked();
if (mBatteryStatsHistory != null) {
mBatteryStatsHistory.resetAllFiles();
}
// Flush external data, gathering snapshots, but don't process it since it is pre-reset data
mIgnoreNextExternalStats = true;
mExternalSync.scheduleSync("reset", ExternalStatsSync.UPDATE_ON_RESET);
mHandler.sendEmptyMessage(MSG_REPORT_RESET_STATS);
}
@GuardedBy("this")
private void initActiveHistoryEventsLocked(long elapsedRealtimeMs, long uptimeMs) {
for (int i=0; i<HistoryItem.EVENT_COUNT; i++) {
if (!mRecordAllHistory && i == HistoryItem.EVENT_PROC) {
// Not recording process starts/stops.
continue;
}
HashMap<String, SparseIntArray> active = mActiveEvents.getStateForEvent(i);
if (active == null) {
continue;
}
for (HashMap.Entry<String, SparseIntArray> ent : active.entrySet()) {
SparseIntArray uids = ent.getValue();
for (int j=0; j<uids.size(); j++) {
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, i, ent.getKey(),
uids.keyAt(j));
}
}
}
}
@GuardedBy("this")
void updateDischargeScreenLevelsLocked(int oldState, int newState) {
updateOldDischargeScreenLevelLocked(oldState);
updateNewDischargeScreenLevelLocked(newState);
}
@GuardedBy("this")
private void updateOldDischargeScreenLevelLocked(int state) {
if (Display.isOnState(state)) {
int diff = mDischargeScreenOnUnplugLevel - mDischargeCurrentLevel;
if (diff > 0) {
mDischargeAmountScreenOn += diff;
mDischargeAmountScreenOnSinceCharge += diff;
}
} else if (Display.isDozeState(state)) {
int diff = mDischargeScreenDozeUnplugLevel - mDischargeCurrentLevel;
if (diff > 0) {
mDischargeAmountScreenDoze += diff;
mDischargeAmountScreenDozeSinceCharge += diff;
}
} else if (Display.isOffState(state)) {
int diff = mDischargeScreenOffUnplugLevel - mDischargeCurrentLevel;
if (diff > 0) {
mDischargeAmountScreenOff += diff;
mDischargeAmountScreenOffSinceCharge += diff;
}
}
}
@GuardedBy("this")
private void updateNewDischargeScreenLevelLocked(int state) {
if (Display.isOnState(state)) {
mDischargeScreenOnUnplugLevel = mDischargeCurrentLevel;
mDischargeScreenOffUnplugLevel = 0;
mDischargeScreenDozeUnplugLevel = 0;
} else if (Display.isDozeState(state)) {
mDischargeScreenOnUnplugLevel = 0;
mDischargeScreenDozeUnplugLevel = mDischargeCurrentLevel;
mDischargeScreenOffUnplugLevel = 0;
} else if (Display.isOffState(state)) {
mDischargeScreenOnUnplugLevel = 0;
mDischargeScreenDozeUnplugLevel = 0;
mDischargeScreenOffUnplugLevel = mDischargeCurrentLevel;
}
}
@GuardedBy("this")
public void pullPendingStateUpdatesLocked() {
if (mOnBatteryInternal) {
updateDischargeScreenLevelsLocked(mScreenState, mScreenState);
}
}
private final Object mWifiNetworkLock = new Object();
@GuardedBy("mWifiNetworkLock")
private String[] mWifiIfaces = EmptyArray.STRING;
@GuardedBy("mWifiNetworkLock")
private NetworkStats mLastWifiNetworkStats = new NetworkStats(0, -1);
private final Object mModemNetworkLock = new Object();
@GuardedBy("mModemNetworkLock")
private String[] mModemIfaces = EmptyArray.STRING;
@GuardedBy("mModemNetworkLock")
private NetworkStats mLastModemNetworkStats = new NetworkStats(0, -1);
@VisibleForTesting
protected NetworkStats readMobileNetworkStatsLocked(
@NonNull NetworkStatsManager networkStatsManager) {
return networkStatsManager.getMobileUidStats();
}
@VisibleForTesting
protected NetworkStats readWifiNetworkStatsLocked(
@NonNull NetworkStatsManager networkStatsManager) {
return networkStatsManager.getWifiUidStats();
}
/**
* Distribute WiFi energy info and network traffic to apps.
* @param info The energy information from the WiFi controller.
*/
@GuardedBy("this")
public void updateWifiState(@Nullable final WifiActivityEnergyInfo info,
final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs,
@NonNull NetworkStatsManager networkStatsManager) {
if (DEBUG_ENERGY) {
synchronized (mWifiNetworkLock) {
Slog.d(TAG, "Updating wifi stats: " + Arrays.toString(mWifiIfaces));
}
}
// Grab a separate lock to acquire the network stats, which may do I/O.
NetworkStats delta = null;
synchronized (mWifiNetworkLock) {
final NetworkStats latestStats = readWifiNetworkStatsLocked(networkStatsManager);
if (latestStats != null) {
delta = latestStats.subtract(mLastWifiNetworkStats);
mLastWifiNetworkStats = latestStats;
}
}
synchronized (this) {
if (!mOnBatteryInternal || mIgnoreNextExternalStats) {
if (mIgnoreNextExternalStats) {
// TODO: Strictly speaking, we should re-mark all 5 timers for each uid (and the
// global one) here like we do for display. But I'm not sure it's worth the
// complicated code for a codepath that shouldn't ever actually happen in real
// life.
}
return;
}
final SparseDoubleArray uidEstimatedConsumptionMah =
(mGlobalMeasuredEnergyStats != null
&& mWifiPowerCalculator != null && consumedChargeUC > 0) ?
new SparseDoubleArray() : null;
double totalEstimatedConsumptionMah = 0;
SparseLongArray rxPackets = new SparseLongArray();
SparseLongArray txPackets = new SparseLongArray();
SparseLongArray rxTimesMs = new SparseLongArray();
SparseLongArray txTimesMs = new SparseLongArray();
long totalTxPackets = 0;
long totalRxPackets = 0;
if (delta != null) {
for (NetworkStats.Entry entry : delta) {
if (DEBUG_ENERGY) {
Slog.d(TAG, "Wifi uid " + entry.getUid()
+ ": delta rx=" + entry.getRxBytes()
+ " tx=" + entry.getTxBytes()
+ " rxPackets=" + entry.getRxPackets()
+ " txPackets=" + entry.getTxPackets());
}
if (entry.getRxBytes() == 0 && entry.getTxBytes() == 0) {
// Skip the lookup below since there is no work to do.
continue;
}
final int uid = mapUid(entry.getUid());
final Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs);
if (entry.getRxBytes() != 0) {
u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.getRxBytes(),
entry.getRxPackets());
if (entry.getSet() == NetworkStats.SET_DEFAULT) { // Background transfers
u.noteNetworkActivityLocked(NETWORK_WIFI_BG_RX_DATA, entry.getRxBytes(),
entry.getRxPackets());
}
mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
entry.getRxBytes());
mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
entry.getRxPackets());
rxPackets.incrementValue(uid, entry.getRxPackets());
// Sum the total number of packets so that the Rx Power can
// be evenly distributed amongst the apps.
totalRxPackets += entry.getRxPackets();
}
if (entry.getTxBytes() != 0) {
u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.getTxBytes(),
entry.getTxPackets());
if (entry.getSet() == NetworkStats.SET_DEFAULT) { // Background transfers
u.noteNetworkActivityLocked(NETWORK_WIFI_BG_TX_DATA, entry.getTxBytes(),
entry.getTxPackets());
}
mNetworkByteActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
entry.getTxBytes());
mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
entry.getTxPackets());
txPackets.incrementValue(uid, entry.getTxPackets());
// Sum the total number of packets so that the Tx Power can
// be evenly distributed amongst the apps.
totalTxPackets += entry.getTxPackets();
}
// Calculate consumed energy for this uid. Only do so if WifiReporting isn't
// enabled (if it is, we'll do it later instead using info).
if (uidEstimatedConsumptionMah != null && info == null && !mHasWifiReporting) {
final long uidRunningMs = u.mWifiRunningTimer
.getTimeSinceMarkLocked(elapsedRealtimeMs * 1000) / 1000;
if (uidRunningMs > 0) u.mWifiRunningTimer.setMark(elapsedRealtimeMs);
final long uidScanMs = u.mWifiScanTimer
.getTimeSinceMarkLocked(elapsedRealtimeMs * 1000) / 1000;
if (uidScanMs > 0) u.mWifiScanTimer.setMark(elapsedRealtimeMs);
long uidBatchScanMs = 0;
for (int bn = 0; bn < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bn++) {
if (u.mWifiBatchedScanTimer[bn] != null) {
long bnMs = u.mWifiBatchedScanTimer[bn]
.getTimeSinceMarkLocked(elapsedRealtimeMs * 1000) / 1000;
if (bnMs > 0) {
u.mWifiBatchedScanTimer[bn].setMark(elapsedRealtimeMs);
}
uidBatchScanMs += bnMs;
}
}
uidEstimatedConsumptionMah.incrementValue(u.getUid(),
mWifiPowerCalculator.calcPowerWithoutControllerDataMah(
entry.getRxPackets(), entry.getTxPackets(),
uidRunningMs, uidScanMs, uidBatchScanMs));
}
}
delta = null;
}
if (info != null) {
mHasWifiReporting = true;
// Measured in mAms
final long txTimeMs = info.getControllerTxDurationMillis();
final long rxTimeMs = info.getControllerRxDurationMillis();
final long scanTimeMs = info.getControllerScanDurationMillis();
final long idleTimeMs = info.getControllerIdleDurationMillis();
final long totalTimeMs = txTimeMs + rxTimeMs + idleTimeMs;
long leftOverRxTimeMs = rxTimeMs;
long leftOverTxTimeMs = txTimeMs;
if (DEBUG_ENERGY) {
Slog.d(TAG, "------ BEGIN WiFi power blaming ------");
Slog.d(TAG, " Tx Time: " + txTimeMs + " ms");
Slog.d(TAG, " Rx Time: " + rxTimeMs + " ms");
Slog.d(TAG, " Idle Time: " + idleTimeMs + " ms");
Slog.d(TAG, " Total Time: " + totalTimeMs + " ms");
Slog.d(TAG, " Scan Time: " + scanTimeMs + " ms");
}
long totalWifiLockTimeMs = 0;
long totalScanTimeMs = 0;
// On the first pass, collect some totals so that we can normalize power
// calculations if we need to.
final int uidStatsSize = mUidStats.size();
for (int i = 0; i < uidStatsSize; i++) {
final Uid uid = mUidStats.valueAt(i);
// Sum the total scan power for all apps.
totalScanTimeMs += uid.mWifiScanTimer.getTimeSinceMarkLocked(
elapsedRealtimeMs * 1000) / 1000;
// Sum the total time holding wifi lock for all apps.
totalWifiLockTimeMs += uid.mFullWifiLockTimer.getTimeSinceMarkLocked(
elapsedRealtimeMs * 1000) / 1000;
}
if (DEBUG_ENERGY && totalScanTimeMs > rxTimeMs) {
Slog.d(TAG,
" !Estimated scan time > Actual rx time (" + totalScanTimeMs + " ms > "
+ rxTimeMs + " ms). Normalizing scan time.");
}
if (DEBUG_ENERGY && totalScanTimeMs > txTimeMs) {
Slog.d(TAG,
" !Estimated scan time > Actual tx time (" + totalScanTimeMs + " ms > "
+ txTimeMs + " ms). Normalizing scan time.");
}
// Actually assign and distribute power usage to apps.
for (int i = 0; i < uidStatsSize; i++) {
final Uid uid = mUidStats.valueAt(i);
final long scanTimeSinceMarkMs = uid.mWifiScanTimer.getTimeSinceMarkLocked(
elapsedRealtimeMs * 1000) / 1000;
long scanRxTimeSinceMarkMs = scanTimeSinceMarkMs; // not final
long scanTxTimeSinceMarkMs = scanTimeSinceMarkMs; // not final
if (scanTimeSinceMarkMs > 0) {
// Set the new mark so that next time we get new data since this point.
uid.mWifiScanTimer.setMark(elapsedRealtimeMs);
// Our total scan time is more than the reported Tx/Rx time.
// This is possible because the cost of a scan is approximate.
// Let's normalize the result so that we evenly blame each app
// scanning.
//
// This means that we may have apps that transmitted/received packets not be
// blamed for this, but this is fine as scans are relatively more expensive.
if (totalScanTimeMs > rxTimeMs) {
scanRxTimeSinceMarkMs = (rxTimeMs * scanRxTimeSinceMarkMs) /
totalScanTimeMs;
}
if (totalScanTimeMs > txTimeMs) {
scanTxTimeSinceMarkMs = (txTimeMs * scanTxTimeSinceMarkMs) /
totalScanTimeMs;
}
if (DEBUG_ENERGY) {
Slog.d(TAG, " ScanTime for UID " + uid.getUid() + ": Rx:"
+ scanRxTimeSinceMarkMs + " ms Tx:"
+ scanTxTimeSinceMarkMs + " ms)");
}
rxTimesMs.incrementValue(uid.getUid(), scanRxTimeSinceMarkMs);
txTimesMs.incrementValue(uid.getUid(), scanTxTimeSinceMarkMs);
leftOverRxTimeMs -= scanRxTimeSinceMarkMs;
leftOverTxTimeMs -= scanTxTimeSinceMarkMs;
}
// Distribute evenly the power consumed while Idle to each app holding a WiFi
// lock.
long myIdleTimeMs = 0;
final long wifiLockTimeSinceMarkMs =
uid.mFullWifiLockTimer.getTimeSinceMarkLocked(
elapsedRealtimeMs * 1000) / 1000;
if (wifiLockTimeSinceMarkMs > 0) {
// Set the new mark so that next time we get new data since this point.
uid.mFullWifiLockTimer.setMark(elapsedRealtimeMs);
myIdleTimeMs = (wifiLockTimeSinceMarkMs * idleTimeMs) / totalWifiLockTimeMs;
if (DEBUG_ENERGY) {
Slog.d(TAG, " IdleTime for UID " + uid.getUid() + ": "
+ myIdleTimeMs + " ms");
}
uid.getOrCreateWifiControllerActivityLocked().getOrCreateIdleTimeCounter()
.increment(myIdleTimeMs, elapsedRealtimeMs);
}
if (uidEstimatedConsumptionMah != null) {
double uidEstMah = mWifiPowerCalculator.calcPowerFromControllerDataMah(
scanRxTimeSinceMarkMs, scanTxTimeSinceMarkMs, myIdleTimeMs);
uidEstimatedConsumptionMah.incrementValue(uid.getUid(), uidEstMah);
}
}
if (DEBUG_ENERGY) {
Slog.d(TAG, " New RxPower: " + leftOverRxTimeMs + " ms");
Slog.d(TAG, " New TxPower: " + leftOverTxTimeMs + " ms");
}
// Distribute the remaining Tx power appropriately between all apps that transmitted
// packets.
for (int i = 0; i < txPackets.size(); i++) {
final int uid = txPackets.keyAt(i);
final long myTxTimeMs = (txPackets.valueAt(i) * leftOverTxTimeMs)
/ totalTxPackets;
txTimesMs.incrementValue(uid, myTxTimeMs);
}
// Distribute the remaining Rx power appropriately between all apps that received
// packets.
for (int i = 0; i < rxPackets.size(); i++) {
final int uid = rxPackets.keyAt(i);
final long myRxTimeMs = (rxPackets.valueAt(i) * leftOverRxTimeMs)
/ totalRxPackets;
rxTimesMs.incrementValue(uid, myRxTimeMs);
}
for (int i = 0; i < txTimesMs.size(); i++) {
final int uid = txTimesMs.keyAt(i);
final long myTxTimeMs = txTimesMs.valueAt(i);
if (DEBUG_ENERGY) {
Slog.d(TAG, " TxTime for UID " + uid + ": " + myTxTimeMs + " ms");
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.getOrCreateWifiControllerActivityLocked()
.getOrCreateTxTimeCounters()[0]
.increment(myTxTimeMs, elapsedRealtimeMs);
if (uidEstimatedConsumptionMah != null) {
uidEstimatedConsumptionMah.incrementValue(uid,
mWifiPowerCalculator.calcPowerFromControllerDataMah(
0, myTxTimeMs, 0));
}
}
for (int i = 0; i < rxTimesMs.size(); i++) {
final int uid = rxTimesMs.keyAt(i);
final long myRxTimeMs = rxTimesMs.valueAt(i);
if (DEBUG_ENERGY) {
Slog.d(TAG, " RxTime for UID " + uid + ": " + myRxTimeMs + " ms");
}
getUidStatsLocked(rxTimesMs.keyAt(i), elapsedRealtimeMs, uptimeMs)
.getOrCreateWifiControllerActivityLocked()
.getOrCreateRxTimeCounter()
.increment(myRxTimeMs, elapsedRealtimeMs);
if (uidEstimatedConsumptionMah != null) {
uidEstimatedConsumptionMah.incrementValue(uid,
mWifiPowerCalculator.calcPowerFromControllerDataMah(
myRxTimeMs, 0, 0));
}
}
// Any left over power use will be picked up by the WiFi category in BatteryStatsHelper.
// Update WiFi controller stats.
mWifiActivity.getOrCreateRxTimeCounter().increment(
info.getControllerRxDurationMillis(), elapsedRealtimeMs);
mWifiActivity.getOrCreateTxTimeCounters()[0].increment(
info.getControllerTxDurationMillis(), elapsedRealtimeMs);
mWifiActivity.getScanTimeCounter().addCountLocked(
info.getControllerScanDurationMillis());
mWifiActivity.getOrCreateIdleTimeCounter().increment(
info.getControllerIdleDurationMillis(), elapsedRealtimeMs);
// POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
final double opVolt = mPowerProfile.getAveragePower(
PowerProfile.POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
double controllerMaMs = 0;
if (opVolt != 0) {
// We store the power drain as mAms.
controllerMaMs = info.getControllerEnergyUsedMicroJoules() / opVolt;
mWifiActivity.getPowerCounter().addCountLocked((long) controllerMaMs);
}
// Converting uWs to mAms.
// Conversion: (uWs * (1000ms / 1s) * (1mW / 1000uW)) / mV = mAms
long monitoredRailChargeConsumedMaMs =
(long) (mTmpRailStats.getWifiTotalEnergyUseduWs() / opVolt);
mWifiActivity.getMonitoredRailChargeConsumedMaMs().addCountLocked(
monitoredRailChargeConsumedMaMs);
mHistoryCur.wifiRailChargeMah +=
(monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR);
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mTmpRailStats.resetWifiTotalEnergyUsed();
if (uidEstimatedConsumptionMah != null) {
totalEstimatedConsumptionMah = Math.max(controllerMaMs / MILLISECONDS_IN_HOUR,
mWifiPowerCalculator.calcPowerFromControllerDataMah(
rxTimeMs, txTimeMs, idleTimeMs));
}
}
// Update the MeasuredEnergyStats information.
if (uidEstimatedConsumptionMah != null) {
mGlobalMeasuredEnergyStats.updateStandardBucket(
MeasuredEnergyStats.POWER_BUCKET_WIFI, consumedChargeUC);
// Now calculate the consumption for each uid, according to its proportional usage.
if (!mHasWifiReporting) {
final long globalTimeMs = mGlobalWifiRunningTimer
.getTimeSinceMarkLocked(elapsedRealtimeMs * 1000) / 1000;
mGlobalWifiRunningTimer.setMark(elapsedRealtimeMs);
totalEstimatedConsumptionMah = mWifiPowerCalculator
.calcGlobalPowerWithoutControllerDataMah(globalTimeMs);
}
distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_WIFI,
consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedConsumptionMah,
elapsedRealtimeMs);
}
}
}
private ModemActivityInfo mLastModemActivityInfo = null;
/**
* Distribute Cell radio energy info and network traffic to apps.
*/
public void noteModemControllerActivity(@Nullable final ModemActivityInfo activityInfo,
final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs,
@NonNull NetworkStatsManager networkStatsManager) {
if (DEBUG_ENERGY) {
Slog.d(TAG, "Updating mobile radio stats with " + activityInfo);
}
ModemActivityInfo deltaInfo = mLastModemActivityInfo == null ? activityInfo
: mLastModemActivityInfo.getDelta(activityInfo);
mLastModemActivityInfo = activityInfo;
// Add modem tx power to history.
addModemTxPowerToHistory(deltaInfo, elapsedRealtimeMs, uptimeMs);
// Grab a separate lock to acquire the network stats, which may do I/O.
NetworkStats delta = null;
synchronized (mModemNetworkLock) {
final NetworkStats latestStats = readMobileNetworkStatsLocked(networkStatsManager);
if (latestStats != null) {
delta = latestStats.subtract(mLastModemNetworkStats);
mLastModemNetworkStats = latestStats;
}
}
synchronized (this) {
if (!mOnBatteryInternal || mIgnoreNextExternalStats) {
return;
}
final SparseDoubleArray uidEstimatedConsumptionMah;
if (consumedChargeUC > 0 && mMobileRadioPowerCalculator != null
&& mGlobalMeasuredEnergyStats != null) {
mGlobalMeasuredEnergyStats.updateStandardBucket(
MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO, consumedChargeUC);
uidEstimatedConsumptionMah = new SparseDoubleArray();
} else {
uidEstimatedConsumptionMah = null;
}
if (deltaInfo != null) {
mHasModemReporting = true;
mModemActivity.getOrCreateIdleTimeCounter()
.increment(deltaInfo.getIdleTimeMillis(), elapsedRealtimeMs);
mModemActivity.getSleepTimeCounter().addCountLocked(
deltaInfo.getSleepTimeMillis());
mModemActivity.getOrCreateRxTimeCounter()
.increment(deltaInfo.getReceiveTimeMillis(), elapsedRealtimeMs);
for (int lvl = 0; lvl < ModemActivityInfo.getNumTxPowerLevels(); lvl++) {
mModemActivity.getOrCreateTxTimeCounters()[lvl]
.increment(deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl),
elapsedRealtimeMs);
}
// POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
final double opVolt = mPowerProfile.getAveragePower(
PowerProfile.POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
if (opVolt != 0) {
double energyUsed =
deltaInfo.getSleepTimeMillis() *
mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_SLEEP)
+ deltaInfo.getIdleTimeMillis() *
mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE)
+ deltaInfo.getReceiveTimeMillis() *
mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX);
for (int i = 0; i < Math.min(ModemActivityInfo.getNumTxPowerLevels(),
CellSignalStrength.getNumSignalStrengthLevels()); i++) {
energyUsed += deltaInfo.getTransmitDurationMillisAtPowerLevel(i)
* mPowerProfile.getAveragePower(
PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
}
// We store the power drain as mAms.
mModemActivity.getPowerCounter().addCountLocked((long) energyUsed);
// Converting uWs to mAms.
// Conversion: (uWs * (1000ms / 1s) * (1mW / 1000uW)) / mV = mAms
long monitoredRailChargeConsumedMaMs =
(long) (mTmpRailStats.getCellularTotalEnergyUseduWs() / opVolt);
mModemActivity.getMonitoredRailChargeConsumedMaMs().addCountLocked(
monitoredRailChargeConsumedMaMs);
mHistoryCur.modemRailChargeMah +=
(monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR);
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mTmpRailStats.resetCellularTotalEnergyUsed();
}
incrementPerRatDataLocked(deltaInfo, elapsedRealtimeMs);
}
long totalAppRadioTimeUs = mMobileRadioActivePerAppTimer.getTimeSinceMarkLocked(
elapsedRealtimeMs * 1000);
mMobileRadioActivePerAppTimer.setMark(elapsedRealtimeMs);
long totalRxPackets = 0;
long totalTxPackets = 0;
if (delta != null) {
for (NetworkStats.Entry entry : delta) {
if (entry.getRxPackets() == 0 && entry.getTxPackets() == 0) {
continue;
}
if (DEBUG_ENERGY) {
Slog.d(TAG, "Mobile uid " + entry.getUid() + ": delta rx="
+ entry.getRxBytes() + " tx=" + entry.getTxBytes()
+ " rxPackets=" + entry.getRxPackets()
+ " txPackets=" + entry.getTxPackets());
}
totalRxPackets += entry.getRxPackets();
totalTxPackets += entry.getTxPackets();
final Uid u = getUidStatsLocked(
mapUid(entry.getUid()), elapsedRealtimeMs, uptimeMs);
u.noteNetworkActivityLocked(NETWORK_MOBILE_RX_DATA, entry.getRxBytes(),
entry.getRxPackets());
u.noteNetworkActivityLocked(NETWORK_MOBILE_TX_DATA, entry.getTxBytes(),
entry.getTxPackets());
if (entry.getSet() == NetworkStats.SET_DEFAULT) { // Background transfers
u.noteNetworkActivityLocked(NETWORK_MOBILE_BG_RX_DATA,
entry.getRxBytes(), entry.getRxPackets());
u.noteNetworkActivityLocked(NETWORK_MOBILE_BG_TX_DATA,
entry.getTxBytes(), entry.getTxPackets());
}
mNetworkByteActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked(
entry.getRxBytes());
mNetworkByteActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked(
entry.getTxBytes());
mNetworkPacketActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked(
entry.getRxPackets());
mNetworkPacketActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked(
entry.getTxPackets());
}
// Now distribute proportional blame to the apps that did networking.
long totalPackets = totalRxPackets + totalTxPackets;
if (totalPackets > 0) {
for (NetworkStats.Entry entry : delta) {
if (entry.getRxPackets() == 0 && entry.getTxPackets() == 0) {
continue;
}
final Uid u = getUidStatsLocked(mapUid(entry.getUid()),
elapsedRealtimeMs, uptimeMs);
// Distribute total radio active time in to this app.
final long appPackets = entry.getRxPackets() + entry.getTxPackets();
final long appRadioTimeUs =
(totalAppRadioTimeUs * appPackets) / totalPackets;
u.noteMobileRadioActiveTimeLocked(appRadioTimeUs, elapsedRealtimeMs);
// Distribute measured mobile radio charge consumption based on app radio
// active time
if (uidEstimatedConsumptionMah != null) {
uidEstimatedConsumptionMah.incrementValue(u.getUid(),
mMobileRadioPowerCalculator.calcPowerFromRadioActiveDurationMah(
appRadioTimeUs / 1000));
}
// Remove this app from the totals, so that we don't lose any time
// due to rounding.
totalAppRadioTimeUs -= appRadioTimeUs;
totalPackets -= appPackets;
if (deltaInfo != null) {
ControllerActivityCounterImpl activityCounter =
u.getOrCreateModemControllerActivityLocked();
if (totalRxPackets > 0 && entry.getRxPackets() > 0) {
final long rxMs = (entry.getRxPackets()
* deltaInfo.getReceiveTimeMillis()) / totalRxPackets;
activityCounter.getOrCreateRxTimeCounter()
.increment(rxMs, elapsedRealtimeMs);
}
if (totalTxPackets > 0 && entry.getTxPackets() > 0) {
for (int lvl = 0; lvl < ModemActivityInfo.getNumTxPowerLevels();
lvl++) {
long txMs = entry.getTxPackets()
* deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl);
txMs /= totalTxPackets;
activityCounter.getOrCreateTxTimeCounters()[lvl]
.increment(txMs, elapsedRealtimeMs);
}
}
}
}
}
if (totalAppRadioTimeUs > 0) {
// Whoops, there is some radio time we can't blame on an app!
mMobileRadioActiveUnknownTime.addCountLocked(totalAppRadioTimeUs);
mMobileRadioActiveUnknownCount.addCountLocked(1);
}
// Update the MeasuredEnergyStats information.
if (uidEstimatedConsumptionMah != null) {
double totalEstimatedConsumptionMah = 0.0;
// Estimate total active radio power consumption since last mark.
final long totalRadioTimeMs = mMobileRadioActiveTimer.getTimeSinceMarkLocked(
elapsedRealtimeMs * 1000) / 1000;
mMobileRadioActiveTimer.setMark(elapsedRealtimeMs);
totalEstimatedConsumptionMah +=
mMobileRadioPowerCalculator.calcPowerFromRadioActiveDurationMah(
totalRadioTimeMs);
// Estimate idle power consumption at each signal strength level
final int numSignalStrengthLevels = mPhoneSignalStrengthsTimer.length;
for (int strengthLevel = 0; strengthLevel < numSignalStrengthLevels;
strengthLevel++) {
final long strengthLevelDurationMs =
mPhoneSignalStrengthsTimer[strengthLevel].getTimeSinceMarkLocked(
elapsedRealtimeMs * 1000) / 1000;
mPhoneSignalStrengthsTimer[strengthLevel].setMark(elapsedRealtimeMs);
totalEstimatedConsumptionMah +=
mMobileRadioPowerCalculator.calcIdlePowerAtSignalStrengthMah(
strengthLevelDurationMs, strengthLevel);
}
// Estimate total active radio power consumption since last mark.
final long scanTimeMs = mPhoneSignalScanningTimer.getTimeSinceMarkLocked(
elapsedRealtimeMs * 1000) / 1000;
mPhoneSignalScanningTimer.setMark(elapsedRealtimeMs);
totalEstimatedConsumptionMah +=
mMobileRadioPowerCalculator.calcScanTimePowerMah(scanTimeMs);
distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO,
consumedChargeUC, uidEstimatedConsumptionMah,
totalEstimatedConsumptionMah, elapsedRealtimeMs);
}
delta = null;
}
}
}
@GuardedBy("this")
private void incrementPerRatDataLocked(ModemActivityInfo deltaInfo, long elapsedRealtimeMs) {
final int infoSize = deltaInfo.getSpecificInfoLength();
if (infoSize == 1 && deltaInfo.getSpecificInfoRat(0)
== AccessNetworkConstants.AccessNetworkType.UNKNOWN
&& deltaInfo.getSpecificInfoFrequencyRange(0)
== ServiceState.FREQUENCY_RANGE_UNKNOWN) {
// Specific info data unavailable. Proportionally smear Rx and Tx times across each RAT.
final int levelCount = CellSignalStrength.getNumSignalStrengthLevels();
long[] perSignalStrengthActiveTimeMs = new long[levelCount];
long totalActiveTimeMs = 0;
for (int rat = 0; rat < RADIO_ACCESS_TECHNOLOGY_COUNT; rat++) {
final RadioAccessTechnologyBatteryStats ratStats = mPerRatBatteryStats[rat];
if (ratStats == null) continue;
final int freqCount = ratStats.getFrequencyRangeCount();
for (int freq = 0; freq < freqCount; freq++) {
for (int level = 0; level < levelCount; level++) {
final long durationMs = ratStats.getTimeSinceMark(freq, level,
elapsedRealtimeMs);
perSignalStrengthActiveTimeMs[level] += durationMs;
totalActiveTimeMs += durationMs;
}
}
}
if (totalActiveTimeMs != 0) {
// Smear the provided Tx/Rx durations across each RAT, frequency, and signal
// strength.
for (int rat = 0; rat < RADIO_ACCESS_TECHNOLOGY_COUNT; rat++) {
final RadioAccessTechnologyBatteryStats ratStats = mPerRatBatteryStats[rat];
if (ratStats == null) continue;
final int freqCount = ratStats.getFrequencyRangeCount();
for (int freq = 0; freq < freqCount; freq++) {
long frequencyDurationMs = 0;
for (int level = 0; level < levelCount; level++) {
final long durationMs = ratStats.getTimeSinceMark(freq, level,
elapsedRealtimeMs);
final long totalLvlDurationMs =
perSignalStrengthActiveTimeMs[level];
if (totalLvlDurationMs == 0) continue;
final long totalTxLvlDurations =
deltaInfo.getTransmitDurationMillisAtPowerLevel(level);
// Smear HAL provided Tx power level duration based on active modem
// duration in a given state. (Add totalLvlDurationMs / 2 before
// the integer division with totalLvlDurationMs for rounding.)
final long proportionalTxDurationMs =
(durationMs * totalTxLvlDurations
+ (totalLvlDurationMs / 2)) / totalLvlDurationMs;
ratStats.incrementTxDuration(freq, level, proportionalTxDurationMs);
frequencyDurationMs += durationMs;
}
final long totalRxDuration = deltaInfo.getReceiveTimeMillis();
// Smear HAL provided Rx power duration based on active modem
// duration in a given state. (Add totalActiveTimeMs / 2 before the
// integer division with totalActiveTimeMs for rounding.)
final long proportionalRxDurationMs =
(frequencyDurationMs * totalRxDuration + (totalActiveTimeMs
/ 2)) / totalActiveTimeMs;
ratStats.incrementRxDuration(freq, proportionalRxDurationMs);
}
}
}
} else {
// Specific data available.
for (int index = 0; index < infoSize; index++) {
final int rat = deltaInfo.getSpecificInfoRat(index);
final int freq = deltaInfo.getSpecificInfoFrequencyRange(index);
// Map RadioAccessNetworkType to course grain RadioAccessTechnology.
final int ratBucket = mapRadioAccessNetworkTypeToRadioAccessTechnology(rat);
final RadioAccessTechnologyBatteryStats ratStats = getRatBatteryStatsLocked(
ratBucket);
final long rxTimeMs = deltaInfo.getReceiveTimeMillis(rat, freq);
final int[] txTimesMs = deltaInfo.getTransmitTimeMillis(rat, freq);
ratStats.incrementRxDuration(freq, rxTimeMs);
final int numTxLvl = txTimesMs.length;
for (int lvl = 0; lvl < numTxLvl; lvl++) {
ratStats.incrementTxDuration(freq, lvl, txTimesMs[lvl]);
}
}
}
for (int rat = 0; rat < RADIO_ACCESS_TECHNOLOGY_COUNT; rat++) {
final RadioAccessTechnologyBatteryStats ratStats = mPerRatBatteryStats[rat];
if (ratStats == null) continue;
ratStats.setMark(elapsedRealtimeMs);
}
}
/**
* Add modem tx power to history
* Device is said to be in high cellular transmit power when it has spent most of the transmit
* time at the highest power level.
* @param activityInfo
*/
private synchronized void addModemTxPowerToHistory(final ModemActivityInfo activityInfo,
long elapsedRealtimeMs, long uptimeMs) {
if (activityInfo == null) {
return;
}
int levelMaxTimeSpent = 0;
for (int i = 1; i < ModemActivityInfo.getNumTxPowerLevels(); i++) {
if (activityInfo.getTransmitDurationMillisAtPowerLevel(i)
> activityInfo.getTransmitDurationMillisAtPowerLevel(levelMaxTimeSpent)) {
levelMaxTimeSpent = i;
}
}
if (levelMaxTimeSpent == ModemActivityInfo.getNumTxPowerLevels() - 1) {
mHistoryCur.states2 |= HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG;
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
}
private final class BluetoothActivityInfoCache {
long idleTimeMs;
long rxTimeMs;
long txTimeMs;
long energy;
SparseLongArray uidRxBytes = new SparseLongArray();
SparseLongArray uidTxBytes = new SparseLongArray();
void set(BluetoothActivityEnergyInfo info) {
idleTimeMs = info.getControllerIdleTimeMillis();
rxTimeMs = info.getControllerRxTimeMillis();
txTimeMs = info.getControllerTxTimeMillis();
energy = info.getControllerEnergyUsed();
if (!info.getUidTraffic().isEmpty()) {
for (UidTraffic traffic : info.getUidTraffic()) {
uidRxBytes.incrementValue(traffic.getUid(), traffic.getRxBytes());
uidTxBytes.incrementValue(traffic.getUid(), traffic.getTxBytes());
}
}
}
void reset() {
idleTimeMs = 0;
rxTimeMs = 0;
txTimeMs = 0;
energy = 0;
uidRxBytes.clear();
uidTxBytes.clear();
}
}
private final BluetoothActivityInfoCache mLastBluetoothActivityInfo
= new BluetoothActivityInfoCache();
/**
* Distribute Bluetooth energy info and network traffic to apps.
*
* @param info The accumulated energy information from the bluetooth controller.
*/
@GuardedBy("this")
public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info,
final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) {
if (DEBUG_ENERGY) {
Slog.d(TAG, "Updating bluetooth stats: " + info);
}
if (info == null) {
return;
}
if (!mOnBatteryInternal || mIgnoreNextExternalStats) {
mLastBluetoothActivityInfo.set(info);
return;
}
mHasBluetoothReporting = true;
if (info.getControllerRxTimeMillis() < mLastBluetoothActivityInfo.rxTimeMs
|| info.getControllerTxTimeMillis() < mLastBluetoothActivityInfo.txTimeMs
|| info.getControllerIdleTimeMillis() < mLastBluetoothActivityInfo.idleTimeMs
|| info.getControllerEnergyUsed() < mLastBluetoothActivityInfo.energy) {
// A drop in accumulated Bluetooth stats is a sign of a Bluetooth crash.
// Reset the preserved previous snapshot in order to restart accumulating deltas.
mLastBluetoothActivityInfo.reset();
}
final long rxTimeMs =
info.getControllerRxTimeMillis() - mLastBluetoothActivityInfo.rxTimeMs;
final long txTimeMs =
info.getControllerTxTimeMillis() - mLastBluetoothActivityInfo.txTimeMs;
final long idleTimeMs =
info.getControllerIdleTimeMillis() - mLastBluetoothActivityInfo.idleTimeMs;
if (DEBUG_ENERGY) {
Slog.d(TAG, "------ BEGIN BLE power blaming ------");
Slog.d(TAG, " Tx Time: " + txTimeMs + " ms");
Slog.d(TAG, " Rx Time: " + rxTimeMs + " ms");
Slog.d(TAG, " Idle Time: " + idleTimeMs + " ms");
}
final SparseDoubleArray uidEstimatedConsumptionMah =
(mGlobalMeasuredEnergyStats != null
&& mBluetoothPowerCalculator != null && consumedChargeUC > 0) ?
new SparseDoubleArray() : null;
long totalScanTimeMs = 0;
final int uidCount = mUidStats.size();
for (int i = 0; i < uidCount; i++) {
final Uid u = mUidStats.valueAt(i);
if (u.mBluetoothScanTimer == null) {
continue;
}
totalScanTimeMs += u.mBluetoothScanTimer.getTimeSinceMarkLocked(
elapsedRealtimeMs * 1000) / 1000;
}
final boolean normalizeScanRxTime = (totalScanTimeMs > rxTimeMs);
final boolean normalizeScanTxTime = (totalScanTimeMs > txTimeMs);
if (DEBUG_ENERGY) {
Slog.d(TAG, "Normalizing scan power for RX=" + normalizeScanRxTime
+ " TX=" + normalizeScanTxTime);
}
long leftOverRxTimeMs = rxTimeMs;
long leftOverTxTimeMs = txTimeMs;
final SparseLongArray rxTimesMs = new SparseLongArray(uidCount);
final SparseLongArray txTimesMs = new SparseLongArray(uidCount);
for (int i = 0; i < uidCount; i++) {
final Uid u = mUidStats.valueAt(i);
if (u.mBluetoothScanTimer == null) {
continue;
}
long scanTimeSinceMarkMs = u.mBluetoothScanTimer.getTimeSinceMarkLocked(
elapsedRealtimeMs * 1000) / 1000;
if (scanTimeSinceMarkMs > 0) {
// Set the new mark so that next time we get new data since this point.
u.mBluetoothScanTimer.setMark(elapsedRealtimeMs);
long scanTimeRxSinceMarkMs = scanTimeSinceMarkMs;
long scanTimeTxSinceMarkMs = scanTimeSinceMarkMs;
if (normalizeScanRxTime) {
// Scan time is longer than the total rx time in the controller,
// so distribute the scan time proportionately. This means regular traffic
// will not blamed, but scans are more expensive anyways.
scanTimeRxSinceMarkMs = (rxTimeMs * scanTimeRxSinceMarkMs) / totalScanTimeMs;
}
if (normalizeScanTxTime) {
// Scan time is longer than the total tx time in the controller,
// so distribute the scan time proportionately. This means regular traffic
// will not blamed, but scans are more expensive anyways.
scanTimeTxSinceMarkMs = (txTimeMs * scanTimeTxSinceMarkMs) / totalScanTimeMs;
}
rxTimesMs.incrementValue(u.getUid(), scanTimeRxSinceMarkMs);
txTimesMs.incrementValue(u.getUid(), scanTimeTxSinceMarkMs);
if (uidEstimatedConsumptionMah != null) {
uidEstimatedConsumptionMah.incrementValue(u.getUid(),
mBluetoothPowerCalculator.calculatePowerMah(
scanTimeRxSinceMarkMs, scanTimeTxSinceMarkMs, 0));
}
leftOverRxTimeMs -= scanTimeRxSinceMarkMs;
leftOverTxTimeMs -= scanTimeTxSinceMarkMs;
}
}
if (DEBUG_ENERGY) {
Slog.d(TAG, "Left over time for traffic RX=" + leftOverRxTimeMs + " TX="
+ leftOverTxTimeMs);
}
//
// Now distribute blame to apps that did bluetooth traffic.
//
long totalTxBytes = 0;
long totalRxBytes = 0;
final List<UidTraffic> uidTraffic = info.getUidTraffic();
final int numUids = uidTraffic.size();
for (int i = 0; i < numUids; i++) {
final UidTraffic traffic = uidTraffic.get(i);
final long rxBytes = traffic.getRxBytes() - mLastBluetoothActivityInfo.uidRxBytes.get(
traffic.getUid());
final long txBytes = traffic.getTxBytes() - mLastBluetoothActivityInfo.uidTxBytes.get(
traffic.getUid());
// Add to the global counters.
mNetworkByteActivityCounters[NETWORK_BT_RX_DATA].addCountLocked(rxBytes);
mNetworkByteActivityCounters[NETWORK_BT_TX_DATA].addCountLocked(txBytes);
// Add to the UID counters.
final Uid u = getUidStatsLocked(mapUid(traffic.getUid()), elapsedRealtimeMs, uptimeMs);
u.noteNetworkActivityLocked(NETWORK_BT_RX_DATA, rxBytes, 0);
u.noteNetworkActivityLocked(NETWORK_BT_TX_DATA, txBytes, 0);
// Calculate the total traffic.
totalRxBytes += rxBytes;
totalTxBytes += txBytes;
}
if ((totalTxBytes != 0 || totalRxBytes != 0) && (leftOverRxTimeMs != 0
|| leftOverTxTimeMs != 0)) {
for (int i = 0; i < numUids; i++) {
final UidTraffic traffic = uidTraffic.get(i);
final int uid = traffic.getUid();
final long rxBytes =
traffic.getRxBytes() - mLastBluetoothActivityInfo.uidRxBytes.get(uid);
final long txBytes =
traffic.getTxBytes() - mLastBluetoothActivityInfo.uidTxBytes.get(uid);
final Uid u = getUidStatsLocked(mapUid(uid), elapsedRealtimeMs, uptimeMs);
final ControllerActivityCounterImpl counter =
u.getOrCreateBluetoothControllerActivityLocked();
if (totalRxBytes > 0 && rxBytes > 0) {
final long timeRxMs = (leftOverRxTimeMs * rxBytes) / totalRxBytes;
rxTimesMs.incrementValue(uid, timeRxMs);
}
if (totalTxBytes > 0 && txBytes > 0) {
final long timeTxMs = (leftOverTxTimeMs * txBytes) / totalTxBytes;
txTimesMs.incrementValue(uid, timeTxMs);
}
}
for (int i = 0; i < txTimesMs.size(); i++) {
final int uid = txTimesMs.keyAt(i);
final long myTxTimeMs = txTimesMs.valueAt(i);
if (DEBUG_ENERGY) {
Slog.d(TAG, " TxTime for UID " + uid + ": " + myTxTimeMs + " ms");
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.getOrCreateBluetoothControllerActivityLocked()
.getOrCreateTxTimeCounters()[0]
.increment(myTxTimeMs, elapsedRealtimeMs);
if (uidEstimatedConsumptionMah != null) {
uidEstimatedConsumptionMah.incrementValue(uid,
mBluetoothPowerCalculator.calculatePowerMah(0, myTxTimeMs, 0));
}
}
for (int i = 0; i < rxTimesMs.size(); i++) {
final int uid = rxTimesMs.keyAt(i);
final long myRxTimeMs = rxTimesMs.valueAt(i);
if (DEBUG_ENERGY) {
Slog.d(TAG, " RxTime for UID " + uid + ": " + myRxTimeMs + " ms");
}
getUidStatsLocked(rxTimesMs.keyAt(i), elapsedRealtimeMs, uptimeMs)
.getOrCreateBluetoothControllerActivityLocked()
.getOrCreateRxTimeCounter()
.increment(myRxTimeMs, elapsedRealtimeMs);
if (uidEstimatedConsumptionMah != null) {
uidEstimatedConsumptionMah.incrementValue(uid,
mBluetoothPowerCalculator.calculatePowerMah(myRxTimeMs, 0, 0));
}
}
}
mBluetoothActivity.getOrCreateRxTimeCounter().increment(rxTimeMs, elapsedRealtimeMs);
mBluetoothActivity.getOrCreateTxTimeCounters()[0].increment(txTimeMs, elapsedRealtimeMs);
mBluetoothActivity.getOrCreateIdleTimeCounter().increment(idleTimeMs, elapsedRealtimeMs);
// POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
final double opVolt = mPowerProfile.getAveragePower(
PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
double controllerMaMs = 0;
if (opVolt != 0) {
controllerMaMs = (info.getControllerEnergyUsed() - mLastBluetoothActivityInfo.energy)
/ opVolt;
// We store the power drain as mAms.
mBluetoothActivity.getPowerCounter().addCountLocked((long) controllerMaMs);
}
// Update the MeasuredEnergyStats information.
if (uidEstimatedConsumptionMah != null) {
mGlobalMeasuredEnergyStats.updateStandardBucket(
MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH, consumedChargeUC);
double totalEstimatedMah
= mBluetoothPowerCalculator.calculatePowerMah(rxTimeMs, txTimeMs, idleTimeMs);
totalEstimatedMah = Math.max(totalEstimatedMah, controllerMaMs / MILLISECONDS_IN_HOUR);
distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH,
consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedMah,
elapsedRealtimeMs);
}
mLastBluetoothActivityInfo.set(info);
}
/**
* Read Resource Power Manager (RPM) state and voter times.
* If RPM stats were fetched more recently than RPM_STATS_UPDATE_FREQ_MS ago, uses the old data
* instead of fetching it anew.
*
* Note: This should be called without synchronizing this BatteryStatsImpl object
*/
public void fillLowPowerStats() {
if (mPlatformIdleStateCallback == null) return;
RpmStats rpmStats = new RpmStats();
long now = SystemClock.elapsedRealtime();
if (now - mLastRpmStatsUpdateTimeMs >= RPM_STATS_UPDATE_FREQ_MS) {
mPlatformIdleStateCallback.fillLowPowerStats(rpmStats);
synchronized (this) {
mTmpRpmStats = rpmStats;
mLastRpmStatsUpdateTimeMs = now;
}
}
}
/**
* Record Resource Power Manager (RPM) state and voter times.
* TODO(b/185252376): Remove this logging. PowerStatsService logs the same data more
* efficiently.
*/
public void updateRpmStatsLocked(long elapsedRealtimeUs) {
if (mTmpRpmStats == null) return;
for (Map.Entry<String, RpmStats.PowerStatePlatformSleepState> pstate
: mTmpRpmStats.mPlatformLowPowerStats.entrySet()) {
// Update values for this platform state.
final String pName = pstate.getKey();
final long pTimeUs = pstate.getValue().mTimeMs * 1000;
final int pCount = pstate.getValue().mCount;
getRpmTimerLocked(pName).update(pTimeUs, pCount, elapsedRealtimeUs);
if (SCREEN_OFF_RPM_STATS_ENABLED) {
getScreenOffRpmTimerLocked(pName).update(pTimeUs, pCount, elapsedRealtimeUs);
}
// Update values for each voter of this platform state.
for (Map.Entry<String, RpmStats.PowerStateElement> voter
: pstate.getValue().mVoters.entrySet()) {
final String vName = pName + "." + voter.getKey();
final long vTimeUs = voter.getValue().mTimeMs * 1000;
final int vCount = voter.getValue().mCount;
getRpmTimerLocked(vName).update(vTimeUs, vCount, elapsedRealtimeUs);
if (SCREEN_OFF_RPM_STATS_ENABLED) {
getScreenOffRpmTimerLocked(vName).update(vTimeUs, vCount, elapsedRealtimeUs);
}
}
}
for (Map.Entry<String, RpmStats.PowerStateSubsystem> subsys
: mTmpRpmStats.mSubsystemLowPowerStats.entrySet()) {
final String subsysName = subsys.getKey();
for (Map.Entry<String, RpmStats.PowerStateElement> sstate
: subsys.getValue().mStates.entrySet()) {
final String name = subsysName + "." + sstate.getKey();
final long timeUs = sstate.getValue().mTimeMs * 1000;
final int count = sstate.getValue().mCount;
getRpmTimerLocked(name).update(timeUs, count, elapsedRealtimeUs);
if (SCREEN_OFF_RPM_STATS_ENABLED) {
getScreenOffRpmTimerLocked(name).update(timeUs, count, elapsedRealtimeUs);
}
}
}
}
/**
* Accumulate Cpu charge consumption and distribute it to the correct state and the apps.
* Only call if device is on battery.
*
* @param clusterChargeUC amount of charge (microcoulombs) consumed by each Cpu Cluster
* @param accumulator collection of calculated uid cpu power consumption to smear
* clusterChargeUC against.
*/
@GuardedBy("this")
@SuppressWarnings("GuardedBy") // errorprone false positive on u.addChargeToStandardBucketLocked
private void updateCpuMeasuredEnergyStatsLocked(@NonNull long[] clusterChargeUC,
@NonNull CpuDeltaPowerAccumulator accumulator) {
if (DEBUG_ENERGY) {
Slog.d(TAG,
"Updating cpu cluster stats: " + clusterChargeUC.toString());
}
if (mGlobalMeasuredEnergyStats == null) {
return;
}
final int numClusters = clusterChargeUC.length;
long totalCpuChargeUC = 0;
for (int i = 0; i < numClusters; i++) {
totalCpuChargeUC += clusterChargeUC[i];
}
if (totalCpuChargeUC <= 0) return;
final long timestampMs = mClock.elapsedRealtime();
mGlobalMeasuredEnergyStats.updateStandardBucket(MeasuredEnergyStats.POWER_BUCKET_CPU,
totalCpuChargeUC, timestampMs);
// Calculate the measured microcoulombs/calculated milliamp-hour charge ratio for each
// cluster to normalize each uid's estimated power usage against actual power usage for
// a given cluster.
final double[] clusterChargeRatio = new double[numClusters];
for (int cluster = 0; cluster < numClusters; cluster++) {
final double totalClusterChargeMah = accumulator.totalClusterChargesMah[cluster];
if (totalClusterChargeMah <= 0.0) {
// This cluster did not have any work on it, since last update.
// Avoid dividing by zero.
clusterChargeRatio[cluster] = 0.0;
} else {
clusterChargeRatio[cluster] =
clusterChargeUC[cluster] / accumulator.totalClusterChargesMah[cluster];
}
}
// Assign and distribute power usage to apps based on their calculated cpu cluster charge.
final long uidChargeArraySize = accumulator.perUidCpuClusterChargesMah.size();
for (int i = 0; i < uidChargeArraySize; i++) {
final Uid uid = accumulator.perUidCpuClusterChargesMah.keyAt(i);
final double[] uidClusterChargesMah = accumulator.perUidCpuClusterChargesMah.valueAt(i);
// Iterate each cpu cluster and sum the proportional measured cpu cluster charge to
// get the total cpu charge consumed by a uid.
long uidCpuChargeUC = 0;
for (int cluster = 0; cluster < numClusters; cluster++) {
final double uidClusterChargeMah = uidClusterChargesMah[cluster];
// Proportionally allocate the measured cpu cluster charge to a uid using the
// measured charge/calculated charge ratio. Add 0.5 to round the proportional
// charge double to the nearest long value.
final long uidClusterChargeUC =
(long) (uidClusterChargeMah * clusterChargeRatio[cluster]
+ 0.5);
uidCpuChargeUC += uidClusterChargeUC;
}
if (uidCpuChargeUC < 0) {
Slog.wtf(TAG,
"Unexpected proportional measured charge (" + uidCpuChargeUC + ") for uid "
+ uid.mUid);
continue;
}
uid.addChargeToStandardBucketLocked(uidCpuChargeUC,
MeasuredEnergyStats.POWER_BUCKET_CPU, timestampMs);
}
}
/**
* Accumulate Display charge consumption and distribute it to the correct state and the apps.
*
* NOTE: The algorithm used makes the strong assumption that app foreground activity time
* is always 0 when the screen is not "ON" and whenever the rail energy is 0 (if supported).
* To the extent that those assumptions are violated, the algorithm will err.
*
* @param chargesUC amount of charge (microcoulombs) used by each Display since this was last
* called.
* @param screenStates each screen state at the time this data collection was scheduled
*/
@GuardedBy("this")
public void updateDisplayMeasuredEnergyStatsLocked(long[] chargesUC, int[] screenStates,
long elapsedRealtimeMs) {
if (DEBUG_ENERGY) Slog.d(TAG, "Updating display stats: " + Arrays.toString(chargesUC));
if (mGlobalMeasuredEnergyStats == null) {
return;
}
final int numDisplays;
if (mPerDisplayBatteryStats.length == screenStates.length) {
numDisplays = screenStates.length;
} else {
// if this point is reached, it will be reached every display state change.
// Rate limit the wtf logging to once every 100 display updates.
if (mDisplayMismatchWtfCount++ % 100 == 0) {
Slog.wtf(TAG, "Mismatch between PowerProfile reported display count ("
+ mPerDisplayBatteryStats.length
+ ") and PowerStatsHal reported display count (" + screenStates.length
+ ")");
}
// Keep the show going, use the shorter of the two.
numDisplays = mPerDisplayBatteryStats.length < screenStates.length
? mPerDisplayBatteryStats.length : screenStates.length;
}
final int[] oldScreenStates = new int[numDisplays];
for (int i = 0; i < numDisplays; i++) {
final int screenState = screenStates[i];
oldScreenStates[i] = mPerDisplayBatteryStats[i].screenStateAtLastEnergyMeasurement;
mPerDisplayBatteryStats[i].screenStateAtLastEnergyMeasurement = screenState;
}
if (!mOnBatteryInternal) {
// There's nothing further to update.
return;
}
if (mIgnoreNextExternalStats) {
// Although under ordinary resets we won't get here, and typically a new sync will
// happen right after the reset, strictly speaking we need to set all mark times to now.
final int uidStatsSize = mUidStats.size();
for (int i = 0; i < uidStatsSize; i++) {
final Uid uid = mUidStats.valueAt(i);
uid.markProcessForegroundTimeUs(elapsedRealtimeMs, false);
}
return;
}
long totalScreenOnChargeUC = 0;
for (int i = 0; i < numDisplays; i++) {
final long chargeUC = chargesUC[i];
if (chargeUC <= 0) {
// There's nothing further to update.
continue;
}
final @StandardPowerBucket int powerBucket =
MeasuredEnergyStats.getDisplayPowerBucket(oldScreenStates[i]);
mGlobalMeasuredEnergyStats.updateStandardBucket(powerBucket, chargeUC);
if (powerBucket == MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON) {
totalScreenOnChargeUC += chargeUC;
}
}
// Now we blame individual apps, but only if the display was ON.
if (totalScreenOnChargeUC <= 0) {
return;
}
// TODO(b/175726779): Consider unifying the code with the non-rail display power blaming.
// NOTE: fg time is NOT pooled. If two uids are both somehow in fg, then that time is
// 'double counted' and will simply exceed the realtime that elapsed.
// TODO(b/175726779): collect per display uid visibility for display power attribution.
// Collect total time since mark so that we can normalize power.
final SparseDoubleArray fgTimeUsArray = new SparseDoubleArray();
final long elapsedRealtimeUs = elapsedRealtimeMs * 1000;
// TODO(b/175726779): Update and optimize the algorithm (e.g. avoid iterating over ALL uids)
final int uidStatsSize = mUidStats.size();
for (int i = 0; i < uidStatsSize; i++) {
final Uid uid = mUidStats.valueAt(i);
final long fgTimeUs = uid.markProcessForegroundTimeUs(elapsedRealtimeMs, true);
if (fgTimeUs == 0) continue;
fgTimeUsArray.put(uid.getUid(), (double) fgTimeUs);
}
distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON,
totalScreenOnChargeUC, fgTimeUsArray, 0, elapsedRealtimeMs);
}
/**
* Accumulate GNSS charge consumption and distribute it to the correct state and the apps.
*
* @param chargeUC amount of charge (microcoulombs) used by GNSS since this was last called.
*/
@GuardedBy("this")
public void updateGnssMeasuredEnergyStatsLocked(long chargeUC, long elapsedRealtimeMs) {
if (DEBUG_ENERGY) Slog.d(TAG, "Updating gnss stats: " + chargeUC);
if (mGlobalMeasuredEnergyStats == null) {
return;
}
if (!mOnBatteryInternal || chargeUC <= 0) {
// There's nothing further to update.
return;
}
if (mIgnoreNextExternalStats) {
// Although under ordinary resets we won't get here, and typically a new sync will
// happen right after the reset, strictly speaking we need to set all mark times to now.
final int uidStatsSize = mUidStats.size();
for (int i = 0; i < uidStatsSize; i++) {
final Uid uid = mUidStats.valueAt(i);
uid.markGnssTimeUs(elapsedRealtimeMs);
}
return;
}
mGlobalMeasuredEnergyStats.updateStandardBucket(MeasuredEnergyStats.POWER_BUCKET_GNSS,
chargeUC);
// Collect the per uid time since mark so that we can normalize power.
final SparseDoubleArray gnssTimeUsArray = new SparseDoubleArray();
// TODO(b/175726779): Update and optimize the algorithm (e.g. avoid iterating over ALL uids)
final int uidStatsSize = mUidStats.size();
for (int i = 0; i < uidStatsSize; i++) {
final Uid uid = mUidStats.valueAt(i);
final long gnssTimeUs = uid.markGnssTimeUs(elapsedRealtimeMs);
if (gnssTimeUs == 0) continue;
gnssTimeUsArray.put(uid.getUid(), (double) gnssTimeUs);
}
distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_GNSS, chargeUC,
gnssTimeUsArray, 0, elapsedRealtimeMs);
}
/**
* Accumulate Custom power bucket charge, globally and for each app.
*
* @param totalChargeUC charge (microcoulombs) used for this bucket since this was last called.
* @param uidCharges map of uid->charge (microcoulombs) for this bucket since last called.
* Data inside uidCharges will not be modified (treated immutable).
* Uids not already known to BatteryStats will be ignored.
*/
@GuardedBy("this")
@SuppressWarnings("GuardedBy") // errorprone false positive on u.addChargeToCustomBucketLocked
public void updateCustomMeasuredEnergyStatsLocked(int customPowerBucket,
long totalChargeUC, @Nullable SparseLongArray uidCharges) {
if (DEBUG_ENERGY) {
Slog.d(TAG, "Updating attributed measured charge stats for custom bucket "
+ customPowerBucket
+ " with total charge " + totalChargeUC
+ " and uid charges " + String.valueOf(uidCharges));
}
if (mGlobalMeasuredEnergyStats == null) return;
if (!mOnBatteryInternal || mIgnoreNextExternalStats || totalChargeUC <= 0) return;
mGlobalMeasuredEnergyStats.updateCustomBucket(customPowerBucket, totalChargeUC,
mClock.elapsedRealtime());
if (uidCharges == null) return;
final int numUids = uidCharges.size();
for (int i = 0; i < numUids; i++) {
final int uidInt = mapUid(uidCharges.keyAt(i));
final long uidChargeUC = uidCharges.valueAt(i);
if (uidChargeUC == 0) continue;
final Uid uidObj = getAvailableUidStatsLocked(uidInt);
if (uidObj != null) {
uidObj.addChargeToCustomBucketLocked(uidChargeUC, customPowerBucket);
} else {
// Ignore any uid not already known to BatteryStats, rather than creating a new Uid.
// Otherwise we could end up reviving dead Uids. Note that the CPU data is updated
// first, so any uid that has used any CPU should already be known to BatteryStats.
// Recently removed uids (especially common for isolated uids) can reach this path
// and are ignored.
if (!Process.isIsolated(uidInt)) {
Slog.w(TAG, "Received measured charge " + totalChargeUC + " for custom bucket "
+ customPowerBucket + " for non-existent uid " + uidInt);
}
}
}
}
/**
* Attributes energy (for the given bucket) to each uid according to the following formula:
* blamedEnergy[uid] = totalEnergy * ratioNumerators[uid] / ratioDenominator;
* <p>Does nothing if ratioDenominator is 0.
*
* <p>Here, ratioDenominator = max(sumOfAllRatioNumerators, minRatioDenominator),
* so if given minRatioDenominator <= 0, then sumOfAllRatioNumerators will be used implicitly.
*
* <p>Note that ratioNumerators and minRatioDenominator must use the same units, but need not
* use the same units as totalConsumedChargeUC (which must be in microcoulombs).
*
* <p>A consequence of minRatioDenominator is that the sum over all uids might be less than
* totalConsumedChargeUC. This is intentional; the remainder is purposefully unnaccounted rather
* than incorrectly blamed on uids, and implies unknown (non-uid) sources of drain.
*
* <p>All uids in ratioNumerators must exist in mUidStats already.
*/
@GuardedBy("this")
@SuppressWarnings("GuardedBy") // errorprone false positive on u.addChargeToStandardBucketLocked
private void distributeEnergyToUidsLocked(@StandardPowerBucket int bucket,
long totalConsumedChargeUC, SparseDoubleArray ratioNumerators,
double minRatioDenominator, long timestampMs) {
// If the sum of all app usage was greater than the total, use that instead:
double sumRatioNumerators = 0;
for (int i = ratioNumerators.size() - 1; i >= 0; i--) {
sumRatioNumerators += ratioNumerators.valueAt(i);
}
final double ratioDenominator = Math.max(sumRatioNumerators, minRatioDenominator);
if (ratioDenominator <= 0) return;
for (int i = ratioNumerators.size() - 1; i >= 0; i--) {
final Uid uid = getAvailableUidStatsLocked(ratioNumerators.keyAt(i));
final double ratioNumerator = ratioNumerators.valueAt(i);
final long uidActualUC
= (long) (totalConsumedChargeUC * ratioNumerator / ratioDenominator + 0.5);
uid.addChargeToStandardBucketLocked(uidActualUC, bucket, timestampMs);
}
}
/**
* Read and record Rail Energy data.
*/
public void updateRailStatsLocked() {
if (mMeasuredEnergyRetriever == null || !mTmpRailStats.isRailStatsAvailable()) {
return;
}
mMeasuredEnergyRetriever.fillRailDataStats(mTmpRailStats);
}
/** Informs that external stats data has been completely flushed. */
public void informThatAllExternalStatsAreFlushed() {
synchronized (this) {
// Any data from the pre-reset era is flushed, so we can henceforth process future data.
mIgnoreNextExternalStats = false;
}
}
/**
* Read and distribute kernel wake lock use across apps.
*/
public void updateKernelWakelocksLocked() {
updateKernelWakelocksLocked(mClock.elapsedRealtime() * 1000);
}
/**
* @see #updateKernelWakelocksLocked()
*/
public void updateKernelWakelocksLocked(long elapsedRealtimeUs) {
final KernelWakelockStats wakelockStats = mKernelWakelockReader.readKernelWakelockStats(
mTmpWakelockStats);
if (wakelockStats == null) {
// Not crashing might make board bringup easier.
Slog.w(TAG, "Couldn't get kernel wake lock stats");
return;
}
for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
String name = ent.getKey();
KernelWakelockStats.Entry kws = ent.getValue();
SamplingTimer kwlt = mKernelWakelockStats.get(name);
if (kwlt == null) {
kwlt = new SamplingTimer(mClock, mOnBatteryScreenOffTimeBase);
mKernelWakelockStats.put(name, kwlt);
}
kwlt.update(kws.mTotalTime, kws.mCount, elapsedRealtimeUs);
kwlt.setUpdateVersion(kws.mVersion);
}
int numWakelocksSetStale = 0;
// Set timers to stale if they didn't appear in /d/wakeup_sources (or /proc/wakelocks)
// this time.
for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
SamplingTimer st = ent.getValue();
if (st.getUpdateVersion() != wakelockStats.kernelWakelockVersion) {
st.endSample(elapsedRealtimeUs);
numWakelocksSetStale++;
}
}
// Record whether we've seen a non-zero time (for debugging b/22716723).
if (wakelockStats.isEmpty()) {
Slog.wtf(TAG, "All kernel wakelocks had time of zero");
}
if (numWakelocksSetStale == mKernelWakelockStats.size()) {
Slog.wtf(TAG, "All kernel wakelocks were set stale. new version=" +
wakelockStats.kernelWakelockVersion);
}
}
// We use an anonymous class to access these variables,
// so they can't live on the stack or they'd have to be
// final MutableLong objects (more allocations).
// Used in updateCpuTimeLocked().
long mTempTotalCpuUserTimeUs;
long mTempTotalCpuSystemTimeUs;
long[][] mWakeLockAllocationsUs;
/**
* Reads the newest memory stats from the kernel.
*/
public void updateKernelMemoryBandwidthLocked() {
updateKernelMemoryBandwidthLocked(mClock.elapsedRealtime() * 1000);
}
public void updateKernelMemoryBandwidthLocked(long elapsedRealtimeUs) {
mKernelMemoryBandwidthStats.updateStats();
LongSparseLongArray bandwidthEntries = mKernelMemoryBandwidthStats.getBandwidthEntries();
final int bandwidthEntryCount = bandwidthEntries.size();
int index;
for (int i = 0; i < bandwidthEntryCount; i++) {
SamplingTimer timer;
if ((index = mKernelMemoryStats.indexOfKey(bandwidthEntries.keyAt(i))) >= 0) {
timer = mKernelMemoryStats.valueAt(index);
} else {
timer = new SamplingTimer(mClock, mOnBatteryTimeBase);
mKernelMemoryStats.put(bandwidthEntries.keyAt(i), timer);
}
timer.update(bandwidthEntries.valueAt(i), 1, elapsedRealtimeUs);
if (DEBUG_MEMORY) {
Slog.d(TAG, String.format("Added entry %d and updated timer to: "
+ "mUnpluggedReportedTotalTimeUs %d size %d", bandwidthEntries.keyAt(i),
mKernelMemoryStats.get(
bandwidthEntries.keyAt(i)).mUnpluggedReportedTotalTimeUs,
mKernelMemoryStats.size()));
}
}
}
public boolean isOnBatteryLocked() {
return mOnBatteryTimeBase.isRunning();
}
public boolean isOnBatteryScreenOffLocked() {
return mOnBatteryScreenOffTimeBase.isRunning();
}
/**
* Object for calculating and accumulating the estimated cpu power used while reading the
* various cpu kernel files.
*/
@VisibleForTesting
public static class CpuDeltaPowerAccumulator {
// Keeps track of total charge used per cluster.
public final double[] totalClusterChargesMah;
// Keeps track of charge used per cluster per uid.
public final ArrayMap<Uid, double[]> perUidCpuClusterChargesMah;
private final CpuPowerCalculator mCalculator;
private Uid mCachedUid = null;
private double[] mUidClusterCache = null;
CpuDeltaPowerAccumulator(CpuPowerCalculator calculator, int nClusters) {
mCalculator = calculator;
totalClusterChargesMah = new double[nClusters];
perUidCpuClusterChargesMah = new ArrayMap<>();
}
/** Add per cpu cluster durations to the currently cached uid. */
public void addCpuClusterDurationsMs(Uid uid, long[] durationsMs) {
final double[] uidChargesMah = getOrCreateUidCpuClusterCharges(uid);
for (int cluster = 0; cluster < durationsMs.length; cluster++) {
final double estimatedDeltaMah = mCalculator.calculatePerCpuClusterPowerMah(cluster,
durationsMs[cluster]);
uidChargesMah[cluster] += estimatedDeltaMah;
totalClusterChargesMah[cluster] += estimatedDeltaMah;
}
}
/** Add per speed per cpu cluster durations to the currently cached uid. */
public void addCpuClusterSpeedDurationsMs(Uid uid, int cluster, int speed,
long durationsMs) {
final double[] uidChargesMah = getOrCreateUidCpuClusterCharges(uid);
final double estimatedDeltaMah = mCalculator.calculatePerCpuFreqPowerMah(cluster, speed,
durationsMs);
uidChargesMah[cluster] += estimatedDeltaMah;
totalClusterChargesMah[cluster] += estimatedDeltaMah;
}
private double[] getOrCreateUidCpuClusterCharges(Uid uid) {
// Repeated additions on the same uid is very likely.
// Skip a lookup if getting the same uid as the last get.
if (uid == mCachedUid) return mUidClusterCache;
double[] uidChargesMah = perUidCpuClusterChargesMah.get(uid);
if (uidChargesMah == null) {
uidChargesMah = new double[totalClusterChargesMah.length];
perUidCpuClusterChargesMah.put(uid, uidChargesMah);
}
mCachedUid = uid;
mUidClusterCache = uidChargesMah;
return uidChargesMah;
}
}
/**
* Read and distribute CPU usage across apps. If their are partial wakelocks being held
* and we are on battery with screen off, we give more of the cpu time to those apps holding
* wakelocks. If the screen is on, we just assign the actual cpu time an app used.
* It's possible this will be invoked after the internal battery/screen states are updated, so
* passing the appropriate battery/screen states to try attribute the cpu times to correct
* buckets.
*/
@GuardedBy("this")
public void updateCpuTimeLocked(boolean onBattery, boolean onBatteryScreenOff,
long[] measuredCpuClusterChargeUC) {
if (mPowerProfile == null) {
return;
}
if (DEBUG_ENERGY_CPU) {
Slog.d(TAG, "!Cpu updating!");
}
if (mCpuFreqs == null) {
mCpuFreqs = mCpuUidFreqTimeReader.readFreqs(mPowerProfile);
}
// Calculate the wakelocks we have to distribute amongst. The system is excluded as it is
// usually holding the wakelock on behalf of an app.
// And Only distribute cpu power to wakelocks if the screen is off and we're on battery.
ArrayList<StopwatchTimer> partialTimersToConsider = null;
if (onBatteryScreenOff) {
partialTimersToConsider = new ArrayList<>();
for (int i = mPartialTimers.size() - 1; i >= 0; --i) {
final StopwatchTimer timer = mPartialTimers.get(i);
// Since the collection and blaming of wakelocks can be scheduled to run after
// some delay, the mPartialTimers list may have new entries. We can't blame
// the newly added timer for past cpu time, so we only consider timers that
// were present for one round of collection. Once a timer has gone through
// a round of collection, its mInList field is set to true.
if (timer.mInList && timer.mUid != null && timer.mUid.mUid != Process.SYSTEM_UID) {
partialTimersToConsider.add(timer);
}
}
}
markPartialTimersAsEligible();
// When the battery is not on, we don't attribute the cpu times to any timers but we still
// need to take the snapshots.
if (!onBattery) {
mCpuUidUserSysTimeReader.readDelta(false, null);
mCpuUidFreqTimeReader.readDelta(false, null);
mNumAllUidCpuTimeReads += 2;
if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
mCpuUidActiveTimeReader.readDelta(false, null);
mCpuUidClusterTimeReader.readDelta(false, null);
mNumAllUidCpuTimeReads += 2;
}
for (int cluster = mKernelCpuSpeedReaders.length - 1; cluster >= 0; --cluster) {
mKernelCpuSpeedReaders[cluster].readDelta();
}
mSystemServerCpuThreadReader.readDelta();
return;
}
mUserInfoProvider.refreshUserIds();
final SparseLongArray updatedUids = mCpuUidFreqTimeReader.perClusterTimesAvailable()
? null : new SparseLongArray();
final CpuDeltaPowerAccumulator powerAccumulator;
if (mGlobalMeasuredEnergyStats != null
&& mGlobalMeasuredEnergyStats.isStandardBucketSupported(
MeasuredEnergyStats.POWER_BUCKET_CPU) && mCpuPowerCalculator != null) {
if (measuredCpuClusterChargeUC == null) {
Slog.wtf(TAG,
"POWER_BUCKET_CPU supported but no measured Cpu Cluster charge reported "
+ "on updateCpuTimeLocked!");
powerAccumulator = null;
} else {
// Cpu Measured Energy is supported, create an object to accumulate the estimated
// charge consumption since the last cpu update
final int numClusters = mPowerProfile.getNumCpuClusters();
powerAccumulator = new CpuDeltaPowerAccumulator(mCpuPowerCalculator, numClusters);
}
} else {
powerAccumulator = null;
}
readKernelUidCpuTimesLocked(partialTimersToConsider, updatedUids, onBattery);
// updatedUids=null means /proc/uid_time_in_state provides snapshots of per-cluster cpu
// freqs, so no need to approximate these values.
if (updatedUids != null) {
updateClusterSpeedTimes(updatedUids, onBattery, powerAccumulator);
}
readKernelUidCpuFreqTimesLocked(partialTimersToConsider, onBattery, onBatteryScreenOff,
powerAccumulator);
mNumAllUidCpuTimeReads += 2;
if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
// Cpu Active times do not get any info ony how to attribute measured Cpu Cluster
// charge, so not need to provide the powerAccumulator
readKernelUidCpuActiveTimesLocked(onBattery);
readKernelUidCpuClusterTimesLocked(onBattery, powerAccumulator);
mNumAllUidCpuTimeReads += 2;
}
updateSystemServerThreadStats();
if (powerAccumulator != null) {
updateCpuMeasuredEnergyStatsLocked(measuredCpuClusterChargeUC, powerAccumulator);
}
}
/**
* Estimates the proportion of the System Server CPU activity (per cluster per speed)
* spent on handling incoming binder calls.
*/
@VisibleForTesting
public void updateSystemServerThreadStats() {
// There are some simplifying assumptions made in this algorithm
// 1) We assume that if a thread handles incoming binder calls, all of its activity
// is spent doing that. Most incoming calls are handled by threads allocated
// by the native layer in the binder thread pool, so this assumption is reasonable.
// 2) We use the aggregate CPU time spent in different threads as a proxy for the CPU
// cost. In reality, in multi-core CPUs, the CPU cost may not be linearly
// affected by additional threads.
SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
mSystemServerCpuThreadReader.readDelta();
if (systemServiceCpuThreadTimes == null) {
return;
}
if (mBinderThreadCpuTimesUs == null) {
mBinderThreadCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase);
}
mBinderThreadCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
if (DEBUG_BINDER_STATS) {
Slog.d(TAG, "System server threads per CPU cluster (incoming binder threads)");
long binderThreadTimeMs = 0;
int cpuIndex = 0;
final long[] binderThreadCpuTimesUs = mBinderThreadCpuTimesUs.getCountsLocked(
BatteryStats.STATS_SINCE_CHARGED);
int index = 0;
int numCpuClusters = mPowerProfile.getNumCpuClusters();
for (int cluster = 0; cluster < numCpuClusters; cluster++) {
StringBuilder sb = new StringBuilder();
sb.append("cpu").append(cpuIndex).append(": [");
int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
for (int speed = 0; speed < numSpeeds; speed++) {
if (speed != 0) {
sb.append(", ");
}
long binderCountMs = binderThreadCpuTimesUs[index] / 1000;
sb.append(TextUtils.formatSimple("%10d", binderCountMs));
binderThreadTimeMs += binderCountMs;
index++;
}
cpuIndex += mPowerProfile.getNumCoresInCpuCluster(cluster);
Slog.d(TAG, sb.toString());
}
}
}
/**
* Mark the current partial timers as gone through a collection so that they will be
* considered in the next cpu times distribution to wakelock holders.
*/
@VisibleForTesting
public void markPartialTimersAsEligible() {
if (ArrayUtils.referenceEquals(mPartialTimers, mLastPartialTimers)) {
// No difference, so each timer is now considered for the next collection.
for (int i = mPartialTimers.size() - 1; i >= 0; --i) {
mPartialTimers.get(i).mInList = true;
}
} else {
// The lists are different, meaning we added (or removed a timer) since the last
// collection.
for (int i = mLastPartialTimers.size() - 1; i >= 0; --i) {
mLastPartialTimers.get(i).mInList = false;
}
mLastPartialTimers.clear();
// Mark the current timers as gone through a collection.
final int numPartialTimers = mPartialTimers.size();
for (int i = 0; i < numPartialTimers; ++i) {
final StopwatchTimer timer = mPartialTimers.get(i);
timer.mInList = true;
mLastPartialTimers.add(timer);
}
}
}
/**
* Take snapshot of cpu times (aggregated over all uids) at different frequencies and
* calculate cpu times spent by each uid at different frequencies. Will also add estimated
* power consumptions, if powerAccumulator data structure is provided.
*
* @param updatedUids The uids for which times spent at different frequencies are calculated.
* @param onBattery whether or not this is onBattery
* @param powerAccumulator object to accumulate the estimated cluster charge consumption.
*/
@VisibleForTesting
public void updateClusterSpeedTimes(@NonNull SparseLongArray updatedUids, boolean onBattery,
@Nullable CpuDeltaPowerAccumulator powerAccumulator) {
long totalCpuClustersTimeMs = 0;
// Read the time spent for each cluster at various cpu frequencies.
final long[][] clusterSpeedTimesMs = new long[mKernelCpuSpeedReaders.length][];
for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
clusterSpeedTimesMs[cluster] = mKernelCpuSpeedReaders[cluster].readDelta();
if (clusterSpeedTimesMs[cluster] != null) {
for (int speed = clusterSpeedTimesMs[cluster].length - 1; speed >= 0; --speed) {
totalCpuClustersTimeMs += clusterSpeedTimesMs[cluster][speed];
}
}
}
if (totalCpuClustersTimeMs != 0) {
// We have cpu times per freq aggregated over all uids but we need the times per uid.
// So, we distribute total time spent by an uid to different cpu freqs based on the
// amount of time cpu was running at that freq.
final int updatedUidsCount = updatedUids.size();
final long elapsedRealtimeMs = mClock.elapsedRealtime();
final long uptimeMs = mClock.uptimeMillis();
for (int i = 0; i < updatedUidsCount; ++i) {
final Uid u = getUidStatsLocked(updatedUids.keyAt(i), elapsedRealtimeMs, uptimeMs);
final long appCpuTimeUs = updatedUids.valueAt(i);
// Add the cpu speeds to this UID.
final int numClusters = mPowerProfile.getNumCpuClusters();
if (u.mCpuClusterSpeedTimesUs == null ||
u.mCpuClusterSpeedTimesUs.length != numClusters) {
u.mCpuClusterSpeedTimesUs = new LongSamplingCounter[numClusters][];
}
for (int cluster = 0; cluster < clusterSpeedTimesMs.length; cluster++) {
final int speedsInCluster = clusterSpeedTimesMs[cluster].length;
if (u.mCpuClusterSpeedTimesUs[cluster] == null || speedsInCluster !=
u.mCpuClusterSpeedTimesUs[cluster].length) {
u.mCpuClusterSpeedTimesUs[cluster]
= new LongSamplingCounter[speedsInCluster];
}
final LongSamplingCounter[] cpuSpeeds = u.mCpuClusterSpeedTimesUs[cluster];
for (int speed = 0; speed < speedsInCluster; speed++) {
if (cpuSpeeds[speed] == null) {
cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase);
}
final long deltaSpeedCount = appCpuTimeUs
* clusterSpeedTimesMs[cluster][speed]
/ totalCpuClustersTimeMs;
cpuSpeeds[speed].addCountLocked(deltaSpeedCount, onBattery);
if (powerAccumulator != null) {
powerAccumulator.addCpuClusterSpeedDurationsMs(u, cluster,
speed, deltaSpeedCount);
}
}
}
}
}
}
/**
* Take a snapshot of the cpu times spent by each uid and update the corresponding counters.
* If {@param partialTimers} is not null and empty, then we assign a portion of cpu times to
* wakelock holders.
*
* @param partialTimers The wakelock holders among which the cpu times will be distributed.
* @param updatedUids If not null, then the uids found in the snapshot will be added to this.
*/
@VisibleForTesting
public void readKernelUidCpuTimesLocked(@Nullable ArrayList<StopwatchTimer> partialTimers,
@Nullable SparseLongArray updatedUids, boolean onBattery) {
mTempTotalCpuUserTimeUs = mTempTotalCpuSystemTimeUs = 0;
final int numWakelocks = partialTimers == null ? 0 : partialTimers.size();
final long startTimeMs = mClock.uptimeMillis();
final long elapsedRealtimeMs = mClock.elapsedRealtime();
mCpuUidUserSysTimeReader.readDelta(false, (uid, timesUs) -> {
long userTimeUs = timesUs[0], systemTimeUs = timesUs[1];
uid = mapUid(uid);
if (Process.isIsolated(uid)) {
// This could happen if the isolated uid mapping was removed before that process
// was actually killed.
if (DEBUG) Slog.d(TAG, "Got readings for an isolated uid: " + uid);
return;
}
if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
if (DEBUG) Slog.d(TAG, "Got readings for an invalid user's uid " + uid);
return;
}
final Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, startTimeMs);
// Accumulate the total system and user time.
mTempTotalCpuUserTimeUs += userTimeUs;
mTempTotalCpuSystemTimeUs += systemTimeUs;
StringBuilder sb = null;
if (DEBUG_ENERGY_CPU) {
sb = new StringBuilder();
sb.append(" got time for uid=").append(u.mUid).append(": u=");
TimeUtils.formatDuration(userTimeUs / 1000, sb);
sb.append(" s=");
TimeUtils.formatDuration(systemTimeUs / 1000, sb);
sb.append("\n");
}
if (numWakelocks > 0) {
// We have wakelocks being held, so only give a portion of the
// time to the process. The rest will be distributed among wakelock
// holders.
userTimeUs = (userTimeUs * WAKE_LOCK_WEIGHT) / 100;
systemTimeUs = (systemTimeUs * WAKE_LOCK_WEIGHT) / 100;
}
if (sb != null) {
sb.append(" adding to uid=").append(u.mUid).append(": u=");
TimeUtils.formatDuration(userTimeUs / 1000, sb);
sb.append(" s=");
TimeUtils.formatDuration(systemTimeUs / 1000, sb);
Slog.d(TAG, sb.toString());
}
u.mUserCpuTime.addCountLocked(userTimeUs, onBattery);
u.mSystemCpuTime.addCountLocked(systemTimeUs, onBattery);
if (updatedUids != null) {
updatedUids.put(u.getUid(), userTimeUs + systemTimeUs);
}
});
final long elapsedTimeMs = mClock.uptimeMillis() - startTimeMs;
if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
Slog.d(TAG, "Reading cpu stats took " + elapsedTimeMs + "ms");
}
if (numWakelocks > 0) {
// Distribute a portion of the total cpu time to wakelock holders.
mTempTotalCpuUserTimeUs = (mTempTotalCpuUserTimeUs * (100 - WAKE_LOCK_WEIGHT)) / 100;
mTempTotalCpuSystemTimeUs =
(mTempTotalCpuSystemTimeUs * (100 - WAKE_LOCK_WEIGHT)) / 100;
for (int i = 0; i < numWakelocks; ++i) {
final StopwatchTimer timer = partialTimers.get(i);
final int userTimeUs = (int) (mTempTotalCpuUserTimeUs / (numWakelocks - i));
final int systemTimeUs = (int) (mTempTotalCpuSystemTimeUs / (numWakelocks - i));
if (DEBUG_ENERGY_CPU) {
final StringBuilder sb = new StringBuilder();
sb.append(" Distributing wakelock uid=").append(timer.mUid.mUid)
.append(": u=");
TimeUtils.formatDuration(userTimeUs / 1000, sb);
sb.append(" s=");
TimeUtils.formatDuration(systemTimeUs / 1000, sb);
Slog.d(TAG, sb.toString());
}
timer.mUid.mUserCpuTime.addCountLocked(userTimeUs, onBattery);
timer.mUid.mSystemCpuTime.addCountLocked(systemTimeUs, onBattery);
if (updatedUids != null) {
final int uid = timer.mUid.getUid();
updatedUids.put(uid, updatedUids.get(uid, 0) + userTimeUs + systemTimeUs);
}
final Uid.Proc proc = timer.mUid.getProcessStatsLocked("*wakelock*");
proc.addCpuTimeLocked(userTimeUs / 1000, systemTimeUs / 1000, onBattery);
mTempTotalCpuUserTimeUs -= userTimeUs;
mTempTotalCpuSystemTimeUs -= systemTimeUs;
}
}
}
/**
* Take a snapshot of the cpu times spent by each uid in each freq and update the
* corresponding counters. Will also add estimated power consumptions, if powerAccumulator
* data structure is provided.
*
* @param partialTimers The wakelock holders among which the cpu freq times will be distributed.
* @param onBattery whether or not this is onBattery
* @param onBatteryScreenOff whether or not this is onBattery with the screen off.
* @param powerAccumulator object to accumulate the estimated cluster charge consumption.
*/
@VisibleForTesting
public void readKernelUidCpuFreqTimesLocked(@Nullable ArrayList<StopwatchTimer> partialTimers,
boolean onBattery, boolean onBatteryScreenOff,
@Nullable CpuDeltaPowerAccumulator powerAccumulator) {
final boolean perClusterTimesAvailable =
mCpuUidFreqTimeReader.perClusterTimesAvailable();
final int numWakelocks = partialTimers == null ? 0 : partialTimers.size();
final int numClusters = mPowerProfile.getNumCpuClusters();
mWakeLockAllocationsUs = null;
final long startTimeMs = mClock.uptimeMillis();
final long elapsedRealtimeMs = mClock.elapsedRealtime();
// If power is being accumulated for attribution, data needs to be read immediately.
final boolean forceRead = powerAccumulator != null;
mCpuUidFreqTimeReader.readDelta(forceRead, (uid, cpuFreqTimeMs) -> {
uid = mapUid(uid);
if (Process.isIsolated(uid)) {
if (DEBUG) Slog.d(TAG, "Got freq readings for an isolated uid: " + uid);
return;
}
if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
if (DEBUG) Slog.d(TAG, "Got freq readings for an invalid user's uid " + uid);
return;
}
final Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, startTimeMs);
if (u.mCpuFreqTimeMs == null || u.mCpuFreqTimeMs.getSize() != cpuFreqTimeMs.length) {
detachIfNotNull(u.mCpuFreqTimeMs);
u.mCpuFreqTimeMs = new LongSamplingCounterArray(mOnBatteryTimeBase);
}
u.mCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs, onBattery);
if (u.mScreenOffCpuFreqTimeMs == null ||
u.mScreenOffCpuFreqTimeMs.getSize() != cpuFreqTimeMs.length) {
detachIfNotNull(u.mScreenOffCpuFreqTimeMs);
u.mScreenOffCpuFreqTimeMs = new LongSamplingCounterArray(
mOnBatteryScreenOffTimeBase);
}
u.mScreenOffCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs, onBatteryScreenOff);
if (perClusterTimesAvailable) {
if (u.mCpuClusterSpeedTimesUs == null ||
u.mCpuClusterSpeedTimesUs.length != numClusters) {
detachIfNotNull(u.mCpuClusterSpeedTimesUs);
u.mCpuClusterSpeedTimesUs = new LongSamplingCounter[numClusters][];
}
if (numWakelocks > 0 && mWakeLockAllocationsUs == null) {
mWakeLockAllocationsUs = new long[numClusters][];
}
int freqIndex = 0;
for (int cluster = 0; cluster < numClusters; ++cluster) {
final int speedsInCluster = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
if (u.mCpuClusterSpeedTimesUs[cluster] == null ||
u.mCpuClusterSpeedTimesUs[cluster].length != speedsInCluster) {
detachIfNotNull(u.mCpuClusterSpeedTimesUs[cluster]);
u.mCpuClusterSpeedTimesUs[cluster]
= new LongSamplingCounter[speedsInCluster];
}
if (numWakelocks > 0 && mWakeLockAllocationsUs[cluster] == null) {
mWakeLockAllocationsUs[cluster] = new long[speedsInCluster];
}
final LongSamplingCounter[] cpuTimesUs = u.mCpuClusterSpeedTimesUs[cluster];
for (int speed = 0; speed < speedsInCluster; ++speed) {
if (cpuTimesUs[speed] == null) {
cpuTimesUs[speed] = new LongSamplingCounter(mOnBatteryTimeBase);
}
final long appAllocationUs;
if (mWakeLockAllocationsUs != null) {
appAllocationUs =
(cpuFreqTimeMs[freqIndex] * 1000 * WAKE_LOCK_WEIGHT) / 100;
mWakeLockAllocationsUs[cluster][speed] +=
(cpuFreqTimeMs[freqIndex] * 1000 - appAllocationUs);
} else {
appAllocationUs = cpuFreqTimeMs[freqIndex] * 1000;
}
cpuTimesUs[speed].addCountLocked(appAllocationUs, onBattery);
if (powerAccumulator != null) {
powerAccumulator.addCpuClusterSpeedDurationsMs(u, cluster,
speed, appAllocationUs / 1000);
}
freqIndex++;
}
}
}
});
final long elapsedTimeMs = mClock.uptimeMillis() - startTimeMs;
if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
Slog.d(TAG, "Reading cpu freq times took " + elapsedTimeMs + "ms");
}
if (mWakeLockAllocationsUs != null) {
for (int i = 0; i < numWakelocks; ++i) {
final Uid u = partialTimers.get(i).mUid;
if (u.mCpuClusterSpeedTimesUs == null ||
u.mCpuClusterSpeedTimesUs.length != numClusters) {
detachIfNotNull(u.mCpuClusterSpeedTimesUs);
u.mCpuClusterSpeedTimesUs = new LongSamplingCounter[numClusters][];
}
for (int cluster = 0; cluster < numClusters; ++cluster) {
final int speedsInCluster = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
if (u.mCpuClusterSpeedTimesUs[cluster] == null ||
u.mCpuClusterSpeedTimesUs[cluster].length != speedsInCluster) {
detachIfNotNull(u.mCpuClusterSpeedTimesUs[cluster]);
u.mCpuClusterSpeedTimesUs[cluster]
= new LongSamplingCounter[speedsInCluster];
}
final LongSamplingCounter[] cpuTimeUs = u.mCpuClusterSpeedTimesUs[cluster];
for (int speed = 0; speed < speedsInCluster; ++speed) {
if (cpuTimeUs[speed] == null) {
cpuTimeUs[speed] = new LongSamplingCounter(mOnBatteryTimeBase);
}
final long allocationUs =
mWakeLockAllocationsUs[cluster][speed] / (numWakelocks - i);
cpuTimeUs[speed].addCountLocked(allocationUs, onBattery);
mWakeLockAllocationsUs[cluster][speed] -= allocationUs;
if (powerAccumulator != null) {
powerAccumulator.addCpuClusterSpeedDurationsMs(u, cluster,
speed, allocationUs / 1000);
}
}
}
}
}
}
/**
* Take a snapshot of the cpu active times spent by each uid and update the corresponding
* counters.
*/
@VisibleForTesting
public void readKernelUidCpuActiveTimesLocked(boolean onBattery) {
final long startTimeMs = mClock.uptimeMillis();
final long elapsedRealtimeMs = mClock.elapsedRealtime();
mCpuUidActiveTimeReader.readAbsolute((uid, cpuActiveTimesMs) -> {
final int parentUid = mapUid(uid);
if (Process.isIsolated(parentUid)) {
if (DEBUG) Slog.w(TAG, "Got active times for an isolated uid: " + uid);
return;
}
if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
if (DEBUG) Slog.w(TAG, "Got active times for an invalid user's uid " + uid);
return;
}
final Uid u = getUidStatsLocked(parentUid, elapsedRealtimeMs, startTimeMs);
if (parentUid == uid) {
u.getCpuActiveTimeCounter().update(cpuActiveTimesMs, elapsedRealtimeMs);
} else {
final SparseArray<Uid.ChildUid> childUids = u.mChildUids;
if (childUids == null) {
return;
}
Uid.ChildUid childUid = childUids.get(uid);
if (childUid != null) {
final long delta =
childUid.cpuActiveCounter.update(cpuActiveTimesMs, elapsedRealtimeMs);
u.getCpuActiveTimeCounter().increment(delta, elapsedRealtimeMs);
}
}
});
final long elapsedTimeMs = mClock.uptimeMillis() - startTimeMs;
if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
Slog.d(TAG, "Reading cpu active times took " + elapsedTimeMs + "ms");
}
}
/**
* Take a snapshot of the cpu cluster times spent by each uid and update the corresponding
* counters. Will also add estimated power consumptions, if powerAccumulator data structure
* is provided.
*
* @param onBattery whether or not this is onBattery
* @param powerAccumulator object to accumulate the estimated cluster charge consumption.
*/
@VisibleForTesting
public void readKernelUidCpuClusterTimesLocked(boolean onBattery,
@Nullable CpuDeltaPowerAccumulator powerAccumulator) {
final long startTimeMs = mClock.uptimeMillis();
final long elapsedRealtimeMs = mClock.elapsedRealtime();
// If power is being accumulated for attribution, data needs to be read immediately.
final boolean forceRead = powerAccumulator != null;
mCpuUidClusterTimeReader.readDelta(forceRead, (uid, cpuClusterTimesMs) -> {
uid = mapUid(uid);
if (Process.isIsolated(uid)) {
if (DEBUG) Slog.w(TAG, "Got cluster times for an isolated uid: " + uid);
return;
}
if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
if (DEBUG) Slog.w(TAG, "Got cluster times for an invalid user's uid " + uid);
return;
}
final Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, startTimeMs);
u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesMs, onBattery);
if (powerAccumulator != null) {
powerAccumulator.addCpuClusterDurationsMs(u, cpuClusterTimesMs);
}
});
final long elapsedTimeMs = mClock.uptimeMillis() - startTimeMs;
if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
Slog.d(TAG, "Reading cpu cluster times took " + elapsedTimeMs + "ms");
}
}
boolean setChargingLocked(boolean charging) {
// if the device is no longer charging, remove the callback
// if the device is now charging, it means that this is either called
// 1. directly when level >= 90
// 2. or from within the runnable that we deferred
// For 1. if we have an existing callback, remove it, since we will immediately send a
// ACTION_CHARGING
// For 2. we remove existing callback so we don't send multiple ACTION_CHARGING
mHandler.removeCallbacks(mDeferSetCharging);
if (mCharging != charging) {
mCharging = charging;
if (charging) {
mHistoryCur.states2 |= HistoryItem.STATE2_CHARGING_FLAG;
} else {
mHistoryCur.states2 &= ~HistoryItem.STATE2_CHARGING_FLAG;
}
mHandler.sendEmptyMessage(MSG_REPORT_CHARGING);
return true;
}
return false;
}
/**
* Notifies BatteryStatsImpl that the system server is ready.
*/
public void onSystemReady() {
mSystemReady = true;
}
@GuardedBy("this")
protected void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime,
final boolean onBattery, final int oldStatus, final int level, final int chargeUah) {
boolean doWrite = false;
Message m = mHandler.obtainMessage(MSG_REPORT_POWER_CHANGE);
m.arg1 = onBattery ? 1 : 0;
mHandler.sendMessage(m);
final long uptimeUs = mSecUptime * 1000;
final long realtimeUs = mSecRealtime * 1000;
final int screenState = mScreenState;
if (onBattery) {
// We will reset our status if we are unplugging after the
// battery was last full, or the level is at 100, or
// we have gone through a significant charge (from a very low
// level to a now very high level).
// Also, we will reset the stats if battery got partially charged
// and discharged repeatedly without ever reaching the full charge.
// This reset is done in order to prevent stats sessions from going on forever.
// Exceedingly long battery sessions would lead to an overflow of
// data structures such as mWakeupReasonStats.
boolean reset = false;
if (!mNoAutoReset && mSystemReady
&& (oldStatus == BatteryManager.BATTERY_STATUS_FULL
|| level >= 90
|| (mDischargeCurrentLevel < 20 && level >= 80)
|| getHighDischargeAmountSinceCharge() >= 200)) {
Slog.i(TAG, "Resetting battery stats: level=" + level + " status=" + oldStatus
+ " dischargeLevel=" + mDischargeCurrentLevel
+ " lowAmount=" + getLowDischargeAmountSinceCharge()
+ " highAmount=" + getHighDischargeAmountSinceCharge());
// Before we write, collect a snapshot of the final aggregated
// stats to be reported in the next checkin. Only do this if we have
// a sufficient amount of data to make it interesting.
if (getLowDischargeAmountSinceCharge() >= 20) {
final long startTimeMs = SystemClock.uptimeMillis();
final Parcel parcel = Parcel.obtain();
writeSummaryToParcel(parcel, true);
final long initialTimeMs = SystemClock.uptimeMillis() - startTimeMs;
BackgroundThread.getHandler().post(new Runnable() {
@Override public void run() {
synchronized (mCheckinFile) {
final long startTimeMs2 = SystemClock.uptimeMillis();
FileOutputStream stream = null;
try {
stream = mCheckinFile.startWrite();
stream.write(parcel.marshall());
stream.flush();
mCheckinFile.finishWrite(stream);
com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
"batterystats-checkin", initialTimeMs
+ SystemClock.uptimeMillis() - startTimeMs2);
} catch (IOException e) {
Slog.w("BatteryStats",
"Error writing checkin battery statistics", e);
mCheckinFile.failWrite(stream);
} finally {
parcel.recycle();
}
}
}
});
}
doWrite = true;
resetAllStatsLocked(mSecUptime, mSecRealtime, RESET_REASON_FULL_CHARGE);
if (chargeUah > 0 && level > 0) {
// Only use the reported coulomb charge value if it is supported and reported.
mEstimatedBatteryCapacityMah = (int) ((chargeUah / 1000) / (level / 100.0));
}
mDischargeStartLevel = level;
reset = true;
mDischargeStepTracker.init();
}
if (mCharging) {
setChargingLocked(false);
}
mLastChargingStateLevel = level;
mOnBattery = mOnBatteryInternal = true;
mLastDischargeStepLevel = level;
mMinDischargeStepLevel = level;
mDischargeStepTracker.clearTime();
mDailyDischargeStepTracker.clearTime();
mInitStepMode = mCurStepMode;
mModStepMode = 0;
pullPendingStateUpdatesLocked();
mHistoryCur.batteryLevel = (byte)level;
mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Battery unplugged to: "
+ Integer.toHexString(mHistoryCur.states));
if (reset) {
mRecordingHistory = true;
startRecordingHistory(mSecRealtime, mSecUptime, reset);
}
addHistoryRecordLocked(mSecRealtime, mSecUptime);
mDischargeCurrentLevel = mDischargeUnplugLevel = level;
if (Display.isOnState(screenState)) {
mDischargeScreenOnUnplugLevel = level;
mDischargeScreenDozeUnplugLevel = 0;
mDischargeScreenOffUnplugLevel = 0;
} else if (Display.isDozeState(screenState)) {
mDischargeScreenOnUnplugLevel = 0;
mDischargeScreenDozeUnplugLevel = level;
mDischargeScreenOffUnplugLevel = 0;
} else {
mDischargeScreenOnUnplugLevel = 0;
mDischargeScreenDozeUnplugLevel = 0;
mDischargeScreenOffUnplugLevel = level;
}
mDischargeAmountScreenOn = 0;
mDischargeAmountScreenDoze = 0;
mDischargeAmountScreenOff = 0;
updateTimeBasesLocked(true, screenState, uptimeUs, realtimeUs);
} else {
mLastChargingStateLevel = level;
mOnBattery = mOnBatteryInternal = false;
pullPendingStateUpdatesLocked();
mHistoryCur.batteryLevel = (byte)level;
mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Battery plugged to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(mSecRealtime, mSecUptime);
mDischargeCurrentLevel = mDischargePlugLevel = level;
if (level < mDischargeUnplugLevel) {
mLowDischargeAmountSinceCharge += mDischargeUnplugLevel-level-1;
mHighDischargeAmountSinceCharge += mDischargeUnplugLevel-level;
}
updateDischargeScreenLevelsLocked(screenState, screenState);
updateTimeBasesLocked(false, screenState, uptimeUs, realtimeUs);
mChargeStepTracker.init();
mLastChargeStepLevel = level;
mMaxChargeStepLevel = level;
mInitStepMode = mCurStepMode;
mModStepMode = 0;
}
if (doWrite || (mLastWriteTimeMs + (60 * 1000)) < mSecRealtime) {
if (mStatsFile != null && mBatteryStatsHistory.getActiveFile() != null) {
writeAsyncLocked();
}
}
}
@GuardedBy("this")
private void startRecordingHistory(final long elapsedRealtimeMs, final long uptimeMs,
boolean reset) {
mRecordingHistory = true;
mHistoryCur.currentTime = mClock.currentTimeMillis();
addHistoryBufferLocked(elapsedRealtimeMs,
reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME,
mHistoryCur);
mHistoryCur.currentTime = 0;
if (reset) {
initActiveHistoryEventsLocked(elapsedRealtimeMs, uptimeMs);
}
}
@GuardedBy("this")
private void recordCurrentTimeChangeLocked(final long currentTimeMs,
final long elapsedRealtimeMs, final long uptimeMs) {
if (mRecordingHistory) {
mHistoryCur.currentTime = currentTimeMs;
addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_CURRENT_TIME, mHistoryCur);
mHistoryCur.currentTime = 0;
}
}
@GuardedBy("this")
private void recordShutdownLocked(final long currentTimeMs, final long elapsedRealtimeMs) {
if (mRecordingHistory) {
mHistoryCur.currentTime = currentTimeMs;
addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_SHUTDOWN, mHistoryCur);
mHistoryCur.currentTime = 0;
}
}
private void scheduleSyncExternalStatsLocked(String reason, int updateFlags) {
if (mExternalSync != null) {
mExternalSync.scheduleSync(reason, updateFlags);
}
}
// This should probably be exposed in the API, though it's not critical
public static final int BATTERY_PLUGGED_NONE = OsProtoEnums.BATTERY_PLUGGED_NONE; // = 0
@GuardedBy("this")
public void setBatteryStateLocked(final int status, final int health, final int plugType,
final int level, /* not final */ int temp, final int voltageMv, final int chargeUah,
final int chargeFullUah, final long chargeTimeToFullSeconds) {
setBatteryStateLocked(status, health, plugType, level, temp, voltageMv, chargeUah,
chargeFullUah, chargeTimeToFullSeconds,
mClock.elapsedRealtime(), mClock.uptimeMillis(), mClock.currentTimeMillis());
}
@GuardedBy("this")
public void setBatteryStateLocked(final int status, final int health, final int plugType,
final int level, /* not final */ int temp, final int voltageMv, final int chargeUah,
final int chargeFullUah, final long chargeTimeToFullSeconds,
final long elapsedRealtimeMs, final long uptimeMs, final long currentTimeMs) {
// Temperature is encoded without the signed bit, so clamp any negative temperatures to 0.
temp = Math.max(0, temp);
reportChangesToStatsLog(mHaveBatteryLevel ? mHistoryCur : null,
status, plugType, level);
final boolean onBattery = isOnBattery(plugType, status);
if (!mHaveBatteryLevel) {
mHaveBatteryLevel = true;
// We start out assuming that the device is plugged in (not
// on battery). If our first report is now that we are indeed
// plugged in, then twiddle our state to correctly reflect that
// since we won't be going through the full setOnBattery().
if (onBattery == mOnBattery) {
if (onBattery) {
mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
} else {
mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
}
}
// Always start out assuming charging, that will be updated later.
mHistoryCur.states2 |= HistoryItem.STATE2_CHARGING_FLAG;
mHistoryCur.batteryStatus = (byte)status;
mHistoryCur.batteryLevel = (byte)level;
mHistoryCur.batteryChargeUah = chargeUah;
mMaxChargeStepLevel = mMinDischargeStepLevel =
mLastChargeStepLevel = mLastDischargeStepLevel = level;
mLastChargingStateLevel = level;
} else if (mCurrentBatteryLevel != level || mOnBattery != onBattery) {
recordDailyStatsIfNeededLocked(level >= 100 && onBattery, currentTimeMs);
}
int oldStatus = mHistoryCur.batteryStatus;
if (onBattery) {
mDischargeCurrentLevel = level;
if (!mRecordingHistory) {
mRecordingHistory = true;
startRecordingHistory(elapsedRealtimeMs, uptimeMs, true);
}
} else if (level < 96 &&
status != BatteryManager.BATTERY_STATUS_UNKNOWN) {
if (!mRecordingHistory) {
mRecordingHistory = true;
startRecordingHistory(elapsedRealtimeMs, uptimeMs, true);
}
}
mBatteryVoltageMv = voltageMv;
mCurrentBatteryLevel = level;
if (mDischargePlugLevel < 0) {
mDischargePlugLevel = level;
}
if (onBattery != mOnBattery) {
mHistoryCur.batteryLevel = (byte)level;
mHistoryCur.batteryStatus = (byte)status;
mHistoryCur.batteryHealth = (byte)health;
mHistoryCur.batteryPlugType = (byte)plugType;
mHistoryCur.batteryTemperature = (short)temp;
mHistoryCur.batteryVoltage = (char) voltageMv;
if (chargeUah < mHistoryCur.batteryChargeUah) {
// Only record discharges
final long chargeDiff = mHistoryCur.batteryChargeUah - chargeUah;
mDischargeCounter.addCountLocked(chargeDiff);
mDischargeScreenOffCounter.addCountLocked(chargeDiff);
if (Display.isDozeState(mScreenState)) {
mDischargeScreenDozeCounter.addCountLocked(chargeDiff);
}
if (mDeviceIdleMode == DEVICE_IDLE_MODE_LIGHT) {
mDischargeLightDozeCounter.addCountLocked(chargeDiff);
} else if (mDeviceIdleMode == DEVICE_IDLE_MODE_DEEP) {
mDischargeDeepDozeCounter.addCountLocked(chargeDiff);
}
}
mHistoryCur.batteryChargeUah = chargeUah;
setOnBatteryLocked(elapsedRealtimeMs, uptimeMs, onBattery, oldStatus, level, chargeUah);
} else {
boolean changed = false;
if (mHistoryCur.batteryLevel != level) {
mHistoryCur.batteryLevel = (byte)level;
changed = true;
// TODO(adamlesinski): Schedule the creation of a HistoryStepDetails record
// which will pull external stats.
mExternalSync.scheduleSyncDueToBatteryLevelChange(
mConstants.BATTERY_LEVEL_COLLECTION_DELAY_MS);
}
if (mHistoryCur.batteryStatus != status) {
mHistoryCur.batteryStatus = (byte)status;
changed = true;
}
if (mHistoryCur.batteryHealth != health) {
mHistoryCur.batteryHealth = (byte)health;
changed = true;
}
if (mHistoryCur.batteryPlugType != plugType) {
mHistoryCur.batteryPlugType = (byte)plugType;
changed = true;
}
if (temp >= (mHistoryCur.batteryTemperature+10)
|| temp <= (mHistoryCur.batteryTemperature-10)) {
mHistoryCur.batteryTemperature = (short)temp;
changed = true;
}
if (voltageMv > (mHistoryCur.batteryVoltage + 20)
|| voltageMv < (mHistoryCur.batteryVoltage - 20)) {
mHistoryCur.batteryVoltage = (char) voltageMv;
changed = true;
}
if (chargeUah >= (mHistoryCur.batteryChargeUah + 10)
|| chargeUah <= (mHistoryCur.batteryChargeUah - 10)) {
if (chargeUah < mHistoryCur.batteryChargeUah) {
// Only record discharges
final long chargeDiff = mHistoryCur.batteryChargeUah - chargeUah;
mDischargeCounter.addCountLocked(chargeDiff);
mDischargeScreenOffCounter.addCountLocked(chargeDiff);
if (Display.isDozeState(mScreenState)) {
mDischargeScreenDozeCounter.addCountLocked(chargeDiff);
}
if (mDeviceIdleMode == DEVICE_IDLE_MODE_LIGHT) {
mDischargeLightDozeCounter.addCountLocked(chargeDiff);
} else if (mDeviceIdleMode == DEVICE_IDLE_MODE_DEEP) {
mDischargeDeepDozeCounter.addCountLocked(chargeDiff);
}
}
mHistoryCur.batteryChargeUah = chargeUah;
changed = true;
}
long modeBits = (((long)mInitStepMode) << STEP_LEVEL_INITIAL_MODE_SHIFT)
| (((long)mModStepMode) << STEP_LEVEL_MODIFIED_MODE_SHIFT)
| (((long)(level&0xff)) << STEP_LEVEL_LEVEL_SHIFT);
if (onBattery) {
changed |= setChargingLocked(false);
if (mLastDischargeStepLevel != level && mMinDischargeStepLevel > level) {
mDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level,
modeBits, elapsedRealtimeMs);
mDailyDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level,
modeBits, elapsedRealtimeMs);
mLastDischargeStepLevel = level;
mMinDischargeStepLevel = level;
mInitStepMode = mCurStepMode;
mModStepMode = 0;
}
} else {
if (level >= 90) {
// If the battery level is at least 90%, always consider the device to be
// charging even if it happens to go down a level.
changed |= setChargingLocked(true);
} else if (!mCharging) {
if (mLastChargeStepLevel < level) {
// We have not reported that we are charging, but the level has gone up,
// but we would like to not have tons of activity from charging-constraint
// jobs, so instead of reporting ACTION_CHARGING immediately, we defer it.
if (!mHandler.hasCallbacks(mDeferSetCharging)) {
mHandler.postDelayed(
mDeferSetCharging,
mConstants.BATTERY_CHARGED_DELAY_MS);
}
} else if (mLastChargeStepLevel > level) {
// if we had deferred a runnable due to charge level increasing, but then
// later the charge level drops (could be due to thermal issues), we don't
// want to trigger the deferred runnable, so remove it here
mHandler.removeCallbacks(mDeferSetCharging);
}
} else {
if (mLastChargeStepLevel > level) {
// We had reported that the device was charging, but here we are with
// power connected and the level going down. Looks like the current
// power supplied isn't enough, so consider the device to now be
// discharging.
changed |= setChargingLocked(false);
}
}
if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) {
mChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
modeBits, elapsedRealtimeMs);
mDailyChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
modeBits, elapsedRealtimeMs);
mMaxChargeStepLevel = level;
mInitStepMode = mCurStepMode;
mModStepMode = 0;
}
mLastChargeStepLevel = level;
}
if (changed) {
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
}
if (!onBattery &&
(status == BatteryManager.BATTERY_STATUS_FULL ||
status == BatteryManager.BATTERY_STATUS_UNKNOWN)) {
// We don't record history while we are plugged in and fully charged
// (or when battery is not present). The next time we are
// unplugged, history will be cleared.
mRecordingHistory = DEBUG;
}
mLastLearnedBatteryCapacityUah = chargeFullUah;
if (mMinLearnedBatteryCapacityUah == -1) {
mMinLearnedBatteryCapacityUah = chargeFullUah;
} else {
mMinLearnedBatteryCapacityUah = Math.min(mMinLearnedBatteryCapacityUah, chargeFullUah);
}
mMaxLearnedBatteryCapacityUah = Math.max(mMaxLearnedBatteryCapacityUah, chargeFullUah);
mBatteryTimeToFullSeconds = chargeTimeToFullSeconds;
}
public static boolean isOnBattery(int plugType, int status) {
return plugType == BATTERY_PLUGGED_NONE && status != BatteryManager.BATTERY_STATUS_UNKNOWN;
}
// Inform StatsLog of setBatteryState changes.
// If this is the first reporting, pass in recentPast == null.
private void reportChangesToStatsLog(HistoryItem recentPast,
final int status, final int plugType, final int level) {
if (recentPast == null || recentPast.batteryStatus != status) {
FrameworkStatsLog.write(FrameworkStatsLog.CHARGING_STATE_CHANGED, status);
}
if (recentPast == null || recentPast.batteryPlugType != plugType) {
FrameworkStatsLog.write(FrameworkStatsLog.PLUGGED_STATE_CHANGED, plugType);
}
if (recentPast == null || recentPast.batteryLevel != level) {
FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_LEVEL_CHANGED, level);
}
}
@UnsupportedAppUsage
public long getAwakeTimeBattery() {
// This previously evaluated to mOnBatteryTimeBase.getUptime(getBatteryUptimeLocked());
// for over a decade, but surely that was a mistake.
return getBatteryUptimeLocked(mClock.uptimeMillis());
}
@UnsupportedAppUsage
public long getAwakeTimePlugged() {
return (mClock.uptimeMillis() * 1000) - getAwakeTimeBattery();
}
@Override
public long computeUptime(long curTimeUs, int which) {
return mUptimeUs + (curTimeUs - mUptimeStartUs);
}
@Override
public long computeRealtime(long curTimeUs, int which) {
return mRealtimeUs + (curTimeUs - mRealtimeStartUs);
}
@Override
@UnsupportedAppUsage
public long computeBatteryUptime(long curTimeUs, int which) {
return mOnBatteryTimeBase.computeUptime(curTimeUs, which);
}
@Override
@UnsupportedAppUsage
public long computeBatteryRealtime(long curTimeUs, int which) {
return mOnBatteryTimeBase.computeRealtime(curTimeUs, which);
}
@Override
public long computeBatteryScreenOffUptime(long curTimeUs, int which) {
return mOnBatteryScreenOffTimeBase.computeUptime(curTimeUs, which);
}
@Override
public long computeBatteryScreenOffRealtime(long curTimeUs, int which) {
return mOnBatteryScreenOffTimeBase.computeRealtime(curTimeUs, which);
}
private long computeTimePerLevel(long[] steps, int numSteps) {
// For now we'll do a simple average across all steps.
if (numSteps <= 0) {
return -1;
}
long total = 0;
for (int i=0; i<numSteps; i++) {
total += steps[i] & STEP_LEVEL_TIME_MASK;
}
return total / numSteps;
/*
long[] buckets = new long[numSteps];
int numBuckets = 0;
int numToAverage = 4;
int i = 0;
while (i < numSteps) {
long totalTime = 0;
int num = 0;
for (int j=0; j<numToAverage && (i+j)<numSteps; j++) {
totalTime += steps[i+j] & STEP_LEVEL_TIME_MASK;
num++;
}
buckets[numBuckets] = totalTime / num;
numBuckets++;
numToAverage *= 2;
i += num;
}
if (numBuckets < 1) {
return -1;
}
long averageTime = buckets[numBuckets-1];
for (i=numBuckets-2; i>=0; i--) {
averageTime = (averageTime + buckets[i]) / 2;
}
return averageTime;
*/
}
@Override
@UnsupportedAppUsage
public long computeBatteryTimeRemaining(long curTime) {
if (!mOnBattery) {
return -1;
}
/* Simple implementation just looks at the average discharge per level across the
entire sample period.
int discharge = (getLowDischargeAmountSinceCharge()+getHighDischargeAmountSinceCharge())/2;
if (discharge < 2) {
return -1;
}
long duration = computeBatteryRealtime(curTime, STATS_SINCE_CHARGED);
if (duration < 1000*1000) {
return -1;
}
long usPerLevel = duration/discharge;
return usPerLevel * mCurrentBatteryLevel;
*/
if (mDischargeStepTracker.mNumStepDurations < 1) {
return -1;
}
long msPerLevel = mDischargeStepTracker.computeTimePerLevel();
if (msPerLevel <= 0) {
return -1;
}
return (msPerLevel * mCurrentBatteryLevel) * 1000;
}
@Override
public LevelStepTracker getDischargeLevelStepTracker() {
return mDischargeStepTracker;
}
@Override
public LevelStepTracker getDailyDischargeLevelStepTracker() {
return mDailyDischargeStepTracker;
}
@Override
public long computeChargeTimeRemaining(long curTime) {
if (mOnBattery) {
// Not yet working.
return -1;
}
if (mBatteryTimeToFullSeconds >= 0) {
return mBatteryTimeToFullSeconds * (1000 * 1000); // s to us
}
// Else use algorithmic approach
if (mChargeStepTracker.mNumStepDurations < 1) {
return -1;
}
long msPerLevel = mChargeStepTracker.computeTimePerLevel();
if (msPerLevel <= 0) {
return -1;
}
return (msPerLevel * (100 - mCurrentBatteryLevel)) * 1000;
}
/*@hide */
public CellularBatteryStats getCellularBatteryStats() {
final int which = STATS_SINCE_CHARGED;
final long rawRealTimeUs = SystemClock.elapsedRealtime() * 1000;
final ControllerActivityCounter counter = getModemControllerActivity();
final long sleepTimeMs = counter.getSleepTimeCounter().getCountLocked(which);
final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which);
final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which);
final long monitoredRailChargeConsumedMaMs =
counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which);
long[] timeInRatMs = new long[BatteryStats.NUM_DATA_CONNECTION_TYPES];
for (int i = 0; i < timeInRatMs.length; i++) {
timeInRatMs[i] = getPhoneDataConnectionTime(i, rawRealTimeUs, which) / 1000;
}
long[] timeInRxSignalStrengthLevelMs =
new long[CellSignalStrength.getNumSignalStrengthLevels()];
for (int i = 0; i < timeInRxSignalStrengthLevelMs.length; i++) {
timeInRxSignalStrengthLevelMs[i] =
getPhoneSignalStrengthTime(i, rawRealTimeUs, which) / 1000;
}
long[] txTimeMs = new long[Math.min(ModemActivityInfo.getNumTxPowerLevels(),
counter.getTxTimeCounters().length)];
long totalTxTimeMs = 0;
for (int i = 0; i < txTimeMs.length; i++) {
txTimeMs[i] = counter.getTxTimeCounters()[i].getCountLocked(which);
totalTxTimeMs += txTimeMs[i];
}
return new CellularBatteryStats(computeBatteryRealtime(rawRealTimeUs, which) / 1000,
getMobileRadioActiveTime(rawRealTimeUs, which) / 1000,
getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which),
getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which),
getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which),
getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which),
sleepTimeMs, idleTimeMs, rxTimeMs, energyConsumedMaMs, timeInRatMs,
timeInRxSignalStrengthLevelMs, txTimeMs,
monitoredRailChargeConsumedMaMs);
}
/*@hide */
public WifiBatteryStats getWifiBatteryStats() {
final int which = STATS_SINCE_CHARGED;
final long rawRealTimeUs = SystemClock.elapsedRealtime() * 1000;
final ControllerActivityCounter counter = getWifiControllerActivity();
final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which);
final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which);
final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(which);
final long totalControllerActivityTimeMs
= computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000;
final long sleepTimeMs
= totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + txTimeMs);
final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which);
final long monitoredRailChargeConsumedMaMs =
counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which);
long numAppScanRequest = 0;
for (int i = 0; i < mUidStats.size(); i++) {
numAppScanRequest += mUidStats.valueAt(i).mWifiScanTimer.getCountLocked(which);
}
long[] timeInStateMs = new long[NUM_WIFI_STATES];
for (int i=0; i<NUM_WIFI_STATES; i++) {
timeInStateMs[i] = getWifiStateTime(i, rawRealTimeUs, which) / 1000;
}
long[] timeInSupplStateMs = new long[NUM_WIFI_SUPPL_STATES];
for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
timeInSupplStateMs[i] = getWifiSupplStateTime(i, rawRealTimeUs, which) / 1000;
}
long[] timeSignalStrengthTimeMs = new long[NUM_WIFI_SIGNAL_STRENGTH_BINS];
for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
timeSignalStrengthTimeMs[i] = getWifiSignalStrengthTime(i, rawRealTimeUs, which) / 1000;
}
return new WifiBatteryStats(
computeBatteryRealtime(rawRealTimeUs, which) / 1000,
getWifiActiveTime(rawRealTimeUs, which) / 1000,
getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which),
getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which),
getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which),
getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which),
sleepTimeMs, scanTimeMs, idleTimeMs, rxTimeMs, txTimeMs, energyConsumedMaMs,
numAppScanRequest, timeInStateMs, timeSignalStrengthTimeMs, timeInSupplStateMs,
monitoredRailChargeConsumedMaMs);
}
/*@hide */
public GpsBatteryStats getGpsBatteryStats() {
GpsBatteryStats s = new GpsBatteryStats();
final int which = STATS_SINCE_CHARGED;
final long rawRealTimeUs = SystemClock.elapsedRealtime() * 1000;
s.setLoggingDurationMs(computeBatteryRealtime(rawRealTimeUs, which) / 1000);
s.setEnergyConsumedMaMs(getGpsBatteryDrainMaMs());
long[] time = new long[mGpsSignalQualityTimer.length];
for (int i=0; i<time.length; i++) {
time[i] = getGpsSignalQualityTime(i, rawRealTimeUs, which) / 1000;
}
s.setTimeInGpsSignalQualityLevel(time);
return s;
}
@Override
public LevelStepTracker getChargeLevelStepTracker() {
return mChargeStepTracker;
}
@Override
public LevelStepTracker getDailyChargeLevelStepTracker() {
return mDailyChargeStepTracker;
}
@Override
public ArrayList<PackageChange> getDailyPackageChanges() {
return mDailyPackageChanges;
}
/**
* @return battery uptime in microseconds
*/
protected long getBatteryUptimeLocked() {
return getBatteryUptimeLocked(mClock.uptimeMillis());
}
/**
* @return battery uptime in microseconds
*/
protected long getBatteryUptimeLocked(long uptimeMs) {
return mOnBatteryTimeBase.getUptime(uptimeMs * 1000);
}
@Override
public long getBatteryUptime(long curTimeUs) {
return mOnBatteryTimeBase.getUptime(curTimeUs);
}
@Override
@UnsupportedAppUsage
public long getBatteryRealtime(long curTimeUs) {
return mOnBatteryTimeBase.getRealtime(curTimeUs);
}
@Override
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int getDischargeStartLevel() {
synchronized(this) {
return getDischargeStartLevelLocked();
}
}
public int getDischargeStartLevelLocked() {
return mDischargeUnplugLevel;
}
@Override
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int getDischargeCurrentLevel() {
synchronized(this) {
return getDischargeCurrentLevelLocked();
}
}
public int getDischargeCurrentLevelLocked() {
return mDischargeCurrentLevel;
}
@Override
public int getLowDischargeAmountSinceCharge() {
synchronized(this) {
int val = mLowDischargeAmountSinceCharge;
if (mOnBattery && mDischargeCurrentLevel < mDischargeUnplugLevel) {
val += mDischargeUnplugLevel-mDischargeCurrentLevel-1;
}
return val;
}
}
@Override
public int getHighDischargeAmountSinceCharge() {
synchronized(this) {
int val = mHighDischargeAmountSinceCharge;
if (mOnBattery && mDischargeCurrentLevel < mDischargeUnplugLevel) {
val += mDischargeUnplugLevel-mDischargeCurrentLevel;
}
return val;
}
}
@Override
@UnsupportedAppUsage
public int getDischargeAmount(int which) {
int dischargeAmount = which == STATS_SINCE_CHARGED
? getHighDischargeAmountSinceCharge()
: (getDischargeStartLevel() - getDischargeCurrentLevel());
if (dischargeAmount < 0) {
dischargeAmount = 0;
}
return dischargeAmount;
}
@Override
@UnsupportedAppUsage
public int getDischargeAmountScreenOn() {
synchronized(this) {
int val = mDischargeAmountScreenOn;
if (mOnBattery && Display.isOnState(mScreenState)
&& mDischargeCurrentLevel < mDischargeScreenOnUnplugLevel) {
val += mDischargeScreenOnUnplugLevel-mDischargeCurrentLevel;
}
return val;
}
}
@Override
public int getDischargeAmountScreenOnSinceCharge() {
synchronized(this) {
int val = mDischargeAmountScreenOnSinceCharge;
if (mOnBattery && Display.isOnState(mScreenState)
&& mDischargeCurrentLevel < mDischargeScreenOnUnplugLevel) {
val += mDischargeScreenOnUnplugLevel-mDischargeCurrentLevel;
}
return val;
}
}
@Override
@UnsupportedAppUsage
public int getDischargeAmountScreenOff() {
synchronized(this) {
int val = mDischargeAmountScreenOff;
if (mOnBattery && Display.isOffState(mScreenState)
&& mDischargeCurrentLevel < mDischargeScreenOffUnplugLevel) {
val += mDischargeScreenOffUnplugLevel-mDischargeCurrentLevel;
}
// For backward compatibility, doze discharge is counted into screen off.
return val + getDischargeAmountScreenDoze();
}
}
@Override
public int getDischargeAmountScreenOffSinceCharge() {
synchronized(this) {
int val = mDischargeAmountScreenOffSinceCharge;
if (mOnBattery && Display.isOffState(mScreenState)
&& mDischargeCurrentLevel < mDischargeScreenOffUnplugLevel) {
val += mDischargeScreenOffUnplugLevel-mDischargeCurrentLevel;
}
// For backward compatibility, doze discharge is counted into screen off.
return val + getDischargeAmountScreenDozeSinceCharge();
}
}
@Override
public int getDischargeAmountScreenDoze() {
synchronized(this) {
int val = mDischargeAmountScreenDoze;
if (mOnBattery && Display.isDozeState(mScreenState)
&& mDischargeCurrentLevel < mDischargeScreenDozeUnplugLevel) {
val += mDischargeScreenDozeUnplugLevel-mDischargeCurrentLevel;
}
return val;
}
}
@Override
public int getDischargeAmountScreenDozeSinceCharge() {
synchronized(this) {
int val = mDischargeAmountScreenDozeSinceCharge;
if (mOnBattery && Display.isDozeState(mScreenState)
&& mDischargeCurrentLevel < mDischargeScreenDozeUnplugLevel) {
val += mDischargeScreenDozeUnplugLevel-mDischargeCurrentLevel;
}
return val;
}
}
/**
* Estimates the time spent by the system server handling incoming binder requests.
*/
@Override
public long[] getSystemServiceTimeAtCpuSpeeds() {
if (mBinderThreadCpuTimesUs == null) {
return null;
}
return mBinderThreadCpuTimesUs.getCountsLocked(BatteryStats.STATS_SINCE_CHARGED);
}
/**
* Retrieve the statistics object for a particular uid, creating if needed.
*/
@UnsupportedAppUsage
public Uid getUidStatsLocked(int uid) {
return getUidStatsLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public Uid getUidStatsLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
Uid u = mUidStats.get(uid);
if (u == null) {
if (Process.isSdkSandboxUid(uid)) {
Log.wtf(TAG, "Tracking an SDK Sandbox UID");
}
u = new Uid(this, uid, elapsedRealtimeMs, uptimeMs);
mUidStats.put(uid, u);
}
return u;
}
/**
* Retrieve the statistics object for a particular uid. Returns null if the object is not
* available.
*/
public Uid getAvailableUidStatsLocked(int uid) {
Uid u = mUidStats.get(uid);
return u;
}
@GuardedBy("this")
public void onCleanupUserLocked(int userId, long elapsedRealtimeMs) {
final int firstUidForUser = UserHandle.getUid(userId, 0);
final int lastUidForUser = UserHandle.getUid(userId, UserHandle.PER_USER_RANGE - 1);
mPendingRemovedUids.add(
new UidToRemove(firstUidForUser, lastUidForUser, elapsedRealtimeMs));
}
@GuardedBy("this")
public void onUserRemovedLocked(int userId) {
if (mExternalSync != null) {
// Clear out the removed user's UIDs after a short delay. The delay is needed
// because at the point that this method is called, some activities are still
// being wrapped up by those UIDs
mExternalSync.scheduleCleanupDueToRemovedUser(userId);
}
}
/**
* Removes battery stats for UIDs corresponding to a removed user.
*/
@GuardedBy("this")
public void clearRemovedUserUidsLocked(int userId) {
final int firstUidForUser = UserHandle.getUid(userId, 0);
final int lastUidForUser = UserHandle.getUid(userId, UserHandle.PER_USER_RANGE - 1);
mUidStats.put(firstUidForUser, null);
mUidStats.put(lastUidForUser, null);
final int firstIndex = mUidStats.indexOfKey(firstUidForUser);
final int lastIndex = mUidStats.indexOfKey(lastUidForUser);
for (int i = firstIndex; i <= lastIndex; i++) {
final Uid uid = mUidStats.valueAt(i);
if (uid != null) {
uid.detachFromTimeBase();
}
}
mUidStats.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
removeCpuStatsForUidRangeLocked(firstUidForUser, lastUidForUser);
}
/**
* Remove the statistics object for a particular uid.
*/
@UnsupportedAppUsage
@GuardedBy("this")
public void removeUidStatsLocked(int uid) {
removeUidStatsLocked(uid, mClock.elapsedRealtime());
}
/**
* @see #removeUidStatsLocked(int)
*/
@GuardedBy("this")
public void removeUidStatsLocked(int uid, long elapsedRealtimeMs) {
final Uid u = mUidStats.get(uid);
if (u != null) {
u.detachFromTimeBase();
}
mUidStats.remove(uid);
mPendingRemovedUids.add(new UidToRemove(uid, elapsedRealtimeMs));
}
/**
* Removes the data for the deleted UIDs from the underlying kernel eBPF tables.
*/
@GuardedBy("this")
private void removeCpuStatsForUidRangeLocked(int startUid, int endUid) {
if (startUid == endUid) {
mCpuUidUserSysTimeReader.removeUid(startUid);
mCpuUidFreqTimeReader.removeUid(startUid);
if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
mCpuUidActiveTimeReader.removeUid(startUid);
mCpuUidClusterTimeReader.removeUid(startUid);
}
if (mKernelSingleUidTimeReader != null) {
mKernelSingleUidTimeReader.removeUid(startUid);
}
mNumUidsRemoved++;
} else if (startUid < endUid) {
mCpuUidFreqTimeReader.removeUidsInRange(startUid, endUid);
mCpuUidUserSysTimeReader.removeUidsInRange(startUid, endUid);
if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
mCpuUidActiveTimeReader.removeUidsInRange(startUid, endUid);
mCpuUidClusterTimeReader.removeUidsInRange(startUid, endUid);
}
if (mKernelSingleUidTimeReader != null) {
mKernelSingleUidTimeReader.removeUidsInRange(startUid, endUid);
}
// Treat as one. We don't know how many uids there are in between.
mNumUidsRemoved++;
} else {
Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid);
}
}
/**
* Retrieve the statistics object for a particular process, creating
* if needed.
*/
@UnsupportedAppUsage
public Uid.Proc getProcessStatsLocked(int uid, String name) {
return getProcessStatsLocked(uid, name, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
/**
* @see #getProcessStatsLocked(int, String)
*/
public Uid.Proc getProcessStatsLocked(int uid, String name,
long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs);
return u.getProcessStatsLocked(name);
}
/**
* Retrieve the statistics object for a particular process, creating
* if needed.
*/
@UnsupportedAppUsage
public Uid.Pkg getPackageStatsLocked(int uid, String pkg) {
return getPackageStatsLocked(uid, pkg, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
/**
* @see getPackageStatsLocked(int, String)
*/
public Uid.Pkg getPackageStatsLocked(int uid, String pkg,
long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs);
return u.getPackageStatsLocked(pkg);
}
/**
* Retrieve the statistics object for a particular service, creating
* if needed.
*/
@UnsupportedAppUsage
public Uid.Pkg.Serv getServiceStatsLocked(int uid, String pkg, String name) {
return getServiceStatsLocked(uid, pkg, name,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public Uid.Pkg.Serv getServiceStatsLocked(int uid, String pkg, String name,
long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs);
return u.getServiceStatsLocked(pkg, name);
}
@GuardedBy("this")
public void shutdownLocked() {
recordShutdownLocked(mClock.currentTimeMillis(), mClock.elapsedRealtime());
writeSyncLocked();
mShuttingDown = true;
}
@GuardedBy("this")
@Override
public boolean isProcessStateDataAvailable() {
return trackPerProcStateCpuTimes();
}
@GuardedBy("this")
public boolean trackPerProcStateCpuTimes() {
return mCpuUidFreqTimeReader.isFastCpuTimesReader();
}
@GuardedBy("this")
public void systemServicesReady(Context context) {
mConstants.startObserving(context.getContentResolver());
registerUsbStateReceiver(context);
}
/**
* Initialize the measured charge stats data structures.
*
* @param supportedStandardBuckets boolean array indicating which {@link StandardPowerBucket}s
* are currently supported. If null, none are supported
* (regardless of customBucketNames).
* @param customBucketNames names of custom (OTHER) EnergyConsumers on this device
*/
@GuardedBy("this")
public void initMeasuredEnergyStatsLocked(@Nullable boolean[] supportedStandardBuckets,
String[] customBucketNames) {
final int numDisplays = mPerDisplayBatteryStats.length;
for (int i = 0; i < numDisplays; i++) {
final int screenState = mPerDisplayBatteryStats[i].screenState;
mPerDisplayBatteryStats[i].screenStateAtLastEnergyMeasurement = screenState;
}
final boolean compatibleConfig;
if (supportedStandardBuckets != null) {
final MeasuredEnergyStats.Config config = new MeasuredEnergyStats.Config(
supportedStandardBuckets, customBucketNames,
SUPPORTED_PER_PROCESS_STATE_STANDARD_ENERGY_BUCKETS,
getBatteryConsumerProcessStateNames());
if (mMeasuredEnergyStatsConfig == null) {
compatibleConfig = true;
} else {
compatibleConfig = mMeasuredEnergyStatsConfig.isCompatible(config);
}
mMeasuredEnergyStatsConfig = config;
mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(config);
if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH]) {
mBluetoothPowerCalculator = new BluetoothPowerCalculator(mPowerProfile);
}
if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_CPU]) {
mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);
}
if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO]) {
mMobileRadioPowerCalculator = new MobileRadioPowerCalculator(mPowerProfile);
}
if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_WIFI]) {
mWifiPowerCalculator = new WifiPowerCalculator(mPowerProfile);
}
} else {
compatibleConfig = (mMeasuredEnergyStatsConfig == null);
// Measured energy no longer supported, wipe out the existing data.
mMeasuredEnergyStatsConfig = null;
mGlobalMeasuredEnergyStats = null;
}
if (!compatibleConfig) {
// Supported power buckets changed since last boot.
// Existing data is no longer reliable.
resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime(),
RESET_REASON_MEASURED_ENERGY_BUCKETS_CHANGE);
}
}
@NonNull
private static String[] getBatteryConsumerProcessStateNames() {
String[] procStateNames = new String[BatteryConsumer.PROCESS_STATE_COUNT];
for (int procState = 0; procState < BatteryConsumer.PROCESS_STATE_COUNT; procState++) {
procStateNames[procState] = BatteryConsumer.processStateToString(procState);
}
return procStateNames;
}
/** Get the last known Battery voltage (in millivolts), returns -1 if unknown */
@GuardedBy("this")
public int getBatteryVoltageMvLocked() {
return mBatteryVoltageMv;
}
@VisibleForTesting
public final class Constants extends ContentObserver {
public static final String KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME
= "track_cpu_active_cluster_time";
public static final String KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS
= "proc_state_cpu_times_read_delay_ms";
public static final String KEY_KERNEL_UID_READERS_THROTTLE_TIME
= "kernel_uid_readers_throttle_time";
public static final String KEY_UID_REMOVE_DELAY_MS
= "uid_remove_delay_ms";
public static final String KEY_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS
= "external_stats_collection_rate_limit_ms";
public static final String KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS
= "battery_level_collection_delay_ms";
public static final String KEY_PROC_STATE_CHANGE_COLLECTION_DELAY_MS =
"procstate_change_collection_delay_ms";
public static final String KEY_MAX_HISTORY_FILES = "max_history_files";
public static final String KEY_MAX_HISTORY_BUFFER_KB = "max_history_buffer_kb";
public static final String KEY_BATTERY_CHARGED_DELAY_MS =
"battery_charged_delay_ms";
private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true;
private static final long DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME = 1_000;
private static final long DEFAULT_UID_REMOVE_DELAY_MS = 5L * 60L * 1000L;
private static final long DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS = 600_000;
private static final long DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS = 300_000;
private static final long DEFAULT_PROC_STATE_CHANGE_COLLECTION_DELAY_MS = 60_000;
private static final int DEFAULT_MAX_HISTORY_FILES = 32;
private static final int DEFAULT_MAX_HISTORY_BUFFER_KB = 128; /*Kilo Bytes*/
private static final int DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE = 64;
private static final int DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB = 64; /*Kilo Bytes*/
private static final int DEFAULT_BATTERY_CHARGED_DELAY_MS = 900000; /* 15 min */
public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME;
/* Do not set default value for KERNEL_UID_READERS_THROTTLE_TIME. Need to trigger an
* update when startObserving. */
public long KERNEL_UID_READERS_THROTTLE_TIME;
public long UID_REMOVE_DELAY_MS = DEFAULT_UID_REMOVE_DELAY_MS;
public long EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS
= DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS;
public long BATTERY_LEVEL_COLLECTION_DELAY_MS
= DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS;
public long PROC_STATE_CHANGE_COLLECTION_DELAY_MS =
DEFAULT_PROC_STATE_CHANGE_COLLECTION_DELAY_MS;
public int MAX_HISTORY_FILES;
public int MAX_HISTORY_BUFFER; /*Bytes*/
public int BATTERY_CHARGED_DELAY_MS = DEFAULT_BATTERY_CHARGED_DELAY_MS;
private ContentResolver mResolver;
private final KeyValueListParser mParser = new KeyValueListParser(',');
public Constants(Handler handler) {
super(handler);
if (ActivityManager.isLowRamDeviceStatic()) {
MAX_HISTORY_FILES = DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE;
MAX_HISTORY_BUFFER = DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB * 1024;
} else {
MAX_HISTORY_FILES = DEFAULT_MAX_HISTORY_FILES;
MAX_HISTORY_BUFFER = DEFAULT_MAX_HISTORY_BUFFER_KB * 1024;
}
}
public void startObserving(ContentResolver resolver) {
mResolver = resolver;
mResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.BATTERY_STATS_CONSTANTS),
false /* notifyForDescendants */, this);
mResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.BATTERY_CHARGING_STATE_UPDATE_DELAY),
false /* notifyForDescendants */, this);
updateConstants();
}
@Override
public void onChange(boolean selfChange, Uri uri) {
if (uri.equals(
Settings.Global.getUriFor(
Settings.Global.BATTERY_CHARGING_STATE_UPDATE_DELAY))) {
synchronized (BatteryStatsImpl.this) {
updateBatteryChargedDelayMsLocked();
}
return;
}
updateConstants();
}
private void updateConstants() {
synchronized (BatteryStatsImpl.this) {
try {
mParser.setString(Settings.Global.getString(mResolver,
Settings.Global.BATTERY_STATS_CONSTANTS));
} catch (IllegalArgumentException e) {
// Failed to parse the settings string, log this and move on
// with defaults.
Slog.e(TAG, "Bad batterystats settings", e);
}
TRACK_CPU_ACTIVE_CLUSTER_TIME = mParser.getBoolean(
KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME, DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME);
updateKernelUidReadersThrottleTime(KERNEL_UID_READERS_THROTTLE_TIME,
mParser.getLong(KEY_KERNEL_UID_READERS_THROTTLE_TIME,
DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME));
updateUidRemoveDelay(
mParser.getLong(KEY_UID_REMOVE_DELAY_MS, DEFAULT_UID_REMOVE_DELAY_MS));
EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS = mParser.getLong(
KEY_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS,
DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS);
BATTERY_LEVEL_COLLECTION_DELAY_MS = mParser.getLong(
KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS,
DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS);
PROC_STATE_CHANGE_COLLECTION_DELAY_MS = mParser.getLong(
KEY_PROC_STATE_CHANGE_COLLECTION_DELAY_MS,
DEFAULT_PROC_STATE_CHANGE_COLLECTION_DELAY_MS);
MAX_HISTORY_FILES = mParser.getInt(KEY_MAX_HISTORY_FILES,
ActivityManager.isLowRamDeviceStatic() ?
DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE
: DEFAULT_MAX_HISTORY_FILES);
MAX_HISTORY_BUFFER = mParser.getInt(KEY_MAX_HISTORY_BUFFER_KB,
ActivityManager.isLowRamDeviceStatic() ?
DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB
: DEFAULT_MAX_HISTORY_BUFFER_KB)
* 1024;
updateBatteryChargedDelayMsLocked();
}
}
private void updateBatteryChargedDelayMsLocked() {
// a negative value indicates that we should ignore this override
final int delay = Settings.Global.getInt(mResolver,
Settings.Global.BATTERY_CHARGING_STATE_UPDATE_DELAY,
-1);
BATTERY_CHARGED_DELAY_MS = delay >= 0 ? delay : mParser.getInt(
KEY_BATTERY_CHARGED_DELAY_MS,
DEFAULT_BATTERY_CHARGED_DELAY_MS);
if (mHandler.hasCallbacks(mDeferSetCharging)) {
mHandler.removeCallbacks(mDeferSetCharging);
mHandler.postDelayed(mDeferSetCharging, BATTERY_CHARGED_DELAY_MS);
}
}
private void updateKernelUidReadersThrottleTime(long oldTimeMs, long newTimeMs) {
KERNEL_UID_READERS_THROTTLE_TIME = newTimeMs;
if (oldTimeMs != newTimeMs) {
mCpuUidUserSysTimeReader.setThrottle(KERNEL_UID_READERS_THROTTLE_TIME);
mCpuUidFreqTimeReader.setThrottle(KERNEL_UID_READERS_THROTTLE_TIME);
mCpuUidActiveTimeReader.setThrottle(KERNEL_UID_READERS_THROTTLE_TIME);
mCpuUidClusterTimeReader
.setThrottle(KERNEL_UID_READERS_THROTTLE_TIME);
}
}
@GuardedBy("BatteryStatsImpl.this")
private void updateUidRemoveDelay(long newTimeMs) {
UID_REMOVE_DELAY_MS = newTimeMs;
clearPendingRemovedUidsLocked();
}
public void dumpLocked(PrintWriter pw) {
pw.print(KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME); pw.print("=");
pw.println(TRACK_CPU_ACTIVE_CLUSTER_TIME);
pw.print(KEY_KERNEL_UID_READERS_THROTTLE_TIME); pw.print("=");
pw.println(KERNEL_UID_READERS_THROTTLE_TIME);
pw.print(KEY_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS); pw.print("=");
pw.println(EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS);
pw.print(KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS); pw.print("=");
pw.println(BATTERY_LEVEL_COLLECTION_DELAY_MS);
pw.print(KEY_PROC_STATE_CHANGE_COLLECTION_DELAY_MS); pw.print("=");
pw.println(PROC_STATE_CHANGE_COLLECTION_DELAY_MS);
pw.print(KEY_MAX_HISTORY_FILES); pw.print("=");
pw.println(MAX_HISTORY_FILES);
pw.print(KEY_MAX_HISTORY_BUFFER_KB); pw.print("=");
pw.println(MAX_HISTORY_BUFFER/1024);
pw.print(KEY_BATTERY_CHARGED_DELAY_MS); pw.print("=");
pw.println(BATTERY_CHARGED_DELAY_MS);
}
}
public long getExternalStatsCollectionRateLimitMs() {
synchronized (this) {
return mConstants.EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS;
}
}
@GuardedBy("this")
public void dumpConstantsLocked(PrintWriter pw) {
final IndentingPrintWriter iPw = new IndentingPrintWriter(pw, " ");
iPw.println("BatteryStats constants:");
iPw.increaseIndent();
mConstants.dumpLocked(iPw);
iPw.decreaseIndent();
}
@GuardedBy("this")
public void dumpCpuStatsLocked(PrintWriter pw) {
int size = mUidStats.size();
pw.println("Per UID CPU user & system time in ms:");
for (int i = 0; i < size; i++) {
int u = mUidStats.keyAt(i);
Uid uid = mUidStats.get(u);
pw.print(" "); pw.print(u); pw.print(": ");
pw.print(uid.getUserCpuTimeUs(STATS_SINCE_CHARGED) / 1000); pw.print(" ");
pw.println(uid.getSystemCpuTimeUs(STATS_SINCE_CHARGED) / 1000);
}
pw.println("Per UID CPU active time in ms:");
for (int i = 0; i < size; i++) {
int u = mUidStats.keyAt(i);
Uid uid = mUidStats.get(u);
if (uid.getCpuActiveTime() > 0) {
pw.print(" "); pw.print(u); pw.print(": "); pw.println(uid.getCpuActiveTime());
}
}
pw.println("Per UID CPU cluster time in ms:");
for (int i = 0; i < size; i++) {
int u = mUidStats.keyAt(i);
long[] times = mUidStats.get(u).getCpuClusterTimes();
if (times != null) {
pw.print(" "); pw.print(u); pw.print(": "); pw.println(Arrays.toString(times));
}
}
pw.println("Per UID CPU frequency time in ms:");
for (int i = 0; i < size; i++) {
int u = mUidStats.keyAt(i);
long[] times = mUidStats.get(u).getCpuFreqTimes(STATS_SINCE_CHARGED);
if (times != null) {
pw.print(" "); pw.print(u); pw.print(": "); pw.println(Arrays.toString(times));
}
}
updateSystemServiceCallStats();
if (mBinderThreadCpuTimesUs != null) {
pw.println("Per UID System server binder time in ms:");
long[] systemServiceTimeAtCpuSpeeds = getSystemServiceTimeAtCpuSpeeds();
for (int i = 0; i < size; i++) {
int u = mUidStats.keyAt(i);
Uid uid = mUidStats.get(u);
double proportionalSystemServiceUsage = uid.getProportionalSystemServiceUsage();
long timeUs = 0;
for (int j = systemServiceTimeAtCpuSpeeds.length - 1; j >= 0; j--) {
timeUs += systemServiceTimeAtCpuSpeeds[j] * proportionalSystemServiceUsage;
}
pw.print(" ");
pw.print(u);
pw.print(": ");
pw.println(timeUs / 1000);
}
}
}
/**
* Dump measured charge stats
*/
@GuardedBy("this")
public void dumpMeasuredEnergyStatsLocked(PrintWriter pw) {
pw.printf("On battery measured charge stats (microcoulombs) \n");
if (mGlobalMeasuredEnergyStats == null) {
pw.printf(" Not supported on this device.\n");
return;
}
dumpMeasuredEnergyStatsLocked(pw, "global usage", mGlobalMeasuredEnergyStats);
int size = mUidStats.size();
for (int i = 0; i < size; i++) {
final int u = mUidStats.keyAt(i);
final Uid uid = mUidStats.get(u);
final String name = "uid " + uid.mUid;
dumpMeasuredEnergyStatsLocked(pw, name, uid.mUidMeasuredEnergyStats);
}
}
/** Dump measured charge stats for the given uid */
@GuardedBy("this")
private void dumpMeasuredEnergyStatsLocked(PrintWriter pw, String name,
MeasuredEnergyStats stats) {
if (stats == null) return;
final IndentingPrintWriter iPw = new IndentingPrintWriter(pw, " ");
iPw.increaseIndent();
iPw.printf("%s:\n", name);
iPw.increaseIndent();
stats.dump(iPw);
iPw.decreaseIndent();
}
/**
* Dump Power Profile
*/
@GuardedBy("this")
public void dumpPowerProfileLocked(PrintWriter pw) {
final IndentingPrintWriter iPw = new IndentingPrintWriter(pw, " ");
iPw.printf("Power Profile: \n");
iPw.increaseIndent();
mPowerProfile.dump(iPw);
iPw.decreaseIndent();
}
final ReentrantLock mWriteLock = new ReentrantLock();
@GuardedBy("this")
public void writeAsyncLocked() {
writeStatsLocked(false);
writeHistoryLocked(false);
}
@GuardedBy("this")
public void writeSyncLocked() {
writeStatsLocked(true);
writeHistoryLocked(true);
}
@GuardedBy("this")
void writeStatsLocked(boolean sync) {
if (mStatsFile == null) {
Slog.w(TAG,
"writeStatsLocked: no file associated with this instance");
return;
}
if (mShuttingDown) {
return;
}
final Parcel p = Parcel.obtain();
final long start = SystemClock.uptimeMillis();
writeSummaryToParcel(p, false/*history is in separate file*/);
if (DEBUG) {
Slog.d(TAG, "writeSummaryToParcel duration ms:"
+ (SystemClock.uptimeMillis() - start) + " bytes:" + p.dataSize());
}
mLastWriteTimeMs = mClock.elapsedRealtime();
writeParcelToFileLocked(p, mStatsFile, sync);
}
void writeHistoryLocked(boolean sync) {
if (mBatteryStatsHistory.getActiveFile() == null) {
Slog.w(TAG,
"writeHistoryLocked: no history file associated with this instance");
return;
}
if (mShuttingDown) {
return;
}
Parcel p = Parcel.obtain();
final long start = SystemClock.uptimeMillis();
writeHistoryBuffer(p, true);
if (DEBUG) {
Slog.d(TAG, "writeHistoryBuffer duration ms:"
+ (SystemClock.uptimeMillis() - start) + " bytes:" + p.dataSize());
}
writeParcelToFileLocked(p, mBatteryStatsHistory.getActiveFile(), sync);
}
void writeParcelToFileLocked(Parcel p, AtomicFile file, boolean sync) {
if (sync) {
commitPendingDataToDisk(p, file);
} else {
BackgroundThread.getHandler().post(new Runnable() {
@Override public void run() {
commitPendingDataToDisk(p, file);
}
});
}
}
private void commitPendingDataToDisk(Parcel p, AtomicFile file) {
mWriteLock.lock();
FileOutputStream fos = null;
try {
final long startTimeMs = SystemClock.uptimeMillis();
fos = file.startWrite();
fos.write(p.marshall());
fos.flush();
file.finishWrite(fos);
if (DEBUG) {
Slog.d(TAG, "commitPendingDataToDisk file:" + file.getBaseFile().getPath()
+ " duration ms:" + (SystemClock.uptimeMillis() - startTimeMs)
+ " bytes:" + p.dataSize());
}
com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
"batterystats", SystemClock.uptimeMillis() - startTimeMs);
} catch (IOException e) {
Slog.w(TAG, "Error writing battery statistics", e);
file.failWrite(fos);
} finally {
p.recycle();
mWriteLock.unlock();
}
}
@UnsupportedAppUsage
@GuardedBy("this")
public void readLocked() {
if (mDailyFile != null) {
readDailyStatsLocked();
}
if (mStatsFile == null) {
Slog.w(TAG, "readLocked: no file associated with this instance");
return;
}
final AtomicFile activeHistoryFile = mBatteryStatsHistory.getActiveFile();
if (activeHistoryFile == null) {
Slog.w(TAG,
"readLocked: no history file associated with this instance");
return;
}
mUidStats.clear();
Parcel stats = Parcel.obtain();
try {
final long start = SystemClock.uptimeMillis();
if (mStatsFile.exists()) {
byte[] raw = mStatsFile.readFully();
stats.unmarshall(raw, 0, raw.length);
stats.setDataPosition(0);
readSummaryFromParcel(stats);
if (DEBUG) {
Slog.d(TAG, "readLocked stats file:" + mStatsFile.getBaseFile().getPath()
+ " bytes:" + raw.length + " takes ms:" + (SystemClock.uptimeMillis()
- start));
}
}
} catch (Exception e) {
Slog.e(TAG, "Error reading battery statistics", e);
resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime(),
RESET_REASON_CORRUPT_FILE);
} finally {
stats.recycle();
}
Parcel history = Parcel.obtain();
try {
final long start = SystemClock.uptimeMillis();
if (activeHistoryFile.exists()) {
byte[] raw = activeHistoryFile.readFully();
if (raw.length > 0) {
history.unmarshall(raw, 0, raw.length);
history.setDataPosition(0);
readHistoryBuffer(history);
}
if (DEBUG) {
Slog.d(TAG, "readLocked history file::"
+ activeHistoryFile.getBaseFile().getPath()
+ " bytes:" + raw.length + " takes ms:" + (SystemClock.uptimeMillis()
- start));
}
}
} catch (Exception e) {
Slog.e(TAG, "Error reading battery history", e);
clearHistoryLocked();
mBatteryStatsHistory.resetAllFiles();
} finally {
history.recycle();
}
mEndPlatformVersion = Build.ID;
if (mHistoryBuffer.dataPosition() > 0
|| mBatteryStatsHistory.getFilesNumbers().size() > 1) {
mRecordingHistory = true;
final long elapsedRealtimeMs = mClock.elapsedRealtime();
final long uptimeMs = mClock.uptimeMillis();
addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_START, mHistoryCur);
startRecordingHistory(elapsedRealtimeMs, uptimeMs, false);
}
recordDailyStatsIfNeededLocked(false, mClock.currentTimeMillis());
}
public int describeContents() {
return 0;
}
@GuardedBy("this")
void readHistoryBuffer(Parcel in) throws ParcelFormatException {
final int version = in.readInt();
if (version != VERSION) {
Slog.w("BatteryStats", "readHistoryBuffer: version got " + version
+ ", expected " + VERSION + "; erasing old stats");
return;
}
final long historyBaseTime = in.readLong();
mHistoryBuffer.setDataSize(0);
mHistoryBuffer.setDataPosition(0);
int bufSize = in.readInt();
int curPos = in.dataPosition();
if (bufSize >= (mConstants.MAX_HISTORY_BUFFER*100)) {
throw new ParcelFormatException("File corrupt: history data buffer too large " +
bufSize);
} else if ((bufSize&~3) != bufSize) {
throw new ParcelFormatException("File corrupt: history data buffer not aligned " +
bufSize);
} else {
if (DEBUG_HISTORY) Slog.i(TAG, "***************** READING NEW HISTORY: " + bufSize
+ " bytes at " + curPos);
mHistoryBuffer.appendFrom(in, curPos, bufSize);
in.setDataPosition(curPos + bufSize);
}
if (DEBUG_HISTORY) {
StringBuilder sb = new StringBuilder(128);
sb.append("****************** OLD mHistoryBaseTimeMs: ");
TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
Slog.i(TAG, sb.toString());
}
mHistoryBaseTimeMs = historyBaseTime;
if (DEBUG_HISTORY) {
StringBuilder sb = new StringBuilder(128);
sb.append("****************** NEW mHistoryBaseTimeMs: ");
TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
Slog.i(TAG, sb.toString());
}
// We are just arbitrarily going to insert 1 minute from the sample of
// the last run until samples in this run.
if (mHistoryBaseTimeMs > 0) {
long oldnow = mClock.elapsedRealtime();
mHistoryBaseTimeMs = mHistoryBaseTimeMs - oldnow + 1;
if (DEBUG_HISTORY) {
StringBuilder sb = new StringBuilder(128);
sb.append("****************** ADJUSTED mHistoryBaseTimeMs: ");
TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
Slog.i(TAG, sb.toString());
}
}
}
void writeHistoryBuffer(Parcel out, boolean inclData) {
if (DEBUG_HISTORY) {
StringBuilder sb = new StringBuilder(128);
sb.append("****************** WRITING mHistoryBaseTimeMs: ");
TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
sb.append(" mLastHistoryElapsedRealtimeMs: ");
TimeUtils.formatDuration(mLastHistoryElapsedRealtimeMs, sb);
Slog.i(TAG, sb.toString());
}
out.writeInt(VERSION);
out.writeLong(mHistoryBaseTimeMs + mLastHistoryElapsedRealtimeMs);
if (!inclData) {
out.writeInt(0);
out.writeInt(0);
return;
}
out.writeInt(mHistoryBuffer.dataSize());
if (DEBUG_HISTORY) Slog.i(TAG, "***************** WRITING HISTORY: "
+ mHistoryBuffer.dataSize() + " bytes at " + out.dataPosition());
out.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize());
}
@GuardedBy("this")
public void readSummaryFromParcel(Parcel in) throws ParcelFormatException {
final int version = in.readInt();
if (version != VERSION) {
Slog.w("BatteryStats", "readFromParcel: version got " + version
+ ", expected " + VERSION + "; erasing old stats");
return;
}
boolean inclHistory = in.readBoolean();
if (inclHistory) {
readHistoryBuffer(in);
mBatteryStatsHistory.readFromParcel(in);
}
mHistoryTagPool.clear();
mNextHistoryTagIdx = 0;
mNumHistoryTagChars = 0;
int numTags = in.readInt();
for (int i=0; i<numTags; i++) {
int idx = in.readInt();
String str = in.readString();
int uid = in.readInt();
HistoryTag tag = new HistoryTag();
tag.string = str;
tag.uid = uid;
tag.poolIdx = idx;
mHistoryTagPool.put(tag, idx);
if (idx >= mNextHistoryTagIdx) {
mNextHistoryTagIdx = idx+1;
}
mNumHistoryTagChars += tag.string.length() + 1;
}
mStartCount = in.readInt();
mUptimeUs = in.readLong();
mRealtimeUs = in.readLong();
mStartClockTimeMs = in.readLong();
mStartPlatformVersion = in.readString();
mEndPlatformVersion = in.readString();
mOnBatteryTimeBase.readSummaryFromParcel(in);
mOnBatteryScreenOffTimeBase.readSummaryFromParcel(in);
mDischargeUnplugLevel = in.readInt();
mDischargePlugLevel = in.readInt();
mDischargeCurrentLevel = in.readInt();
mCurrentBatteryLevel = in.readInt();
mEstimatedBatteryCapacityMah = in.readInt();
mLastLearnedBatteryCapacityUah = in.readInt();
mMinLearnedBatteryCapacityUah = in.readInt();
mMaxLearnedBatteryCapacityUah = in.readInt();
mLowDischargeAmountSinceCharge = in.readInt();
mHighDischargeAmountSinceCharge = in.readInt();
mDischargeAmountScreenOnSinceCharge = in.readInt();
mDischargeAmountScreenOffSinceCharge = in.readInt();
mDischargeAmountScreenDozeSinceCharge = in.readInt();
mDischargeStepTracker.readFromParcel(in);
mChargeStepTracker.readFromParcel(in);
mDailyDischargeStepTracker.readFromParcel(in);
mDailyChargeStepTracker.readFromParcel(in);
mDischargeCounter.readSummaryFromParcelLocked(in);
mDischargeScreenOffCounter.readSummaryFromParcelLocked(in);
mDischargeScreenDozeCounter.readSummaryFromParcelLocked(in);
mDischargeLightDozeCounter.readSummaryFromParcelLocked(in);
mDischargeDeepDozeCounter.readSummaryFromParcelLocked(in);
int NPKG = in.readInt();
if (NPKG > 0) {
mDailyPackageChanges = new ArrayList<>(NPKG);
while (NPKG > 0) {
NPKG--;
PackageChange pc = new PackageChange();
pc.mPackageName = in.readString();
pc.mUpdate = in.readInt() != 0;
pc.mVersionCode = in.readLong();
mDailyPackageChanges.add(pc);
}
} else {
mDailyPackageChanges = null;
}
mDailyStartTimeMs = in.readLong();
mNextMinDailyDeadlineMs = in.readLong();
mNextMaxDailyDeadlineMs = in.readLong();
mBatteryTimeToFullSeconds = in.readLong();
final MeasuredEnergyStats.Config config = MeasuredEnergyStats.Config.createFromParcel(in);
final MeasuredEnergyStats measuredEnergyStats =
MeasuredEnergyStats.createAndReadSummaryFromParcel(mMeasuredEnergyStatsConfig, in);
if (config != null && Arrays.equals(config.getStateNames(),
getBatteryConsumerProcessStateNames())) {
/**
* WARNING: Supported buckets may have changed across boots. Bucket mismatch is handled
* later when {@link #initMeasuredEnergyStatsLocked} is called.
*/
mMeasuredEnergyStatsConfig = config;
mGlobalMeasuredEnergyStats = measuredEnergyStats;
}
mStartCount++;
mScreenState = Display.STATE_UNKNOWN;
mScreenOnTimer.readSummaryFromParcelLocked(in);
mScreenDozeTimer.readSummaryFromParcelLocked(in);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].readSummaryFromParcelLocked(in);
}
mInteractive = false;
mInteractiveTimer.readSummaryFromParcelLocked(in);
mPhoneOn = false;
mPowerSaveModeEnabledTimer.readSummaryFromParcelLocked(in);
mLongestLightIdleTimeMs = in.readLong();
mLongestFullIdleTimeMs = in.readLong();
mDeviceIdleModeLightTimer.readSummaryFromParcelLocked(in);
mDeviceIdleModeFullTimer.readSummaryFromParcelLocked(in);
mDeviceLightIdlingTimer.readSummaryFromParcelLocked(in);
mDeviceIdlingTimer.readSummaryFromParcelLocked(in);
mPhoneOnTimer.readSummaryFromParcelLocked(in);
for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
mPhoneSignalStrengthsTimer[i].readSummaryFromParcelLocked(in);
}
mPhoneSignalScanningTimer.readSummaryFromParcelLocked(in);
for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
mPhoneDataConnectionsTimer[i].readSummaryFromParcelLocked(in);
}
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
mNetworkByteActivityCounters[i].readSummaryFromParcelLocked(in);
mNetworkPacketActivityCounters[i].readSummaryFromParcelLocked(in);
}
final int numRat = in.readInt();
for (int i = 0; i < numRat; i++) {
if (in.readInt() == 0) continue;
getRatBatteryStatsLocked(i).readSummaryFromParcel(in);
}
mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
mMobileRadioActiveTimer.readSummaryFromParcelLocked(in);
mMobileRadioActivePerAppTimer.readSummaryFromParcelLocked(in);
mMobileRadioActiveAdjustedTime.readSummaryFromParcelLocked(in);
mMobileRadioActiveUnknownTime.readSummaryFromParcelLocked(in);
mMobileRadioActiveUnknownCount.readSummaryFromParcelLocked(in);
mWifiMulticastWakelockTimer.readSummaryFromParcelLocked(in);
mWifiRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
mWifiOn = false;
mWifiOnTimer.readSummaryFromParcelLocked(in);
mGlobalWifiRunning = false;
mGlobalWifiRunningTimer.readSummaryFromParcelLocked(in);
for (int i=0; i<NUM_WIFI_STATES; i++) {
mWifiStateTimer[i].readSummaryFromParcelLocked(in);
}
for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
mWifiSupplStateTimer[i].readSummaryFromParcelLocked(in);
}
for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
mWifiSignalStrengthsTimer[i].readSummaryFromParcelLocked(in);
}
mWifiActiveTimer.readSummaryFromParcelLocked(in);
mWifiActivity.readSummaryFromParcel(in);
for (int i=0; i<mGpsSignalQualityTimer.length; i++) {
mGpsSignalQualityTimer[i].readSummaryFromParcelLocked(in);
}
mBluetoothActivity.readSummaryFromParcel(in);
mModemActivity.readSummaryFromParcel(in);
mHasWifiReporting = in.readInt() != 0;
mHasBluetoothReporting = in.readInt() != 0;
mHasModemReporting = in.readInt() != 0;
mNumConnectivityChange = in.readInt();
mFlashlightOnNesting = 0;
mFlashlightOnTimer.readSummaryFromParcelLocked(in);
mCameraOnNesting = 0;
mCameraOnTimer.readSummaryFromParcelLocked(in);
mBluetoothScanNesting = 0;
mBluetoothScanTimer.readSummaryFromParcelLocked(in);
int NRPMS = in.readInt();
if (NRPMS > 10000) {
throw new ParcelFormatException("File corrupt: too many rpm stats " + NRPMS);
}
for (int irpm = 0; irpm < NRPMS; irpm++) {
if (in.readInt() != 0) {
String rpmName = in.readString();
getRpmTimerLocked(rpmName).readSummaryFromParcelLocked(in);
}
}
int NSORPMS = in.readInt();
if (NSORPMS > 10000) {
throw new ParcelFormatException("File corrupt: too many screen-off rpm stats " + NSORPMS);
}
for (int irpm = 0; irpm < NSORPMS; irpm++) {
if (in.readInt() != 0) {
String rpmName = in.readString();
getScreenOffRpmTimerLocked(rpmName).readSummaryFromParcelLocked(in);
}
}
int NKW = in.readInt();
if (NKW > 10000) {
throw new ParcelFormatException("File corrupt: too many kernel wake locks " + NKW);
}
for (int ikw = 0; ikw < NKW; ikw++) {
if (in.readInt() != 0) {
String kwltName = in.readString();
getKernelWakelockTimerLocked(kwltName).readSummaryFromParcelLocked(in);
}
}
int NWR = in.readInt();
if (NWR > 10000) {
throw new ParcelFormatException("File corrupt: too many wakeup reasons " + NWR);
}
for (int iwr = 0; iwr < NWR; iwr++) {
if (in.readInt() != 0) {
String reasonName = in.readString();
getWakeupReasonTimerLocked(reasonName).readSummaryFromParcelLocked(in);
}
}
int NMS = in.readInt();
for (int ims = 0; ims < NMS; ims++) {
if (in.readInt() != 0) {
long kmstName = in.readLong();
getKernelMemoryTimerLocked(kmstName).readSummaryFromParcelLocked(in);
}
}
final int NU = in.readInt();
if (NU > 10000) {
throw new ParcelFormatException("File corrupt: too many uids " + NU);
}
final long elapsedRealtimeMs = mClock.elapsedRealtime();
final long uptimeMs = mClock.uptimeMillis();
for (int iu = 0; iu < NU; iu++) {
int uid = in.readInt();
Uid u = new Uid(this, uid, elapsedRealtimeMs, uptimeMs);
mUidStats.put(uid, u);
u.mOnBatteryBackgroundTimeBase.readSummaryFromParcel(in);
u.mOnBatteryScreenOffBackgroundTimeBase.readSummaryFromParcel(in);
u.mWifiRunning = false;
if (in.readInt() != 0) {
u.mWifiRunningTimer.readSummaryFromParcelLocked(in);
}
u.mFullWifiLockOut = false;
if (in.readInt() != 0) {
u.mFullWifiLockTimer.readSummaryFromParcelLocked(in);
}
u.mWifiScanStarted = false;
if (in.readInt() != 0) {
u.mWifiScanTimer.readSummaryFromParcelLocked(in);
}
u.mWifiBatchedScanBinStarted = Uid.NO_BATCHED_SCAN_STARTED;
for (int i = 0; i < Uid.NUM_WIFI_BATCHED_SCAN_BINS; i++) {
if (in.readInt() != 0) {
u.makeWifiBatchedScanBin(i, null);
u.mWifiBatchedScanTimer[i].readSummaryFromParcelLocked(in);
}
}
u.mWifiMulticastWakelockCount = 0;
if (in.readInt() != 0) {
u.mWifiMulticastTimer.readSummaryFromParcelLocked(in);
}
if (in.readInt() != 0) {
u.createAudioTurnedOnTimerLocked().readSummaryFromParcelLocked(in);
}
if (in.readInt() != 0) {
u.createVideoTurnedOnTimerLocked().readSummaryFromParcelLocked(in);
}
if (in.readInt() != 0) {
u.createFlashlightTurnedOnTimerLocked().readSummaryFromParcelLocked(in);
}
if (in.readInt() != 0) {
u.createCameraTurnedOnTimerLocked().readSummaryFromParcelLocked(in);
}
if (in.readInt() != 0) {
u.createForegroundActivityTimerLocked().readSummaryFromParcelLocked(in);
}
if (in.readInt() != 0) {
u.createForegroundServiceTimerLocked().readSummaryFromParcelLocked(in);
}
if (in.readInt() != 0) {
u.createAggregatedPartialWakelockTimerLocked().readSummaryFromParcelLocked(in);
}
if (in.readInt() != 0) {
u.createBluetoothScanTimerLocked().readSummaryFromParcelLocked(in);
}
if (in.readInt() != 0) {
u.createBluetoothUnoptimizedScanTimerLocked().readSummaryFromParcelLocked(in);
}
if (in.readInt() != 0) {
u.createBluetoothScanResultCounterLocked().readSummaryFromParcelLocked(in);
}
if (in.readInt() != 0) {
u.createBluetoothScanResultBgCounterLocked().readSummaryFromParcelLocked(in);
}
u.mProcessState = Uid.PROCESS_STATE_NONEXISTENT;
for (int i = 0; i < NUM_PROCESS_STATE; i++) {
if (in.readInt() != 0) {
u.makeProcessState(i, null);
u.mProcessStateTimer[i].readSummaryFromParcelLocked(in);
}
}
if (in.readInt() != 0) {
u.createVibratorOnTimerLocked().readSummaryFromParcelLocked(in);
}
if (in.readInt() != 0) {
if (u.mUserActivityCounters == null) {
u.initUserActivityLocked();
}
for (int i=0; i<Uid.NUM_USER_ACTIVITY_TYPES; i++) {
u.mUserActivityCounters[i].readSummaryFromParcelLocked(in);
}
}
if (in.readInt() != 0) {
u.ensureNetworkActivityLocked();
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
u.mNetworkByteActivityCounters[i].readSummaryFromParcelLocked(in);
u.mNetworkPacketActivityCounters[i].readSummaryFromParcelLocked(in);
}
if (in.readBoolean()) {
u.mMobileRadioActiveTime = TimeMultiStateCounter.readFromParcel(in,
mOnBatteryTimeBase, BatteryConsumer.PROCESS_STATE_COUNT,
elapsedRealtimeMs);
}
u.mMobileRadioActiveCount.readSummaryFromParcelLocked(in);
}
u.mUserCpuTime.readSummaryFromParcelLocked(in);
u.mSystemCpuTime.readSummaryFromParcelLocked(in);
if (in.readInt() != 0) {
final int numClusters = in.readInt();
if (mPowerProfile != null && mPowerProfile.getNumCpuClusters() != numClusters) {
throw new ParcelFormatException("Incompatible cpu cluster arrangement");
}
detachIfNotNull(u.mCpuClusterSpeedTimesUs);
u.mCpuClusterSpeedTimesUs = new LongSamplingCounter[numClusters][];
for (int cluster = 0; cluster < numClusters; cluster++) {
if (in.readInt() != 0) {
final int NSB = in.readInt();
if (mPowerProfile != null &&
mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != NSB) {
throw new ParcelFormatException("File corrupt: too many speed bins " +
NSB);
}
u.mCpuClusterSpeedTimesUs[cluster] = new LongSamplingCounter[NSB];
for (int speed = 0; speed < NSB; speed++) {
if (in.readInt() != 0) {
u.mCpuClusterSpeedTimesUs[cluster][speed] = new LongSamplingCounter(
mOnBatteryTimeBase);
u.mCpuClusterSpeedTimesUs[cluster][speed].readSummaryFromParcelLocked(in);
}
}
} else {
u.mCpuClusterSpeedTimesUs[cluster] = null;
}
}
} else {
detachIfNotNull(u.mCpuClusterSpeedTimesUs);
u.mCpuClusterSpeedTimesUs = null;
}
detachIfNotNull(u.mCpuFreqTimeMs);
u.mCpuFreqTimeMs = LongSamplingCounterArray.readSummaryFromParcelLocked(
in, mOnBatteryTimeBase);
detachIfNotNull(u.mScreenOffCpuFreqTimeMs);
u.mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readSummaryFromParcelLocked(
in, mOnBatteryScreenOffTimeBase);
int stateCount = in.readInt();
if (stateCount != 0) {
u.mCpuActiveTimeMs = TimeMultiStateCounter.readFromParcel(in,
mOnBatteryTimeBase, BatteryConsumer.PROCESS_STATE_COUNT,
mClock.elapsedRealtime());
}
u.mCpuClusterTimesMs.readSummaryFromParcelLocked(in);
detachIfNotNull(u.mProcStateTimeMs);
u.mProcStateTimeMs = null;
stateCount = in.readInt();
if (stateCount != 0) {
detachIfNotNull(u.mProcStateTimeMs);
u.mProcStateTimeMs = TimeInFreqMultiStateCounter.readFromParcel(in,
mOnBatteryTimeBase, PROC_STATE_TIME_COUNTER_STATE_COUNT,
getCpuFreqCount(), mClock.elapsedRealtime());
}
detachIfNotNull(u.mProcStateScreenOffTimeMs);
u.mProcStateScreenOffTimeMs = null;
stateCount = in.readInt();
if (stateCount != 0) {
detachIfNotNull(u.mProcStateScreenOffTimeMs);
u.mProcStateScreenOffTimeMs = TimeInFreqMultiStateCounter.readFromParcel(in,
mOnBatteryScreenOffTimeBase, PROC_STATE_TIME_COUNTER_STATE_COUNT,
getCpuFreqCount(), mClock.elapsedRealtime());
}
if (in.readInt() != 0) {
detachIfNotNull(u.mMobileRadioApWakeupCount);
u.mMobileRadioApWakeupCount = new LongSamplingCounter(mOnBatteryTimeBase);
u.mMobileRadioApWakeupCount.readSummaryFromParcelLocked(in);
} else {
detachIfNotNull(u.mMobileRadioApWakeupCount);
u.mMobileRadioApWakeupCount = null;
}
if (in.readInt() != 0) {
detachIfNotNull(u.mWifiRadioApWakeupCount);
u.mWifiRadioApWakeupCount = new LongSamplingCounter(mOnBatteryTimeBase);
u.mWifiRadioApWakeupCount.readSummaryFromParcelLocked(in);
} else {
detachIfNotNull(u.mWifiRadioApWakeupCount);
u.mWifiRadioApWakeupCount = null;
}
u.mUidMeasuredEnergyStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(
mMeasuredEnergyStatsConfig, in);
int NW = in.readInt();
if (NW > (MAX_WAKELOCKS_PER_UID+1)) {
throw new ParcelFormatException("File corrupt: too many wake locks " + NW);
}
for (int iw = 0; iw < NW; iw++) {
String wlName = in.readString();
u.readWakeSummaryFromParcelLocked(wlName, in);
}
int NS = in.readInt();
if (NS > (MAX_WAKELOCKS_PER_UID+1)) {
throw new ParcelFormatException("File corrupt: too many syncs " + NS);
}
for (int is = 0; is < NS; is++) {
String name = in.readString();
u.readSyncSummaryFromParcelLocked(name, in);
}
int NJ = in.readInt();
if (NJ > (MAX_WAKELOCKS_PER_UID+1)) {
throw new ParcelFormatException("File corrupt: too many job timers " + NJ);
}
for (int ij = 0; ij < NJ; ij++) {
String name = in.readString();
u.readJobSummaryFromParcelLocked(name, in);
}
u.readJobCompletionsFromParcelLocked(in);
u.mJobsDeferredEventCount.readSummaryFromParcelLocked(in);
u.mJobsDeferredCount.readSummaryFromParcelLocked(in);
u.mJobsFreshnessTimeMs.readSummaryFromParcelLocked(in);
detachIfNotNull(u.mJobsFreshnessBuckets);
for (int i = 0; i < JOB_FRESHNESS_BUCKETS.length; i++) {
if (in.readInt() != 0) {
u.mJobsFreshnessBuckets[i] = new Counter(u.mBsi.mOnBatteryTimeBase);
u.mJobsFreshnessBuckets[i].readSummaryFromParcelLocked(in);
}
}
int NP = in.readInt();
if (NP > 1000) {
throw new ParcelFormatException("File corrupt: too many sensors " + NP);
}
for (int is = 0; is < NP; is++) {
int seNumber = in.readInt();
if (in.readInt() != 0) {
u.getSensorTimerLocked(seNumber, true).readSummaryFromParcelLocked(in);
}
}
NP = in.readInt();
if (NP > 1000) {
throw new ParcelFormatException("File corrupt: too many processes " + NP);
}
for (int ip = 0; ip < NP; ip++) {
String procName = in.readString();
Uid.Proc p = u.getProcessStatsLocked(procName);
p.mUserTimeMs = in.readLong();
p.mSystemTimeMs = in.readLong();
p.mForegroundTimeMs = in.readLong();
p.mStarts = in.readInt();
p.mNumCrashes = in.readInt();
p.mNumAnrs = in.readInt();
p.readExcessivePowerFromParcelLocked(in);
}
NP = in.readInt();
if (NP > 10000) {
throw new ParcelFormatException("File corrupt: too many packages " + NP);
}
for (int ip = 0; ip < NP; ip++) {
String pkgName = in.readString();
detachIfNotNull(u.mPackageStats.get(pkgName));
Uid.Pkg p = u.getPackageStatsLocked(pkgName);
final int NWA = in.readInt();
if (NWA > 10000) {
throw new ParcelFormatException("File corrupt: too many wakeup alarms " + NWA);
}
p.mWakeupAlarms.clear();
for (int iwa = 0; iwa < NWA; iwa++) {
String tag = in.readString();
Counter c = new Counter(mOnBatteryScreenOffTimeBase);
c.readSummaryFromParcelLocked(in);
p.mWakeupAlarms.put(tag, c);
}
NS = in.readInt();
if (NS > 10000) {
throw new ParcelFormatException("File corrupt: too many services " + NS);
}
for (int is = 0; is < NS; is++) {
String servName = in.readString();
Uid.Pkg.Serv s = u.getServiceStatsLocked(pkgName, servName);
s.mStartTimeMs = in.readLong();
s.mStarts = in.readInt();
s.mLaunches = in.readInt();
}
}
}
mBinderThreadCpuTimesUs =
LongSamplingCounterArray.readSummaryFromParcelLocked(in, mOnBatteryTimeBase);
}
/**
* Writes a summary of the statistics to a Parcel, in a format suitable to be written to
* disk. This format does not allow a lossless round-trip.
*
* @param out the Parcel to be written to.
*/
@GuardedBy("this")
public void writeSummaryToParcel(Parcel out, boolean inclHistory) {
pullPendingStateUpdatesLocked();
// Pull the clock time. This may update the time and make a new history entry
// if we had originally pulled a time before the RTC was set.
getStartClockTime();
final long nowUptime = mClock.uptimeMillis() * 1000;
final long nowRealtime = mClock.elapsedRealtime() * 1000;
out.writeInt(VERSION);
out.writeBoolean(inclHistory);
if (inclHistory) {
writeHistoryBuffer(out, true);
mBatteryStatsHistory.writeToParcel(out);
}
out.writeInt(mHistoryTagPool.size());
for (HashMap.Entry<HistoryTag, Integer> ent : mHistoryTagPool.entrySet()) {
HistoryTag tag = ent.getKey();
out.writeInt(ent.getValue());
out.writeString(tag.string);
out.writeInt(tag.uid);
}
out.writeInt(mStartCount);
out.writeLong(computeUptime(nowUptime, STATS_SINCE_CHARGED));
out.writeLong(computeRealtime(nowRealtime, STATS_SINCE_CHARGED));
out.writeLong(mStartClockTimeMs);
out.writeString(mStartPlatformVersion);
out.writeString(mEndPlatformVersion);
mOnBatteryTimeBase.writeSummaryToParcel(out, nowUptime, nowRealtime);
mOnBatteryScreenOffTimeBase.writeSummaryToParcel(out, nowUptime, nowRealtime);
out.writeInt(mDischargeUnplugLevel);
out.writeInt(mDischargePlugLevel);
out.writeInt(mDischargeCurrentLevel);
out.writeInt(mCurrentBatteryLevel);
out.writeInt(mEstimatedBatteryCapacityMah);
out.writeInt(mLastLearnedBatteryCapacityUah);
out.writeInt(mMinLearnedBatteryCapacityUah);
out.writeInt(mMaxLearnedBatteryCapacityUah);
out.writeInt(getLowDischargeAmountSinceCharge());
out.writeInt(getHighDischargeAmountSinceCharge());
out.writeInt(getDischargeAmountScreenOnSinceCharge());
out.writeInt(getDischargeAmountScreenOffSinceCharge());
out.writeInt(getDischargeAmountScreenDozeSinceCharge());
mDischargeStepTracker.writeToParcel(out);
mChargeStepTracker.writeToParcel(out);
mDailyDischargeStepTracker.writeToParcel(out);
mDailyChargeStepTracker.writeToParcel(out);
mDischargeCounter.writeSummaryFromParcelLocked(out);
mDischargeScreenOffCounter.writeSummaryFromParcelLocked(out);
mDischargeScreenDozeCounter.writeSummaryFromParcelLocked(out);
mDischargeLightDozeCounter.writeSummaryFromParcelLocked(out);
mDischargeDeepDozeCounter.writeSummaryFromParcelLocked(out);
if (mDailyPackageChanges != null) {
final int NPKG = mDailyPackageChanges.size();
out.writeInt(NPKG);
for (int i=0; i<NPKG; i++) {
PackageChange pc = mDailyPackageChanges.get(i);
out.writeString(pc.mPackageName);
out.writeInt(pc.mUpdate ? 1 : 0);
out.writeLong(pc.mVersionCode);
}
} else {
out.writeInt(0);
}
out.writeLong(mDailyStartTimeMs);
out.writeLong(mNextMinDailyDeadlineMs);
out.writeLong(mNextMaxDailyDeadlineMs);
out.writeLong(mBatteryTimeToFullSeconds);
MeasuredEnergyStats.Config.writeToParcel(mMeasuredEnergyStatsConfig, out);
MeasuredEnergyStats.writeSummaryToParcel(mGlobalMeasuredEnergyStats, out);
mScreenOnTimer.writeSummaryFromParcelLocked(out, nowRealtime);
mScreenDozeTimer.writeSummaryFromParcelLocked(out, nowRealtime);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].writeSummaryFromParcelLocked(out, nowRealtime);
}
mInteractiveTimer.writeSummaryFromParcelLocked(out, nowRealtime);
mPowerSaveModeEnabledTimer.writeSummaryFromParcelLocked(out, nowRealtime);
out.writeLong(mLongestLightIdleTimeMs);
out.writeLong(mLongestFullIdleTimeMs);
mDeviceIdleModeLightTimer.writeSummaryFromParcelLocked(out, nowRealtime);
mDeviceIdleModeFullTimer.writeSummaryFromParcelLocked(out, nowRealtime);
mDeviceLightIdlingTimer.writeSummaryFromParcelLocked(out, nowRealtime);
mDeviceIdlingTimer.writeSummaryFromParcelLocked(out, nowRealtime);
mPhoneOnTimer.writeSummaryFromParcelLocked(out, nowRealtime);
for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
mPhoneSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, nowRealtime);
}
mPhoneSignalScanningTimer.writeSummaryFromParcelLocked(out, nowRealtime);
for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
mPhoneDataConnectionsTimer[i].writeSummaryFromParcelLocked(out, nowRealtime);
}
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
mNetworkByteActivityCounters[i].writeSummaryFromParcelLocked(out);
mNetworkPacketActivityCounters[i].writeSummaryFromParcelLocked(out);
}
final int numRat = mPerRatBatteryStats.length;
out.writeInt(numRat);
for (int i = 0; i < numRat; i++) {
final RadioAccessTechnologyBatteryStats ratStat = mPerRatBatteryStats[i];
if (ratStat == null) {
out.writeInt(0);
continue;
}
out.writeInt(1);
ratStat.writeSummaryToParcel(out, nowRealtime);
}
mMobileRadioActiveTimer.writeSummaryFromParcelLocked(out, nowRealtime);
mMobileRadioActivePerAppTimer.writeSummaryFromParcelLocked(out, nowRealtime);
mMobileRadioActiveAdjustedTime.writeSummaryFromParcelLocked(out);
mMobileRadioActiveUnknownTime.writeSummaryFromParcelLocked(out);
mMobileRadioActiveUnknownCount.writeSummaryFromParcelLocked(out);
mWifiMulticastWakelockTimer.writeSummaryFromParcelLocked(out, nowRealtime);
mWifiOnTimer.writeSummaryFromParcelLocked(out, nowRealtime);
mGlobalWifiRunningTimer.writeSummaryFromParcelLocked(out, nowRealtime);
for (int i=0; i<NUM_WIFI_STATES; i++) {
mWifiStateTimer[i].writeSummaryFromParcelLocked(out, nowRealtime);
}
for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
mWifiSupplStateTimer[i].writeSummaryFromParcelLocked(out, nowRealtime);
}
for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
mWifiSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, nowRealtime);
}
mWifiActiveTimer.writeSummaryFromParcelLocked(out, nowRealtime);
mWifiActivity.writeSummaryToParcel(out);
for (int i=0; i< mGpsSignalQualityTimer.length; i++) {
mGpsSignalQualityTimer[i].writeSummaryFromParcelLocked(out, nowRealtime);
}
mBluetoothActivity.writeSummaryToParcel(out);
mModemActivity.writeSummaryToParcel(out);
out.writeInt(mHasWifiReporting ? 1 : 0);
out.writeInt(mHasBluetoothReporting ? 1 : 0);
out.writeInt(mHasModemReporting ? 1 : 0);
out.writeInt(mNumConnectivityChange);
mFlashlightOnTimer.writeSummaryFromParcelLocked(out, nowRealtime);
mCameraOnTimer.writeSummaryFromParcelLocked(out, nowRealtime);
mBluetoothScanTimer.writeSummaryFromParcelLocked(out, nowRealtime);
out.writeInt(mRpmStats.size());
for (Map.Entry<String, SamplingTimer> ent : mRpmStats.entrySet()) {
Timer rpmt = ent.getValue();
if (rpmt != null) {
out.writeInt(1);
out.writeString(ent.getKey());
rpmt.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
}
out.writeInt(mScreenOffRpmStats.size());
for (Map.Entry<String, SamplingTimer> ent : mScreenOffRpmStats.entrySet()) {
Timer rpmt = ent.getValue();
if (rpmt != null) {
out.writeInt(1);
out.writeString(ent.getKey());
rpmt.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
}
out.writeInt(mKernelWakelockStats.size());
for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
Timer kwlt = ent.getValue();
if (kwlt != null) {
out.writeInt(1);
out.writeString(ent.getKey());
kwlt.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
}
out.writeInt(mWakeupReasonStats.size());
for (Map.Entry<String, SamplingTimer> ent : mWakeupReasonStats.entrySet()) {
SamplingTimer timer = ent.getValue();
if (timer != null) {
out.writeInt(1);
out.writeString(ent.getKey());
timer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
}
out.writeInt(mKernelMemoryStats.size());
for (int i = 0; i < mKernelMemoryStats.size(); i++) {
Timer kmt = mKernelMemoryStats.valueAt(i);
if (kmt != null) {
out.writeInt(1);
out.writeLong(mKernelMemoryStats.keyAt(i));
kmt.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
}
final int NU = mUidStats.size();
out.writeInt(NU);
for (int iu = 0; iu < NU; iu++) {
out.writeInt(mUidStats.keyAt(iu));
Uid u = mUidStats.valueAt(iu);
u.mOnBatteryBackgroundTimeBase.writeSummaryToParcel(out, nowUptime, nowRealtime);
u.mOnBatteryScreenOffBackgroundTimeBase.writeSummaryToParcel(out, nowUptime,
nowRealtime);
if (u.mWifiRunningTimer != null) {
out.writeInt(1);
u.mWifiRunningTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mFullWifiLockTimer != null) {
out.writeInt(1);
u.mFullWifiLockTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mWifiScanTimer != null) {
out.writeInt(1);
u.mWifiScanTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
for (int i = 0; i < Uid.NUM_WIFI_BATCHED_SCAN_BINS; i++) {
if (u.mWifiBatchedScanTimer[i] != null) {
out.writeInt(1);
u.mWifiBatchedScanTimer[i].writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
}
if (u.mWifiMulticastTimer != null) {
out.writeInt(1);
u.mWifiMulticastTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mAudioTurnedOnTimer != null) {
out.writeInt(1);
u.mAudioTurnedOnTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mVideoTurnedOnTimer != null) {
out.writeInt(1);
u.mVideoTurnedOnTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mFlashlightTurnedOnTimer != null) {
out.writeInt(1);
u.mFlashlightTurnedOnTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mCameraTurnedOnTimer != null) {
out.writeInt(1);
u.mCameraTurnedOnTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mForegroundActivityTimer != null) {
out.writeInt(1);
u.mForegroundActivityTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mForegroundServiceTimer != null) {
out.writeInt(1);
u.mForegroundServiceTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mAggregatedPartialWakelockTimer != null) {
out.writeInt(1);
u.mAggregatedPartialWakelockTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mBluetoothScanTimer != null) {
out.writeInt(1);
u.mBluetoothScanTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mBluetoothUnoptimizedScanTimer != null) {
out.writeInt(1);
u.mBluetoothUnoptimizedScanTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mBluetoothScanResultCounter != null) {
out.writeInt(1);
u.mBluetoothScanResultCounter.writeSummaryFromParcelLocked(out);
} else {
out.writeInt(0);
}
if (u.mBluetoothScanResultBgCounter != null) {
out.writeInt(1);
u.mBluetoothScanResultBgCounter.writeSummaryFromParcelLocked(out);
} else {
out.writeInt(0);
}
for (int i = 0; i < NUM_PROCESS_STATE; i++) {
if (u.mProcessStateTimer[i] != null) {
out.writeInt(1);
u.mProcessStateTimer[i].writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
}
if (u.mVibratorOnTimer != null) {
out.writeInt(1);
u.mVibratorOnTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mUserActivityCounters == null) {
out.writeInt(0);
} else {
out.writeInt(1);
for (int i=0; i<Uid.NUM_USER_ACTIVITY_TYPES; i++) {
u.mUserActivityCounters[i].writeSummaryFromParcelLocked(out);
}
}
if (u.mNetworkByteActivityCounters == null) {
out.writeInt(0);
} else {
out.writeInt(1);
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
u.mNetworkByteActivityCounters[i].writeSummaryFromParcelLocked(out);
u.mNetworkPacketActivityCounters[i].writeSummaryFromParcelLocked(out);
}
if (u.mMobileRadioActiveTime != null) {
out.writeBoolean(true);
u.mMobileRadioActiveTime.writeToParcel(out);
} else {
out.writeBoolean(false);
}
u.mMobileRadioActiveCount.writeSummaryFromParcelLocked(out);
}
u.mUserCpuTime.writeSummaryFromParcelLocked(out);
u.mSystemCpuTime.writeSummaryFromParcelLocked(out);
if (u.mCpuClusterSpeedTimesUs != null) {
out.writeInt(1);
out.writeInt(u.mCpuClusterSpeedTimesUs.length);
for (LongSamplingCounter[] cpuSpeeds : u.mCpuClusterSpeedTimesUs) {
if (cpuSpeeds != null) {
out.writeInt(1);
out.writeInt(cpuSpeeds.length);
for (LongSamplingCounter c : cpuSpeeds) {
if (c != null) {
out.writeInt(1);
c.writeSummaryFromParcelLocked(out);
} else {
out.writeInt(0);
}
}
} else {
out.writeInt(0);
}
}
} else {
out.writeInt(0);
}
LongSamplingCounterArray.writeSummaryToParcelLocked(out, u.mCpuFreqTimeMs);
LongSamplingCounterArray.writeSummaryToParcelLocked(out, u.mScreenOffCpuFreqTimeMs);
if (u.mCpuActiveTimeMs != null) {
out.writeInt(u.mCpuActiveTimeMs.getStateCount());
u.mCpuActiveTimeMs.writeToParcel(out);
} else {
out.writeInt(0);
}
u.mCpuClusterTimesMs.writeSummaryToParcelLocked(out);
if (u.mProcStateTimeMs != null) {
out.writeInt(u.mProcStateTimeMs.getStateCount());
u.mProcStateTimeMs.writeToParcel(out);
} else {
out.writeInt(0);
}
if (u.mProcStateScreenOffTimeMs != null) {
out.writeInt(u.mProcStateScreenOffTimeMs.getStateCount());
u.mProcStateScreenOffTimeMs.writeToParcel(out);
} else {
out.writeInt(0);
}
if (u.mMobileRadioApWakeupCount != null) {
out.writeInt(1);
u.mMobileRadioApWakeupCount.writeSummaryFromParcelLocked(out);
} else {
out.writeInt(0);
}
if (u.mWifiRadioApWakeupCount != null) {
out.writeInt(1);
u.mWifiRadioApWakeupCount.writeSummaryFromParcelLocked(out);
} else {
out.writeInt(0);
}
MeasuredEnergyStats.writeSummaryToParcel(u.mUidMeasuredEnergyStats, out);
final ArrayMap<String, Uid.Wakelock> wakeStats = u.mWakelockStats.getMap();
int NW = wakeStats.size();
out.writeInt(NW);
for (int iw=0; iw<NW; iw++) {
out.writeString(wakeStats.keyAt(iw));
Uid.Wakelock wl = wakeStats.valueAt(iw);
if (wl.mTimerFull != null) {
out.writeInt(1);
wl.mTimerFull.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (wl.mTimerPartial != null) {
out.writeInt(1);
wl.mTimerPartial.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (wl.mTimerWindow != null) {
out.writeInt(1);
wl.mTimerWindow.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (wl.mTimerDraw != null) {
out.writeInt(1);
wl.mTimerDraw.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
}
final ArrayMap<String, DualTimer> syncStats = u.mSyncStats.getMap();
int NS = syncStats.size();
out.writeInt(NS);
for (int is=0; is<NS; is++) {
out.writeString(syncStats.keyAt(is));
syncStats.valueAt(is).writeSummaryFromParcelLocked(out, nowRealtime);
}
final ArrayMap<String, DualTimer> jobStats = u.mJobStats.getMap();
int NJ = jobStats.size();
out.writeInt(NJ);
for (int ij=0; ij<NJ; ij++) {
out.writeString(jobStats.keyAt(ij));
jobStats.valueAt(ij).writeSummaryFromParcelLocked(out, nowRealtime);
}
u.writeJobCompletionsToParcelLocked(out);
u.mJobsDeferredEventCount.writeSummaryFromParcelLocked(out);
u.mJobsDeferredCount.writeSummaryFromParcelLocked(out);
u.mJobsFreshnessTimeMs.writeSummaryFromParcelLocked(out);
for (int i = 0; i < JOB_FRESHNESS_BUCKETS.length; i++) {
if (u.mJobsFreshnessBuckets[i] != null) {
out.writeInt(1);
u.mJobsFreshnessBuckets[i].writeSummaryFromParcelLocked(out);
} else {
out.writeInt(0);
}
}
int NSE = u.mSensorStats.size();
out.writeInt(NSE);
for (int ise=0; ise<NSE; ise++) {
out.writeInt(u.mSensorStats.keyAt(ise));
Uid.Sensor se = u.mSensorStats.valueAt(ise);
if (se.mTimer != null) {
out.writeInt(1);
se.mTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
}
int NP = u.mProcessStats.size();
out.writeInt(NP);
for (int ip=0; ip<NP; ip++) {
out.writeString(u.mProcessStats.keyAt(ip));
Uid.Proc ps = u.mProcessStats.valueAt(ip);
out.writeLong(ps.mUserTimeMs);
out.writeLong(ps.mSystemTimeMs);
out.writeLong(ps.mForegroundTimeMs);
out.writeInt(ps.mStarts);
out.writeInt(ps.mNumCrashes);
out.writeInt(ps.mNumAnrs);
ps.writeExcessivePowerToParcelLocked(out);
}
NP = u.mPackageStats.size();
out.writeInt(NP);
if (NP > 0) {
for (Map.Entry<String, BatteryStatsImpl.Uid.Pkg> ent
: u.mPackageStats.entrySet()) {
out.writeString(ent.getKey());
Uid.Pkg ps = ent.getValue();
final int NWA = ps.mWakeupAlarms.size();
out.writeInt(NWA);
for (int iwa=0; iwa<NWA; iwa++) {
out.writeString(ps.mWakeupAlarms.keyAt(iwa));
ps.mWakeupAlarms.valueAt(iwa).writeSummaryFromParcelLocked(out);
}
NS = ps.mServiceStats.size();
out.writeInt(NS);
for (int is=0; is<NS; is++) {
out.writeString(ps.mServiceStats.keyAt(is));
BatteryStatsImpl.Uid.Pkg.Serv ss = ps.mServiceStats.valueAt(is);
long time = ss.getStartTimeToNowLocked(
mOnBatteryTimeBase.getUptime(nowUptime) / 1000);
out.writeLong(time);
out.writeInt(ss.mStarts);
out.writeInt(ss.mLaunches);
}
}
}
}
LongSamplingCounterArray.writeSummaryToParcelLocked(out, mBinderThreadCpuTimesUs);
}
@GuardedBy("this")
public void readFromParcel(Parcel in) {
readFromParcelLocked(in);
}
@GuardedBy("this")
@SuppressWarnings("GuardedBy") // errorprone false positive on u.readFromParcelLocked
void readFromParcelLocked(Parcel in) {
int magic = in.readInt();
if (magic != MAGIC) {
throw new ParcelFormatException("Bad magic number: #" + Integer.toHexString(magic));
}
readHistoryBuffer(in);
mBatteryStatsHistory.readFromParcel(in);
mStartCount = in.readInt();
mStartClockTimeMs = in.readLong();
mStartPlatformVersion = in.readString();
mEndPlatformVersion = in.readString();
mUptimeUs = in.readLong();
mUptimeStartUs = in.readLong();
mRealtimeUs = in.readLong();
mRealtimeStartUs = in.readLong();
mOnBattery = in.readInt() != 0;
mEstimatedBatteryCapacityMah = in.readInt();
mLastLearnedBatteryCapacityUah = in.readInt();
mMinLearnedBatteryCapacityUah = in.readInt();
mMaxLearnedBatteryCapacityUah = in.readInt();
mOnBatteryInternal = false; // we are no longer really running.
mOnBatteryTimeBase.readFromParcel(in);
mOnBatteryScreenOffTimeBase.readFromParcel(in);
mScreenState = Display.STATE_UNKNOWN;
mScreenOnTimer = new StopwatchTimer(mClock, null, -1, null, mOnBatteryTimeBase, in);
mScreenDozeTimer = new StopwatchTimer(mClock, null, -1, null, mOnBatteryTimeBase, in);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i] = new StopwatchTimer(mClock, null, -100 - i, null,
mOnBatteryTimeBase, in);
}
mInteractive = false;
mInteractiveTimer = new StopwatchTimer(mClock, null, -10, null, mOnBatteryTimeBase, in);
mPhoneOn = false;
mPowerSaveModeEnabledTimer = new StopwatchTimer(mClock, null, -2, null,
mOnBatteryTimeBase, in);
mLongestLightIdleTimeMs = in.readLong();
mLongestFullIdleTimeMs = in.readLong();
mDeviceIdleModeLightTimer = new StopwatchTimer(mClock, null, -14, null,
mOnBatteryTimeBase, in);
mDeviceIdleModeFullTimer = new StopwatchTimer(mClock, null, -11, null,
mOnBatteryTimeBase, in);
mDeviceLightIdlingTimer = new StopwatchTimer(mClock, null, -15, null,
mOnBatteryTimeBase, in);
mDeviceIdlingTimer = new StopwatchTimer(mClock, null, -12, null, mOnBatteryTimeBase, in);
mPhoneOnTimer = new StopwatchTimer(mClock, null, -3, null, mOnBatteryTimeBase, in);
for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(mClock, null, -200 - i,
null, mOnBatteryTimeBase, in);
}
mPhoneSignalScanningTimer = new StopwatchTimer(mClock, null, -200 + 1, null,
mOnBatteryTimeBase, in);
for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
mPhoneDataConnectionsTimer[i] = new StopwatchTimer(mClock, null, -300 - i,
null, mOnBatteryTimeBase, in);
}
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
mNetworkByteActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase, in);
mNetworkPacketActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase, in);
}
mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
mMobileRadioActiveTimer = new StopwatchTimer(mClock, null, -400, null,
mOnBatteryTimeBase, in);
mMobileRadioActivePerAppTimer = new StopwatchTimer(mClock, null, -401, null,
mOnBatteryTimeBase, in);
mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
mMobileRadioActiveUnknownCount = new LongSamplingCounter(mOnBatteryTimeBase, in);
mWifiMulticastWakelockTimer = new StopwatchTimer(mClock, null, -4, null,
mOnBatteryTimeBase, in);
mWifiRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
mWifiOn = false;
mWifiOnTimer = new StopwatchTimer(mClock, null, -4, null, mOnBatteryTimeBase, in);
mGlobalWifiRunning = false;
mGlobalWifiRunningTimer = new StopwatchTimer(mClock, null, -5, null,
mOnBatteryTimeBase, in);
for (int i=0; i<NUM_WIFI_STATES; i++) {
mWifiStateTimer[i] = new StopwatchTimer(mClock, null, -600 - i,
null, mOnBatteryTimeBase, in);
}
for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
mWifiSupplStateTimer[i] = new StopwatchTimer(mClock, null, -700 - i,
null, mOnBatteryTimeBase, in);
}
for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
mWifiSignalStrengthsTimer[i] = new StopwatchTimer(mClock, null, -800 - i,
null, mOnBatteryTimeBase, in);
}
mWifiActiveTimer = new StopwatchTimer(mClock, null, -900, null,
mOnBatteryTimeBase, in);
mWifiActivity = new ControllerActivityCounterImpl(mClock, mOnBatteryTimeBase,
NUM_WIFI_TX_LEVELS, in);
for (int i=0; i<mGpsSignalQualityTimer.length; i++) {
mGpsSignalQualityTimer[i] = new StopwatchTimer(mClock, null, -1000 - i,
null, mOnBatteryTimeBase, in);
}
mBluetoothActivity = new ControllerActivityCounterImpl(mClock, mOnBatteryTimeBase,
NUM_BT_TX_LEVELS, in);
mModemActivity = new ControllerActivityCounterImpl(mClock, mOnBatteryTimeBase,
ModemActivityInfo.getNumTxPowerLevels(), in);
mHasWifiReporting = in.readInt() != 0;
mHasBluetoothReporting = in.readInt() != 0;
mHasModemReporting = in.readInt() != 0;
mNumConnectivityChange = in.readInt();
mAudioOnNesting = 0;
// TODO: It's likely a mistake that mAudioOnTimer/mVideoOnTimer don't write/read to parcel!
mAudioOnTimer = new StopwatchTimer(mClock, null, -7, null, mOnBatteryTimeBase);
mVideoOnNesting = 0;
mVideoOnTimer = new StopwatchTimer(mClock, null, -8, null, mOnBatteryTimeBase);
mFlashlightOnNesting = 0;
mFlashlightOnTimer = new StopwatchTimer(mClock, null, -9, null, mOnBatteryTimeBase, in);
mCameraOnNesting = 0;
mCameraOnTimer = new StopwatchTimer(mClock, null, -13, null, mOnBatteryTimeBase, in);
mBluetoothScanNesting = 0;
mBluetoothScanTimer = new StopwatchTimer(mClock, null, -14, null, mOnBatteryTimeBase, in);
mDischargeUnplugLevel = in.readInt();
mDischargePlugLevel = in.readInt();
mDischargeCurrentLevel = in.readInt();
mCurrentBatteryLevel = in.readInt();
mLowDischargeAmountSinceCharge = in.readInt();
mHighDischargeAmountSinceCharge = in.readInt();
mDischargeAmountScreenOn = in.readInt();
mDischargeAmountScreenOnSinceCharge = in.readInt();
mDischargeAmountScreenOff = in.readInt();
mDischargeAmountScreenOffSinceCharge = in.readInt();
mDischargeAmountScreenDoze = in.readInt();
mDischargeAmountScreenDozeSinceCharge = in.readInt();
mDischargeStepTracker.readFromParcel(in);
mChargeStepTracker.readFromParcel(in);
mDischargeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
mDischargeScreenOffCounter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase, in);
mDischargeScreenDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
mDischargeLightDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
mDischargeDeepDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
mLastWriteTimeMs = in.readLong();
mBatteryTimeToFullSeconds = in.readLong();
final MeasuredEnergyStats.Config config = MeasuredEnergyStats.Config.createFromParcel(in);
final MeasuredEnergyStats measuredEnergyStats =
MeasuredEnergyStats.createFromParcel(mMeasuredEnergyStatsConfig, in);
if (config != null && Arrays.equals(config.getStateNames(),
getBatteryConsumerProcessStateNames())) {
mMeasuredEnergyStatsConfig = config;
mGlobalMeasuredEnergyStats = measuredEnergyStats;
}
mRpmStats.clear();
int NRPMS = in.readInt();
for (int irpm = 0; irpm < NRPMS; irpm++) {
if (in.readInt() != 0) {
String rpmName = in.readString();
SamplingTimer rpmt = new SamplingTimer(mClock, mOnBatteryTimeBase, in);
mRpmStats.put(rpmName, rpmt);
}
}
mScreenOffRpmStats.clear();
int NSORPMS = in.readInt();
for (int irpm = 0; irpm < NSORPMS; irpm++) {
if (in.readInt() != 0) {
String rpmName = in.readString();
SamplingTimer rpmt = new SamplingTimer(mClock, mOnBatteryScreenOffTimeBase, in);
mScreenOffRpmStats.put(rpmName, rpmt);
}
}
mKernelWakelockStats.clear();
int NKW = in.readInt();
for (int ikw = 0; ikw < NKW; ikw++) {
if (in.readInt() != 0) {
String wakelockName = in.readString();
SamplingTimer kwlt = new SamplingTimer(mClock, mOnBatteryScreenOffTimeBase, in);
mKernelWakelockStats.put(wakelockName, kwlt);
}
}
mWakeupReasonStats.clear();
int NWR = in.readInt();
for (int iwr = 0; iwr < NWR; iwr++) {
if (in.readInt() != 0) {
String reasonName = in.readString();
SamplingTimer timer = new SamplingTimer(mClock, mOnBatteryTimeBase, in);
mWakeupReasonStats.put(reasonName, timer);
}
}
mKernelMemoryStats.clear();
int nmt = in.readInt();
for (int imt = 0; imt < nmt; imt++) {
if (in.readInt() != 0) {
Long bucket = in.readLong();
SamplingTimer kmt = new SamplingTimer(mClock, mOnBatteryTimeBase, in);
mKernelMemoryStats.put(bucket, kmt);
}
}
mPartialTimers.clear();
mFullTimers.clear();
mWindowTimers.clear();
mWifiRunningTimers.clear();
mFullWifiLockTimers.clear();
mWifiScanTimers.clear();
mWifiBatchedScanTimers.clear();
mWifiMulticastTimers.clear();
mAudioTurnedOnTimers.clear();
mVideoTurnedOnTimers.clear();
mFlashlightTurnedOnTimers.clear();
mCameraTurnedOnTimers.clear();
int numUids = in.readInt();
mUidStats.clear();
final long elapsedRealtimeMs = mClock.elapsedRealtime();
final long uptimeMs = mClock.uptimeMillis();
for (int i = 0; i < numUids; i++) {
int uid = in.readInt();
Uid u = new Uid(this, uid, elapsedRealtimeMs, uptimeMs);
u.readFromParcelLocked(mOnBatteryTimeBase, mOnBatteryScreenOffTimeBase,
in);
mUidStats.append(uid, u);
}
mBinderThreadCpuTimesUs = LongSamplingCounterArray.readFromParcel(in, mOnBatteryTimeBase);
}
@GuardedBy("this")
public void writeToParcel(Parcel out, int flags) {
writeToParcelLocked(out, true, flags);
}
@GuardedBy("this")
public void writeToParcelWithoutUids(Parcel out, int flags) {
writeToParcelLocked(out, false, flags);
}
@SuppressWarnings("unused")
@GuardedBy("this")
void writeToParcelLocked(Parcel out, boolean inclUids, int flags) {
// Need to update with current kernel wake lock counts.
pullPendingStateUpdatesLocked();
updateSystemServiceCallStats();
// Pull the clock time. This may update the time and make a new history entry
// if we had originally pulled a time before the RTC was set.
getStartClockTime();
final long uSecUptime = mClock.uptimeMillis() * 1000;
final long uSecRealtime = mClock.elapsedRealtime() * 1000;
final long batteryRealtime = mOnBatteryTimeBase.getRealtime(uSecRealtime);
final long batteryScreenOffRealtime = mOnBatteryScreenOffTimeBase.getRealtime(uSecRealtime);
out.writeInt(MAGIC);
writeHistoryBuffer(out, true);
mBatteryStatsHistory.writeToParcel(out);
out.writeInt(mStartCount);
out.writeLong(mStartClockTimeMs);
out.writeString(mStartPlatformVersion);
out.writeString(mEndPlatformVersion);
out.writeLong(mUptimeUs);
out.writeLong(mUptimeStartUs);
out.writeLong(mRealtimeUs);
out.writeLong(mRealtimeStartUs);
out.writeInt(mOnBattery ? 1 : 0);
out.writeInt(mEstimatedBatteryCapacityMah);
out.writeInt(mLastLearnedBatteryCapacityUah);
out.writeInt(mMinLearnedBatteryCapacityUah);
out.writeInt(mMaxLearnedBatteryCapacityUah);
mOnBatteryTimeBase.writeToParcel(out, uSecUptime, uSecRealtime);
mOnBatteryScreenOffTimeBase.writeToParcel(out, uSecUptime, uSecRealtime);
mScreenOnTimer.writeToParcel(out, uSecRealtime);
mScreenDozeTimer.writeToParcel(out, uSecRealtime);
for (int i = 0; i < NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].writeToParcel(out, uSecRealtime);
}
mInteractiveTimer.writeToParcel(out, uSecRealtime);
mPowerSaveModeEnabledTimer.writeToParcel(out, uSecRealtime);
out.writeLong(mLongestLightIdleTimeMs);
out.writeLong(mLongestFullIdleTimeMs);
mDeviceIdleModeLightTimer.writeToParcel(out, uSecRealtime);
mDeviceIdleModeFullTimer.writeToParcel(out, uSecRealtime);
mDeviceLightIdlingTimer.writeToParcel(out, uSecRealtime);
mDeviceIdlingTimer.writeToParcel(out, uSecRealtime);
mPhoneOnTimer.writeToParcel(out, uSecRealtime);
for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
mPhoneSignalStrengthsTimer[i].writeToParcel(out, uSecRealtime);
}
mPhoneSignalScanningTimer.writeToParcel(out, uSecRealtime);
for (int i = 0; i < NUM_DATA_CONNECTION_TYPES; i++) {
mPhoneDataConnectionsTimer[i].writeToParcel(out, uSecRealtime);
}
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
mNetworkByteActivityCounters[i].writeToParcel(out);
mNetworkPacketActivityCounters[i].writeToParcel(out);
}
mMobileRadioActiveTimer.writeToParcel(out, uSecRealtime);
mMobileRadioActivePerAppTimer.writeToParcel(out, uSecRealtime);
mMobileRadioActiveAdjustedTime.writeToParcel(out);
mMobileRadioActiveUnknownTime.writeToParcel(out);
mMobileRadioActiveUnknownCount.writeToParcel(out);
mWifiMulticastWakelockTimer.writeToParcel(out, uSecRealtime);
mWifiOnTimer.writeToParcel(out, uSecRealtime);
mGlobalWifiRunningTimer.writeToParcel(out, uSecRealtime);
for (int i = 0; i < NUM_WIFI_STATES; i++) {
mWifiStateTimer[i].writeToParcel(out, uSecRealtime);
}
for (int i = 0; i < NUM_WIFI_SUPPL_STATES; i++) {
mWifiSupplStateTimer[i].writeToParcel(out, uSecRealtime);
}
for (int i = 0; i < NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
mWifiSignalStrengthsTimer[i].writeToParcel(out, uSecRealtime);
}
mWifiActiveTimer.writeToParcel(out, uSecRealtime);
mWifiActivity.writeToParcel(out, 0);
for (int i = 0; i < mGpsSignalQualityTimer.length; i++) {
mGpsSignalQualityTimer[i].writeToParcel(out, uSecRealtime);
}
mBluetoothActivity.writeToParcel(out, 0);
mModemActivity.writeToParcel(out, 0);
out.writeInt(mHasWifiReporting ? 1 : 0);
out.writeInt(mHasBluetoothReporting ? 1 : 0);
out.writeInt(mHasModemReporting ? 1 : 0);
out.writeInt(mNumConnectivityChange);
mFlashlightOnTimer.writeToParcel(out, uSecRealtime);
mCameraOnTimer.writeToParcel(out, uSecRealtime);
mBluetoothScanTimer.writeToParcel(out, uSecRealtime);
out.writeInt(mDischargeUnplugLevel);
out.writeInt(mDischargePlugLevel);
out.writeInt(mDischargeCurrentLevel);
out.writeInt(mCurrentBatteryLevel);
out.writeInt(mLowDischargeAmountSinceCharge);
out.writeInt(mHighDischargeAmountSinceCharge);
out.writeInt(mDischargeAmountScreenOn);
out.writeInt(mDischargeAmountScreenOnSinceCharge);
out.writeInt(mDischargeAmountScreenOff);
out.writeInt(mDischargeAmountScreenOffSinceCharge);
out.writeInt(mDischargeAmountScreenDoze);
out.writeInt(mDischargeAmountScreenDozeSinceCharge);
mDischargeStepTracker.writeToParcel(out);
mChargeStepTracker.writeToParcel(out);
mDischargeCounter.writeToParcel(out);
mDischargeScreenOffCounter.writeToParcel(out);
mDischargeScreenDozeCounter.writeToParcel(out);
mDischargeLightDozeCounter.writeToParcel(out);
mDischargeDeepDozeCounter.writeToParcel(out);
out.writeLong(mLastWriteTimeMs);
out.writeLong(mBatteryTimeToFullSeconds);
MeasuredEnergyStats.Config.writeToParcel(mMeasuredEnergyStatsConfig, out);
if (mGlobalMeasuredEnergyStats != null) {
out.writeInt(1);
mGlobalMeasuredEnergyStats.writeToParcel(out);
} else {
out.writeInt(0);
}
out.writeInt(mRpmStats.size());
for (Map.Entry<String, SamplingTimer> ent : mRpmStats.entrySet()) {
SamplingTimer rpmt = ent.getValue();
if (rpmt != null) {
out.writeInt(1);
out.writeString(ent.getKey());
rpmt.writeToParcel(out, uSecRealtime);
} else {
out.writeInt(0);
}
}
out.writeInt(mScreenOffRpmStats.size());
for (Map.Entry<String, SamplingTimer> ent : mScreenOffRpmStats.entrySet()) {
SamplingTimer rpmt = ent.getValue();
if (rpmt != null) {
out.writeInt(1);
out.writeString(ent.getKey());
rpmt.writeToParcel(out, uSecRealtime);
} else {
out.writeInt(0);
}
}
if (inclUids) {
out.writeInt(mKernelWakelockStats.size());
for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
SamplingTimer kwlt = ent.getValue();
if (kwlt != null) {
out.writeInt(1);
out.writeString(ent.getKey());
kwlt.writeToParcel(out, uSecRealtime);
} else {
out.writeInt(0);
}
}
out.writeInt(mWakeupReasonStats.size());
for (Map.Entry<String, SamplingTimer> ent : mWakeupReasonStats.entrySet()) {
SamplingTimer timer = ent.getValue();
if (timer != null) {
out.writeInt(1);
out.writeString(ent.getKey());
timer.writeToParcel(out, uSecRealtime);
} else {
out.writeInt(0);
}
}
} else {
out.writeInt(0);
out.writeInt(0);
}
out.writeInt(mKernelMemoryStats.size());
for (int i = 0; i < mKernelMemoryStats.size(); i++) {
SamplingTimer kmt = mKernelMemoryStats.valueAt(i);
if (kmt != null) {
out.writeInt(1);
out.writeLong(mKernelMemoryStats.keyAt(i));
kmt.writeToParcel(out, uSecRealtime);
} else {
out.writeInt(0);
}
}
if (inclUids) {
int size = mUidStats.size();
out.writeInt(size);
for (int i = 0; i < size; i++) {
out.writeInt(mUidStats.keyAt(i));
Uid uid = mUidStats.valueAt(i);
uid.writeToParcelLocked(out, uSecUptime, uSecRealtime);
}
} else {
out.writeInt(0);
}
LongSamplingCounterArray.writeToParcel(out, mBinderThreadCpuTimesUs);
}
private void writeCpuSpeedCountersToParcel(Parcel out, LongSamplingCounter[][] counters) {
if (counters == null) {
out.writeInt(0);
return;
}
out.writeInt(1);
out.writeInt(counters.length);
for (int i = 0; i < counters.length; i++) {
LongSamplingCounter[] counterArray = counters[i];
if (counterArray == null) {
out.writeInt(0);
continue;
}
out.writeInt(1);
out.writeInt(counterArray.length);
for (int j = 0; j < counterArray.length; j++) {
LongSamplingCounter c = counterArray[j];
if (c != null) {
out.writeInt(1);
c.writeToParcel(out);
} else {
out.writeInt(0);
}
}
}
}
private LongSamplingCounter[][] readCpuSpeedCountersFromParcel(Parcel in) {
LongSamplingCounter[][] counters;
if (in.readInt() != 0) {
int numCpuClusters = in.readInt();
if (mPowerProfile != null
&& mPowerProfile.getNumCpuClusters() != numCpuClusters) {
throw new ParcelFormatException("Incompatible number of cpu clusters");
}
counters = new LongSamplingCounter[numCpuClusters][];
for (int cluster = 0; cluster < numCpuClusters; cluster++) {
if (in.readInt() != 0) {
int numSpeeds = in.readInt();
if (mPowerProfile != null
&& mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != numSpeeds) {
throw new ParcelFormatException("Incompatible number of cpu speeds");
}
final LongSamplingCounter[] cpuSpeeds = new LongSamplingCounter[numSpeeds];
counters[cluster] = cpuSpeeds;
for (int speed = 0; speed < numSpeeds; speed++) {
if (in.readInt() != 0) {
cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase, in);
}
}
} else {
counters[cluster] = null;
}
}
} else {
counters = null;
}
return counters;
}
@UnsupportedAppUsage
public static final Parcelable.Creator<BatteryStatsImpl> CREATOR =
new Parcelable.Creator<BatteryStatsImpl>() {
public BatteryStatsImpl createFromParcel(Parcel in) {
return new BatteryStatsImpl(in);
}
public BatteryStatsImpl[] newArray(int size) {
return new BatteryStatsImpl[size];
}
};
@GuardedBy("this")
public void prepareForDumpLocked() {
// Need to retrieve current kernel wake lock stats before printing.
pullPendingStateUpdatesLocked();
// Pull the clock time. This may update the time and make a new history entry
// if we had originally pulled a time before the RTC was set.
getStartClockTime();
updateSystemServiceCallStats();
}
@GuardedBy("this")
public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
if (DEBUG) {
pw.println("mOnBatteryTimeBase:");
mOnBatteryTimeBase.dump(pw, " ");
pw.println("mOnBatteryScreenOffTimeBase:");
mOnBatteryScreenOffTimeBase.dump(pw, " ");
Printer pr = new PrintWriterPrinter(pw);
pr.println("*** Screen on timer:");
mScreenOnTimer.logState(pr, " ");
pr.println("*** Screen doze timer:");
mScreenDozeTimer.logState(pr, " ");
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
pr.println("*** Screen brightness #" + i + ":");
mScreenBrightnessTimer[i].logState(pr, " ");
}
pr.println("*** Interactive timer:");
mInteractiveTimer.logState(pr, " ");
pr.println("*** Power save mode timer:");
mPowerSaveModeEnabledTimer.logState(pr, " ");
pr.println("*** Device idle mode light timer:");
mDeviceIdleModeLightTimer.logState(pr, " ");
pr.println("*** Device idle mode full timer:");
mDeviceIdleModeFullTimer.logState(pr, " ");
pr.println("*** Device light idling timer:");
mDeviceLightIdlingTimer.logState(pr, " ");
pr.println("*** Device idling timer:");
mDeviceIdlingTimer.logState(pr, " ");
pr.println("*** Phone timer:");
mPhoneOnTimer.logState(pr, " ");
for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
pr.println("*** Phone signal strength #" + i + ":");
mPhoneSignalStrengthsTimer[i].logState(pr, " ");
}
pr.println("*** Signal scanning :");
mPhoneSignalScanningTimer.logState(pr, " ");
for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
pr.println("*** Data connection type #" + i + ":");
mPhoneDataConnectionsTimer[i].logState(pr, " ");
}
pr.println("*** mMobileRadioPowerState=" + mMobileRadioPowerState);
pr.println("*** Mobile network active timer:");
mMobileRadioActiveTimer.logState(pr, " ");
pr.println("*** Mobile network active adjusted timer:");
mMobileRadioActiveAdjustedTime.logState(pr, " ");
pr.println("*** Wifi Multicast WakeLock Timer:");
mWifiMulticastWakelockTimer.logState(pr, " ");
pr.println("*** mWifiRadioPowerState=" + mWifiRadioPowerState);
pr.println("*** Wifi timer:");
mWifiOnTimer.logState(pr, " ");
pr.println("*** WifiRunning timer:");
mGlobalWifiRunningTimer.logState(pr, " ");
for (int i=0; i<NUM_WIFI_STATES; i++) {
pr.println("*** Wifi state #" + i + ":");
mWifiStateTimer[i].logState(pr, " ");
}
for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
pr.println("*** Wifi suppl state #" + i + ":");
mWifiSupplStateTimer[i].logState(pr, " ");
}
for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
pr.println("*** Wifi signal strength #" + i + ":");
mWifiSignalStrengthsTimer[i].logState(pr, " ");
}
for (int i=0; i<mGpsSignalQualityTimer.length; i++) {
pr.println("*** GPS signal quality #" + i + ":");
mGpsSignalQualityTimer[i].logState(pr, " ");
}
pr.println("*** Flashlight timer:");
mFlashlightOnTimer.logState(pr, " ");
pr.println("*** Camera timer:");
mCameraOnTimer.logState(pr, " ");
}
super.dumpLocked(context, pw, flags, reqUid, histStart);
pw.print("Per process state tracking available: ");
pw.println(trackPerProcStateCpuTimes());
pw.print("Total cpu time reads: ");
pw.println(mNumSingleUidCpuTimeReads);
pw.print("Batching Duration (min): ");
pw.println((mClock.uptimeMillis() - mCpuTimeReadsTrackingStartTimeMs) / (60 * 1000));
pw.print("All UID cpu time reads since the later of device start or stats reset: ");
pw.println(mNumAllUidCpuTimeReads);
pw.print("UIDs removed since the later of device start or stats reset: ");
pw.println(mNumUidsRemoved);
pw.println("Currently mapped isolated uids:");
final int numIsolatedUids = mIsolatedUids.size();
for (int i = 0; i < numIsolatedUids; i++) {
final int isolatedUid = mIsolatedUids.keyAt(i);
final int ownerUid = mIsolatedUids.valueAt(i);
final int refCount = mIsolatedUidRefCounts.get(isolatedUid);
pw.println(" " + isolatedUid + "->" + ownerUid + " (ref count = " + refCount + ")");
}
pw.println();
dumpConstantsLocked(pw);
pw.println();
dumpMeasuredEnergyStatsLocked(pw);
}
}