Import Android SDK Platform P [4697573]
/google/data/ro/projects/android/fetch_artifact \
--bid 4697573 \
--target sdk_phone_armv7-win_sdk \
sdk-repo-linux-sources-4697573.zip
AndroidVersion.ApiLevel has been modified to appear as 28
Change-Id: If80578c3c657366cc9cf75f8db13d46e2dd4e077
diff --git a/com/android/internal/os/BatterySipper.java b/com/android/internal/os/BatterySipper.java
index 5457c1d..5abc6d4 100644
--- a/com/android/internal/os/BatterySipper.java
+++ b/com/android/internal/os/BatterySipper.java
@@ -73,14 +73,16 @@
public double usagePowerMah;
// Subsystem usage times.
- public long cpuTimeMs;
- public long gpsTimeMs;
- public long wifiRunningTimeMs;
- public long cpuFgTimeMs;
- public long wakeLockTimeMs;
- public long cameraTimeMs;
- public long flashlightTimeMs;
+ public long audioTimeMs;
public long bluetoothRunningTimeMs;
+ public long cameraTimeMs;
+ public long cpuFgTimeMs;
+ public long cpuTimeMs;
+ public long flashlightTimeMs;
+ public long gpsTimeMs;
+ public long videoTimeMs;
+ public long wakeLockTimeMs;
+ public long wifiRunningTimeMs;
public long mobileRxPackets;
public long mobileTxPackets;
@@ -102,30 +104,33 @@
// Measured in mAh (milli-ampere per hour).
// These are included when summed.
- public double wifiPowerMah;
- public double cpuPowerMah;
- public double wakeLockPowerMah;
- public double mobileRadioPowerMah;
- public double gpsPowerMah;
- public double sensorPowerMah;
- public double cameraPowerMah;
- public double flashlightPowerMah;
+ public double audioPowerMah;
public double bluetoothPowerMah;
+ public double cameraPowerMah;
+ public double cpuPowerMah;
+ public double flashlightPowerMah;
+ public double gpsPowerMah;
+ public double mobileRadioPowerMah;
+ public double sensorPowerMah;
+ public double videoPowerMah;
+ public double wakeLockPowerMah;
+ public double wifiPowerMah;
public enum DrainType {
- IDLE,
- CELL,
- PHONE,
- WIFI,
- BLUETOOTH,
- FLASHLIGHT,
- SCREEN,
+ AMBIENT_DISPLAY,
APP,
- USER,
- UNACCOUNTED,
- OVERCOUNTED,
+ BLUETOOTH,
CAMERA,
- MEMORY
+ CELL,
+ FLASHLIGHT,
+ IDLE,
+ MEMORY,
+ OVERCOUNTED,
+ PHONE,
+ SCREEN,
+ UNACCOUNTED,
+ USER,
+ WIFI,
}
public BatterySipper(DrainType drainType, Uid uid, double value) {
@@ -176,10 +181,12 @@
totalPowerMah += other.totalPowerMah;
usageTimeMs += other.usageTimeMs;
usagePowerMah += other.usagePowerMah;
+ audioTimeMs += other.audioTimeMs;
cpuTimeMs += other.cpuTimeMs;
gpsTimeMs += other.gpsTimeMs;
wifiRunningTimeMs += other.wifiRunningTimeMs;
cpuFgTimeMs += other.cpuFgTimeMs;
+ videoTimeMs += other.videoTimeMs;
wakeLockTimeMs += other.wakeLockTimeMs;
cameraTimeMs += other.cameraTimeMs;
flashlightTimeMs += other.flashlightTimeMs;
@@ -196,6 +203,7 @@
wifiTxBytes += other.wifiTxBytes;
btRxBytes += other.btRxBytes;
btTxBytes += other.btTxBytes;
+ audioPowerMah += other.audioPowerMah;
wifiPowerMah += other.wifiPowerMah;
gpsPowerMah += other.gpsPowerMah;
cpuPowerMah += other.cpuPowerMah;
@@ -206,6 +214,7 @@
flashlightPowerMah += other.flashlightPowerMah;
bluetoothPowerMah += other.bluetoothPowerMah;
screenPowerMah += other.screenPowerMah;
+ videoPowerMah += other.videoPowerMah;
proportionalSmearMah += other.proportionalSmearMah;
totalSmearedPowerMah += other.totalSmearedPowerMah;
}
@@ -219,7 +228,7 @@
public double sumPower() {
totalPowerMah = usagePowerMah + wifiPowerMah + gpsPowerMah + cpuPowerMah +
sensorPowerMah + mobileRadioPowerMah + wakeLockPowerMah + cameraPowerMah +
- flashlightPowerMah + bluetoothPowerMah;
+ flashlightPowerMah + bluetoothPowerMah + audioPowerMah + videoPowerMah;
totalSmearedPowerMah = totalPowerMah + screenPowerMah + proportionalSmearMah;
return totalPowerMah;
diff --git a/com/android/internal/os/BatteryStatsHelper.java b/com/android/internal/os/BatteryStatsHelper.java
index 5a59e70..1e5bd18 100644
--- a/com/android/internal/os/BatteryStatsHelper.java
+++ b/com/android/internal/os/BatteryStatsHelper.java
@@ -136,6 +136,7 @@
PowerCalculator mCameraPowerCalculator;
PowerCalculator mFlashlightPowerCalculator;
PowerCalculator mMemoryPowerCalculator;
+ PowerCalculator mMediaPowerCalculator;
boolean mHasWifiPowerReporting = false;
boolean mHasBluetoothPowerReporting = false;
@@ -424,6 +425,11 @@
}
mFlashlightPowerCalculator.reset();
+ if (mMediaPowerCalculator == null) {
+ mMediaPowerCalculator = new MediaPowerCalculator(mPowerProfile);
+ }
+ mMediaPowerCalculator.reset();
+
mStatsType = statsType;
mRawUptimeUs = rawUptimeUs;
mRawRealtimeUs = rawRealtimeUs;
@@ -560,6 +566,7 @@
mCameraPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,
mStatsType);
+ mMediaPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
final double totalPower = app.sumPower();
if (DEBUG && totalPower != 0) {
@@ -643,6 +650,21 @@
}
}
+ /**
+ * Ambient display power is the additional power the screen takes while in ambient display/
+ * screen doze/ always-on display (interchangeable terms) mode. Ambient display power should
+ * be hidden {@link #shouldHideSipper(BatterySipper)}, but should not be included in smearing
+ * {@link #removeHiddenBatterySippers(List)}.
+ */
+ private void addAmbientDisplayUsage() {
+ long ambientDisplayMs = mStats.getScreenDozeTime(mRawRealtimeUs, mStatsType);
+ double power = mPowerProfile.getAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY)
+ * ambientDisplayMs / (60 * 60 * 1000);
+ if (power > 0) {
+ addEntry(DrainType.AMBIENT_DISPLAY, ambientDisplayMs, power);
+ }
+ }
+
private void addRadioUsage() {
BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0);
mMobileRadioPowerCalculator.calculateRemaining(radio, mStats, mRawRealtimeUs, mRawUptimeUs,
@@ -741,6 +763,7 @@
addUserUsage();
addPhoneUsage();
addScreenUsage();
+ addAmbientDisplayUsage();
addWiFiUsage();
addBluetoothUsage();
addMemoryUsage();
@@ -841,12 +864,13 @@
final BatterySipper sipper = sippers.get(i);
sipper.shouldHide = shouldHideSipper(sipper);
if (sipper.shouldHide) {
- if (sipper.drainType != BatterySipper.DrainType.OVERCOUNTED
- && sipper.drainType != BatterySipper.DrainType.SCREEN
- && sipper.drainType != BatterySipper.DrainType.UNACCOUNTED
- && sipper.drainType != BatterySipper.DrainType.BLUETOOTH
- && sipper.drainType != BatterySipper.DrainType.WIFI
- && sipper.drainType != BatterySipper.DrainType.IDLE) {
+ if (sipper.drainType != DrainType.OVERCOUNTED
+ && sipper.drainType != DrainType.SCREEN
+ && sipper.drainType != DrainType.AMBIENT_DISPLAY
+ && sipper.drainType != DrainType.UNACCOUNTED
+ && sipper.drainType != DrainType.BLUETOOTH
+ && sipper.drainType != DrainType.WIFI
+ && sipper.drainType != DrainType.IDLE) {
// Don't add it if it is overcounted, unaccounted or screen
proportionalSmearPowerMah += sipper.totalPowerMah;
}
@@ -893,13 +917,14 @@
* Check whether we should hide the battery sipper.
*/
public boolean shouldHideSipper(BatterySipper sipper) {
- final BatterySipper.DrainType drainType = sipper.drainType;
+ final DrainType drainType = sipper.drainType;
- return drainType == BatterySipper.DrainType.IDLE
- || drainType == BatterySipper.DrainType.CELL
- || drainType == BatterySipper.DrainType.SCREEN
- || drainType == BatterySipper.DrainType.UNACCOUNTED
- || drainType == BatterySipper.DrainType.OVERCOUNTED
+ return drainType == DrainType.IDLE
+ || drainType == DrainType.CELL
+ || drainType == DrainType.SCREEN
+ || drainType == DrainType.AMBIENT_DISPLAY
+ || drainType == DrainType.UNACCOUNTED
+ || drainType == DrainType.OVERCOUNTED
|| isTypeService(sipper)
|| isTypeSystem(sipper);
}
diff --git a/com/android/internal/os/BatteryStatsImpl.java b/com/android/internal/os/BatteryStatsImpl.java
index 51f51c2..89f6156 100644
--- a/com/android/internal/os/BatteryStatsImpl.java
+++ b/com/android/internal/os/BatteryStatsImpl.java
@@ -33,17 +33,16 @@
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Build;
-import android.os.connectivity.CellularBatteryStats;
-import android.os.connectivity.WifiBatteryStats;
-import android.os.connectivity.GpsBatteryStats;
import android.os.FileUtils;
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;
@@ -51,6 +50,9 @@
import android.os.UserHandle;
import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
+import android.os.connectivity.CellularBatteryStats;
+import android.os.connectivity.GpsBatteryStats;
+import android.os.connectivity.WifiBatteryStats;
import android.provider.Settings;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.ModemActivityInfo;
@@ -88,8 +90,8 @@
import com.android.internal.util.JournaledFile;
import com.android.internal.util.XmlUtils;
-import java.util.List;
import libcore.util.EmptyArray;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -107,7 +109,10 @@
import java.util.Calendar;
import java.util.HashMap;
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;
@@ -132,7 +137,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 174 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 177 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS;
@@ -185,7 +190,7 @@
public final AtomicFile mCheckinFile;
public final AtomicFile mDailyFile;
- static final int MSG_UPDATE_WAKELOCKS = 1;
+ 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 long DELAY_UPDATE_WAKELOCKS = 5*1000;
@@ -228,12 +233,84 @@
@VisibleForTesting
protected final SparseIntArray mPendingUids = new SparseIntArray();
+ @GuardedBy("this")
+ private long mNumSingleUidCpuTimeReads;
+ @GuardedBy("this")
+ private long mNumBatchedSingleUidCpuTimeReads;
+ @GuardedBy("this")
+ private long mCpuTimeReadsTrackingStartTime = SystemClock.uptimeMillis();
+ @GuardedBy("this")
+ private int mNumUidsRemoved;
+ @GuardedBy("this")
+ private int mNumAllUidCpuTimeReads;
+
/** Container for Resource Power Manager stats. Updated by updateRpmStatsLocked. */
private final RpmStats mTmpRpmStats = new RpmStats();
/** 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;
+ /**
+ * Use a queue to delay removing UIDs from {@link KernelUidCpuTimeReader},
+ * {@link KernelUidCpuActiveTimeReader}, {@link KernelUidCpuClusterTimeReader},
+ * {@link KernelUidCpuFreqTimeReader} 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 {
+ int startUid;
+ int endUid;
+ long timeAddedInQueue;
+
+ /** 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) {
+ this.startUid = startUid;
+ this.endUid = endUid;
+ timeAddedInQueue = timestamp;
+ }
+
+ void remove() {
+ if (startUid == endUid) {
+ mKernelUidCpuTimeReader.removeUid(startUid);
+ mKernelUidCpuFreqTimeReader.removeUid(startUid);
+ if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
+ mKernelUidCpuActiveTimeReader.removeUid(startUid);
+ mKernelUidCpuClusterTimeReader.removeUid(startUid);
+ }
+ if (mKernelSingleUidTimeReader != null) {
+ mKernelSingleUidTimeReader.removeUid(startUid);
+ }
+ mNumUidsRemoved++;
+ } else if (startUid < endUid) {
+ mKernelUidCpuFreqTimeReader.removeUidsInRange(startUid, endUid);
+ mKernelUidCpuTimeReader.removeUidsInRange(startUid, endUid);
+ if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
+ mKernelUidCpuActiveTimeReader.removeUidsInRange(startUid, endUid);
+ mKernelUidCpuClusterTimeReader.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);
+ }
+ }
+ }
public interface BatteryCallback {
public void batteryNeedsCpuUpdate();
@@ -271,10 +348,7 @@
public void handleMessage(Message msg) {
BatteryCallback cb = mCallback;
switch (msg.what) {
- case MSG_UPDATE_WAKELOCKS:
- synchronized (BatteryStatsImpl.this) {
- updateCpuTimeLocked();
- }
+ case MSG_REPORT_CPU_UPDATE_NEEDED:
if (cb != null) {
cb.batteryNeedsCpuUpdate();
}
@@ -300,6 +374,10 @@
}
}
+ public void postBatteryNeedsCpuUpdateMsg() {
+ mHandler.sendEmptyMessage(MSG_REPORT_CPU_UPDATE_NEEDED);
+ }
+
/**
* Update per-freq cpu times for all the uids in {@link #mPendingUids}.
*/
@@ -363,6 +441,14 @@
}
}
+ public void clearPendingRemovedUids() {
+ long cutOffTime = mClocks.elapsedRealtime() - mConstants.UID_REMOVE_DELAY_MS;
+ while (!mPendingRemovedUids.isEmpty()
+ && mPendingRemovedUids.peek().timeAddedInQueue < cutOffTime) {
+ mPendingRemovedUids.poll().remove();
+ }
+ }
+
public void copyFromAllUidsCpuTimes() {
synchronized (BatteryStatsImpl.this) {
copyFromAllUidsCpuTimes(
@@ -482,9 +568,14 @@
Future<?> scheduleSync(String reason, int flags);
Future<?> scheduleCpuSyncDueToRemovedUid(int uid);
- Future<?> scheduleReadProcStateCpuTimes(boolean onBattery, boolean onBatteryScreenOff);
+ Future<?> scheduleReadProcStateCpuTimes(boolean onBattery, boolean onBatteryScreenOff,
+ long delayMillis);
Future<?> scheduleCopyFromAllUidsCpuTimes(boolean onBattery, boolean onBatteryScreenOff);
Future<?> scheduleCpuSyncDueToSettingChange();
+ Future<?> scheduleCpuSyncDueToScreenStateChange(boolean onBattery,
+ boolean onBatteryScreenOff);
+ Future<?> scheduleCpuSyncDueToWakelockChange(long delayMillis);
+ void cancelCpuSyncDueToWakelockChange();
}
public Handler mHandler;
@@ -675,8 +766,11 @@
int mCameraOnNesting;
StopwatchTimer mCameraOnTimer;
+ int mUsbDataState; // 0: unknown, 1: disconnected, 2: connected
+
int mGpsSignalQualityBin = -1;
- final StopwatchTimer[] mGpsSignalQualityTimer =
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ protected final StopwatchTimer[] mGpsSignalQualityTimer =
new StopwatchTimer[GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS];
int mPhoneSignalStrengthBin = -1;
@@ -758,6 +852,8 @@
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
protected StopwatchTimer mBluetoothScanTimer;
+ boolean mIsCellularTxPowerHigh = false;
+
int mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
long mMobileRadioActiveStartTime;
StopwatchTimer mMobileRadioActiveTimer;
@@ -1204,7 +1300,7 @@
* @param out the Parcel to be written to.
* @param counter a Counter, or null.
*/
- public static void writeCounterToParcel(Parcel out, Counter counter) {
+ public static void writeCounterToParcel(Parcel out, @Nullable Counter counter) {
if (counter == null) {
out.writeInt(0); // indicates null
return;
@@ -1214,6 +1310,19 @@
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.
+ */
+ public static @Nullable Counter readCounterFromParcel(TimeBase timeBase, Parcel in) {
+ if (in.readInt() == 0) {
+ return null;
+ }
+ return new Counter(timeBase, in);
+ }
+
@Override
public int getCountLocked(int which) {
int val = mCount.get();
@@ -1443,45 +1552,46 @@
}
}
+ @VisibleForTesting
public static class LongSamplingCounter extends LongCounter implements TimeBaseObs {
final TimeBase mTimeBase;
- long mCount;
- long mLoadedCount;
- long mUnpluggedCount;
- long mPluggedCount;
+ public long mCount;
+ public long mCurrentCount;
+ public long mLoadedCount;
+ public long mUnpluggedCount;
- LongSamplingCounter(TimeBase timeBase, Parcel in) {
+ public LongSamplingCounter(TimeBase timeBase, Parcel in) {
mTimeBase = timeBase;
- mPluggedCount = in.readLong();
- mCount = mPluggedCount;
+ mCount = in.readLong();
+ mCurrentCount = in.readLong();
mLoadedCount = in.readLong();
mUnpluggedCount = in.readLong();
timeBase.add(this);
}
- LongSamplingCounter(TimeBase timeBase) {
+ public LongSamplingCounter(TimeBase timeBase) {
mTimeBase = timeBase;
timeBase.add(this);
}
public void writeToParcel(Parcel out) {
out.writeLong(mCount);
+ out.writeLong(mCurrentCount);
out.writeLong(mLoadedCount);
out.writeLong(mUnpluggedCount);
}
@Override
public void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime) {
- mUnpluggedCount = mPluggedCount;
+ mUnpluggedCount = mCount;
}
@Override
public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
- mPluggedCount = mCount;
}
public long getCountLocked(int which) {
- long val = mTimeBase.isRunning() ? mCount : mPluggedCount;
+ long val = mCount;
if (which == STATS_SINCE_UNPLUGGED) {
val -= mUnpluggedCount;
} else if (which != STATS_SINCE_CHARGED) {
@@ -1493,40 +1603,54 @@
@Override
public void logState(Printer pw, String prefix) {
pw.println(prefix + "mCount=" + mCount
+ + " mCurrentCount=" + mCurrentCount
+ " mLoadedCount=" + mLoadedCount
- + " mUnpluggedCount=" + mUnpluggedCount
- + " mPluggedCount=" + mPluggedCount);
+ + " mUnpluggedCount=" + mUnpluggedCount);
}
- void addCountLocked(long count) {
- if (mTimeBase.isRunning()) {
- mCount += count;
+ public void addCountLocked(long count) {
+ update(mCurrentCount + count, mTimeBase.isRunning());
+ }
+
+ public void addCountLocked(long count, boolean isRunning) {
+ update(mCurrentCount + count, isRunning);
+ }
+
+ public void update(long count) {
+ update(count, mTimeBase.isRunning());
+ }
+
+ public void update(long count, boolean isRunning) {
+ if (count < mCurrentCount) {
+ mCurrentCount = 0;
}
+ if (isRunning) {
+ mCount += count - mCurrentCount;
+ }
+ mCurrentCount = count;
}
/**
* Clear state of this counter.
*/
- void reset(boolean detachIfReset) {
+ public void reset(boolean detachIfReset) {
mCount = 0;
- mLoadedCount = mPluggedCount = mUnpluggedCount = 0;
+ mLoadedCount = mUnpluggedCount = 0;
if (detachIfReset) {
detach();
}
}
- void detach() {
+ public void detach() {
mTimeBase.remove(this);
}
- void writeSummaryFromParcelLocked(Parcel out) {
+ public void writeSummaryFromParcelLocked(Parcel out) {
out.writeLong(mCount);
}
- void readSummaryFromParcelLocked(Parcel in) {
- mLoadedCount = in.readLong();
- mCount = mLoadedCount;
- mUnpluggedCount = mPluggedCount = mLoadedCount;
+ public void readSummaryFromParcelLocked(Parcel in) {
+ mCount = mUnpluggedCount= mLoadedCount = in.readLong();
}
}
@@ -2789,6 +2913,7 @@
implements Parcelable {
private final LongSamplingCounter mIdleTimeMillis;
private final LongSamplingCounter mScanTimeMillis;
+ private final LongSamplingCounter mSleepTimeMillis;
private final LongSamplingCounter mRxTimeMillis;
private final LongSamplingCounter[] mTxTimeMillis;
private final LongSamplingCounter mPowerDrainMaMs;
@@ -2796,6 +2921,7 @@
public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates) {
mIdleTimeMillis = new LongSamplingCounter(timeBase);
mScanTimeMillis = new LongSamplingCounter(timeBase);
+ mSleepTimeMillis = new LongSamplingCounter(timeBase);
mRxTimeMillis = new LongSamplingCounter(timeBase);
mTxTimeMillis = new LongSamplingCounter[numTxStates];
for (int i = 0; i < numTxStates; i++) {
@@ -2807,6 +2933,7 @@
public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates, Parcel in) {
mIdleTimeMillis = new LongSamplingCounter(timeBase, in);
mScanTimeMillis = new LongSamplingCounter(timeBase, in);
+ mSleepTimeMillis = new LongSamplingCounter(timeBase, in);
mRxTimeMillis = new LongSamplingCounter(timeBase, in);
final int recordedTxStates = in.readInt();
if (recordedTxStates != numTxStates) {
@@ -2823,6 +2950,7 @@
public void readSummaryFromParcel(Parcel in) {
mIdleTimeMillis.readSummaryFromParcelLocked(in);
mScanTimeMillis.readSummaryFromParcelLocked(in);
+ mSleepTimeMillis.readSummaryFromParcelLocked(in);
mRxTimeMillis.readSummaryFromParcelLocked(in);
final int recordedTxStates = in.readInt();
if (recordedTxStates != mTxTimeMillis.length) {
@@ -2842,6 +2970,7 @@
public void writeSummaryToParcel(Parcel dest) {
mIdleTimeMillis.writeSummaryFromParcelLocked(dest);
mScanTimeMillis.writeSummaryFromParcelLocked(dest);
+ mSleepTimeMillis.writeSummaryFromParcelLocked(dest);
mRxTimeMillis.writeSummaryFromParcelLocked(dest);
dest.writeInt(mTxTimeMillis.length);
for (LongSamplingCounter counter : mTxTimeMillis) {
@@ -2854,6 +2983,7 @@
public void writeToParcel(Parcel dest, int flags) {
mIdleTimeMillis.writeToParcel(dest);
mScanTimeMillis.writeToParcel(dest);
+ mSleepTimeMillis.writeToParcel(dest);
mRxTimeMillis.writeToParcel(dest);
dest.writeInt(mTxTimeMillis.length);
for (LongSamplingCounter counter : mTxTimeMillis) {
@@ -2865,6 +2995,7 @@
public void reset(boolean detachIfReset) {
mIdleTimeMillis.reset(detachIfReset);
mScanTimeMillis.reset(detachIfReset);
+ mSleepTimeMillis.reset(detachIfReset);
mRxTimeMillis.reset(detachIfReset);
for (LongSamplingCounter counter : mTxTimeMillis) {
counter.reset(detachIfReset);
@@ -2875,6 +3006,7 @@
public void detach() {
mIdleTimeMillis.detach();
mScanTimeMillis.detach();
+ mSleepTimeMillis.detach();
mRxTimeMillis.detach();
for (LongSamplingCounter counter : mTxTimeMillis) {
counter.detach();
@@ -2901,6 +3033,15 @@
}
/**
+ * @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.
*/
@@ -3520,7 +3661,7 @@
mHistoryLastWritten.cmd = HistoryItem.CMD_NULL;
}
- void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
+ void addHistoryBufferLocked(long elapsedRealtimeMs, HistoryItem cur) {
if (!mHaveBatteryLevel || !mRecordingHistory) {
return;
}
@@ -3601,8 +3742,8 @@
} else if (dataSize >= MAX_HISTORY_BUFFER) {
if (!mHistoryOverflow) {
mHistoryOverflow = true;
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW, cur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW, cur);
return;
}
@@ -3640,7 +3781,7 @@
return;
}
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
return;
}
@@ -3648,15 +3789,14 @@
// The history is currently empty; we need it to start with a time stamp.
cur.currentTime = System.currentTimeMillis();
if (recordResetDueToOverflow) {
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW, cur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW, cur);
}
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_RESET, cur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_RESET, cur);
}
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
}
- private void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd,
- HistoryItem cur) {
+ private void addHistoryBufferLocked(long elapsedRealtimeMs, byte cmd, HistoryItem cur) {
if (mIteratingHistory) {
throw new IllegalStateException("Can't do this while iterating history!");
}
@@ -3690,17 +3830,17 @@
mHistoryAddTmp.wakeReasonTag = null;
mHistoryAddTmp.eventCode = HistoryItem.EVENT_NONE;
mHistoryAddTmp.states &= ~HistoryItem.STATE_CPU_RUNNING_FLAG;
- addHistoryRecordInnerLocked(wakeElapsedTime, uptimeMs, mHistoryAddTmp);
+ addHistoryRecordInnerLocked(wakeElapsedTime, mHistoryAddTmp);
}
}
mHistoryCur.states |= HistoryItem.STATE_CPU_RUNNING_FLAG;
mTrackRunningHistoryElapsedRealtime = elapsedRealtimeMs;
mTrackRunningHistoryUptime = uptimeMs;
- addHistoryRecordInnerLocked(elapsedRealtimeMs, uptimeMs, mHistoryCur);
+ addHistoryRecordInnerLocked(elapsedRealtimeMs, mHistoryCur);
}
- void addHistoryRecordInnerLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, cur);
+ void addHistoryRecordInnerLocked(long elapsedRealtimeMs, HistoryItem cur) {
+ addHistoryBufferLocked(elapsedRealtimeMs, cur);
if (!USE_OLD_HISTORY) {
return;
@@ -3741,7 +3881,7 @@
if (mNumHistoryItems == MAX_HISTORY_ITEMS
|| mNumHistoryItems == MAX_MAX_HISTORY_ITEMS) {
- addHistoryRecordLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW, cur);
}
if (mNumHistoryItems >= MAX_HISTORY_ITEMS) {
@@ -3758,7 +3898,7 @@
}
}
- addHistoryRecordLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
}
public void addHistoryEventLocked(long elapsedRealtimeMs, long uptimeMs, int code,
@@ -3824,6 +3964,7 @@
mActiveHistoryStates2 = 0xffffffff;
}
+ @GuardedBy("this")
public void updateTimeBasesLocked(boolean unplugged, int screenState, long uptime,
long realtime) {
final boolean screenOff = !isScreenOn(screenState);
@@ -3847,9 +3988,6 @@
+ Display.stateToString(screenState)
+ " and battery is " + (unplugged ? "on" : "off"));
}
- updateCpuTimeLocked();
- mExternalSync.scheduleCopyFromAllUidsCpuTimes(mOnBatteryTimeBase.isRunning(),
- mOnBatteryScreenOffTimeBase.isRunning());
mOnBatteryTimeBase.setRunning(unplugged, uptime, realtime);
if (updateOnBatteryTimeBase) {
@@ -3878,7 +4016,8 @@
public void addIsolatedUidLocked(int isolatedUid, int appUid) {
mIsolatedUids.put(isolatedUid, appUid);
- StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, appUid, isolatedUid, 1);
+ StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, appUid, isolatedUid,
+ StatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
final Uid u = getUidStatsLocked(appUid);
u.addIsolatedUid(isolatedUid);
}
@@ -3900,9 +4039,11 @@
* This should only be called after the cpu times have been read.
* @see #scheduleRemoveIsolatedUidLocked(int, int)
*/
+ @GuardedBy("this")
public void removeIsolatedUidLocked(int isolatedUid) {
StatsLog.write(
- StatsLog.ISOLATED_UID_CHANGED, mIsolatedUids.get(isolatedUid, -1), isolatedUid, 0);
+ StatsLog.ISOLATED_UID_CHANGED, mIsolatedUids.get(isolatedUid, -1),
+ isolatedUid, StatsLog.ISOLATED_UID_CHANGED__EVENT__REMOVED);
final int idx = mIsolatedUids.indexOfKey(isolatedUid);
if (idx >= 0) {
final int ownerUid = mIsolatedUids.valueAt(idx);
@@ -3910,12 +4051,7 @@
u.removeIsolatedUid(isolatedUid);
mIsolatedUids.removeAt(idx);
}
- mKernelUidCpuTimeReader.removeUid(isolatedUid);
- mKernelUidCpuFreqTimeReader.removeUid(isolatedUid);
- if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
- mKernelUidCpuActiveTimeReader.removeUid(isolatedUid);
- mKernelUidCpuClusterTimeReader.removeUid(isolatedUid);
- }
+ mPendingRemovedUids.add(new UidToRemove(isolatedUid, mClocks.elapsedRealtime()));
}
public int mapUid(int uid) {
@@ -4053,6 +4189,11 @@
addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_JOB_FINISH, name, uid);
}
+ public void noteJobsDeferredLocked(int uid, int numDeferred, long sinceLast) {
+ uid = mapUid(uid);
+ getUidStatsLocked(uid).noteJobsDeferredLocked(numDeferred, sinceLast);
+ }
+
public void noteAlarmStartLocked(String name, WorkSource workSource, int uid) {
noteAlarmStartOrFinishLocked(HistoryItem.EVENT_ALARM_START, name, workSource, uid);
}
@@ -4135,15 +4276,11 @@
}
private void requestWakelockCpuUpdate() {
- if (!mHandler.hasMessages(MSG_UPDATE_WAKELOCKS)) {
- Message m = mHandler.obtainMessage(MSG_UPDATE_WAKELOCKS);
- mHandler.sendMessageDelayed(m, DELAY_UPDATE_WAKELOCKS);
- }
+ mExternalSync.scheduleCpuSyncDueToWakelockChange(DELAY_UPDATE_WAKELOCKS);
}
private void requestImmediateCpuUpdate() {
- mHandler.removeMessages(MSG_UPDATE_WAKELOCKS);
- mHandler.sendEmptyMessage(MSG_UPDATE_WAKELOCKS);
+ mExternalSync.scheduleCpuSyncDueToWakelockChange(0 /* delayMillis */);
}
public void setRecordAllHistoryLocked(boolean enabled) {
@@ -4252,11 +4389,13 @@
getUidStatsLocked(uid).noteStartWakeLocked(pid, name, type, elapsedRealtime);
if (wc != null) {
- StatsLog.write(
- StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(), type, name, 1);
+ StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(),
+ getPowerManagerWakeLockLevel(type), name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
} else {
- StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null, type, name,
- 1);
+ StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null,
+ getPowerManagerWakeLockLevel(type), name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
}
}
}
@@ -4295,15 +4434,47 @@
getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type, elapsedRealtime);
if (wc != null) {
- StatsLog.write(
- StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(), type, name, 0);
+ StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(),
+ getPowerManagerWakeLockLevel(type), name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
} else {
- StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null, type, name,
- 0);
+ StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null,
+ getPowerManagerWakeLockLevel(type), name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
}
}
}
+ /**
+ * 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, StatsLog.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;
+ }
+ }
+
public void noteStartWakeFromSourceLocked(WorkSource ws, int pid, String name,
String historyName, int type, boolean unimportantForLogging) {
final long elapsedRealtime = mClocks.elapsedRealtime();
@@ -4394,7 +4565,8 @@
public void noteLongPartialWakelockStart(String name, String historyName, int uid) {
StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- uid, null, name, historyName, 1);
+ uid, null, name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
uid = mapUid(uid);
noteLongPartialWakeLockStartInternal(name, historyName, uid);
@@ -4407,7 +4579,8 @@
final int uid = mapUid(workSource.get(i));
noteLongPartialWakeLockStartInternal(name, historyName, uid);
StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- workSource.get(i), workSource.getName(i), name, historyName, 1);
+ workSource.get(i), workSource.getName(i), name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
}
final ArrayList<WorkChain> workChains = workSource.getWorkChains();
@@ -4418,7 +4591,8 @@
noteLongPartialWakeLockStartInternal(name, historyName, uid);
StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), name, historyName, 1);
+ workChain.getUids(), workChain.getTags(), name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
}
}
}
@@ -4438,8 +4612,8 @@
}
public void noteLongPartialWakelockFinish(String name, String historyName, int uid) {
- StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- uid, null, name, historyName, 0);
+ StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, uid, null,
+ name, historyName, StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
uid = mapUid(uid);
noteLongPartialWakeLockFinishInternal(name, historyName, uid);
@@ -4452,7 +4626,8 @@
final int uid = mapUid(workSource.get(i));
noteLongPartialWakeLockFinishInternal(name, historyName, uid);
StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- workSource.get(i), workSource.getName(i), name, historyName, 0);
+ workSource.get(i), workSource.getName(i), name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
}
final ArrayList<WorkChain> workChains = workSource.getWorkChains();
@@ -4462,7 +4637,8 @@
final int uid = workChain.getAttributionUid();
noteLongPartialWakeLockFinishInternal(name, historyName, uid);
StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), name, historyName, 0);
+ workChain.getUids(), workChain.getTags(), name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -4486,7 +4662,8 @@
long deltaUptime = uptimeMs - mLastWakeupUptimeMs;
SamplingTimer timer = getWakeupReasonTimerLocked(mLastWakeupReason);
timer.add(deltaUptime * 1000, 1); // time in in microseconds
- StatsLog.write(StatsLog.KERNEL_WAKEUP_REPORTED, mLastWakeupReason, deltaUptime * 1000);
+ StatsLog.write(StatsLog.KERNEL_WAKEUP_REPORTED, mLastWakeupReason,
+ /* duration_usec */ deltaUptime * 1000);
mLastWakeupReason = null;
}
}
@@ -4506,7 +4683,7 @@
}
public boolean startAddingCpuLocked() {
- mHandler.removeMessages(MSG_UPDATE_WAKELOCKS);
+ mExternalSync.cancelCpuSyncDueToWakelockChange();
return mOnBatteryInternal;
}
@@ -4587,8 +4764,35 @@
int mGpsNesting;
- public void noteStartGpsLocked(int uid) {
- uid = mapUid(uid);
+ public void noteGpsChangedLocked(WorkSource oldWs, WorkSource newWs) {
+ for (int i = 0; i < newWs.size(); ++i) {
+ noteStartGpsLocked(newWs.get(i), null);
+ }
+
+ for (int i = 0; i < oldWs.size(); ++i) {
+ noteStopGpsLocked((oldWs.get(i)), null);
+ }
+
+ 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));
+ }
+ }
+
+ if (wcs[1] != null) {
+ final List<WorkChain> goneChains = wcs[1];
+ for (int i = 0; i < goneChains.size(); ++i) {
+ noteStopGpsLocked(-1, goneChains.get(i));
+ }
+ }
+ }
+ }
+
+ private void noteStartGpsLocked(int uid, WorkChain workChain) {
+ uid = getAttributionUid(uid, workChain);
final long elapsedRealtime = mClocks.elapsedRealtime();
final long uptime = mClocks.uptimeMillis();
if (mGpsNesting == 0) {
@@ -4598,11 +4802,21 @@
addHistoryRecordLocked(elapsedRealtime, uptime);
}
mGpsNesting++;
+
+ if (workChain == null) {
+ StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null,
+ StatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON);
+ } else {
+ StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED,
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON);
+ }
+
getUidStatsLocked(uid).noteStartGps(elapsedRealtime);
}
- public void noteStopGpsLocked(int uid) {
- uid = mapUid(uid);
+ private void noteStopGpsLocked(int uid, WorkChain workChain) {
+ uid = getAttributionUid(uid, workChain);
final long elapsedRealtime = mClocks.elapsedRealtime();
final long uptime = mClocks.uptimeMillis();
mGpsNesting--;
@@ -4614,6 +4828,15 @@
stopAllGpsSignalQualityTimersLocked(-1);
mGpsSignalQualityBin = -1;
}
+
+ if (workChain == null) {
+ StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null,
+ StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF);
+ } else {
+ StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, workChain.getUids(),
+ workChain.getTags(), StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF);
+ }
+
getUidStatsLocked(uid).noteStopGps(elapsedRealtime);
}
@@ -4642,6 +4865,7 @@
return;
}
+ @GuardedBy("this")
public void noteScreenStateLocked(int state) {
state = mPretendScreenOff ? Display.STATE_OFF : state;
@@ -4712,6 +4936,8 @@
+ Display.stateToString(state));
addHistoryRecordLocked(elapsedRealtime, uptime);
}
+ mExternalSync.scheduleCpuSyncDueToScreenStateChange(
+ mOnBatteryTimeBase.isRunning(), mOnBatteryScreenOffTimeBase.isRunning());
if (isScreenOn(state)) {
updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), state,
mClocks.uptimeMillis() * 1000, elapsedRealtime * 1000);
@@ -4866,7 +5092,9 @@
mPowerSaveModeEnabledTimer.stopRunningLocked(elapsedRealtime);
}
addHistoryRecordLocked(elapsedRealtime, uptime);
- StatsLog.write(StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED, enabled ? 1 : 0);
+ StatsLog.write(StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED, enabled ?
+ StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON :
+ StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
}
}
@@ -5013,6 +5241,19 @@
}
}
+ public void noteUsbConnectionStateLocked(boolean connected) {
+ int newState = connected ? 2 : 1;
+ 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(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ }
+ }
+
void stopAllPhoneSignalStrengthTimersLocked(int except) {
final long elapsedRealtime = mClocks.elapsedRealtime();
for (int i = 0; i < SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
@@ -5141,57 +5382,15 @@
}
public void notePhoneDataConnectionStateLocked(int dataType, boolean hasData) {
+ // 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_NONE;
if (hasData) {
- switch (dataType) {
- case TelephonyManager.NETWORK_TYPE_EDGE:
- bin = DATA_CONNECTION_EDGE;
- break;
- case TelephonyManager.NETWORK_TYPE_GPRS:
- bin = DATA_CONNECTION_GPRS;
- break;
- case TelephonyManager.NETWORK_TYPE_UMTS:
- bin = DATA_CONNECTION_UMTS;
- break;
- case TelephonyManager.NETWORK_TYPE_CDMA:
- bin = DATA_CONNECTION_CDMA;
- break;
- case TelephonyManager.NETWORK_TYPE_EVDO_0:
- bin = DATA_CONNECTION_EVDO_0;
- break;
- case TelephonyManager.NETWORK_TYPE_EVDO_A:
- bin = DATA_CONNECTION_EVDO_A;
- break;
- case TelephonyManager.NETWORK_TYPE_1xRTT:
- bin = DATA_CONNECTION_1xRTT;
- break;
- case TelephonyManager.NETWORK_TYPE_HSDPA:
- bin = DATA_CONNECTION_HSDPA;
- break;
- case TelephonyManager.NETWORK_TYPE_HSUPA:
- bin = DATA_CONNECTION_HSUPA;
- break;
- case TelephonyManager.NETWORK_TYPE_HSPA:
- bin = DATA_CONNECTION_HSPA;
- break;
- case TelephonyManager.NETWORK_TYPE_IDEN:
- bin = DATA_CONNECTION_IDEN;
- break;
- case TelephonyManager.NETWORK_TYPE_EVDO_B:
- bin = DATA_CONNECTION_EVDO_B;
- break;
- case TelephonyManager.NETWORK_TYPE_LTE:
- bin = DATA_CONNECTION_LTE;
- break;
- case TelephonyManager.NETWORK_TYPE_EHRPD:
- bin = DATA_CONNECTION_EHRPD;
- break;
- case TelephonyManager.NETWORK_TYPE_HSPAP:
- bin = DATA_CONNECTION_HSPAP;
- break;
- default:
- bin = DATA_CONNECTION_OTHER;
- break;
+ if (dataType > 0 && dataType <= TelephonyManager.MAX_NETWORK_TYPE) {
+ bin = dataType;
+ } else {
+ bin = DATA_CONNECTION_OTHER;
}
}
if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData);
@@ -5467,26 +5666,7 @@
mBluetoothScanTimer.startRunningLocked(elapsedRealtime);
}
mBluetoothScanNesting++;
-
- if (workChain != null) {
- StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 1);
- if (isUnoptimized) {
- StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 1);
- }
- } else {
- StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null, 1);
- if (isUnoptimized) {
- StatsLog.write_non_chained(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, null,
- 1);
- }
- }
-
getUidStatsLocked(uid).noteBluetoothScanStartedLocked(elapsedRealtime, isUnoptimized);
- if (workChain != null) {
- getUidStatsLocked(uid).addBluetoothWorkChain(workChain, isUnoptimized);
- }
}
public void noteBluetoothScanStartedFromSourceLocked(WorkSource ws, boolean isUnoptimized) {
@@ -5516,26 +5696,7 @@
addHistoryRecordLocked(elapsedRealtime, uptime);
mBluetoothScanTimer.stopRunningLocked(elapsedRealtime);
}
-
- if (workChain != null) {
- StatsLog.write(
- StatsLog.BLE_SCAN_STATE_CHANGED, workChain.getUids(), workChain.getTags(), 0);
- if (isUnoptimized) {
- StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 0);
- }
- } else {
- StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null, 0);
- if (isUnoptimized) {
- StatsLog.write_non_chained(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, null,
- 0);
- }
- }
-
getUidStatsLocked(uid).noteBluetoothScanStoppedLocked(elapsedRealtime, isUnoptimized);
- if (workChain != null) {
- getUidStatsLocked(uid).removeBluetoothWorkChain(workChain, isUnoptimized);
- }
}
private int getAttributionUid(int uid, WorkChain workChain) {
@@ -5570,31 +5731,9 @@
+ Integer.toHexString(mHistoryCur.states2));
addHistoryRecordLocked(elapsedRealtime, uptime);
mBluetoothScanTimer.stopAllRunningLocked(elapsedRealtime);
-
-
for (int i=0; i<mUidStats.size(); i++) {
BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
uid.noteResetBluetoothScanLocked(elapsedRealtime);
-
- List<WorkChain> allWorkChains = uid.getAllBluetoothWorkChains();
- if (allWorkChains != null) {
- for (int j = 0; j < allWorkChains.size(); ++j) {
- StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED,
- allWorkChains.get(j).getUids(),
- allWorkChains.get(j).getTags(), 0);
- }
- allWorkChains.clear();
- }
-
- List<WorkChain> unoptimizedWorkChains = uid.getUnoptimizedBluetoothWorkChains();
- if (unoptimizedWorkChains != null) {
- for (int j = 0; j < unoptimizedWorkChains.size(); ++j) {
- StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
- unoptimizedWorkChains.get(j).getUids(),
- unoptimizedWorkChains.get(j).getTags(), 0);
- }
- unoptimizedWorkChains.clear();
- }
}
}
}
@@ -5806,7 +5945,6 @@
if (strengthBin >= 0) {
if (!mWifiSignalStrengthsTimer[strengthBin].isRunningLocked()) {
mWifiSignalStrengthsTimer[strengthBin].startRunningLocked(elapsedRealtime);
- StatsLog.write(StatsLog.WIFI_SIGNAL_STRENGTH_CHANGED, strengthBin);
}
mHistoryCur.states2 =
(mHistoryCur.states2&~HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK)
@@ -5817,6 +5955,7 @@
} else {
stopAllWifiSignalStrengthTimersLocked(-1);
}
+ StatsLog.write(StatsLog.WIFI_SIGNAL_STRENGTH_CHANGED, strengthBin);
mWifiSignalStrengthBin = strengthBin;
}
}
@@ -5936,7 +6075,8 @@
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteFullWifiLockAcquiredLocked(uid);
- StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), 1);
+ StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i),
+ StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__ON);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -5946,7 +6086,8 @@
final int uid = mapUid(workChain.getAttributionUid());
noteFullWifiLockAcquiredLocked(uid);
StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 1);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__ON);
}
}
}
@@ -5956,7 +6097,8 @@
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteFullWifiLockReleasedLocked(uid);
- StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), 0);
+ StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i),
+ StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__OFF);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -5966,7 +6108,8 @@
final int uid = mapUid(workChain.getAttributionUid());
noteFullWifiLockReleasedLocked(uid);
StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 0);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -5977,7 +6120,7 @@
final int uid = mapUid(ws.get(i));
noteWifiScanStartedLocked(uid);
StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i),
- 1);
+ StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__ON);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -5987,7 +6130,7 @@
final int uid = mapUid(workChain.getAttributionUid());
noteWifiScanStartedLocked(uid);
StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED, workChain.getUids(),
- workChain.getTags(), 1);
+ workChain.getTags(), StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__ON);
}
}
}
@@ -5998,7 +6141,7 @@
final int uid = mapUid(ws.get(i));
noteWifiScanStoppedLocked(uid);
StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i),
- 0);
+ StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__OFF);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -6008,7 +6151,8 @@
final int uid = mapUid(workChain.getAttributionUid());
noteWifiScanStoppedLocked(uid);
StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 0);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -6619,6 +6763,29 @@
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<>();
@@ -6638,15 +6805,6 @@
*/
final SparseArray<Pid> mPids = new SparseArray<>();
- /**
- * The list of WorkChains associated with active bluetooth scans.
- *
- * NOTE: This is a hack and it only needs to exist because there's a "reset" API that is
- * supposed to stop and log all WorkChains that were currently active.
- */
- ArrayList<WorkChain> mAllBluetoothChains = null;
- ArrayList<WorkChain> mUnoptimizedBluetoothChains = null;
-
public Uid(BatteryStatsImpl bsi, int uid) {
mBsi = bsi;
mUid = uid;
@@ -6692,6 +6850,10 @@
mWifiMulticastTimer = new StopwatchTimer(mBsi.mClocks, 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];
}
@VisibleForTesting
@@ -6960,7 +7122,8 @@
}
mWifiMulticastTimer.startRunningLocked(elapsedRealtimeMs);
StatsLog.write_non_chained(
- StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null, 1);
+ StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null,
+ StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__ON);
}
}
@@ -6970,7 +7133,8 @@
mWifiMulticastEnabled = false;
mWifiMulticastTimer.stopRunningLocked(elapsedRealtimeMs);
StatsLog.write_non_chained(
- StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null,
+ StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__OFF);
}
}
@@ -7023,22 +7187,17 @@
public void noteAudioTurnedOnLocked(long elapsedRealtimeMs) {
createAudioTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 1);
}
public void noteAudioTurnedOffLocked(long elapsedRealtimeMs) {
if (mAudioTurnedOnTimer != null) {
mAudioTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
- if (!mAudioTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
- StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 0);
- }
}
}
public void noteResetAudioLocked(long elapsedRealtimeMs) {
if (mAudioTurnedOnTimer != null) {
mAudioTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 0);
}
}
@@ -7052,24 +7211,17 @@
public void noteVideoTurnedOnLocked(long elapsedRealtimeMs) {
createVideoTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null, 1);
}
public void noteVideoTurnedOffLocked(long elapsedRealtimeMs) {
if (mVideoTurnedOnTimer != null) {
mVideoTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
- if (!mVideoTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
- StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(),
- null, 0);
- }
}
}
public void noteResetVideoLocked(long elapsedRealtimeMs) {
if (mVideoTurnedOnTimer != null) {
mVideoTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null,
- 0);
}
}
@@ -7083,23 +7235,17 @@
public void noteFlashlightTurnedOnLocked(long elapsedRealtimeMs) {
createFlashlightTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,1);
}
public void noteFlashlightTurnedOffLocked(long elapsedRealtimeMs) {
if (mFlashlightTurnedOnTimer != null) {
mFlashlightTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
- if (!mFlashlightTurnedOnTimer.isRunningLocked()) {
- StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
- 0);
- }
}
}
public void noteResetFlashlightLocked(long elapsedRealtimeMs) {
if (mFlashlightTurnedOnTimer != null) {
mFlashlightTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null, 0);
}
}
@@ -7113,22 +7259,17 @@
public void noteCameraTurnedOnLocked(long elapsedRealtimeMs) {
createCameraTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 1);
}
public void noteCameraTurnedOffLocked(long elapsedRealtimeMs) {
if (mCameraTurnedOnTimer != null) {
mCameraTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
- if (!mCameraTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
- StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 0);
- }
}
}
public void noteResetCameraLocked(long elapsedRealtimeMs) {
if (mCameraTurnedOnTimer != null) {
mCameraTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 0);
}
}
@@ -7192,40 +7333,6 @@
}
}
- public void addBluetoothWorkChain(WorkChain workChain, boolean isUnoptimized) {
- if (mAllBluetoothChains == null) {
- mAllBluetoothChains = new ArrayList<WorkChain>(4);
- }
-
- if (isUnoptimized && mUnoptimizedBluetoothChains == null) {
- mUnoptimizedBluetoothChains = new ArrayList<WorkChain>(4);
- }
-
- mAllBluetoothChains.add(workChain);
- if (isUnoptimized) {
- mUnoptimizedBluetoothChains.add(workChain);
- }
- }
-
- public void removeBluetoothWorkChain(WorkChain workChain, boolean isUnoptimized) {
- if (mAllBluetoothChains != null) {
- mAllBluetoothChains.remove(workChain);
- }
-
- if (isUnoptimized && mUnoptimizedBluetoothChains != null) {
- mUnoptimizedBluetoothChains.remove(workChain);
- }
- }
-
- public List<WorkChain> getAllBluetoothWorkChains() {
- return mAllBluetoothChains;
- }
-
- public List<WorkChain> getUnoptimizedBluetoothWorkChains() {
- return mUnoptimizedBluetoothChains;
- }
-
-
public void noteResetBluetoothScanLocked(long elapsedRealtimeMs) {
if (mBluetoothScanTimer != null) {
mBluetoothScanTimer.stopAllRunningLocked(elapsedRealtimeMs);
@@ -7658,6 +7765,51 @@
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 initNetworkActivityLocked() {
mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
mNetworkPacketActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
@@ -7841,6 +7993,16 @@
}
mJobStats.cleanup();
mJobCompletions.clear();
+
+ mJobsDeferredEventCount.reset(false);
+ mJobsDeferredCount.reset(false);
+ mJobsFreshnessTimeMs.reset(false);
+ for (int ij = 0; ij < JOB_FRESHNESS_BUCKETS.length; ij++) {
+ if (mJobsFreshnessBuckets[ij] != null) {
+ mJobsFreshnessBuckets[ij].reset(false);
+ }
+ }
+
for (int ise=mSensorStats.size()-1; ise>=0; ise--) {
Sensor s = mSensorStats.valueAt(ise);
if (s.reset()) {
@@ -7849,6 +8011,7 @@
active = true;
}
}
+
for (int ip=mProcessStats.size()-1; ip>=0; ip--) {
Proc proc = mProcessStats.valueAt(ip);
proc.detach();
@@ -8066,6 +8229,13 @@
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++) {
@@ -8361,6 +8531,14 @@
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++) {
@@ -8629,6 +8807,26 @@
}
}
+ 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;
+ }
+ }
+ }
+ }
+
/**
* The statistics associated with a particular wake lock.
*/
@@ -9075,8 +9273,14 @@
}
public void addCpuTimeLocked(int utime, int stime) {
- mUserTime += utime;
- mSystemTime += stime;
+ addCpuTimeLocked(utime, stime, mBsi.mOnBatteryTimeBase.isRunning());
+ }
+
+ public void addCpuTimeLocked(int utime, int stime, boolean isRunning) {
+ if (isRunning) {
+ mUserTime += utime;
+ mSystemTime += stime;
+ }
}
public void addForegroundTimeLocked(long ttime) {
@@ -9532,6 +9736,7 @@
return ps;
}
+ @GuardedBy("mBsi")
public void updateUidProcessStateLocked(int procState) {
int uidRunningState;
// Make special note of Foreground Services
@@ -9554,7 +9759,11 @@
if (mBsi.mPendingUids.size() == 0) {
mBsi.mExternalSync.scheduleReadProcStateCpuTimes(
mBsi.mOnBatteryTimeBase.isRunning(),
- mBsi.mOnBatteryScreenOffTimeBase.isRunning());
+ mBsi.mOnBatteryScreenOffTimeBase.isRunning(),
+ mBsi.mConstants.PROC_STATE_CPU_TIMES_READ_DELAY_MS);
+ mBsi.mNumSingleUidCpuTimeReads++;
+ } else {
+ mBsi.mNumBatchedSingleUidCpuTimeReads++;
}
if (mBsi.mPendingUids.indexOfKey(mUid) < 0
|| ArrayUtils.contains(CRITICAL_PROC_STATES, mProcessState)) {
@@ -9702,7 +9911,6 @@
DualTimer t = mSyncStats.startObject(name);
if (t != null) {
t.startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name, 1);
}
}
@@ -9710,9 +9918,6 @@
DualTimer t = mSyncStats.stopObject(name);
if (t != null) {
t.stopRunningLocked(elapsedRealtimeMs);
- if (!t.isRunningLocked()) { // only tell statsd if truly stopped
- StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name, 0);
- }
}
}
@@ -9720,8 +9925,6 @@
DualTimer t = mJobStats.startObject(name);
if (t != null) {
t.startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
- name, 1);
}
}
@@ -9729,10 +9932,6 @@
DualTimer t = mJobStats.stopObject(name);
if (t != null) {
t.stopRunningLocked(elapsedRealtimeMs);
- if (!t.isRunningLocked()) { // only tell statsd if truly stopped
- StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
- name, 0);
- }
}
if (mBsi.mOnBatteryTimeBase.isRunning()) {
SparseIntArray types = mJobCompletions.get(name);
@@ -9813,8 +10012,6 @@
if (wl != null) {
StopwatchTimer wlt = getWakelockTimerLocked(wl, type);
wlt.stopRunningLocked(elapsedRealtimeMs);
- if (!wlt.isRunningLocked()) { // only tell statsd if truly stopped
- }
}
if (type == WAKE_TYPE_PARTIAL) {
if (mAggregatedPartialWakelockTimer != null) {
@@ -9842,12 +10039,6 @@
public void noteStartSensor(int sensor, long elapsedRealtimeMs) {
DualTimer t = getSensorTimerLocked(sensor, /* create= */ true);
t.startRunningLocked(elapsedRealtimeMs);
- if (sensor == Sensor.GPS) {
- StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, getUid(), null, 1);
- } else {
- StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null, sensor,
- 1);
- }
}
public void noteStopSensor(int sensor, long elapsedRealtimeMs) {
@@ -9855,15 +10046,6 @@
DualTimer t = getSensorTimerLocked(sensor, false);
if (t != null) {
t.stopRunningLocked(elapsedRealtimeMs);
- if (!t.isRunningLocked()) { // only tell statsd if truly stopped
- if (sensor == Sensor.GPS) {
- StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, getUid(), null,
- 0);
- } else {
- StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null,
- sensor, 0);
- }
- }
}
}
@@ -10101,6 +10283,7 @@
updateDailyDeadlineLocked();
if (hasData) {
+ final long startTime = SystemClock.uptimeMillis();
mDailyItems.add(item);
while (mDailyItems.size() > MAX_DAILY_ITEMS) {
mDailyItems.remove(0);
@@ -10110,10 +10293,12 @@
XmlSerializer out = new FastXmlSerializer();
out.setOutput(memStream, StandardCharsets.UTF_8.name());
writeDailyItemsLocked(out);
+ final long initialTime = SystemClock.uptimeMillis() - startTime;
BackgroundThread.getHandler().post(new Runnable() {
@Override
public void run() {
synchronized (mCheckinFile) {
+ final long startTime2 = SystemClock.uptimeMillis();
FileOutputStream stream = null;
try {
stream = mDailyFile.startWrite();
@@ -10122,6 +10307,9 @@
FileUtils.sync(stream);
stream.close();
mDailyFile.finishWrite(stream);
+ com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
+ "batterystats-daily",
+ initialTime + SystemClock.uptimeMillis() - startTime2);
} catch (IOException e) {
Slog.w("BatteryStats",
"Error writing battery daily items", e);
@@ -10717,6 +10905,9 @@
mLastStepStatSoftIrqTime = mCurStepStatSoftIrqTime = 0;
mLastStepStatIdleTime = mCurStepStatIdleTime = 0;
+ mNumAllUidCpuTimeReads = 0;
+ mNumUidsRemoved = 0;
+
initDischarge();
clearHistoryLocked();
@@ -10822,7 +11013,7 @@
return null;
}
- /**
+ /**
* Distribute WiFi energy info and network traffic to apps.
* @param info The energy information from the WiFi controller.
*/
@@ -11086,6 +11277,28 @@
}
}
+ private ModemActivityInfo mLastModemActivityInfo =
+ new ModemActivityInfo(0, 0, 0, new int[0], 0, 0);
+
+ private ModemActivityInfo getDeltaModemActivityInfo(ModemActivityInfo activityInfo) {
+ if (activityInfo == null) {
+ return null;
+ }
+ int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
+ for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) {
+ txTimeMs[i] = activityInfo.getTxTimeMillis()[i]
+ - mLastModemActivityInfo.getTxTimeMillis()[i];
+ }
+ ModemActivityInfo deltaInfo = new ModemActivityInfo(activityInfo.getTimestamp(),
+ activityInfo.getSleepTimeMillis() - mLastModemActivityInfo.getSleepTimeMillis(),
+ activityInfo.getIdleTimeMillis() - mLastModemActivityInfo.getIdleTimeMillis(),
+ txTimeMs,
+ activityInfo.getRxTimeMillis() - mLastModemActivityInfo.getRxTimeMillis(),
+ activityInfo.getEnergyUsed() - mLastModemActivityInfo.getEnergyUsed());
+ mLastModemActivityInfo = activityInfo;
+ return deltaInfo;
+ }
+
/**
* Distribute Cell radio energy info and network traffic to apps.
*/
@@ -11093,6 +11306,10 @@
if (DEBUG_ENERGY) {
Slog.d(TAG, "Updating mobile radio stats with " + activityInfo);
}
+ ModemActivityInfo deltaInfo = getDeltaModemActivityInfo(activityInfo);
+
+ // Add modem tx power to history.
+ addModemTxPowerToHistory(deltaInfo);
// Grab a separate lock to acquire the network stats, which may do I/O.
NetworkStats delta = null;
@@ -11114,14 +11331,16 @@
return;
}
- if (activityInfo != null) {
+ if (deltaInfo != null) {
mHasModemReporting = true;
mModemActivity.getIdleTimeCounter().addCountLocked(
- activityInfo.getIdleTimeMillis());
- mModemActivity.getRxTimeCounter().addCountLocked(activityInfo.getRxTimeMillis());
+ deltaInfo.getIdleTimeMillis());
+ mModemActivity.getSleepTimeCounter().addCountLocked(
+ deltaInfo.getSleepTimeMillis());
+ mModemActivity.getRxTimeCounter().addCountLocked(deltaInfo.getRxTimeMillis());
for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
mModemActivity.getTxTimeCounters()[lvl]
- .addCountLocked(activityInfo.getTxTimeMillis()[lvl]);
+ .addCountLocked(deltaInfo.getTxTimeMillis()[lvl]);
}
// POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
@@ -11129,17 +11348,17 @@
PowerProfile.POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
if (opVolt != 0) {
double energyUsed =
- activityInfo.getSleepTimeMillis() *
+ deltaInfo.getSleepTimeMillis() *
mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_SLEEP)
- + activityInfo.getIdleTimeMillis() *
+ + deltaInfo.getIdleTimeMillis() *
mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE)
- + activityInfo.getRxTimeMillis() *
+ + deltaInfo.getRxTimeMillis() *
mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX);
- int[] txCurrentMa = activityInfo.getTxTimeMillis();
- for (int i = 0; i < Math.min(txCurrentMa.length,
- SignalStrength.NUM_SIGNAL_STRENGTH_BINS); i++) {
- energyUsed += txCurrentMa[i] * mPowerProfile.getAveragePower(
- PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
+ int[] txTimeMs = deltaInfo.getTxTimeMillis();
+ for (int i = 0; i < Math.min(txTimeMs.length,
+ SignalStrength.NUM_SIGNAL_STRENGTH_BINS); i++) {
+ energyUsed += txTimeMs[i] * mPowerProfile.getAveragePower(
+ PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
}
// We store the power drain as mAms.
@@ -11215,11 +11434,11 @@
radioTime -= appRadioTime;
totalPackets -= appPackets;
- if (activityInfo != null) {
+ if (deltaInfo != null) {
ControllerActivityCounterImpl activityCounter =
u.getOrCreateModemControllerActivityLocked();
if (totalRxPackets > 0 && entry.rxPackets > 0) {
- final long rxMs = (entry.rxPackets * activityInfo.getRxTimeMillis())
+ final long rxMs = (entry.rxPackets * deltaInfo.getRxTimeMillis())
/ totalRxPackets;
activityCounter.getRxTimeCounter().addCountLocked(rxMs);
}
@@ -11227,7 +11446,7 @@
if (totalTxPackets > 0 && entry.txPackets > 0) {
for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
long txMs =
- entry.txPackets * activityInfo.getTxTimeMillis()[lvl];
+ entry.txPackets * deltaInfo.getTxTimeMillis()[lvl];
txMs /= totalTxPackets;
activityCounter.getTxTimeCounters()[lvl].addCountLocked(txMs);
}
@@ -11249,7 +11468,72 @@
}
/**
+ * 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) {
+ if (activityInfo == null) {
+ return;
+ }
+ int[] txTimeMs = activityInfo.getTxTimeMillis();
+ if (txTimeMs == null || txTimeMs.length != ModemActivityInfo.TX_POWER_LEVELS) {
+ return;
+ }
+ final long elapsedRealtime = mClocks.elapsedRealtime();
+ final long uptime = mClocks.uptimeMillis();
+ int levelMaxTimeSpent = 0;
+ for (int i = 1; i < txTimeMs.length; i++) {
+ if (txTimeMs[i] > txTimeMs[levelMaxTimeSpent]) {
+ levelMaxTimeSpent = i;
+ }
+ }
+ if (levelMaxTimeSpent == ModemActivityInfo.TX_POWER_LEVELS - 1) {
+ if (!mIsCellularTxPowerHigh) {
+ mHistoryCur.states2 |= HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG;
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ mIsCellularTxPowerHigh = true;
+ }
+ return;
+ }
+ if (mIsCellularTxPowerHigh) {
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG;
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ mIsCellularTxPowerHigh = false;
+ }
+ return;
+ }
+
+ 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() != null) {
+ for (UidTraffic traffic : info.getUidTraffic()) {
+ uidRxBytes.put(traffic.getUid(), traffic.getRxBytes());
+ uidTxBytes.put(traffic.getUid(), traffic.getTxBytes());
+ }
+ }
+ }
+ }
+
+ private final BluetoothActivityInfoCache mLastBluetoothActivityInfo
+ = new BluetoothActivityInfoCache();
+
+ /**
* Distribute Bluetooth energy info and network traffic to apps.
+ *
* @param info The energy information from the bluetooth controller.
*/
public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info) {
@@ -11264,14 +11548,18 @@
mHasBluetoothReporting = true;
final long elapsedRealtimeMs = mClocks.elapsedRealtime();
- final long rxTimeMs = info.getControllerRxTimeMillis();
- final long txTimeMs = info.getControllerTxTimeMillis();
+ 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: " + info.getControllerIdleTimeMillis() + " ms");
+ Slog.d(TAG, " Idle Time: " + idleTimeMs + " ms");
}
long totalScanTimeMs = 0;
@@ -11338,8 +11626,8 @@
}
if (DEBUG_ENERGY) {
- Slog.d(TAG, "Left over time for traffic RX=" + leftOverRxTimeMs
- + " TX=" + leftOverTxTimeMs);
+ Slog.d(TAG, "Left over time for traffic RX=" + leftOverRxTimeMs + " TX="
+ + leftOverTxTimeMs);
}
//
@@ -11353,63 +11641,62 @@
final int numUids = uidTraffic != null ? uidTraffic.length : 0;
for (int i = 0; i < numUids; i++) {
final UidTraffic traffic = uidTraffic[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(
- traffic.getRxBytes());
- mNetworkByteActivityCounters[NETWORK_BT_TX_DATA].addCountLocked(
- traffic.getTxBytes());
+ 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()));
- u.noteNetworkActivityLocked(NETWORK_BT_RX_DATA, traffic.getRxBytes(), 0);
- u.noteNetworkActivityLocked(NETWORK_BT_TX_DATA, traffic.getTxBytes(), 0);
+ u.noteNetworkActivityLocked(NETWORK_BT_RX_DATA, rxBytes, 0);
+ u.noteNetworkActivityLocked(NETWORK_BT_TX_DATA, txBytes, 0);
// Calculate the total traffic.
- totalTxBytes += traffic.getTxBytes();
- totalRxBytes += traffic.getRxBytes();
+ totalRxBytes += rxBytes;
+ totalTxBytes += txBytes;
}
- if ((totalTxBytes != 0 || totalRxBytes != 0) &&
- (leftOverRxTimeMs != 0 || leftOverTxTimeMs != 0)) {
+ if ((totalTxBytes != 0 || totalRxBytes != 0) && (leftOverRxTimeMs != 0
+ || leftOverTxTimeMs != 0)) {
for (int i = 0; i < numUids; i++) {
final UidTraffic traffic = uidTraffic[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(traffic.getUid()));
+ final Uid u = getUidStatsLocked(mapUid(uid));
final ControllerActivityCounterImpl counter =
u.getOrCreateBluetoothControllerActivityLocked();
- if (totalRxBytes > 0 && traffic.getRxBytes() > 0) {
- final long timeRxMs = (leftOverRxTimeMs * traffic.getRxBytes()) / totalRxBytes;
-
+ if (totalRxBytes > 0 && rxBytes > 0) {
+ final long timeRxMs = (leftOverRxTimeMs * rxBytes) / totalRxBytes;
if (DEBUG_ENERGY) {
- Slog.d(TAG, "UID=" + traffic.getUid() + " rx_bytes=" + traffic.getRxBytes()
- + " rx_time=" + timeRxMs);
+ Slog.d(TAG, "UID=" + uid + " rx_bytes=" + rxBytes + " rx_time=" + timeRxMs);
}
counter.getRxTimeCounter().addCountLocked(timeRxMs);
leftOverRxTimeMs -= timeRxMs;
}
- if (totalTxBytes > 0 && traffic.getTxBytes() > 0) {
- final long timeTxMs = (leftOverTxTimeMs * traffic.getTxBytes()) / totalTxBytes;
-
+ if (totalTxBytes > 0 && txBytes > 0) {
+ final long timeTxMs = (leftOverTxTimeMs * txBytes) / totalTxBytes;
if (DEBUG_ENERGY) {
- Slog.d(TAG, "UID=" + traffic.getUid() + " tx_bytes=" + traffic.getTxBytes()
- + " tx_time=" + timeTxMs);
+ Slog.d(TAG, "UID=" + uid + " tx_bytes=" + txBytes + " tx_time=" + timeTxMs);
}
-
counter.getTxTimeCounters()[0].addCountLocked(timeTxMs);
leftOverTxTimeMs -= timeTxMs;
}
}
}
- mBluetoothActivity.getRxTimeCounter().addCountLocked(
- info.getControllerRxTimeMillis());
- mBluetoothActivity.getTxTimeCounters()[0].addCountLocked(
- info.getControllerTxTimeMillis());
- mBluetoothActivity.getIdleTimeCounter().addCountLocked(
- info.getControllerIdleTimeMillis());
+ mBluetoothActivity.getRxTimeCounter().addCountLocked(rxTimeMs);
+ mBluetoothActivity.getTxTimeCounters()[0].addCountLocked(txTimeMs);
+ mBluetoothActivity.getIdleTimeCounter().addCountLocked(idleTimeMs);
// POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
final double opVolt = mPowerProfile.getAveragePower(
@@ -11417,8 +11704,10 @@
if (opVolt != 0) {
// We store the power drain as mAms.
mBluetoothActivity.getPowerCounter().addCountLocked(
- (long) (info.getControllerEnergyUsed() / opVolt));
+ (long) ((info.getControllerEnergyUsed() - mLastBluetoothActivityInfo.energy)
+ / opVolt));
}
+ mLastBluetoothActivityInfo.set(info);
}
/**
@@ -11559,12 +11848,24 @@
}
}
+ public boolean isOnBatteryLocked() {
+ return mOnBatteryTimeBase.isRunning();
+ }
+
+ public boolean isOnBatteryScreenOffLocked() {
+ return mOnBatteryScreenOffTimeBase.isRunning();
+ }
+
/**
* 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.
*/
- public void updateCpuTimeLocked() {
+ @GuardedBy("this")
+ public void updateCpuTimeLocked(boolean onBattery, boolean onBatteryScreenOff) {
if (mPowerProfile == null) {
return;
}
@@ -11581,7 +11882,7 @@
// 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 (mOnBatteryScreenOffTimeBase.isRunning()) {
+ if (onBatteryScreenOff) {
partialTimersToConsider = new ArrayList<>();
for (int i = mPartialTimers.size() - 1; i >= 0; --i) {
final StopwatchTimer timer = mPartialTimers.get(i);
@@ -11599,12 +11900,14 @@
// 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 (!mOnBatteryInternal) {
+ if (!onBattery) {
mKernelUidCpuTimeReader.readDelta(null);
mKernelUidCpuFreqTimeReader.readDelta(null);
+ mNumAllUidCpuTimeReads += 2;
if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
mKernelUidCpuActiveTimeReader.readDelta(null);
mKernelUidCpuClusterTimeReader.readDelta(null);
+ mNumAllUidCpuTimeReads += 2;
}
for (int cluster = mKernelCpuSpeedReaders.length - 1; cluster >= 0; --cluster) {
mKernelCpuSpeedReaders[cluster].readDelta();
@@ -11615,16 +11918,18 @@
mUserInfoProvider.refreshUserIds();
final SparseLongArray updatedUids = mKernelUidCpuFreqTimeReader.perClusterTimesAvailable()
? null : new SparseLongArray();
- readKernelUidCpuTimesLocked(partialTimersToConsider, updatedUids);
+ 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);
+ updateClusterSpeedTimes(updatedUids, onBattery);
}
- readKernelUidCpuFreqTimesLocked(partialTimersToConsider);
+ readKernelUidCpuFreqTimesLocked(partialTimersToConsider, onBattery, onBatteryScreenOff);
+ mNumAllUidCpuTimeReads += 2;
if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
- readKernelUidCpuActiveTimesLocked();
- readKernelUidCpuClusterTimesLocked();
+ readKernelUidCpuActiveTimesLocked(onBattery);
+ readKernelUidCpuClusterTimesLocked(onBattery);
+ mNumAllUidCpuTimeReads += 2;
}
}
@@ -11664,7 +11969,7 @@
* @param updatedUids The uids for which times spent at different frequencies are calculated.
*/
@VisibleForTesting
- public void updateClusterSpeedTimes(@NonNull SparseLongArray updatedUids) {
+ public void updateClusterSpeedTimes(@NonNull SparseLongArray updatedUids, boolean onBattery) {
long totalCpuClustersTimeMs = 0;
// Read the time spent for each cluster at various cpu frequencies.
final long[][] clusterSpeedTimesMs = new long[mKernelCpuSpeedReaders.length][];
@@ -11706,7 +12011,7 @@
}
cpuSpeeds[speed].addCountLocked(appCpuTimeUs
* clusterSpeedTimesMs[cluster][speed]
- / totalCpuClustersTimeMs);
+ / totalCpuClustersTimeMs, onBattery);
}
}
}
@@ -11723,7 +12028,7 @@
*/
@VisibleForTesting
public void readKernelUidCpuTimesLocked(@Nullable ArrayList<StopwatchTimer> partialTimers,
- @Nullable SparseLongArray updatedUids) {
+ @Nullable SparseLongArray updatedUids, boolean onBattery) {
mTempTotalCpuUserTimeUs = mTempTotalCpuSystemTimeUs = 0;
final int numWakelocks = partialTimers == null ? 0 : partialTimers.size();
final long startTimeMs = mClocks.uptimeMillis();
@@ -11774,8 +12079,8 @@
Slog.d(TAG, sb.toString());
}
- u.mUserCpuTime.addCountLocked(userTimeUs);
- u.mSystemCpuTime.addCountLocked(systemTimeUs);
+ u.mUserCpuTime.addCountLocked(userTimeUs, onBattery);
+ u.mSystemCpuTime.addCountLocked(systemTimeUs, onBattery);
if (updatedUids != null) {
updatedUids.put(u.getUid(), userTimeUs + systemTimeUs);
}
@@ -11807,15 +12112,15 @@
Slog.d(TAG, sb.toString());
}
- timer.mUid.mUserCpuTime.addCountLocked(userTimeUs);
- timer.mUid.mSystemCpuTime.addCountLocked(systemTimeUs);
+ 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);
+ proc.addCpuTimeLocked(userTimeUs / 1000, systemTimeUs / 1000, onBattery);
mTempTotalCpuUserTimeUs -= userTimeUs;
mTempTotalCpuSystemTimeUs -= systemTimeUs;
@@ -11830,7 +12135,8 @@
* @param partialTimers The wakelock holders among which the cpu freq times will be distributed.
*/
@VisibleForTesting
- public void readKernelUidCpuFreqTimesLocked(@Nullable ArrayList<StopwatchTimer> partialTimers) {
+ public void readKernelUidCpuFreqTimesLocked(@Nullable ArrayList<StopwatchTimer> partialTimers,
+ boolean onBattery, boolean onBatteryScreenOff) {
final boolean perClusterTimesAvailable =
mKernelUidCpuFreqTimeReader.perClusterTimesAvailable();
final int numWakelocks = partialTimers == null ? 0 : partialTimers.size();
@@ -11853,13 +12159,13 @@
if (u.mCpuFreqTimeMs == null || u.mCpuFreqTimeMs.getSize() != cpuFreqTimeMs.length) {
u.mCpuFreqTimeMs = new LongSamplingCounterArray(mOnBatteryTimeBase);
}
- u.mCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs);
+ u.mCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs, onBattery);
if (u.mScreenOffCpuFreqTimeMs == null ||
u.mScreenOffCpuFreqTimeMs.getSize() != cpuFreqTimeMs.length) {
u.mScreenOffCpuFreqTimeMs = new LongSamplingCounterArray(
mOnBatteryScreenOffTimeBase);
}
- u.mScreenOffCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs);
+ u.mScreenOffCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs, onBatteryScreenOff);
if (perClusterTimesAvailable) {
if (u.mCpuClusterSpeedTimesUs == null ||
@@ -11895,7 +12201,7 @@
} else {
appAllocationUs = cpuFreqTimeMs[freqIndex] * 1000;
}
- cpuTimesUs[speed].addCountLocked(appAllocationUs);
+ cpuTimesUs[speed].addCountLocked(appAllocationUs, onBattery);
freqIndex++;
}
}
@@ -11929,7 +12235,7 @@
}
final long allocationUs =
mWakeLockAllocationsUs[cluster][speed] / (numWakelocks - i);
- cpuTimeUs[speed].addCountLocked(allocationUs);
+ cpuTimeUs[speed].addCountLocked(allocationUs, onBattery);
mWakeLockAllocationsUs[cluster][speed] -= allocationUs;
}
}
@@ -11942,9 +12248,9 @@
* counters.
*/
@VisibleForTesting
- public void readKernelUidCpuActiveTimesLocked() {
+ public void readKernelUidCpuActiveTimesLocked(boolean onBattery) {
final long startTimeMs = mClocks.uptimeMillis();
- mKernelUidCpuActiveTimeReader.readDelta((uid, cpuActiveTimesUs) -> {
+ mKernelUidCpuActiveTimeReader.readDelta((uid, cpuActiveTimesMs) -> {
uid = mapUid(uid);
if (Process.isIsolated(uid)) {
mKernelUidCpuActiveTimeReader.removeUid(uid);
@@ -11957,7 +12263,7 @@
return;
}
final Uid u = getUidStatsLocked(uid);
- u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesUs);
+ u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesMs, onBattery);
});
final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
@@ -11971,9 +12277,9 @@
* counters.
*/
@VisibleForTesting
- public void readKernelUidCpuClusterTimesLocked() {
+ public void readKernelUidCpuClusterTimesLocked(boolean onBattery) {
final long startTimeMs = mClocks.uptimeMillis();
- mKernelUidCpuClusterTimeReader.readDelta((uid, cpuClusterTimesUs) -> {
+ mKernelUidCpuClusterTimeReader.readDelta((uid, cpuClusterTimesMs) -> {
uid = mapUid(uid);
if (Process.isIsolated(uid)) {
mKernelUidCpuClusterTimeReader.removeUid(uid);
@@ -11986,7 +12292,7 @@
return;
}
final Uid u = getUidStatsLocked(uid);
- u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesUs);
+ u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesMs, onBattery);
});
final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
@@ -12009,6 +12315,7 @@
return false;
}
+ @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;
@@ -12038,11 +12345,14 @@
// 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 startTime = SystemClock.uptimeMillis();
final Parcel parcel = Parcel.obtain();
writeSummaryToParcel(parcel, true);
+ final long initialTime = SystemClock.uptimeMillis() - startTime;
BackgroundThread.getHandler().post(new Runnable() {
@Override public void run() {
synchronized (mCheckinFile) {
+ final long startTime2 = SystemClock.uptimeMillis();
FileOutputStream stream = null;
try {
stream = mCheckinFile.startWrite();
@@ -12051,6 +12361,9 @@
FileUtils.sync(stream);
stream.close();
mCheckinFile.finishWrite(stream);
+ com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
+ "batterystats-checkin",
+ initialTime + SystemClock.uptimeMillis() - startTime2);
} catch (IOException e) {
Slog.w("BatteryStats",
"Error writing checkin battery statistics", e);
@@ -12144,7 +12457,7 @@
boolean reset) {
mRecordingHistory = true;
mHistoryCur.currentTime = System.currentTimeMillis();
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs,
+ addHistoryBufferLocked(elapsedRealtimeMs,
reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME,
mHistoryCur);
mHistoryCur.currentTime = 0;
@@ -12157,8 +12470,7 @@
final long uptimeMs) {
if (mRecordingHistory) {
mHistoryCur.currentTime = currentTime;
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_CURRENT_TIME,
- mHistoryCur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_CURRENT_TIME, mHistoryCur);
mHistoryCur.currentTime = 0;
}
}
@@ -12166,8 +12478,7 @@
private void recordShutdownLocked(final long elapsedRealtimeMs, final long uptimeMs) {
if (mRecordingHistory) {
mHistoryCur.currentTime = System.currentTimeMillis();
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_SHUTDOWN,
- mHistoryCur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_SHUTDOWN, mHistoryCur);
mHistoryCur.currentTime = 0;
}
}
@@ -12179,8 +12490,9 @@
}
// This should probably be exposed in the API, though it's not critical
- public static final int BATTERY_PLUGGED_NONE = 0;
+ 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 volt, final int chargeUAh,
final int chargeFullUAh) {
@@ -12188,11 +12500,9 @@
temp = Math.max(0, temp);
reportChangesToStatsLog(mHaveBatteryLevel ? mHistoryCur : null,
- status, plugType, level, temp);
+ status, plugType, level);
- final boolean onBattery =
- plugType == BATTERY_PLUGGED_NONE &&
- status != BatteryManager.BATTERY_STATUS_UNKNOWN;
+ final boolean onBattery = isOnBattery(plugType, status);
final long uptime = mClocks.uptimeMillis();
final long elapsedRealtime = mClocks.elapsedRealtime();
if (!mHaveBatteryLevel) {
@@ -12382,10 +12692,14 @@
mMaxLearnedBatteryCapacity = Math.max(mMaxLearnedBatteryCapacity, chargeFullUAh);
}
+ 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, final int temp) {
+ final int status, final int plugType, final int level) {
if (recentPast == null || recentPast.batteryStatus != status) {
StatsLog.write(StatsLog.CHARGING_STATE_CHANGED, status);
@@ -12396,8 +12710,6 @@
if (recentPast == null || recentPast.batteryLevel != level) {
StatsLog.write(StatsLog.BATTERY_LEVEL_CHANGED, level);
}
- // Let's just always print the temperature, regardless of whether it changed.
- StatsLog.write(StatsLog.DEVICE_TEMPERATURE_REPORTED, temp);
}
public long getAwakeTimeBattery() {
@@ -12559,6 +12871,7 @@
final int which = STATS_SINCE_CHARGED;
final long rawRealTime = 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);
@@ -12578,10 +12891,6 @@
txTimeMs[i] = counter.getTxTimeCounters()[i].getCountLocked(which);
totalTxTimeMs += txTimeMs[i];
}
- final long totalControllerActivityTimeMs
- = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000;
- final long sleepTimeMs
- = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + totalTxTimeMs);
s.setLoggingDurationMs(computeBatteryRealtime(rawRealTime, which) / 1000);
s.setKernelActiveTimeMs(getMobileRadioActiveTime(rawRealTime, which) / 1000);
s.setNumPacketsTx(getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which));
@@ -12845,11 +13154,8 @@
public void onCleanupUserLocked(int userId) {
final int firstUidForUser = UserHandle.getUid(userId, 0);
final int lastUidForUser = UserHandle.getUid(userId, UserHandle.PER_USER_RANGE - 1);
- mKernelUidCpuFreqTimeReader.removeUidsInRange(firstUidForUser, lastUidForUser);
- mKernelUidCpuTimeReader.removeUidsInRange(firstUidForUser, lastUidForUser);
- if (mKernelSingleUidTimeReader != null) {
- mKernelSingleUidTimeReader.removeUidsInRange(firstUidForUser, lastUidForUser);
- }
+ mPendingRemovedUids.add(
+ new UidToRemove(firstUidForUser, lastUidForUser, mClocks.elapsedRealtime()));
}
public void onUserRemovedLocked(int userId) {
@@ -12866,12 +13172,8 @@
* Remove the statistics object for a particular uid.
*/
public void removeUidStatsLocked(int uid) {
- mKernelUidCpuTimeReader.removeUid(uid);
- mKernelUidCpuFreqTimeReader.removeUid(uid);
- if (mKernelSingleUidTimeReader != null) {
- mKernelSingleUidTimeReader.removeUid(uid);
- }
mUidStats.remove(uid);
+ mPendingRemovedUids.add(new UidToRemove(uid, mClocks.elapsedRealtime()));
}
/**
@@ -12924,17 +13226,24 @@
= "track_cpu_times_by_proc_state";
public static final String KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME
= "track_cpu_active_cluster_time";
- public static final String KEY_READ_BINARY_CPU_TIME
- = "read_binary_cpu_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";
private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = true;
private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true;
- private static final boolean DEFAULT_READ_BINARY_CPU_TIME = false;
+ private static final long DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS = 5_000;
+ private static final long DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME = 10_000;
+ private static final long DEFAULT_UID_REMOVE_DELAY_MS = 5L * 60L * 1000L;
public boolean TRACK_CPU_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE;
public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME;
- // Not used right now.
- public boolean READ_BINARY_CPU_TIME = DEFAULT_READ_BINARY_CPU_TIME;
+ public long PROC_STATE_CPU_TIMES_READ_DELAY_MS = DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS;
+ public long KERNEL_UID_READERS_THROTTLE_TIME = DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME;
+ public long UID_REMOVE_DELAY_MS = DEFAULT_UID_REMOVE_DELAY_MS;
private ContentResolver mResolver;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -12972,9 +13281,14 @@
DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE));
TRACK_CPU_ACTIVE_CLUSTER_TIME = mParser.getBoolean(
KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME, DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME);
- READ_BINARY_CPU_TIME = mParser.getBoolean(
- KEY_READ_BINARY_CPU_TIME, DEFAULT_READ_BINARY_CPU_TIME);
-
+ updateProcStateCpuTimesReadDelayMs(PROC_STATE_CPU_TIMES_READ_DELAY_MS,
+ mParser.getLong(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS,
+ DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS));
+ 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));
}
}
@@ -12983,23 +13297,92 @@
if (isEnabled && !wasEnabled) {
mKernelSingleUidTimeReader.markDataAsStale(true);
mExternalSync.scheduleCpuSyncDueToSettingChange();
+
+ mNumSingleUidCpuTimeReads = 0;
+ mNumBatchedSingleUidCpuTimeReads = 0;
+ mCpuTimeReadsTrackingStartTime = mClocks.uptimeMillis();
}
}
+ private void updateProcStateCpuTimesReadDelayMs(long oldDelayMillis, long newDelayMillis) {
+ PROC_STATE_CPU_TIMES_READ_DELAY_MS = newDelayMillis;
+ if (oldDelayMillis != newDelayMillis) {
+ mNumSingleUidCpuTimeReads = 0;
+ mNumBatchedSingleUidCpuTimeReads = 0;
+ mCpuTimeReadsTrackingStartTime = mClocks.uptimeMillis();
+ }
+ }
+
+ private void updateKernelUidReadersThrottleTime(long oldTimeMs, long newTimeMs) {
+ KERNEL_UID_READERS_THROTTLE_TIME = newTimeMs;
+ if (oldTimeMs != newTimeMs) {
+ mKernelUidCpuTimeReader.setThrottleInterval(KERNEL_UID_READERS_THROTTLE_TIME);
+ mKernelUidCpuFreqTimeReader.setThrottleInterval(KERNEL_UID_READERS_THROTTLE_TIME);
+ mKernelUidCpuActiveTimeReader.setThrottleInterval(KERNEL_UID_READERS_THROTTLE_TIME);
+ mKernelUidCpuClusterTimeReader
+ .setThrottleInterval(KERNEL_UID_READERS_THROTTLE_TIME);
+ }
+ }
+
+ private void updateUidRemoveDelay(long newTimeMs) {
+ UID_REMOVE_DELAY_MS = newTimeMs;
+ clearPendingRemovedUids();
+ }
+
public void dumpLocked(PrintWriter pw) {
pw.print(KEY_TRACK_CPU_TIMES_BY_PROC_STATE); pw.print("=");
pw.println(TRACK_CPU_TIMES_BY_PROC_STATE);
pw.print(KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME); pw.print("=");
pw.println(TRACK_CPU_ACTIVE_CLUSTER_TIME);
- pw.print(KEY_READ_BINARY_CPU_TIME); pw.print("=");
- pw.println(READ_BINARY_CPU_TIME);
+ pw.print(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS); pw.print("=");
+ pw.println(PROC_STATE_CPU_TIMES_READ_DELAY_MS);
+ pw.print(KEY_KERNEL_UID_READERS_THROTTLE_TIME); pw.print("=");
+ pw.println(KERNEL_UID_READERS_THROTTLE_TIME);
}
}
+ @GuardedBy("this")
public void dumpConstantsLocked(PrintWriter pw) {
mConstants.dumpLocked(pw);
}
+ @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));
+ }
+ }
+ }
+
Parcel mPendingWrite = null;
final ReentrantLock mWriteLock = new ReentrantLock();
@@ -13053,12 +13436,15 @@
mWriteLock.lock();
try {
+ final long startTime = SystemClock.uptimeMillis();
FileOutputStream stream = new FileOutputStream(mFile.chooseForWrite());
stream.write(next.marshall());
stream.flush();
FileUtils.sync(stream);
stream.close();
mFile.commit();
+ com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
+ "batterystats", SystemClock.uptimeMillis() - startTime);
} catch (IOException e) {
Slog.w("BatteryStats", "Error writing battery statistics", e);
mFile.rollback();
@@ -13108,7 +13494,7 @@
if (USE_OLD_HISTORY) {
addHistoryRecordLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur);
}
- addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur);
+ addHistoryBufferLocked(elapsedRealtime, HistoryItem.CMD_START, mHistoryCur);
startRecordingHistory(elapsedRealtime, uptime, false);
}
@@ -13376,6 +13762,7 @@
mCameraOnTimer.readSummaryFromParcelLocked(in);
mBluetoothScanNesting = 0;
mBluetoothScanTimer.readSummaryFromParcelLocked(in);
+ mIsCellularTxPowerHigh = false;
int NRPMS = in.readInt();
if (NRPMS > 10000) {
@@ -13637,6 +14024,16 @@
u.readJobCompletionsFromParcelLocked(in);
+ u.mJobsDeferredEventCount.readSummaryFromParcelLocked(in);
+ u.mJobsDeferredCount.readSummaryFromParcelLocked(in);
+ u.mJobsFreshnessTimeMs.readSummaryFromParcelLocked(in);
+ 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);
@@ -14135,6 +14532,18 @@
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++) {
@@ -14312,6 +14721,7 @@
mCameraOnTimer = new StopwatchTimer(mClocks, null, -13, null, mOnBatteryTimeBase, in);
mBluetoothScanNesting = 0;
mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase, in);
+ mIsCellularTxPowerHigh = false;
mDischargeUnplugLevel = in.readInt();
mDischargePlugLevel = in.readInt();
mDischargeCurrentLevel = in.readInt();
@@ -14696,5 +15106,15 @@
mCameraOnTimer.logState(pr, " ");
}
super.dumpLocked(context, pw, flags, reqUid, histStart);
+ pw.print("Total cpu time reads: ");
+ pw.println(mNumSingleUidCpuTimeReads);
+ pw.print("Batched cpu time reads: ");
+ pw.println(mNumBatchedSingleUidCpuTimeReads);
+ pw.print("Batching Duration (min): ");
+ pw.println((mClocks.uptimeMillis() - mCpuTimeReadsTrackingStartTime) / (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);
}
}
diff --git a/com/android/internal/os/BinderCallsStats.java b/com/android/internal/os/BinderCallsStats.java
new file mode 100644
index 0000000..2c48506
--- /dev/null
+++ b/com/android/internal/os/BinderCallsStats.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2017 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 android.os.Binder;
+import android.os.SystemClock;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/**
+ * Collects statistics about CPU time spent per binder call across multiple dimensions, e.g.
+ * per thread, uid or call description.
+ */
+public class BinderCallsStats {
+ private static final int CALL_SESSIONS_POOL_SIZE = 100;
+ private static final BinderCallsStats sInstance = new BinderCallsStats();
+
+ private volatile boolean mTrackingEnabled = false;
+ private final SparseArray<UidEntry> mUidEntries = new SparseArray<>();
+ private final Queue<CallSession> mCallSessionsPool = new ConcurrentLinkedQueue<>();
+
+ private BinderCallsStats() {
+ }
+
+ @VisibleForTesting
+ public BinderCallsStats(boolean trackingEnabled) {
+ mTrackingEnabled = trackingEnabled;
+ }
+
+ public CallSession callStarted(Binder binder, int code) {
+ if (!mTrackingEnabled) {
+ return null;
+ }
+
+ return callStarted(binder.getClass().getName(), code);
+ }
+
+ private CallSession callStarted(String className, int code) {
+ CallSession s = mCallSessionsPool.poll();
+ if (s == null) {
+ s = new CallSession();
+ }
+ s.mCallStat.className = className;
+ s.mCallStat.msg = code;
+
+ s.mStarted = getThreadTimeMicro();
+ return s;
+ }
+
+ public void callEnded(CallSession s) {
+ if (!mTrackingEnabled) {
+ return;
+ }
+ Preconditions.checkNotNull(s);
+ final long cpuTimeNow = getThreadTimeMicro();
+ final long duration = cpuTimeNow - s.mStarted;
+ s.mCallingUId = Binder.getCallingUid();
+
+ synchronized (mUidEntries) {
+ UidEntry uidEntry = mUidEntries.get(s.mCallingUId);
+ if (uidEntry == null) {
+ uidEntry = new UidEntry(s.mCallingUId);
+ mUidEntries.put(s.mCallingUId, uidEntry);
+ }
+
+ // Find CallDesc entry and update its total time
+ CallStat callStat = uidEntry.mCallStats.get(s.mCallStat);
+ // Only create CallStat if it's a new entry, otherwise update existing instance
+ if (callStat == null) {
+ callStat = new CallStat(s.mCallStat.className, s.mCallStat.msg);
+ uidEntry.mCallStats.put(callStat, callStat);
+ }
+ uidEntry.time += duration;
+ uidEntry.callCount++;
+ callStat.callCount++;
+ callStat.time += duration;
+ }
+ if (mCallSessionsPool.size() < CALL_SESSIONS_POOL_SIZE) {
+ mCallSessionsPool.add(s);
+ }
+ }
+
+ public void dump(PrintWriter pw) {
+ Map<Integer, Long> uidTimeMap = new HashMap<>();
+ Map<Integer, Long> uidCallCountMap = new HashMap<>();
+ long totalCallsCount = 0;
+ long totalCallsTime = 0;
+ int uidEntriesSize = mUidEntries.size();
+ List<UidEntry> entries = new ArrayList<>();
+ synchronized (mUidEntries) {
+ for (int i = 0; i < uidEntriesSize; i++) {
+ UidEntry e = mUidEntries.valueAt(i);
+ entries.add(e);
+ totalCallsTime += e.time;
+ // Update per-uid totals
+ Long totalTimePerUid = uidTimeMap.get(e.uid);
+ uidTimeMap.put(e.uid,
+ totalTimePerUid == null ? e.time : totalTimePerUid + e.time);
+ Long totalCallsPerUid = uidCallCountMap.get(e.uid);
+ uidCallCountMap.put(e.uid, totalCallsPerUid == null ? e.callCount
+ : totalCallsPerUid + e.callCount);
+ totalCallsCount += e.callCount;
+ }
+ }
+ pw.println("Binder call stats:");
+ pw.println(" Raw data (uid,call_desc,time):");
+ entries.sort((o1, o2) -> {
+ if (o1.time < o2.time) {
+ return 1;
+ } else if (o1.time > o2.time) {
+ return -1;
+ }
+ return 0;
+ });
+ StringBuilder sb = new StringBuilder();
+ for (UidEntry uidEntry : entries) {
+ List<CallStat> callStats = new ArrayList<>(uidEntry.mCallStats.keySet());
+ callStats.sort((o1, o2) -> {
+ if (o1.time < o2.time) {
+ return 1;
+ } else if (o1.time > o2.time) {
+ return -1;
+ }
+ return 0;
+ });
+ for (CallStat e : callStats) {
+ sb.setLength(0);
+ sb.append(" ")
+ .append(uidEntry.uid).append(",").append(e).append(',').append(e.time);
+ pw.println(sb);
+ }
+ }
+ pw.println();
+ pw.println(" Per UID Summary(UID: time, total_time_percentage, calls_count):");
+ List<Map.Entry<Integer, Long>> uidTotals = new ArrayList<>(uidTimeMap.entrySet());
+ uidTotals.sort((o1, o2) -> o2.getValue().compareTo(o1.getValue()));
+ for (Map.Entry<Integer, Long> uidTotal : uidTotals) {
+ Long callCount = uidCallCountMap.get(uidTotal.getKey());
+ pw.println(String.format(" %5d: %11d %3.0f%% %8d",
+ uidTotal.getKey(), uidTotal.getValue(),
+ 100d * uidTotal.getValue() / totalCallsTime, callCount));
+ }
+ pw.println();
+ pw.println(String.format(" Summary: total_time=%d, "
+ + "calls_count=%d, avg_call_time=%.0f",
+ totalCallsTime, totalCallsCount,
+ (double)totalCallsTime / totalCallsCount));
+ }
+
+ private static long getThreadTimeMicro() {
+ return SystemClock.currentThreadTimeMicro();
+ }
+
+ public static BinderCallsStats getInstance() {
+ return sInstance;
+ }
+
+ public void setTrackingEnabled(boolean enabled) {
+ mTrackingEnabled = enabled;
+ }
+
+ public boolean isTrackingEnabled() {
+ return mTrackingEnabled;
+ }
+
+ private static class CallStat {
+ String className;
+ int msg;
+ long time;
+ long callCount;
+
+ CallStat() {
+ }
+
+ CallStat(String className, int msg) {
+ this.className = className;
+ this.msg = msg;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ CallStat callStat = (CallStat) o;
+
+ return msg == callStat.msg && (className == callStat.className);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = className.hashCode();
+ result = 31 * result + msg;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return className + "/" + msg;
+ }
+ }
+
+ public static class CallSession {
+ int mCallingUId;
+ long mStarted;
+ CallStat mCallStat = new CallStat();
+ }
+
+ private static class UidEntry {
+ int uid;
+ long time;
+ long callCount;
+
+ UidEntry(int uid) {
+ this.uid = uid;
+ }
+
+ // Aggregate time spent per each call name: call_desc -> cpu_time_micros
+ Map<CallStat, CallStat> mCallStats = new ArrayMap<>();
+
+ @Override
+ public String toString() {
+ return "UidEntry{" +
+ "time=" + time +
+ ", callCount=" + callCount +
+ ", mCallStats=" + mCallStats +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ UidEntry uidEntry = (UidEntry) o;
+ return uid == uidEntry.uid;
+ }
+
+ @Override
+ public int hashCode() {
+ return uid;
+ }
+ }
+
+}
diff --git a/com/android/internal/os/CpuPowerCalculator.java b/com/android/internal/os/CpuPowerCalculator.java
index a34e7f5..101c321 100644
--- a/com/android/internal/os/CpuPowerCalculator.java
+++ b/com/android/internal/os/CpuPowerCalculator.java
@@ -50,13 +50,14 @@
cpuPowerMaUs += cpuSpeedStepPower;
}
}
- cpuPowerMaUs += u.getCpuActiveTime() * mProfile.getAveragePower(
+ cpuPowerMaUs += u.getCpuActiveTime() * 1000 * mProfile.getAveragePower(
PowerProfile.POWER_CPU_ACTIVE);
long[] cpuClusterTimes = u.getCpuClusterTimes();
if (cpuClusterTimes != null) {
if (cpuClusterTimes.length == numClusters) {
for (int i = 0; i < numClusters; i++) {
- double power = cpuClusterTimes[i] * mProfile.getAveragePowerForCpuCluster(i);
+ double power =
+ cpuClusterTimes[i] * 1000 * mProfile.getAveragePowerForCpuCluster(i);
cpuPowerMaUs += power;
if (DEBUG) {
Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + i + " clusterTimeUs="
diff --git a/com/android/internal/os/FuseAppLoop.java b/com/android/internal/os/FuseAppLoop.java
index 088e726..67fbe5e 100644
--- a/com/android/internal/os/FuseAppLoop.java
+++ b/com/android/internal/os/FuseAppLoop.java
@@ -138,7 +138,7 @@
private static final int FUSE_FSYNC = 20;
// Defined in FuseBuffer.h
- private static final int FUSE_MAX_WRITE = 256 * 1024;
+ private static final int FUSE_MAX_WRITE = 128 * 1024;
@Override
public boolean handleMessage(Message msg) {
@@ -283,6 +283,7 @@
return -OsConstants.EBADF;
}
+ @GuardedBy("mLock")
private CallbackEntry getCallbackEntryOrThrowLocked(long inode) throws ErrnoException {
final CallbackEntry entry = mCallbackMap.get(checkInode(inode));
if (entry == null) {
@@ -291,12 +292,14 @@
return entry;
}
+ @GuardedBy("mLock")
private void recycleLocked(Args args) {
if (mArgsPool.size() < ARGS_POOL_SIZE) {
mArgsPool.add(args);
}
}
+ @GuardedBy("mLock")
private void replySimpleLocked(long unique, int result) {
if (mInstance != 0) {
native_replySimple(mInstance, unique, result);
diff --git a/com/android/internal/os/KernelCpuProcReader.java b/com/android/internal/os/KernelCpuProcReader.java
new file mode 100644
index 0000000..396deb4
--- /dev/null
+++ b/com/android/internal/os/KernelCpuProcReader.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2018 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 android.os.StrictMode;
+import android.os.SystemClock;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.channels.FileChannel;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+
+/**
+ * Reads cpu time proc files with throttling (adjustable interval).
+ *
+ * KernelCpuProcReader is implemented as singletons for built-in kernel proc files. Get___Instance()
+ * method will return corresponding reader instance. In order to prevent frequent GC,
+ * KernelCpuProcReader reuses a {@link ByteBuffer} to store data read from proc files.
+ *
+ * A KernelCpuProcReader instance keeps an error counter. When the number of read errors within that
+ * instance accumulates to 5, this instance will reject all further read requests.
+ *
+ * Each KernelCpuProcReader instance also has a throttler. Throttle interval can be adjusted via
+ * {@link #setThrottleInterval(long)} method. Default throttle interval is 3000ms. If current
+ * timestamp based on {@link SystemClock#elapsedRealtime()} is less than throttle interval from
+ * the last read timestamp, {@link #readBytes()} will return previous result.
+ *
+ * A KernelCpuProcReader instance is thread-unsafe. Caller needs to hold a lock on this object while
+ * accessing its instance methods or digesting the return values.
+ */
+public class KernelCpuProcReader {
+ private static final String TAG = "KernelCpuProcReader";
+ private static final int ERROR_THRESHOLD = 5;
+ // Throttle interval in milliseconds
+ private static final long DEFAULT_THROTTLE_INTERVAL = 3000L;
+ private static final int INITIAL_BUFFER_SIZE = 8 * 1024;
+ private static final int MAX_BUFFER_SIZE = 1024 * 1024;
+ private static final String PROC_UID_FREQ_TIME = "/proc/uid_cpupower/time_in_state";
+ private static final String PROC_UID_ACTIVE_TIME = "/proc/uid_cpupower/concurrent_active_time";
+ private static final String PROC_UID_CLUSTER_TIME = "/proc/uid_cpupower/concurrent_policy_time";
+
+ private static final KernelCpuProcReader mFreqTimeReader = new KernelCpuProcReader(
+ PROC_UID_FREQ_TIME);
+ private static final KernelCpuProcReader mActiveTimeReader = new KernelCpuProcReader(
+ PROC_UID_ACTIVE_TIME);
+ private static final KernelCpuProcReader mClusterTimeReader = new KernelCpuProcReader(
+ PROC_UID_CLUSTER_TIME);
+
+ public static KernelCpuProcReader getFreqTimeReaderInstance() {
+ return mFreqTimeReader;
+ }
+
+ public static KernelCpuProcReader getActiveTimeReaderInstance() {
+ return mActiveTimeReader;
+ }
+
+ public static KernelCpuProcReader getClusterTimeReaderInstance() {
+ return mClusterTimeReader;
+ }
+
+ private int mErrors;
+ private long mThrottleInterval = DEFAULT_THROTTLE_INTERVAL;
+ private long mLastReadTime = Long.MIN_VALUE;
+ private final Path mProc;
+ private ByteBuffer mBuffer;
+
+ @VisibleForTesting
+ public KernelCpuProcReader(String procFile) {
+ mProc = Paths.get(procFile);
+ mBuffer = ByteBuffer.allocateDirect(INITIAL_BUFFER_SIZE);
+ mBuffer.clear();
+ }
+
+ /**
+ * Reads all bytes from the corresponding proc file.
+ *
+ * If elapsed time since last call to this method is less than the throttle interval, it will
+ * return previous result. When IOException accumulates to 5, it will always return null. This
+ * method is thread-unsafe, so is the return value. Caller needs to hold a lock on this
+ * object while calling this method and digesting its return value.
+ *
+ * @return a {@link ByteBuffer} containing all bytes from the proc file.
+ */
+ public ByteBuffer readBytes() {
+ if (mErrors >= ERROR_THRESHOLD) {
+ return null;
+ }
+ if (SystemClock.elapsedRealtime() < mLastReadTime + mThrottleInterval) {
+ if (mBuffer.limit() > 0 && mBuffer.limit() < mBuffer.capacity()) {
+ // mBuffer has data.
+ return mBuffer.asReadOnlyBuffer().order(ByteOrder.nativeOrder());
+ }
+ return null;
+ }
+ mLastReadTime = SystemClock.elapsedRealtime();
+ mBuffer.clear();
+ final int oldMask = StrictMode.allowThreadDiskReadsMask();
+ try (FileChannel fc = FileChannel.open(mProc, StandardOpenOption.READ)) {
+ while (fc.read(mBuffer) == mBuffer.capacity()) {
+ if (!resize()) {
+ mErrors++;
+ Slog.e(TAG, "Proc file is too large: " + mProc);
+ return null;
+ }
+ fc.position(0);
+ }
+ } catch (NoSuchFileException | FileNotFoundException e) {
+ // Happens when the kernel does not provide this file. Not a big issue. Just log it.
+ mErrors++;
+ Slog.w(TAG, "File not exist: " + mProc);
+ return null;
+ } catch (IOException e) {
+ mErrors++;
+ Slog.e(TAG, "Error reading: " + mProc, e);
+ return null;
+ } finally {
+ StrictMode.setThreadPolicyMask(oldMask);
+ }
+ mBuffer.flip();
+ return mBuffer.asReadOnlyBuffer().order(ByteOrder.nativeOrder());
+ }
+
+ /**
+ * Sets the throttle interval. Set to 0 will disable throttling. Thread-unsafe, holding a lock
+ * on this object is recommended.
+ *
+ * @param throttleInterval throttle interval in milliseconds
+ */
+ public void setThrottleInterval(long throttleInterval) {
+ if (throttleInterval >= 0) {
+ mThrottleInterval = throttleInterval;
+ }
+ }
+
+ private boolean resize() {
+ if (mBuffer.capacity() >= MAX_BUFFER_SIZE) {
+ return false;
+ }
+ int newSize = Math.min(mBuffer.capacity() << 1, MAX_BUFFER_SIZE);
+ // Slog.i(TAG, "Resize buffer " + mBuffer.capacity() + " => " + newSize);
+ mBuffer = ByteBuffer.allocateDirect(newSize);
+ return true;
+ }
+}
diff --git a/com/android/internal/os/KernelSingleUidTimeReader.java b/com/android/internal/os/KernelSingleUidTimeReader.java
index ebeb24c..4283917 100644
--- a/com/android/internal/os/KernelSingleUidTimeReader.java
+++ b/com/android/internal/os/KernelSingleUidTimeReader.java
@@ -16,6 +16,7 @@
package com.android.internal.os;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+import static com.android.internal.os.KernelUidCpuFreqTimeReader.UID_TIMES_PROC_FILE;
import android.annotation.NonNull;
import android.util.Slog;
@@ -54,6 +55,12 @@
private boolean mSingleUidCpuTimesAvailable = true;
@GuardedBy("this")
private boolean mHasStaleData;
+ // We use the freq count obtained from /proc/uid_time_in_state to decide how many longs
+ // to read from each /proc/uid/<uid>/time_in_state. On the first read, verify if this is
+ // correct and if not, set {@link #mSingleUidCpuTimesAvailable} to false. This flag will
+ // indicate whether we checked for validity or not.
+ @GuardedBy("this")
+ private boolean mCpuFreqsCountVerified;
private final Injector mInjector;
@@ -82,15 +89,15 @@
final String procFile = new StringBuilder(PROC_FILE_DIR)
.append(uid)
.append(PROC_FILE_NAME).toString();
- final long[] cpuTimesMs = new long[mCpuFreqsCount];
+ final long[] cpuTimesMs;
try {
final byte[] data = mInjector.readData(procFile);
+ if (!mCpuFreqsCountVerified) {
+ verifyCpuFreqsCount(data.length, procFile);
+ }
final ByteBuffer buffer = ByteBuffer.wrap(data);
buffer.order(ByteOrder.nativeOrder());
- for (int i = 0; i < mCpuFreqsCount; ++i) {
- // Times read will be in units of 10ms
- cpuTimesMs[i] = buffer.getLong() * 10;
- }
+ cpuTimesMs = readCpuTimesFromByteBuffer(buffer);
} catch (Exception e) {
if (++mReadErrorCounter >= TOTAL_READ_ERROR_COUNT) {
mSingleUidCpuTimesAvailable = false;
@@ -103,6 +110,27 @@
}
}
+ private void verifyCpuFreqsCount(int numBytes, String procFile) {
+ final int actualCount = (numBytes / Long.BYTES);
+ if (mCpuFreqsCount != actualCount) {
+ mSingleUidCpuTimesAvailable = false;
+ throw new IllegalStateException("Freq count didn't match,"
+ + "count from " + UID_TIMES_PROC_FILE + "=" + mCpuFreqsCount + ", but"
+ + "count from " + procFile + "=" + actualCount);
+ }
+ mCpuFreqsCountVerified = true;
+ }
+
+ private long[] readCpuTimesFromByteBuffer(ByteBuffer buffer) {
+ final long[] cpuTimesMs;
+ cpuTimesMs = new long[mCpuFreqsCount];
+ for (int i = 0; i < mCpuFreqsCount; ++i) {
+ // Times read will be in units of 10ms
+ cpuTimesMs[i] = buffer.getLong() * 10;
+ }
+ return cpuTimesMs;
+ }
+
/**
* Compute and return cpu times delta of an uid using previously read cpu times and
* {@param latestCpuTimesMs}.
diff --git a/com/android/internal/os/KernelUidCpuActiveTimeReader.java b/com/android/internal/os/KernelUidCpuActiveTimeReader.java
index cb96c5c..bd8a67a 100644
--- a/com/android/internal/os/KernelUidCpuActiveTimeReader.java
+++ b/com/android/internal/os/KernelUidCpuActiveTimeReader.java
@@ -17,53 +17,151 @@
package com.android.internal.os;
import android.annotation.Nullable;
-import android.os.StrictMode;
-import android.os.SystemClock;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.TimeUtils;
import com.android.internal.annotations.VisibleForTesting;
-import java.io.BufferedReader;
-import java.io.FileReader;
-import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+import java.util.function.Consumer;
/**
- * Reads /proc/uid_concurrent_active_time which has the format:
- * active: X (X is # cores)
- * [uid0]: [time-0] [time-1] [time-2] ... (# entries = # cores)
- * [uid1]: [time-0] [time-1] [time-2] ... ...
+ * Reads binary proc file /proc/uid_cpupower/concurrent_active_time and reports CPU active time to
+ * BatteryStats to compute {@link PowerProfile#POWER_CPU_ACTIVE}.
+ *
+ * concurrent_active_time is an array of u32's in the following format:
+ * [n, uid0, time0a, time0b, ..., time0n,
+ * uid1, time1a, time1b, ..., time1n,
+ * uid2, time2a, time2b, ..., time2n, etc.]
+ * where n is the total number of cpus (num_possible_cpus)
* ...
- * Time-N means the CPU time a UID spent running concurrently with N other processes.
+ * timeXn means the CPU time that a UID X spent running concurrently with n other processes.
* The file contains a monotonically increasing count of time for a single boot. This class
* maintains the previous results of a call to {@link #readDelta} in order to provide a
* proper delta.
+ *
+ * This class uses a throttler to reject any {@link #readDelta} call within
+ * {@link #mThrottleInterval}. This is different from the throttler in {@link KernelCpuProcReader},
+ * which has a shorter throttle interval and returns cached result from last read when the request
+ * is throttled.
+ *
+ * This class is NOT thread-safe and NOT designed to be accessed by more than one caller since each
+ * caller has its own view of delta.
*/
-public class KernelUidCpuActiveTimeReader {
- private static final boolean DEBUG = false;
- private static final String TAG = "KernelUidCpuActiveTimeReader";
- private static final String UID_TIMES_PROC_FILE = "/proc/uid_concurrent_active_time";
+public class KernelUidCpuActiveTimeReader extends
+ KernelUidCpuTimeReaderBase<KernelUidCpuActiveTimeReader.Callback> {
+ private static final String TAG = KernelUidCpuActiveTimeReader.class.getSimpleName();
- private int mCoreCount;
- private long mLastTimeReadMs;
- private long mNowTimeMs;
- private SparseArray<long[]> mLastUidCpuActiveTimeMs = new SparseArray<>();
+ private final KernelCpuProcReader mProcReader;
+ private SparseArray<Double> mLastUidCpuActiveTimeMs = new SparseArray<>();
+ private int mCores;
- public interface Callback {
+ public interface Callback extends KernelUidCpuTimeReaderBase.Callback {
+ /**
+ * Notifies when new data is available.
+ *
+ * @param uid uid int
+ * @param cpuActiveTimeMs cpu active time spent by this uid in milliseconds
+ */
void onUidCpuActiveTime(int uid, long cpuActiveTimeMs);
}
- public void readDelta(@Nullable Callback cb) {
- final int oldMask = StrictMode.allowThreadDiskReadsMask();
- try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) {
- mNowTimeMs = SystemClock.elapsedRealtime();
- readDeltaInternal(reader, cb);
- mLastTimeReadMs = mNowTimeMs;
- } catch (IOException e) {
- Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
- } finally {
- StrictMode.setThreadPolicyMask(oldMask);
+ public KernelUidCpuActiveTimeReader() {
+ mProcReader = KernelCpuProcReader.getActiveTimeReaderInstance();
+ }
+
+ @VisibleForTesting
+ public KernelUidCpuActiveTimeReader(KernelCpuProcReader procReader) {
+ mProcReader = procReader;
+ }
+
+ @Override
+ protected void readDeltaImpl(@Nullable Callback callback) {
+ readImpl((buf) -> {
+ int uid = buf.get();
+ double activeTime = sumActiveTime(buf);
+ if (activeTime > 0) {
+ double delta = activeTime - mLastUidCpuActiveTimeMs.get(uid, 0.0);
+ if (delta > 0) {
+ mLastUidCpuActiveTimeMs.put(uid, activeTime);
+ if (callback != null) {
+ callback.onUidCpuActiveTime(uid, (long) delta);
+ }
+ } else if (delta < 0) {
+ Slog.e(TAG, "Negative delta from active time proc: " + delta);
+ }
+ }
+ });
+ }
+
+ public void readAbsolute(Callback callback) {
+ readImpl((buf) -> {
+ int uid = buf.get();
+ double activeTime = sumActiveTime(buf);
+ if (activeTime > 0) {
+ callback.onUidCpuActiveTime(uid, (long) activeTime);
+ }
+ });
+ }
+
+ private double sumActiveTime(IntBuffer buffer) {
+ double sum = 0;
+ boolean corrupted = false;
+ for (int j = 1; j <= mCores; j++) {
+ int time = buffer.get();
+ if (time < 0) {
+ // Even if error happens, we still need to continue reading.
+ // Buffer cannot be skipped.
+ Slog.e(TAG, "Negative time from active time proc: " + time);
+ corrupted = true;
+ } else {
+ sum += (double) time * 10 / j; // Unit is 10ms.
+ }
+ }
+ return corrupted ? -1 : sum;
+ }
+
+ /**
+ * readImpl accepts a callback to process the uid entry. readDeltaImpl needs to store the last
+ * seen results while processing the buffer, while readAbsolute returns the absolute value read
+ * from the buffer without storing. So readImpl contains the common logic of the two, leaving
+ * the difference to a processUid function.
+ *
+ * @param processUid the callback function to process the uid entry in the buffer.
+ */
+ private void readImpl(Consumer<IntBuffer> processUid) {
+ synchronized (mProcReader) {
+ final ByteBuffer bytes = mProcReader.readBytes();
+ if (bytes == null || bytes.remaining() <= 4) {
+ // Error already logged in mProcReader.
+ return;
+ }
+ if ((bytes.remaining() & 3) != 0) {
+ Slog.wtf(TAG,
+ "Cannot parse active time proc bytes to int: " + bytes.remaining());
+ return;
+ }
+ final IntBuffer buf = bytes.asIntBuffer();
+ final int cores = buf.get();
+ if (mCores != 0 && cores != mCores) {
+ Slog.wtf(TAG, "Cpu active time wrong # cores: " + cores);
+ return;
+ }
+ mCores = cores;
+ if (cores <= 0 || buf.remaining() % (cores + 1) != 0) {
+ Slog.wtf(TAG,
+ "Cpu active time format error: " + buf.remaining() + " / " + (cores
+ + 1));
+ return;
+ }
+ int numUids = buf.remaining() / (cores + 1);
+ for (int i = 0; i < numUids; i++) {
+ processUid.accept(buf);
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Read uids: " + numUids);
+ }
}
}
@@ -72,75 +170,10 @@
}
public void removeUidsInRange(int startUid, int endUid) {
- if (endUid < startUid) {
- Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid);
- return;
- }
mLastUidCpuActiveTimeMs.put(startUid, null);
mLastUidCpuActiveTimeMs.put(endUid, null);
final int firstIndex = mLastUidCpuActiveTimeMs.indexOfKey(startUid);
final int lastIndex = mLastUidCpuActiveTimeMs.indexOfKey(endUid);
mLastUidCpuActiveTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
}
-
- @VisibleForTesting
- public void readDeltaInternal(BufferedReader reader, @Nullable Callback cb) throws IOException {
- String line = reader.readLine();
- if (line == null || !line.startsWith("active:")) {
- Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE));
- return;
- }
- if (mCoreCount == 0) {
- mCoreCount = Integer.parseInt(line.substring(line.indexOf(' ')+1));
- }
- while ((line = reader.readLine()) != null) {
- final int index = line.indexOf(' ');
- final int uid = Integer.parseInt(line.substring(0, index - 1), 10);
- readTimesForUid(uid, line.substring(index + 1), cb);
- }
- }
-
- private void readTimesForUid(int uid, String line, @Nullable Callback cb) {
- long[] lastActiveTime = mLastUidCpuActiveTimeMs.get(uid);
- if (lastActiveTime == null) {
- lastActiveTime = new long[mCoreCount];
- mLastUidCpuActiveTimeMs.put(uid, lastActiveTime);
- }
- final String[] timesStr = line.split(" ");
- if (timesStr.length != mCoreCount) {
- Slog.e(TAG, String.format("# readings don't match # cores, readings: %d, CPU cores: %d",
- timesStr.length, mCoreCount));
- return;
- }
- long sumDeltas = 0;
- final long[] curActiveTime = new long[mCoreCount];
- boolean notify = false;
- for (int i = 0; i < mCoreCount; i++) {
- // Times read will be in units of 10ms
- curActiveTime[i] = Long.parseLong(timesStr[i], 10) * 10;
- long delta = curActiveTime[i] - lastActiveTime[i];
- if (delta < 0 || curActiveTime[i] < 0) {
- if (DEBUG) {
- final StringBuilder sb = new StringBuilder();
- sb.append(String.format("Malformed cpu active time for UID=%d\n", uid));
- sb.append(String.format("data=(%d,%d)\n", lastActiveTime[i], curActiveTime[i]));
- sb.append("times=(");
- TimeUtils.formatDuration(mLastTimeReadMs, sb);
- sb.append(",");
- TimeUtils.formatDuration(mNowTimeMs, sb);
- sb.append(")");
- Slog.e(TAG, sb.toString());
- }
- return;
- }
- notify |= delta > 0;
- sumDeltas += delta / (i + 1);
- }
- if (notify) {
- System.arraycopy(curActiveTime, 0, lastActiveTime, 0, mCoreCount);
- if (cb != null) {
- cb.onUidCpuActiveTime(uid, sumDeltas);
- }
- }
- }
}
diff --git a/com/android/internal/os/KernelUidCpuClusterTimeReader.java b/com/android/internal/os/KernelUidCpuClusterTimeReader.java
index 85153bc..3cbfaea 100644
--- a/com/android/internal/os/KernelUidCpuClusterTimeReader.java
+++ b/com/android/internal/os/KernelUidCpuClusterTimeReader.java
@@ -17,64 +17,212 @@
package com.android.internal.os;
import android.annotation.Nullable;
-import android.os.StrictMode;
-import android.os.SystemClock;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.TimeUtils;
import com.android.internal.annotations.VisibleForTesting;
-import java.io.BufferedReader;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+import java.util.function.Consumer;
/**
- * Reads /proc/uid_concurrent_policy_time which has the format:
- * policy0: X policy4: Y (there are X cores on policy0, Y cores on policy4)
- * [uid0]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ...
- * [uid1]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ...
- * ...
- * Time-X-Y means the time a UID spent on clusterX running concurrently with Y other processes.
+ * Reads binary proc file /proc/uid_cpupower/concurrent_policy_time and reports CPU cluster times
+ * to BatteryStats to compute cluster power. See
+ * {@link PowerProfile#getAveragePowerForCpuCluster(int)}.
+ *
+ * concurrent_policy_time is an array of u32's in the following format:
+ * [n, x0, ..., xn, uid0, time0a, time0b, ..., time0n,
+ * uid1, time1a, time1b, ..., time1n,
+ * uid2, time2a, time2b, ..., time2n, etc.]
+ * where n is the number of policies
+ * xi is the number cpus on a particular policy
+ * Each uidX is followed by x0 time entries corresponding to the time UID X spent on cluster0
+ * running concurrently with 0, 1, 2, ..., x0 - 1 other processes, then followed by x1, ..., xn
+ * time entries.
+ *
* The file contains a monotonically increasing count of time for a single boot. This class
- * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
- * delta.
+ * maintains the previous results of a call to {@link #readDelta} in order to provide a
+ * proper delta.
+ *
+ * This class uses a throttler to reject any {@link #readDelta} call within
+ * {@link #mThrottleInterval}. This is different from the throttler in {@link KernelCpuProcReader},
+ * which has a shorter throttle interval and returns cached result from last read when the request
+ * is throttled.
+ *
+ * This class is NOT thread-safe and NOT designed to be accessed by more than one caller since each
+ * caller has its own view of delta.
*/
-public class KernelUidCpuClusterTimeReader {
+public class KernelUidCpuClusterTimeReader extends
+ KernelUidCpuTimeReaderBase<KernelUidCpuClusterTimeReader.Callback> {
+ private static final String TAG = KernelUidCpuClusterTimeReader.class.getSimpleName();
- private static final boolean DEBUG = false;
- private static final String TAG = "KernelUidCpuClusterTimeReader";
- private static final String UID_TIMES_PROC_FILE = "/proc/uid_concurrent_policy_time";
+ private final KernelCpuProcReader mProcReader;
+ private SparseArray<double[]> mLastUidPolicyTimeMs = new SparseArray<>();
- // mCoreOnCluster[i] is the # of cores on cluster i
- private int[] mCoreOnCluster;
- private int mCores;
- private long mLastTimeReadMs;
- private long mNowTimeMs;
- private SparseArray<long[]> mLastUidPolicyTimeMs = new SparseArray<>();
+ private int mNumClusters = -1;
+ private int mNumCores;
+ private int[] mNumCoresOnCluster;
- public interface Callback {
+ private double[] mCurTime; // Reuse to avoid GC.
+ private long[] mDeltaTime; // Reuse to avoid GC.
+ private long[] mCurTimeRounded; // Reuse to avoid GC.
+
+ public interface Callback extends KernelUidCpuTimeReaderBase.Callback {
/**
- * @param uid
- * @param cpuActiveTimeMs the first dimension is cluster, the second dimension is the # of
- * processes running concurrently with this uid.
+ * Notifies when new data is available.
+ *
+ * @param uid uid int
+ * @param cpuClusterTimeMs an array of times spent by this uid on corresponding clusters.
+ * The array index is the cluster index.
*/
- void onUidCpuPolicyTime(int uid, long[] cpuActiveTimeMs);
+ void onUidCpuPolicyTime(int uid, long[] cpuClusterTimeMs);
}
- public void readDelta(@Nullable Callback cb) {
- final int oldMask = StrictMode.allowThreadDiskReadsMask();
- try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) {
- mNowTimeMs = SystemClock.elapsedRealtime();
- readDeltaInternal(reader, cb);
- mLastTimeReadMs = mNowTimeMs;
- } catch (IOException e) {
- Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
- } finally {
- StrictMode.setThreadPolicyMask(oldMask);
+ public KernelUidCpuClusterTimeReader() {
+ mProcReader = KernelCpuProcReader.getClusterTimeReaderInstance();
+ }
+
+ @VisibleForTesting
+ public KernelUidCpuClusterTimeReader(KernelCpuProcReader procReader) {
+ mProcReader = procReader;
+ }
+
+ @Override
+ protected void readDeltaImpl(@Nullable Callback cb) {
+ readImpl((buf) -> {
+ int uid = buf.get();
+ double[] lastTimes = mLastUidPolicyTimeMs.get(uid);
+ if (lastTimes == null) {
+ lastTimes = new double[mNumClusters];
+ mLastUidPolicyTimeMs.put(uid, lastTimes);
+ }
+ if (!sumClusterTime(buf, mCurTime)) {
+ return;
+ }
+ boolean valid = true;
+ boolean notify = false;
+ for (int i = 0; i < mNumClusters; i++) {
+ mDeltaTime[i] = (long) (mCurTime[i] - lastTimes[i]);
+ if (mDeltaTime[i] < 0) {
+ Slog.e(TAG, "Negative delta from cluster time proc: " + mDeltaTime[i]);
+ valid = false;
+ }
+ notify |= mDeltaTime[i] > 0;
+ }
+ if (notify && valid) {
+ System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
+ if (cb != null) {
+ cb.onUidCpuPolicyTime(uid, mDeltaTime);
+ }
+ }
+ });
+ }
+
+ public void readAbsolute(Callback callback) {
+ readImpl((buf) -> {
+ int uid = buf.get();
+ if (sumClusterTime(buf, mCurTime)) {
+ for (int i = 0; i < mNumClusters; i++) {
+ mCurTimeRounded[i] = (long) mCurTime[i];
+ }
+ callback.onUidCpuPolicyTime(uid, mCurTimeRounded);
+ }
+ });
+ }
+
+ private boolean sumClusterTime(IntBuffer buffer, double[] clusterTime) {
+ boolean valid = true;
+ for (int i = 0; i < mNumClusters; i++) {
+ clusterTime[i] = 0;
+ for (int j = 1; j <= mNumCoresOnCluster[i]; j++) {
+ int time = buffer.get();
+ if (time < 0) {
+ Slog.e(TAG, "Negative time from cluster time proc: " + time);
+ valid = false;
+ }
+ clusterTime[i] += (double) time * 10 / j; // Unit is 10ms.
+ }
}
+ return valid;
+ }
+
+ /**
+ * readImpl accepts a callback to process the uid entry. readDeltaImpl needs to store the last
+ * seen results while processing the buffer, while readAbsolute returns the absolute value read
+ * from the buffer without storing. So readImpl contains the common logic of the two, leaving
+ * the difference to a processUid function.
+ *
+ * @param processUid the callback function to process the uid entry in the buffer.
+ */
+ private void readImpl(Consumer<IntBuffer> processUid) {
+ synchronized (mProcReader) {
+ ByteBuffer bytes = mProcReader.readBytes();
+ if (bytes == null || bytes.remaining() <= 4) {
+ // Error already logged in mProcReader.
+ return;
+ }
+ if ((bytes.remaining() & 3) != 0) {
+ Slog.wtf(TAG,
+ "Cannot parse cluster time proc bytes to int: " + bytes.remaining());
+ return;
+ }
+ IntBuffer buf = bytes.asIntBuffer();
+ final int numClusters = buf.get();
+ if (numClusters <= 0) {
+ Slog.wtf(TAG, "Cluster time format error: " + numClusters);
+ return;
+ }
+ if (mNumClusters == -1) {
+ mNumClusters = numClusters;
+ }
+ if (buf.remaining() < numClusters) {
+ Slog.wtf(TAG, "Too few data left in the buffer: " + buf.remaining());
+ return;
+ }
+ if (mNumCores <= 0) {
+ if (!readCoreInfo(buf, numClusters)) {
+ return;
+ }
+ } else {
+ buf.position(buf.position() + numClusters);
+ }
+
+ if (buf.remaining() % (mNumCores + 1) != 0) {
+ Slog.wtf(TAG,
+ "Cluster time format error: " + buf.remaining() + " / " + (mNumCores
+ + 1));
+ return;
+ }
+ int numUids = buf.remaining() / (mNumCores + 1);
+
+ for (int i = 0; i < numUids; i++) {
+ processUid.accept(buf);
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Read uids: " + numUids);
+ }
+ }
+ }
+
+ // Returns if it has read valid info.
+ private boolean readCoreInfo(IntBuffer buf, int numClusters) {
+ int numCores = 0;
+ int[] numCoresOnCluster = new int[numClusters];
+ for (int i = 0; i < numClusters; i++) {
+ numCoresOnCluster[i] = buf.get();
+ numCores += numCoresOnCluster[i];
+ }
+ if (numCores <= 0) {
+ Slog.e(TAG, "Invalid # cores from cluster time proc file: " + numCores);
+ return false;
+ }
+ mNumCores = numCores;
+ mNumCoresOnCluster = numCoresOnCluster;
+ mCurTime = new double[numClusters];
+ mDeltaTime = new long[numClusters];
+ mCurTimeRounded = new long[numClusters];
+ return true;
}
public void removeUid(int uid) {
@@ -82,97 +230,10 @@
}
public void removeUidsInRange(int startUid, int endUid) {
- if (endUid < startUid) {
- Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid);
- return;
- }
mLastUidPolicyTimeMs.put(startUid, null);
mLastUidPolicyTimeMs.put(endUid, null);
final int firstIndex = mLastUidPolicyTimeMs.indexOfKey(startUid);
final int lastIndex = mLastUidPolicyTimeMs.indexOfKey(endUid);
mLastUidPolicyTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
}
-
- @VisibleForTesting
- public void readDeltaInternal(BufferedReader reader, @Nullable Callback cb) throws IOException {
- String line = reader.readLine();
- if (line == null || !line.startsWith("policy")) {
- Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE));
- return;
- }
- if (mCoreOnCluster == null) {
- List<Integer> list = new ArrayList<>();
- String[] policies = line.split(" ");
-
- if (policies.length == 0 || policies.length % 2 != 0) {
- Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE));
- return;
- }
-
- for (int i = 0; i < policies.length; i+=2) {
- list.add(Integer.parseInt(policies[i+1]));
- }
-
- mCoreOnCluster = new int[list.size()];
- for(int i=0;i<list.size();i++){
- mCoreOnCluster[i] = list.get(i);
- mCores += mCoreOnCluster[i];
- }
- }
- while ((line = reader.readLine()) != null) {
- final int index = line.indexOf(' ');
- final int uid = Integer.parseInt(line.substring(0, index - 1), 10);
- readTimesForUid(uid, line.substring(index + 1), cb);
- }
- }
-
- private void readTimesForUid(int uid, String line, @Nullable Callback cb) {
- long[] lastPolicyTime = mLastUidPolicyTimeMs.get(uid);
- if (lastPolicyTime == null) {
- lastPolicyTime = new long[mCores];
- mLastUidPolicyTimeMs.put(uid, lastPolicyTime);
- }
- final String[] timeStr = line.split(" ");
- if (timeStr.length != mCores) {
- Slog.e(TAG, String.format("# readings don't match # cores, readings: %d, # CPU cores: %d",
- timeStr.length, mCores));
- return;
- }
- final long[] deltaPolicyTime = new long[mCores];
- final long[] currPolicyTime = new long[mCores];
- boolean notify = false;
- for (int i = 0; i < mCores; i++) {
- // Times read will be in units of 10ms
- currPolicyTime[i] = Long.parseLong(timeStr[i], 10) * 10;
- deltaPolicyTime[i] = currPolicyTime[i] - lastPolicyTime[i];
- if (deltaPolicyTime[i] < 0 || currPolicyTime[i] < 0) {
- if (DEBUG) {
- final StringBuilder sb = new StringBuilder();
- sb.append(String.format("Malformed cpu policy time for UID=%d\n", uid));
- sb.append(String.format("data=(%d,%d)\n", lastPolicyTime[i], currPolicyTime[i]));
- sb.append("times=(");
- TimeUtils.formatDuration(mLastTimeReadMs, sb);
- sb.append(",");
- TimeUtils.formatDuration(mNowTimeMs, sb);
- sb.append(")");
- Slog.e(TAG, sb.toString());
- }
- return;
- }
- notify |= deltaPolicyTime[i] > 0;
- }
- if (notify) {
- System.arraycopy(currPolicyTime, 0, lastPolicyTime, 0, mCores);
- if (cb != null) {
- final long[] times = new long[mCoreOnCluster.length];
- int core = 0;
- for (int i = 0; i < mCoreOnCluster.length; i++) {
- for (int j = 0; j < mCoreOnCluster[i]; j++) {
- times[i] += deltaPolicyTime[core++] / (j+1);
- }
- }
- cb.onUidCpuPolicyTime(uid, times);
- }
- }
- }
}
diff --git a/com/android/internal/os/KernelUidCpuFreqTimeReader.java b/com/android/internal/os/KernelUidCpuFreqTimeReader.java
index b8982cc..5b46d0f 100644
--- a/com/android/internal/os/KernelUidCpuFreqTimeReader.java
+++ b/com/android/internal/os/KernelUidCpuFreqTimeReader.java
@@ -21,17 +21,18 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.StrictMode;
-import android.os.SystemClock;
import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.TimeUtils;
import com.android.internal.annotations.VisibleForTesting;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+import java.util.function.Consumer;
/**
* Reads /proc/uid_time_in_state which has the format:
@@ -41,24 +42,39 @@
* [uid2]: [time in freq1] [time in freq2] [time in freq3] ...
* ...
*
+ * Binary variation reads /proc/uid_cpupower/time_in_state in the following format:
+ * [n, uid0, time0a, time0b, ..., time0n,
+ * uid1, time1a, time1b, ..., time1n,
+ * uid2, time2a, time2b, ..., time2n, etc.]
+ * where n is the total number of frequencies.
+ *
* This provides the times a UID's processes spent executing at each different cpu frequency.
* The file contains a monotonically increasing count of time for a single boot. This class
* maintains the previous results of a call to {@link #readDelta} in order to provide a proper
* delta.
+ *
+ * This class uses a throttler to reject any {@link #readDelta} call within
+ * {@link #mThrottleInterval}. This is different from the throttler in {@link KernelCpuProcReader},
+ * which has a shorter throttle interval and returns cached result from last read when the request
+ * is throttled.
+ *
+ * This class is NOT thread-safe and NOT designed to be accessed by more than one caller since each
+ * caller has its own view of delta.
*/
-public class KernelUidCpuFreqTimeReader {
- private static final boolean DEBUG = false;
- private static final String TAG = "KernelUidCpuFreqTimeReader";
- private static final String UID_TIMES_PROC_FILE = "/proc/uid_time_in_state";
+public class KernelUidCpuFreqTimeReader extends
+ KernelUidCpuTimeReaderBase<KernelUidCpuFreqTimeReader.Callback> {
+ private static final String TAG = KernelUidCpuFreqTimeReader.class.getSimpleName();
+ static final String UID_TIMES_PROC_FILE = "/proc/uid_time_in_state";
- public interface Callback {
+ public interface Callback extends KernelUidCpuTimeReaderBase.Callback {
void onUidCpuFreqTime(int uid, long[] cpuFreqTimeMs);
}
private long[] mCpuFreqs;
+ private long[] mCurTimes; // Reuse to prevent GC.
+ private long[] mDeltaTimes; // Reuse to prevent GC.
private int mCpuFreqsCount;
- private long mLastTimeReadMs;
- private long mNowTimeMs;
+ private final KernelCpuProcReader mProcReader;
private SparseArray<long[]> mLastUidCpuFreqTimeMs = new SparseArray<>();
@@ -69,6 +85,15 @@
private boolean mPerClusterTimesAvailable;
private boolean mAllUidTimesAvailable = true;
+ public KernelUidCpuFreqTimeReader() {
+ mProcReader = KernelCpuProcReader.getFreqTimeReaderInstance();
+ }
+
+ @VisibleForTesting
+ public KernelUidCpuFreqTimeReader(KernelCpuProcReader procReader) {
+ mProcReader = procReader;
+ }
+
public boolean perClusterTimesAvailable() {
return mPerClusterTimesAvailable;
}
@@ -83,7 +108,6 @@
public long[] readFreqs(@NonNull PowerProfile powerProfile) {
checkNotNull(powerProfile);
-
if (mCpuFreqs != null) {
// No need to read cpu freqs more than once.
return mCpuFreqs;
@@ -112,106 +136,12 @@
if (line == null) {
return null;
}
- return readCpuFreqs(line, powerProfile);
- }
-
- public void readDelta(@Nullable Callback callback) {
- if (mCpuFreqs == null) {
- return;
- }
- final int oldMask = StrictMode.allowThreadDiskReadsMask();
- try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) {
- mNowTimeMs = SystemClock.elapsedRealtime();
- readDelta(reader, callback);
- mLastTimeReadMs = mNowTimeMs;
- } catch (IOException e) {
- Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
- } finally {
- StrictMode.setThreadPolicyMask(oldMask);
- }
- }
-
- public void removeUid(int uid) {
- mLastUidCpuFreqTimeMs.delete(uid);
- }
-
- public void removeUidsInRange(int startUid, int endUid) {
- if (endUid < startUid) {
- return;
- }
- mLastUidCpuFreqTimeMs.put(startUid, null);
- mLastUidCpuFreqTimeMs.put(endUid, null);
- final int firstIndex = mLastUidCpuFreqTimeMs.indexOfKey(startUid);
- final int lastIndex = mLastUidCpuFreqTimeMs.indexOfKey(endUid);
- mLastUidCpuFreqTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
- }
-
- @VisibleForTesting
- public void readDelta(BufferedReader reader, @Nullable Callback callback) throws IOException {
- String line = reader.readLine();
- if (line == null) {
- return;
- }
- while ((line = reader.readLine()) != null) {
- final int index = line.indexOf(' ');
- final int uid = Integer.parseInt(line.substring(0, index - 1), 10);
- readTimesForUid(uid, line.substring(index + 1, line.length()), callback);
- }
- }
-
- private void readTimesForUid(int uid, String line, Callback callback) {
- long[] uidTimeMs = mLastUidCpuFreqTimeMs.get(uid);
- if (uidTimeMs == null) {
- uidTimeMs = new long[mCpuFreqsCount];
- mLastUidCpuFreqTimeMs.put(uid, uidTimeMs);
- }
- final String[] timesStr = line.split(" ");
- final int size = timesStr.length;
- if (size != uidTimeMs.length) {
- Slog.e(TAG, "No. of readings don't match cpu freqs, readings: " + size
- + " cpuFreqsCount: " + uidTimeMs.length);
- return;
- }
- final long[] deltaUidTimeMs = new long[size];
- final long[] curUidTimeMs = new long[size];
- boolean notify = false;
- for (int i = 0; i < size; ++i) {
- // Times read will be in units of 10ms
- final long totalTimeMs = Long.parseLong(timesStr[i], 10) * 10;
- deltaUidTimeMs[i] = totalTimeMs - uidTimeMs[i];
- // If there is malformed data for any uid, then we just log about it and ignore
- // the data for that uid.
- if (deltaUidTimeMs[i] < 0 || totalTimeMs < 0) {
- if (DEBUG) {
- final StringBuilder sb = new StringBuilder("Malformed cpu freq data for UID=")
- .append(uid).append("\n");
- sb.append("data=").append("(").append(uidTimeMs[i]).append(",")
- .append(totalTimeMs).append(")").append("\n");
- sb.append("times=").append("(");
- TimeUtils.formatDuration(mLastTimeReadMs, sb);
- sb.append(",");
- TimeUtils.formatDuration(mNowTimeMs, sb);
- sb.append(")");
- Slog.e(TAG, sb.toString());
- }
- return;
- }
- curUidTimeMs[i] = totalTimeMs;
- notify = notify || (deltaUidTimeMs[i] > 0);
- }
- if (notify) {
- System.arraycopy(curUidTimeMs, 0, uidTimeMs, 0, size);
- if (callback != null) {
- callback.onUidCpuFreqTime(uid, deltaUidTimeMs);
- }
- }
- }
-
- private long[] readCpuFreqs(String line, PowerProfile powerProfile) {
final String[] freqStr = line.split(" ");
// First item would be "uid: " which needs to be ignored.
mCpuFreqsCount = freqStr.length - 1;
mCpuFreqs = new long[mCpuFreqsCount];
+ mCurTimes = new long[mCpuFreqsCount];
+ mDeltaTimes = new long[mCpuFreqsCount];
for (int i = 0; i < mCpuFreqsCount; ++i) {
mCpuFreqs[i] = Long.parseLong(freqStr[i + 1], 10);
}
@@ -231,10 +161,116 @@
mPerClusterTimesAvailable = false;
}
Slog.i(TAG, "mPerClusterTimesAvailable=" + mPerClusterTimesAvailable);
-
return mCpuFreqs;
}
+ @Override
+ @VisibleForTesting
+ public void readDeltaImpl(@Nullable Callback callback) {
+ if (mCpuFreqs == null) {
+ return;
+ }
+ readImpl((buf) -> {
+ int uid = buf.get();
+ long[] lastTimes = mLastUidCpuFreqTimeMs.get(uid);
+ if (lastTimes == null) {
+ lastTimes = new long[mCpuFreqsCount];
+ mLastUidCpuFreqTimeMs.put(uid, lastTimes);
+ }
+ if (!getFreqTimeForUid(buf, mCurTimes)) {
+ return;
+ }
+ boolean notify = false;
+ boolean valid = true;
+ for (int i = 0; i < mCpuFreqsCount; i++) {
+ mDeltaTimes[i] = mCurTimes[i] - lastTimes[i];
+ if (mDeltaTimes[i] < 0) {
+ Slog.e(TAG, "Negative delta from freq time proc: " + mDeltaTimes[i]);
+ valid = false;
+ }
+ notify |= mDeltaTimes[i] > 0;
+ }
+ if (notify && valid) {
+ System.arraycopy(mCurTimes, 0, lastTimes, 0, mCpuFreqsCount);
+ if (callback != null) {
+ callback.onUidCpuFreqTime(uid, mDeltaTimes);
+ }
+ }
+ });
+ }
+
+ public void readAbsolute(Callback callback) {
+ readImpl((buf) -> {
+ int uid = buf.get();
+ if (getFreqTimeForUid(buf, mCurTimes)) {
+ callback.onUidCpuFreqTime(uid, mCurTimes);
+ }
+ });
+ }
+
+ private boolean getFreqTimeForUid(IntBuffer buffer, long[] freqTime) {
+ boolean valid = true;
+ for (int i = 0; i < mCpuFreqsCount; i++) {
+ freqTime[i] = (long) buffer.get() * 10; // Unit is 10ms.
+ if (freqTime[i] < 0) {
+ Slog.e(TAG, "Negative time from freq time proc: " + freqTime[i]);
+ valid = false;
+ }
+ }
+ return valid;
+ }
+
+ /**
+ * readImpl accepts a callback to process the uid entry. readDeltaImpl needs to store the last
+ * seen results while processing the buffer, while readAbsolute returns the absolute value read
+ * from the buffer without storing. So readImpl contains the common logic of the two, leaving
+ * the difference to a processUid function.
+ *
+ * @param processUid the callback function to process the uid entry in the buffer.
+ */
+ private void readImpl(Consumer<IntBuffer> processUid) {
+ synchronized (mProcReader) {
+ ByteBuffer bytes = mProcReader.readBytes();
+ if (bytes == null || bytes.remaining() <= 4) {
+ // Error already logged in mProcReader.
+ return;
+ }
+ if ((bytes.remaining() & 3) != 0) {
+ Slog.wtf(TAG, "Cannot parse freq time proc bytes to int: " + bytes.remaining());
+ return;
+ }
+ IntBuffer buf = bytes.asIntBuffer();
+ final int freqs = buf.get();
+ if (freqs != mCpuFreqsCount) {
+ Slog.wtf(TAG, "Cpu freqs expect " + mCpuFreqsCount + " , got " + freqs);
+ return;
+ }
+ if (buf.remaining() % (freqs + 1) != 0) {
+ Slog.wtf(TAG, "Freq time format error: " + buf.remaining() + " / " + (freqs + 1));
+ return;
+ }
+ int numUids = buf.remaining() / (freqs + 1);
+ for (int i = 0; i < numUids; i++) {
+ processUid.accept(buf);
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Read uids: #" + numUids);
+ }
+ }
+ }
+
+ public void removeUid(int uid) {
+ mLastUidCpuFreqTimeMs.delete(uid);
+ }
+
+ public void removeUidsInRange(int startUid, int endUid) {
+ mLastUidCpuFreqTimeMs.put(startUid, null);
+ mLastUidCpuFreqTimeMs.put(endUid, null);
+ final int firstIndex = mLastUidCpuFreqTimeMs.indexOfKey(startUid);
+ final int lastIndex = mLastUidCpuFreqTimeMs.indexOfKey(endUid);
+ mLastUidCpuFreqTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
+ }
+
/**
* Extracts no. of cpu clusters and no. of freqs in each of these clusters from the freqs
* read from the proc file.
diff --git a/com/android/internal/os/KernelUidCpuTimeReader.java b/com/android/internal/os/KernelUidCpuTimeReader.java
index 65615c0..97b7211 100644
--- a/com/android/internal/os/KernelUidCpuTimeReader.java
+++ b/com/android/internal/os/KernelUidCpuTimeReader.java
@@ -38,18 +38,19 @@
* maintains the previous results of a call to {@link #readDelta} in order to provide a proper
* delta.
*/
-public class KernelUidCpuTimeReader {
- private static final String TAG = "KernelUidCpuTimeReader";
+public class KernelUidCpuTimeReader extends
+ KernelUidCpuTimeReaderBase<KernelUidCpuTimeReader.Callback> {
+ private static final String TAG = KernelUidCpuTimeReader.class.getSimpleName();
private static final String sProcFile = "/proc/uid_cputime/show_uid_stat";
private static final String sRemoveUidProcFile = "/proc/uid_cputime/remove_uid_range";
/**
* Callback interface for processing each line of the proc file.
*/
- public interface Callback {
+ public interface Callback extends KernelUidCpuTimeReaderBase.Callback {
/**
- * @param uid UID of the app
- * @param userTimeUs time spent executing in user space in microseconds
+ * @param uid UID of the app
+ * @param userTimeUs time spent executing in user space in microseconds
* @param systemTimeUs time spent executing in kernel space in microseconds
*/
void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs);
@@ -61,11 +62,13 @@
/**
* Reads the proc file, calling into the callback with a delta of time for each UID.
+ *
* @param callback The callback to invoke for each line of the proc file. If null,
* the data is consumed and subsequent calls to readDelta will provide
* a fresh delta.
*/
- public void readDelta(@Nullable Callback callback) {
+ @Override
+ protected void readDeltaImpl(@Nullable Callback callback) {
final int oldMask = StrictMode.allowThreadDiskReadsMask();
long nowUs = SystemClock.elapsedRealtime() * 1000;
try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) {
@@ -78,10 +81,11 @@
final long userTimeUs = Long.parseLong(splitter.next(), 10);
final long systemTimeUs = Long.parseLong(splitter.next(), 10);
+ boolean notifyCallback = false;
+ long userTimeDeltaUs = userTimeUs;
+ long systemTimeDeltaUs = systemTimeUs;
// Only report if there is a callback and if this is not the first read.
if (callback != null && mLastTimeReadUs != 0) {
- long userTimeDeltaUs = userTimeUs;
- long systemTimeDeltaUs = systemTimeUs;
int index = mLastUserTimeUs.indexOfKey(uid);
if (index >= 0) {
userTimeDeltaUs -= mLastUserTimeUs.valueAt(index);
@@ -114,12 +118,13 @@
}
}
- if (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0) {
- callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs);
- }
+ notifyCallback = (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0);
}
mLastUserTimeUs.put(uid, userTimeUs);
mLastSystemTimeUs.put(uid, systemTimeUs);
+ if (notifyCallback) {
+ callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs);
+ }
}
} catch (IOException e) {
Slog.e(TAG, "Failed to read uid_cputime: " + e.getMessage());
@@ -130,7 +135,34 @@
}
/**
- * Removes the UID from the kernel module and from internal accounting data.
+ * Reads the proc file, calling into the callback with raw absolute value of time for each UID.
+ * @param callback The callback to invoke for each line of the proc file.
+ */
+ public void readAbsolute(Callback callback) {
+ final int oldMask = StrictMode.allowThreadDiskReadsMask();
+ try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) {
+ TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' ');
+ String line;
+ while ((line = reader.readLine()) != null) {
+ splitter.setString(line);
+ final String uidStr = splitter.next();
+ final int uid = Integer.parseInt(uidStr.substring(0, uidStr.length() - 1), 10);
+ final long userTimeUs = Long.parseLong(splitter.next(), 10);
+ final long systemTimeUs = Long.parseLong(splitter.next(), 10);
+ callback.onUidCpuTime(uid, userTimeUs, systemTimeUs);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read uid_cputime: " + e.getMessage());
+ } finally {
+ StrictMode.setThreadPolicyMask(oldMask);
+ }
+ }
+
+ /**
+ * Removes the UID from the kernel module and from internal accounting data. Only
+ * {@link BatteryStatsImpl} and its child processes should call this, as the change on Kernel is
+ * visible system wide.
+ *
* @param uid The UID to remove.
*/
public void removeUid(int uid) {
@@ -143,9 +175,12 @@
}
/**
- * Removes UIDs in a given range from the kernel module and internal accounting data.
+ * Removes UIDs in a given range from the kernel module and internal accounting data. Only
+ * {@link BatteryStatsImpl} and its child processes should call this, as the change on Kernel is
+ * visible system wide.
+ *
* @param startUid the first uid to remove
- * @param endUid the last uid to remove
+ * @param endUid the last uid to remove
*/
public void removeUidsInRange(int startUid, int endUid) {
if (endUid < startUid) {
diff --git a/com/android/internal/os/KernelUidCpuTimeReaderBase.java b/com/android/internal/os/KernelUidCpuTimeReaderBase.java
new file mode 100644
index 0000000..11e50e1
--- /dev/null
+++ b/com/android/internal/os/KernelUidCpuTimeReaderBase.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 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 android.annotation.Nullable;
+import android.os.SystemClock;
+import android.util.Slog;
+
+/**
+ * The base class of all KernelUidCpuTimeReaders.
+ *
+ * This class is NOT designed to be thread-safe or accessed by more than one caller (due to
+ * the nature of {@link #readDelta(Callback)}).
+ */
+public abstract class KernelUidCpuTimeReaderBase<T extends KernelUidCpuTimeReaderBase.Callback> {
+ protected static final boolean DEBUG = false;
+ // Throttle interval in milliseconds
+ private static final long DEFAULT_THROTTLE_INTERVAL = 10_000L;
+
+ private final String TAG = this.getClass().getSimpleName();
+ private long mLastTimeReadMs = Long.MIN_VALUE;
+ private long mThrottleInterval = DEFAULT_THROTTLE_INTERVAL;
+
+ // A generic Callback interface (used by readDelta) to be extended by subclasses.
+ public interface Callback {
+ }
+
+ public void readDelta(@Nullable T cb) {
+ if (SystemClock.elapsedRealtime() < mLastTimeReadMs + mThrottleInterval) {
+ if (DEBUG) {
+ Slog.d(TAG, "Throttle");
+ }
+ return;
+ }
+ readDeltaImpl(cb);
+ mLastTimeReadMs = SystemClock.elapsedRealtime();
+ }
+
+ protected abstract void readDeltaImpl(@Nullable T cb);
+
+ public void setThrottleInterval(long throttleInterval) {
+ if (throttleInterval >= 0) {
+ mThrottleInterval = throttleInterval;
+ }
+ }
+}
diff --git a/com/android/internal/os/KernelWakelockReader.java b/com/android/internal/os/KernelWakelockReader.java
index 7178ec7..46667d1 100644
--- a/com/android/internal/os/KernelWakelockReader.java
+++ b/com/android/internal/os/KernelWakelockReader.java
@@ -16,6 +16,7 @@
package com.android.internal.os;
import android.os.Process;
+import android.os.StrictMode;
import android.os.SystemClock;
import android.util.Slog;
@@ -69,6 +70,7 @@
boolean wakeup_sources;
final long startTime = SystemClock.uptimeMillis();
+ final int oldMask = StrictMode.allowThreadDiskReadsMask();
try {
FileInputStream is;
try {
@@ -90,6 +92,8 @@
} catch (java.io.IOException e) {
Slog.wtf(TAG, "failed to read kernel wakelocks", e);
return null;
+ } finally {
+ StrictMode.setThreadPolicyMask(oldMask);
}
final long readTime = SystemClock.uptimeMillis() - startTime;
diff --git a/com/android/internal/os/MediaPowerCalculator.java b/com/android/internal/os/MediaPowerCalculator.java
new file mode 100644
index 0000000..a35c134
--- /dev/null
+++ b/com/android/internal/os/MediaPowerCalculator.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 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 android.os.BatteryStats;
+
+/**
+ * A {@link PowerCalculator} to calculate power consumed by audio and video hardware.
+ *
+ * Also see {@link PowerProfile#POWER_AUDIO} and {@link PowerProfile#POWER_VIDEO}.
+ */
+public class MediaPowerCalculator extends PowerCalculator {
+ private static final int MS_IN_HR = 1000 * 60 * 60;
+ private final double mAudioAveragePowerMa;
+ private final double mVideoAveragePowerMa;
+
+ public MediaPowerCalculator(PowerProfile profile) {
+ mAudioAveragePowerMa = profile.getAveragePower(PowerProfile.POWER_AUDIO);
+ mVideoAveragePowerMa = profile.getAveragePower(PowerProfile.POWER_VIDEO);
+ }
+
+ @Override
+ public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+ long rawUptimeUs, int statsType) {
+ // Calculate audio power usage, an estimate based on the average power routed to different
+ // components like speaker, bluetooth, usb-c, earphone, etc.
+ final BatteryStats.Timer audioTimer = u.getAudioTurnedOnTimer();
+ if (audioTimer == null) {
+ app.audioTimeMs = 0;
+ app.audioPowerMah = 0;
+ } else {
+ final long totalTime = audioTimer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000;
+ app.audioTimeMs = totalTime;
+ app.audioPowerMah = (totalTime * mAudioAveragePowerMa) / MS_IN_HR;
+ }
+
+ // Calculate video power usage.
+ final BatteryStats.Timer videoTimer = u.getVideoTurnedOnTimer();
+ if (videoTimer == null) {
+ app.videoTimeMs = 0;
+ app.videoPowerMah = 0;
+ } else {
+ final long totalTime = videoTimer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000;
+ app.videoTimeMs = totalTime;
+ app.videoPowerMah = (totalTime * mVideoAveragePowerMa) / MS_IN_HR;
+ }
+ }
+}
diff --git a/com/android/internal/os/PowerProfile.java b/com/android/internal/os/PowerProfile.java
index f4436d3..246a50f 100644
--- a/com/android/internal/os/PowerProfile.java
+++ b/com/android/internal/os/PowerProfile.java
@@ -38,17 +38,12 @@
*/
public class PowerProfile {
- /**
- * No power consumption, or accounted for elsewhere.
- */
- public static final String POWER_NONE = "none";
-
- /**
+ /*
* POWER_CPU_SUSPEND: Power consumption when CPU is in power collapse mode.
* POWER_CPU_IDLE: Power consumption when CPU is awake (when a wake lock is held). This should
* be zero on devices that can go into full CPU power collapse even when a wake
* lock is held. Otherwise, this is the power consumption in addition to
- * POWER_CPU_SUSPEND due to a wake lock being held but with no CPU activity.
+ * POWER_CPU_SUSPEND due to a wake lock being held but with no CPU activity.
* POWER_CPU_ACTIVE: Power consumption when CPU is running, excluding power consumed by clusters
* and cores.
*
@@ -84,7 +79,6 @@
// Updated power constants. These are not estimated, they are real world
// currents and voltages for the underlying bluetooth and wifi controllers.
//
-
public static final String POWER_WIFI_CONTROLLER_IDLE = "wifi.controller.idle";
public static final String POWER_WIFI_CONTROLLER_RX = "wifi.controller.rx";
public static final String POWER_WIFI_CONTROLLER_TX = "wifi.controller.tx";
@@ -104,7 +98,7 @@
public static final String POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE =
"modem.controller.voltage";
- /**
+ /**
* Power consumption when GPS is on.
*/
public static final String POWER_GPS_ON = "gps.on";
@@ -117,6 +111,7 @@
/**
* Power consumption when Bluetooth driver is on.
+ *
* @deprecated
*/
@Deprecated
@@ -124,6 +119,7 @@
/**
* Power consumption when Bluetooth driver is transmitting/receiving.
+ *
* @deprecated
*/
@Deprecated
@@ -131,11 +127,16 @@
/**
* Power consumption when Bluetooth driver gets an AT command.
+ *
* @deprecated
*/
@Deprecated
public static final String POWER_BLUETOOTH_AT_CMD = "bluetooth.at";
+ /**
+ * Power consumption when screen is in doze/ambient/always-on mode, including backlight power.
+ */
+ public static final String POWER_AMBIENT_DISPLAY = "ambient.on";
/**
* Power consumption when screen is on, not including the backlight power.
@@ -167,13 +168,13 @@
* Power consumed by the audio hardware when playing back audio content. This is in addition
* to the CPU power, probably due to a DSP and / or amplifier.
*/
- public static final String POWER_AUDIO = "dsp.audio";
+ public static final String POWER_AUDIO = "audio";
/**
* Power consumed by any media hardware when playing back video content. This is in addition
* to the CPU power, probably due to a DSP.
*/
- public static final String POWER_VIDEO = "dsp.video";
+ public static final String POWER_VIDEO = "video";
/**
* Average power consumption when camera flashlight is on.
@@ -405,6 +406,7 @@
/**
* Returns the number of memory bandwidth buckets defined in power_profile.xml, or a
* default value if the subsystem has no recorded value.
+ *
* @return the number of memory bandwidth buckets.
*/
public int getNumElements(String key) {
@@ -419,7 +421,8 @@
/**
* Returns the average current in mA consumed by the subsystem, or the given
* default value if the subsystem has no recorded value.
- * @param type the subsystem type
+ *
+ * @param type the subsystem type
* @param defaultValue the value to return if the subsystem has no recorded value.
* @return the average current in milliAmps.
*/
@@ -435,19 +438,21 @@
/**
* Returns the average current in mA consumed by the subsystem
+ *
* @param type the subsystem type
* @return the average current in milliAmps.
*/
public double getAveragePower(String type) {
return getAveragePowerOrDefault(type, 0);
}
-
+
/**
* Returns the average current in mA consumed by the subsystem for the given level.
- * @param type the subsystem type
+ *
+ * @param type the subsystem type
* @param level the level of power at which the subsystem is running. For instance, the
- * signal strength of the cell network between 0 and 4 (if there are 4 bars max.)
- * If there is no data for multiple levels, the level is ignored.
+ * signal strength of the cell network between 0 and 4 (if there are 4 bars max.)
+ * If there is no data for multiple levels, the level is ignored.
* @return the average current in milliAmps.
*/
public double getAveragePower(String type, int level) {
@@ -470,6 +475,7 @@
/**
* Returns the battery capacity, if available, in milli Amp Hours. If not available,
* it returns zero.
+ *
* @return the battery capacity in mAh
*/
public double getBatteryCapacity() {
diff --git a/com/android/internal/os/RoSystemProperties.java b/com/android/internal/os/RoSystemProperties.java
index 89a4e17..dc660a4 100644
--- a/com/android/internal/os/RoSystemProperties.java
+++ b/com/android/internal/os/RoSystemProperties.java
@@ -31,6 +31,8 @@
SystemProperties.get("ro.control_privapp_permissions");
// ------ ro.config.* -------- //
+ public static final boolean CONFIG_AVOID_GFX_ACCEL =
+ SystemProperties.getBoolean("ro.config.avoid_gfx_accel", false);
public static final boolean CONFIG_LOW_RAM =
SystemProperties.getBoolean("ro.config.low_ram", false);
public static final boolean CONFIG_SMALL_BATTERY =
diff --git a/com/android/internal/os/RuntimeInit.java b/com/android/internal/os/RuntimeInit.java
index 66475e4..bb5a0ad 100644
--- a/com/android/internal/os/RuntimeInit.java
+++ b/com/android/internal/os/RuntimeInit.java
@@ -71,10 +71,11 @@
public void uncaughtException(Thread t, Throwable e) {
// Don't re-enter if KillApplicationHandler has already run
if (mCrashing) return;
- if (mApplicationObject == null) {
- // The "FATAL EXCEPTION" string is still used on Android even though
- // apps can set a custom UncaughtExceptionHandler that renders uncaught
- // exceptions non-fatal.
+
+ // mApplicationObject is null for non-zygote java programs (e.g. "am")
+ // There are also apps running with the system UID. We don't want the
+ // first clause in either of these two cases, only for system_server.
+ if (mApplicationObject == null && (Process.SYSTEM_UID == Process.myUid())) {
Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
} else {
StringBuilder message = new StringBuilder();
@@ -229,7 +230,7 @@
* @param argv Argument vector for main()
* @param classLoader the classLoader to load {@className} with
*/
- private static Runnable findStaticMain(String className, String[] argv,
+ protected static Runnable findStaticMain(String className, String[] argv,
ClassLoader classLoader) {
Class<?> cl;
diff --git a/com/android/internal/os/WebViewZygoteInit.java b/com/android/internal/os/WebViewZygoteInit.java
index cadb66a..9f2434e 100644
--- a/com/android/internal/os/WebViewZygoteInit.java
+++ b/com/android/internal/os/WebViewZygoteInit.java
@@ -18,13 +18,16 @@
import android.app.ApplicationLoaders;
import android.net.LocalSocket;
+import android.net.LocalServerSocket;
import android.os.Build;
import android.system.ErrnoException;
import android.system.Os;
+import android.system.OsConstants;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.WebViewFactory;
import android.webkit.WebViewFactoryProvider;
+import android.webkit.WebViewLibraryLoader;
import java.io.DataOutputStream;
import java.io.File;
@@ -69,7 +72,8 @@
}
@Override
- protected void handlePreloadPackage(String packagePath, String libsPath, String cacheKey) {
+ protected void handlePreloadPackage(String packagePath, String libsPath, String libFileName,
+ String cacheKey) {
Log.i(TAG, "Beginning package preload");
// Ask ApplicationLoaders to create and cache a classloader for the WebView APK so that
// our children will reuse the same classloader instead of creating their own.
@@ -78,6 +82,10 @@
ClassLoader loader = ApplicationLoaders.getDefault().createAndCacheWebViewClassLoader(
packagePath, libsPath, cacheKey);
+ // Load the native library using WebViewLibraryLoader to share the RELRO data with other
+ // processes.
+ WebViewLibraryLoader.loadNativeLibrary(loader, libFileName);
+
// Add the APK to the Zygote's list of allowed files for children.
String[] packageList = TextUtils.split(packagePath, File.pathSeparator);
for (String packageEntry : packageList) {
@@ -118,18 +126,35 @@
}
public static void main(String argv[]) {
- sServer = new WebViewZygoteServer();
+ Log.i(TAG, "Starting WebViewZygoteInit");
- // Zygote goes into its own process group.
- try {
- Os.setpgid(0, 0);
- } catch (ErrnoException ex) {
- throw new RuntimeException("Failed to setpgid(0,0)", ex);
+ String socketName = null;
+ for (String arg : argv) {
+ Log.i(TAG, arg);
+ if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) {
+ socketName = arg.substring(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG.length());
+ }
}
+ if (socketName == null) {
+ throw new RuntimeException("No " + Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + " specified");
+ }
+
+ try {
+ Os.prctl(OsConstants.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ } catch (ErrnoException ex) {
+ throw new RuntimeException("Failed to set PR_SET_NO_NEW_PRIVS", ex);
+ }
+
+ sServer = new WebViewZygoteServer();
final Runnable caller;
try {
- sServer.registerServerSocket("webview_zygote");
+ sServer.registerServerSocketAtAbstractName(socketName);
+
+ // Add the abstract socket to the FD whitelist so that the native zygote code
+ // can properly detach it after forking.
+ Zygote.nativeAllowFileAcrossFork("ABSTRACT/" + socketName);
+
// The select loop returns early in the child process after a fork and
// loops forever in the zygote.
caller = sServer.runSelectLoop(TextUtils.join(",", Build.SUPPORTED_ABIS));
diff --git a/com/android/internal/os/WrapperInit.java b/com/android/internal/os/WrapperInit.java
index 4901080..f0e7796 100644
--- a/com/android/internal/os/WrapperInit.java
+++ b/com/android/internal/os/WrapperInit.java
@@ -115,6 +115,14 @@
command.append(' ');
command.append(appProcess);
+ // Generate bare minimum of debug information to be able to backtrace through JITed code.
+ // We assume that if the invoke wrapper is used, backtraces are desirable:
+ // * The wrap.sh script can only be used by debuggable apps, which would enable this flag
+ // without the script anyway (the fork-zygote path). So this makes the two consistent.
+ // * The wrap.* property can only be used on userdebug builds and is likely to be used by
+ // developers (e.g. enable debug-malloc), in which case backtraces are also useful.
+ command.append(" -Xcompiler-option --generate-mini-debug-info");
+
command.append(" /system/bin --application");
if (niceName != null) {
command.append(" '--nice-name=").append(niceName).append("'");
diff --git a/com/android/internal/os/Zygote.java b/com/android/internal/os/Zygote.java
index f9a2341..cbd3ad5 100644
--- a/com/android/internal/os/Zygote.java
+++ b/com/android/internal/os/Zygote.java
@@ -53,8 +53,21 @@
public static final int DISABLE_VERIFIER = 1 << 9;
/** Only use oat files located in /system. Otherwise use dex/jar/apk . */
public static final int ONLY_USE_SYSTEM_OAT_FILES = 1 << 10;
- /** Do not enfore hidden API access restrictions. */
- public static final int DISABLE_HIDDEN_API_CHECKS = 1 << 11;
+ /** Force generation of native debugging information for backtraces. */
+ public static final int DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 11;
+ /**
+ * Hidden API access restrictions. This is a mask for bits representing the API enforcement
+ * policy, defined by {@code @ApplicationInfo.HiddenApiEnforcementPolicy}.
+ */
+ public static final int API_ENFORCEMENT_POLICY_MASK = (1 << 12) | (1 << 13);
+ /**
+ * Bit shift for use with {@link #API_ENFORCEMENT_POLICY_MASK}.
+ *
+ * (flags & API_ENFORCEMENT_POLICY_MASK) >> API_ENFORCEMENT_POLICY_SHIFT gives
+ * @ApplicationInfo.ApiEnforcementPolicy values.
+ */
+ public static final int API_ENFORCEMENT_POLICY_SHIFT =
+ Integer.numberOfTrailingZeros(API_ENFORCEMENT_POLICY_MASK);
/** No external storage should be mounted. */
public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
@@ -67,6 +80,13 @@
private static final ZygoteHooks VM_HOOKS = new ZygoteHooks();
+ /**
+ * An extraArg passed when a zygote process is forking a child-zygote, specifying a name
+ * in the abstract socket namespace. This socket name is what the new child zygote
+ * should listen for connections on.
+ */
+ public static final String CHILD_ZYGOTE_SOCKET_NAME_ARG = "--zygote-socket=";
+
private Zygote() {}
/** Called for some security initialization before any fork. */
@@ -98,6 +118,8 @@
* @param fdsToIgnore null-ok an array of ints, either null or holding
* one or more POSIX file descriptor numbers that are to be ignored
* in the file descriptor table check.
+ * @param startChildZygote if true, the new child process will itself be a
+ * new zygote process.
* @param instructionSet null-ok the instruction set to use.
* @param appDataDir null-ok the data directory of the app.
*
@@ -106,13 +128,13 @@
*/
public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
- int[] fdsToIgnore, String instructionSet, String appDataDir) {
+ int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir) {
VM_HOOKS.preFork();
// Resets nice priority for zygote process.
resetNicePriority();
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
- fdsToIgnore, instructionSet, appDataDir);
+ fdsToIgnore, startChildZygote, instructionSet, appDataDir);
// Enable tracing as soon as possible for the child process.
if (pid == 0) {
Trace.setTracingEnabled(true, runtimeFlags);
@@ -126,7 +148,7 @@
native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
- int[] fdsToIgnore, String instructionSet, String appDataDir);
+ int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir);
/**
* Called to do any initialization before starting an application.
@@ -158,9 +180,6 @@
*/
public static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
- // SystemServer is always allowed to use hidden APIs.
- runtimeFlags |= DISABLE_HIDDEN_API_CHECKS;
-
VM_HOOKS.preFork();
// Resets nice priority for zygote process.
resetNicePriority();
@@ -189,8 +208,8 @@
native protected static void nativeUnmountStorageOnInit();
private static void callPostForkChildHooks(int runtimeFlags, boolean isSystemServer,
- String instructionSet) {
- VM_HOOKS.postForkChild(runtimeFlags, isSystemServer, instructionSet);
+ boolean isZygote, String instructionSet) {
+ VM_HOOKS.postForkChild(runtimeFlags, isSystemServer, isZygote, instructionSet);
}
/**
diff --git a/com/android/internal/os/ZygoteConnection.java b/com/android/internal/os/ZygoteConnection.java
index 6a87b1f..5d40a73 100644
--- a/com/android/internal/os/ZygoteConnection.java
+++ b/com/android/internal/os/ZygoteConnection.java
@@ -47,6 +47,8 @@
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Arrays;
+
import libcore.io.IoUtils;
/**
@@ -155,7 +157,12 @@
if (parsedArgs.preloadPackage != null) {
handlePreloadPackage(parsedArgs.preloadPackage, parsedArgs.preloadPackageLibs,
- parsedArgs.preloadPackageCacheKey);
+ parsedArgs.preloadPackageLibFileName, parsedArgs.preloadPackageCacheKey);
+ return null;
+ }
+
+ if (parsedArgs.apiBlacklistExemptions != null) {
+ handleApiBlacklistExemptions(parsedArgs.apiBlacklistExemptions);
return null;
}
@@ -221,8 +228,8 @@
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
- parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,
- parsedArgs.appDataDir);
+ parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote,
+ parsedArgs.instructionSet, parsedArgs.appDataDir);
try {
if (pid == 0) {
@@ -233,7 +240,8 @@
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
- return handleChildProc(parsedArgs, descriptors, childPipeFd);
+ return handleChildProc(parsedArgs, descriptors, childPipeFd,
+ parsedArgs.startChildZygote);
} else {
// In the parent. A pid < 0 indicates a failure and will be handled in
// handleParentProc.
@@ -277,6 +285,15 @@
}
}
+ private void handleApiBlacklistExemptions(String[] exemptions) {
+ try {
+ ZygoteInit.setApiBlacklistExemptions(exemptions);
+ mSocketOutStream.writeInt(0);
+ } catch (IOException ioe) {
+ throw new IllegalStateException("Error writing to command socket", ioe);
+ }
+ }
+
protected void preload() {
ZygoteInit.lazyPreload();
}
@@ -289,7 +306,8 @@
return mSocketOutStream;
}
- protected void handlePreloadPackage(String packagePath, String libsPath, String cacheKey) {
+ protected void handlePreloadPackage(String packagePath, String libsPath, String libFileName,
+ String cacheKey) {
throw new RuntimeException("Zyogte does not support package preloading");
}
@@ -401,10 +419,24 @@
String appDataDir;
/**
- * Whether to preload a package, with the package path in the remainingArgs.
+ * The APK path of the package to preload, when using --preload-package.
*/
String preloadPackage;
+
+ /**
+ * The native library path of the package to preload, when using --preload-package.
+ */
String preloadPackageLibs;
+
+ /**
+ * The filename of the native library to preload, when using --preload-package.
+ */
+ String preloadPackageLibFileName;
+
+ /**
+ * The cache key under which to enter the preloaded package into the classloader cache,
+ * when using --preload-package.
+ */
String preloadPackageCacheKey;
/**
@@ -415,6 +447,20 @@
boolean preloadDefault;
/**
+ * Whether this is a request to start a zygote process as a child of this zygote.
+ * Set with --start-child-zygote. The remaining arguments must include the
+ * CHILD_ZYGOTE_SOCKET_NAME_ARG flag to indicate the abstract socket name that
+ * should be used for communication.
+ */
+ boolean startChildZygote;
+
+ /**
+ * Exemptions from API blacklisting. These are sent to the pre-forked zygote at boot time,
+ * or when they change, via --set-api-blacklist-exemptions.
+ */
+ String[] apiBlacklistExemptions;
+
+ /**
* Constructs instance and parses args
* @param args zygote command-line args
* @throws IllegalArgumentException
@@ -562,9 +608,17 @@
} else if (arg.equals("--preload-package")) {
preloadPackage = args[++curArg];
preloadPackageLibs = args[++curArg];
+ preloadPackageLibFileName = args[++curArg];
preloadPackageCacheKey = args[++curArg];
} else if (arg.equals("--preload-default")) {
preloadDefault = true;
+ } else if (arg.equals("--start-child-zygote")) {
+ startChildZygote = true;
+ } else if (arg.equals("--set-api-blacklist-exemptions")) {
+ // consume all remaining args; this is a stand-alone command, never included
+ // with the regular fork command.
+ apiBlacklistExemptions = Arrays.copyOfRange(args, curArg + 1, args.length);
+ curArg = args.length;
} else {
break;
}
@@ -579,7 +633,7 @@
throw new IllegalArgumentException(
"Unexpected arguments after --preload-package.");
}
- } else if (!preloadDefault) {
+ } else if (!preloadDefault && apiBlacklistExemptions == null) {
if (!seenRuntimeArgs) {
throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
}
@@ -587,6 +641,20 @@
remainingArgs = new String[args.length - curArg];
System.arraycopy(args, curArg, remainingArgs, 0, remainingArgs.length);
}
+
+ if (startChildZygote) {
+ boolean seenChildSocketArg = false;
+ for (String arg : remainingArgs) {
+ if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) {
+ seenChildSocketArg = true;
+ break;
+ }
+ }
+ if (!seenChildSocketArg) {
+ throw new IllegalArgumentException("--start-child-zygote specified " +
+ "without " + Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG);
+ }
+ }
}
}
@@ -739,9 +807,10 @@
* @param parsedArgs non-null; zygote args
* @param descriptors null-ok; new file descriptors for stdio if available.
* @param pipeFd null-ok; pipe for communication back to Zygote.
+ * @param isZygote whether this new child process is itself a new Zygote.
*/
private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,
- FileDescriptor pipeFd) {
+ FileDescriptor pipeFd, boolean isZygote) {
/**
* By the time we get here, the native code has closed the two actual Zygote
* socket connections, and substituted /dev/null in their place. The LocalSocket
@@ -778,8 +847,13 @@
// Should not get here.
throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
} else {
- return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,
- null /* classLoader */);
+ if (!isZygote) {
+ return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,
+ null /* classLoader */);
+ } else {
+ return ZygoteInit.childZygoteInit(parsedArgs.targetSdkVersion,
+ parsedArgs.remainingArgs, null /* classLoader */);
+ }
}
}
diff --git a/com/android/internal/os/ZygoteInit.java b/com/android/internal/os/ZygoteInit.java
index 5659470..c5d41db 100644
--- a/com/android/internal/os/ZygoteInit.java
+++ b/com/android/internal/os/ZygoteInit.java
@@ -98,10 +98,6 @@
private static final String SOCKET_NAME_ARG = "--socket-name=";
- /* Dexopt flag to disable hidden API access checks when dexopting SystemServer.
- * Must be kept in sync with com.android.server.pm.Installer. */
- private static final int DEXOPT_DISABLE_HIDDEN_API_CHECKS = 1 << 10;
-
/**
* Used to pre-load resources.
*/
@@ -518,6 +514,10 @@
/* should never reach here */
}
+ public static void setApiBlacklistExemptions(String[] exemptions) {
+ VMRuntime.getRuntime().setHiddenApiExemptions(exemptions);
+ }
+
/**
* Creates a PathClassLoader for the given class path that is associated with a shared
* namespace, i.e., this classloader can access platform-private native libraries. The
@@ -569,10 +569,7 @@
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
final String packageName = "*";
final String outputPath = null;
- // Dexopt with a flag which lifts restrictions on hidden API usage.
- // Offending methods would otherwise be re-verified at runtime and
- // we want to avoid the performance overhead of that.
- final int dexFlags = DEXOPT_DISABLE_HIDDEN_API_CHECKS;
+ final int dexFlags = 0;
final String compilerFilter = systemServerFilter;
final String uuid = StorageManager.UUID_PRIVATE_INTERNAL;
final String seInfo = null;
@@ -583,7 +580,8 @@
installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName,
instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter,
uuid, classLoaderContext, seInfo, false /* downgrade */,
- targetSdkVersion);
+ targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null,
+ "server-dexopt");
} catch (RemoteException | ServiceSpecificException e) {
// Ignore (but log), we need this on the classpath for fallback mode.
Log.w(TAG, "Failed compiling classpath element for system server: "
@@ -658,10 +656,11 @@
String args[] = {
"--setuid=1000",
"--setgid=1000",
- "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1032,1065,3001,3002,3003,3006,3007,3009,3010",
+ "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1024,1032,1065,3001,3002,3003,3006,3007,3009,3010",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
+ "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
"com.android.server.SystemServer",
};
ZygoteConnection.Arguments parsedArgs = null;
@@ -762,7 +761,7 @@
throw new RuntimeException("No ABI list supplied.");
}
- zygoteServer.registerServerSocket(socketName);
+ zygoteServer.registerServerSocketFromEnv(socketName);
// In some configurations, we avoid preloading resources and classes eagerly.
// In such cases, we will preload things prior to our first fork.
if (!enableLazyPreload) {
@@ -877,5 +876,16 @@
return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}
+ /**
+ * The main function called when starting a child zygote process. This is used as an
+ * alternative to zygoteInit(), which skips calling into initialization routines that
+ * start the Binder threadpool.
+ */
+ static final Runnable childZygoteInit(
+ int targetSdkVersion, String[] argv, ClassLoader classLoader) {
+ RuntimeInit.Arguments args = new RuntimeInit.Arguments(argv);
+ return RuntimeInit.findStaticMain(args.startClass, args.startArgs, classLoader);
+ }
+
private static final native void nativeZygoteInit();
}
diff --git a/com/android/internal/os/ZygoteServer.java b/com/android/internal/os/ZygoteServer.java
index 8baa15a..fecf9b9 100644
--- a/com/android/internal/os/ZygoteServer.java
+++ b/com/android/internal/os/ZygoteServer.java
@@ -44,9 +44,21 @@
private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
+ /**
+ * Listening socket that accepts new server connections.
+ */
private LocalServerSocket mServerSocket;
/**
+ * Whether or not mServerSocket's underlying FD should be closed directly.
+ * If mServerSocket is created with an existing FD, closing the socket does
+ * not close the FD and it must be closed explicitly. If the socket is created
+ * with a name instead, then closing the socket will close the underlying FD
+ * and it should not be double-closed.
+ */
+ private boolean mCloseSocketFd;
+
+ /**
* Set by the child process, immediately after a call to {@code Zygote.forkAndSpecialize}.
*/
private boolean mIsForkChild;
@@ -59,11 +71,12 @@
}
/**
- * Registers a server socket for zygote command connections
+ * Registers a server socket for zygote command connections. This locates the server socket
+ * file descriptor through an ANDROID_SOCKET_ environment variable.
*
* @throws RuntimeException when open fails
*/
- void registerServerSocket(String socketName) {
+ void registerServerSocketFromEnv(String socketName) {
if (mServerSocket == null) {
int fileDesc;
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
@@ -78,6 +91,7 @@
FileDescriptor fd = new FileDescriptor();
fd.setInt$(fileDesc);
mServerSocket = new LocalServerSocket(fd);
+ mCloseSocketFd = true;
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
@@ -86,6 +100,22 @@
}
/**
+ * Registers a server socket for zygote command connections. This opens the server socket
+ * at the specified name in the abstract socket namespace.
+ */
+ void registerServerSocketAtAbstractName(String socketName) {
+ if (mServerSocket == null) {
+ try {
+ mServerSocket = new LocalServerSocket(socketName);
+ mCloseSocketFd = false;
+ } catch (IOException ex) {
+ throw new RuntimeException(
+ "Error binding to abstract socket '" + socketName + "'", ex);
+ }
+ }
+ }
+
+ /**
* Waits for and accepts a single command connection. Throws
* RuntimeException on failure.
*/
@@ -112,7 +142,7 @@
if (mServerSocket != null) {
FileDescriptor fd = mServerSocket.getFileDescriptor();
mServerSocket.close();
- if (fd != null) {
+ if (fd != null && mCloseSocketFd) {
Os.close(fd);
}
}
@@ -219,6 +249,11 @@
Log.e(TAG, "Caught post-fork exception in child process.", e);
throw e;
}
+ } finally {
+ // Reset the child flag, in the event that the child process is a child-
+ // zygote. The flag will not be consulted this loop pass after the Runnable
+ // is returned.
+ mIsForkChild = false;
}
}
}
diff --git a/com/android/internal/os/logging/MetricsLoggerWrapper.java b/com/android/internal/os/logging/MetricsLoggerWrapper.java
index 245a66e..bfe3780 100644
--- a/com/android/internal/os/logging/MetricsLoggerWrapper.java
+++ b/com/android/internal/os/logging/MetricsLoggerWrapper.java
@@ -16,8 +16,12 @@
package com.android.internal.os.logging;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.util.Pair;
import android.util.StatsLog;
+import android.view.WindowManager.LayoutParams;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -31,43 +35,49 @@
private static final int METRIC_VALUE_DISMISSED_BY_TAP = 0;
private static final int METRIC_VALUE_DISMISSED_BY_DRAG = 1;
- public static void logPictureInPictureDismissByTap(Context context) {
+ public static void logPictureInPictureDismissByTap(Context context,
+ Pair<ComponentName, Integer> topActivityInfo) {
MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
METRIC_VALUE_DISMISSED_BY_TAP);
StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
- context.getUserId(),
- context.getApplicationInfo().packageName,
- context.getApplicationInfo().className,
+ getUid(context, topActivityInfo.first, topActivityInfo.second),
+ topActivityInfo.first.flattenToString(),
StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__DISMISSED);
}
- public static void logPictureInPictureDismissByDrag(Context context) {
+ public static void logPictureInPictureDismissByDrag(Context context,
+ Pair<ComponentName, Integer> topActivityInfo) {
MetricsLogger.action(context,
MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
METRIC_VALUE_DISMISSED_BY_DRAG);
StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
- context.getUserId(),
- context.getApplicationInfo().packageName,
- context.getApplicationInfo().className,
+ getUid(context, topActivityInfo.first, topActivityInfo.second),
+ topActivityInfo.first.flattenToString(),
StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__DISMISSED);
}
- public static void logPictureInPictureMinimize(Context context, boolean isMinimized) {
+ public static void logPictureInPictureMinimize(Context context, boolean isMinimized,
+ Pair<ComponentName, Integer> topActivityInfo) {
MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MINIMIZED,
isMinimized);
- if (isMinimized) {
- StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
- context.getUserId(),
- context.getApplicationInfo().packageName,
- context.getApplicationInfo().className,
- StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__MINIMIZED);
- } else {
- StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
- context.getUserId(),
- context.getApplicationInfo().packageName,
- context.getApplicationInfo().className,
- StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__EXPANDED_TO_FULL_SCREEN);
+ StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
+ getUid(context, topActivityInfo.first, topActivityInfo.second),
+ topActivityInfo.first.flattenToString(),
+ StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__MINIMIZED);
+ }
+
+ /**
+ * Get uid from component name and user Id
+ * @return uid. -1 if not found.
+ */
+ private static int getUid(Context context, ComponentName componentName, int userId) {
+ int uid = -1;
+ try {
+ uid = context.getPackageManager().getApplicationInfoAsUser(
+ componentName.getPackageName(), 0, userId).uid;
+ } catch (NameNotFoundException e) {
}
+ return uid;
}
public static void logPictureInPictureMenuVisible(Context context, boolean menuStateFull) {
@@ -76,24 +86,45 @@
}
public static void logPictureInPictureEnter(Context context,
- boolean supportsEnterPipOnTaskSwitch) {
+ int uid, String shortComponentName, boolean supportsEnterPipOnTaskSwitch) {
MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_ENTERED,
supportsEnterPipOnTaskSwitch);
- if (supportsEnterPipOnTaskSwitch) {
- StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, context.getUserId(),
- context.getApplicationInfo().packageName,
- context.getApplicationInfo().className,
- StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__ENTERED);
- }
+ StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, uid,
+ shortComponentName,
+ StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__ENTERED);
}
- public static void logPictureInPictureFullScreen(Context context) {
+ public static void logPictureInPictureFullScreen(Context context, int uid,
+ String shortComponentName) {
MetricsLogger.action(context,
MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN);
StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
- context.getUserId(),
- context.getApplicationInfo().packageName,
- context.getApplicationInfo().className,
+ uid,
+ shortComponentName,
StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__EXPANDED_TO_FULL_SCREEN);
}
+
+ public static void logAppOverlayEnter(int uid, String packageName, boolean changed, int type, boolean usingAlertWindow) {
+ if (changed) {
+ if (type != LayoutParams.TYPE_APPLICATION_OVERLAY) {
+ StatsLog.write(StatsLog.OVERLAY_STATE_CHANGED, uid, packageName, true,
+ StatsLog.OVERLAY_STATE_CHANGED__STATE__ENTERED);
+ } else if (!usingAlertWindow){
+ StatsLog.write(StatsLog.OVERLAY_STATE_CHANGED, uid, packageName, false,
+ StatsLog.OVERLAY_STATE_CHANGED__STATE__ENTERED);
+ }
+ }
+ }
+
+ public static void logAppOverlayExit(int uid, String packageName, boolean changed, int type, boolean usingAlertWindow) {
+ if (changed) {
+ if (type != LayoutParams.TYPE_APPLICATION_OVERLAY) {
+ StatsLog.write(StatsLog.OVERLAY_STATE_CHANGED, uid, packageName, true,
+ StatsLog.OVERLAY_STATE_CHANGED__STATE__EXITED);
+ } else if (!usingAlertWindow){
+ StatsLog.write(StatsLog.OVERLAY_STATE_CHANGED, uid, packageName, false,
+ StatsLog.OVERLAY_STATE_CHANGED__STATE__EXITED);
+ }
+ }
+ }
}