summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Hui Yu <huiyu@google.com> 2018-07-23 17:59:59 -0700
committer Hui Yu <huiyu@google.com> 2018-08-03 19:08:37 -0700
commit8e88e955a6b55bac63eb3d60c794ca0cf7c07c0c (patch)
tree43813a28ce5217685ed8df51fed63cc58032436e
parentaaaedf21d53e212cea87869a8d1842fa6f43ed91 (diff)
Fix memory leak in TimeBase.mObservers list.
Leaks happen in following places: 1. Not all timer/counter in Uid class are detached from TimeBase when the Uid object is destructed. 2. When Uid object is removed from mUidStats list in onUserRemovedLocked, removeUidStatsLocked, readLocked, All timer/counter in Uid class are not detached from TimeBase. 3. When timer/counter in Uid class is reassigned, the previous timer/counter object is not detached. Performance improvement: The mObservers list in TimeBase object of BatteryStatsImpl can have size up like 20k, the list type is ArrayList, remove() method on ArrayList is very slow. For long mObservers list, we change to use HashSet as container. The mObservers list in TimeBase object of Uid is short, it continues to use ArrayList as container. Fix: 80443940 Test: "adb shell cmd battery unplug" and "adb shell cmd battery set ac 1", observe memory usage from Android Monitor. Change-Id: I2001ca390a0a86a32082e1012dde4b831f1e7d04
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java666
1 files changed, 366 insertions, 300 deletions
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 76f9a8d5f8e8..bc0d51f274b4 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -36,7 +36,6 @@ import android.net.wifi.WifiManager;
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Build;
-import android.os.FileUtils;
import android.os.Handler;
import android.os.IBatteryPropertiesRegistrar;
import android.os.Looper;
@@ -109,7 +108,9 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
+import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -623,11 +624,11 @@ public class BatteryStatsImpl extends BatteryStats {
// These are the objects that will want to do something when the device
// is unplugged from power.
- protected final TimeBase mOnBatteryTimeBase = new TimeBase();
+ protected final TimeBase mOnBatteryTimeBase = new TimeBase(true);
// These are the objects that will want to do something when the device
// is unplugged from power *and* the screen is off or doze.
- protected final TimeBase mOnBatteryScreenOffTimeBase = new TimeBase();
+ protected final TimeBase mOnBatteryScreenOffTimeBase = new TimeBase(true);
// Set to true when we want to distribute CPU across wakelocks for the next
// CPU update, even if we aren't currently running wake locks.
@@ -1054,15 +1055,29 @@ public class BatteryStatsImpl extends BatteryStats {
mClocks = clocks;
}
+ /**
+ * TimeBase observer.
+ */
public interface TimeBaseObs {
void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime);
void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime);
+
+ /**
+ * Reset the observer's state, returns true if the timer/counter is inactive
+ * so can be completely dropped.
+ * @param detachIfReset detach if true, no-op if false.
+ * @return Returns true if the timer/counter is inactive.
+ */
+ boolean reset(boolean detachIfReset);
+ /**
+ * Detach the observer from TimeBase.
+ */
+ void detach();
}
// methods are protected not private to be VisibleForTesting
public static class TimeBase {
- protected final ArrayList<TimeBaseObs> mObservers = new ArrayList<>();
-
+ protected final Collection<TimeBaseObs> mObservers;
protected long mUptime;
protected long mRealtime;
@@ -1103,15 +1118,33 @@ public class BatteryStatsImpl extends BatteryStats {
sb.append("mUnpluggedRealtime="); formatTimeMs(sb, mUnpluggedRealtime / 1000);
pw.println(sb.toString());
}
+ /**
+ * The mObservers of TimeBase in BatteryStatsImpl object can contain up to 20k entries.
+ * The mObservers of TimeBase in BatteryStatsImpl.Uid object only contains a few or tens of
+ * entries.
+ * mObservers must have good performance on add(), remove(), also be memory efficient.
+ * This is why we provide isLongList parameter for long and short list user cases.
+ * @param isLongList If true, use HashSet for mObservers list.
+ * If false, use ArrayList for mObservers list.
+ */
+ public TimeBase(boolean isLongList) {
+ if (isLongList) {
+ mObservers = new HashSet<TimeBaseObs>();
+ } else {
+ mObservers = new ArrayList<TimeBaseObs>();
+ }
+ }
+
+ public TimeBase() {
+ this(false);
+ }
public void add(TimeBaseObs observer) {
mObservers.add(observer);
}
public void remove(TimeBaseObs observer) {
- if (!mObservers.remove(observer)) {
- Slog.wtf(TAG, "Removed unknown observer: " + observer);
- }
+ mObservers.remove(observer);
}
public boolean hasObserver(TimeBaseObs observer) {
@@ -1196,6 +1229,11 @@ public class BatteryStatsImpl extends BatteryStats {
return mRunning;
}
+ /**
+ * Normally we do not use Iterator in framework code to avoid alloc/dealloc
+ * Iterator object, here is an exception because mObservers' type is Collection
+ * instead of list.
+ */
public boolean setRunning(boolean running, long uptime, long realtime) {
if (mRunning != running) {
mRunning = running;
@@ -1204,19 +1242,18 @@ public class BatteryStatsImpl extends BatteryStats {
mRealtimeStart = realtime;
long batteryUptime = mUnpluggedUptime = getUptime(uptime);
long batteryRealtime = mUnpluggedRealtime = getRealtime(realtime);
-
- for (int i = mObservers.size() - 1; i >= 0; i--) {
- mObservers.get(i).onTimeStarted(realtime, batteryUptime, batteryRealtime);
+ final Iterator<TimeBaseObs> iter = mObservers.iterator();
+ while (iter.hasNext()) {
+ iter.next().onTimeStarted(realtime, batteryUptime, batteryRealtime);
}
} else {
mPastUptime += uptime - mUptimeStart;
mPastRealtime += realtime - mRealtimeStart;
-
long batteryUptime = getUptime(uptime);
long batteryRealtime = getRealtime(realtime);
-
- for (int i = mObservers.size() - 1; i >= 0; i--) {
- mObservers.get(i).onTimeStopped(realtime, batteryUptime, batteryRealtime);
+ final Iterator<TimeBaseObs> iter = mObservers.iterator();
+ while (iter.hasNext()) {
+ iter.next().onTimeStopped(realtime, batteryUptime, batteryRealtime);
}
}
return true;
@@ -1364,15 +1401,18 @@ public class BatteryStatsImpl extends BatteryStats {
/**
* Clear state of this counter.
*/
- void reset(boolean detachIfReset) {
+ @Override
+ public boolean reset(boolean detachIfReset) {
mCount.set(0);
mLoadedCount = mPluggedCount = mUnpluggedCount = 0;
if (detachIfReset) {
detach();
}
+ return true;
}
- void detach() {
+ @Override
+ public void detach() {
mTimeBase.remove(this);
}
@@ -1468,15 +1508,18 @@ public class BatteryStatsImpl extends BatteryStats {
/**
* Clear state of this counter.
*/
- public void reset(boolean detachIfReset) {
+ @Override
+ public boolean reset(boolean detachIfReset) {
fillArray(mCounts, 0);
fillArray(mLoadedCounts, 0);
fillArray(mUnpluggedCounts, 0);
if (detachIfReset) {
detach();
}
+ return true;
}
+ @Override
public void detach() {
mTimeBase.remove(this);
}
@@ -1639,14 +1682,17 @@ public class BatteryStatsImpl extends BatteryStats {
/**
* Clear state of this counter.
*/
- public void reset(boolean detachIfReset) {
+ @Override
+ public boolean reset(boolean detachIfReset) {
mCount = 0;
mLoadedCount = mUnpluggedCount = 0;
if (detachIfReset) {
detach();
}
+ return true;
}
+ @Override
public void detach() {
mTimeBase.remove(this);
}
@@ -1747,6 +1793,7 @@ public class BatteryStatsImpl extends BatteryStats {
* Clear state of this timer. Returns true if the timer is inactive
* so can be completely dropped.
*/
+ @Override
public boolean reset(boolean detachIfReset) {
mTotalTime = mLoadedTime = mLastTime = mTimeBeforeMark = 0;
mCount = mLoadedCount = mLastCount = 0;
@@ -1756,6 +1803,7 @@ public class BatteryStatsImpl extends BatteryStats {
return true;
}
+ @Override
public void detach() {
mTimeBase.remove(this);
}
@@ -6547,37 +6595,68 @@ public class BatteryStatsImpl extends BatteryStats {
return mUidStats;
}
- private static void detachTimerIfNotNull(BatteryStatsImpl.Timer timer) {
- if (timer != null) {
- timer.detach();
+ private static <T extends TimeBaseObs> boolean resetIfNotNull(T t, boolean detachIfReset) {
+ if (t != null) {
+ return t.reset(detachIfReset);
}
+ return true;
}
- private static boolean resetTimerIfNotNull(BatteryStatsImpl.Timer timer,
- boolean detachIfReset) {
- if (timer != null) {
- return timer.reset(detachIfReset);
+ private static <T extends TimeBaseObs> boolean resetIfNotNull(T[] t, boolean detachIfReset) {
+ if (t != null) {
+ boolean ret = true;
+ for (int i = 0; i < t.length; i++) {
+ ret &= resetIfNotNull(t[i], detachIfReset);
+ }
+ return ret;
}
return true;
}
- private static boolean resetTimerIfNotNull(DualTimer timer, boolean detachIfReset) {
- if (timer != null) {
- return timer.reset(detachIfReset);
+ private static <T extends TimeBaseObs> boolean resetIfNotNull(T[][] t, boolean detachIfReset) {
+ if (t != null) {
+ boolean ret = true;
+ for (int i = 0; i < t.length; i++) {
+ ret &= resetIfNotNull(t[i], detachIfReset);
+ }
+ return ret;
}
return true;
}
- private static void detachLongCounterIfNotNull(LongSamplingCounter counter) {
+
+ private static boolean resetIfNotNull(ControllerActivityCounterImpl counter, boolean detachIfReset) {
if (counter != null) {
- counter.detach();
+ counter.reset(detachIfReset);
+ }
+ return true;
+ }
+
+ private static <T extends TimeBaseObs> void detachIfNotNull(T t) {
+ if (t != null) {
+ t.detach();
+ }
+ }
+
+ private static <T extends TimeBaseObs> void detachIfNotNull(T[] t) {
+ if (t != null) {
+ for (int i = 0; i < t.length; i++) {
+ detachIfNotNull(t[i]);
+ }
}
}
- private static void resetLongCounterIfNotNull(LongSamplingCounter counter,
- boolean detachIfReset) {
+ private static <T extends TimeBaseObs> void detachIfNotNull(T[][] t) {
+ if (t != null) {
+ for (int i = 0; i < t.length; i++) {
+ detachIfNotNull(t[i]);
+ }
+ }
+ }
+
+ private static void detachIfNotNull(ControllerActivityCounterImpl counter) {
if (counter != null) {
- counter.reset(detachIfReset);
+ counter.detach();
}
}
@@ -6758,11 +6837,12 @@ public class BatteryStatsImpl extends BatteryStats {
mBsi = bsi;
mUid = uid;
- mOnBatteryBackgroundTimeBase = new TimeBase();
+ /* Observer list of TimeBase object in Uid is short */
+ mOnBatteryBackgroundTimeBase = new TimeBase(false);
mOnBatteryBackgroundTimeBase.init(mBsi.mClocks.uptimeMillis() * 1000,
mBsi.mClocks.elapsedRealtime() * 1000);
-
- mOnBatteryScreenOffBackgroundTimeBase = new TimeBase();
+ /* Observer list of TimeBase object in Uid is short */
+ mOnBatteryScreenOffBackgroundTimeBase = new TimeBase(false);
mOnBatteryScreenOffBackgroundTimeBase.init(mBsi.mClocks.uptimeMillis() * 1000,
mBsi.mClocks.elapsedRealtime() * 1000);
@@ -6914,6 +6994,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
if (mProcStateScreenOffTimeMs[procState] == null
|| mProcStateScreenOffTimeMs[procState].getSize() != cpuTimesMs.length) {
+ detachIfNotNull(mProcStateScreenOffTimeMs[procState]);
mProcStateScreenOffTimeMs[procState] = new LongSamplingCounterArray(
mBsi.mOnBatteryScreenOffTimeBase);
}
@@ -7518,6 +7599,7 @@ public class BatteryStatsImpl extends BatteryStats {
void makeProcessState(int i, Parcel in) {
if (i < 0 || i >= NUM_PROCESS_STATE) return;
+ detachIfNotNull(mProcessStateTimer[i]);
if (in == null) {
mProcessStateTimer[i] = new StopwatchTimer(mBsi.mClocks, this, PROCESS_STATE, null,
mBsi.mOnBatteryTimeBase);
@@ -7581,6 +7663,7 @@ public class BatteryStatsImpl extends BatteryStats {
collected = new ArrayList<StopwatchTimer>();
mBsi.mWifiBatchedScanTimers.put(i, collected);
}
+ detachIfNotNull(mWifiBatchedScanTimer[i]);
if (in == null) {
mWifiBatchedScanTimer[i] = new StopwatchTimer(mBsi.mClocks, this, WIFI_BATCHED_SCAN,
collected, mBsi.mOnBatteryTimeBase);
@@ -7760,13 +7843,17 @@ public class BatteryStatsImpl extends BatteryStats {
}
void initNetworkActivityLocked() {
+ detachIfNotNull(mNetworkByteActivityCounters);
mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
+ detachIfNotNull(mNetworkPacketActivityCounters);
mNetworkPacketActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
mNetworkByteActivityCounters[i] = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
mNetworkPacketActivityCounters[i] = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
}
+ detachIfNotNull(mMobileRadioActiveTime);
mMobileRadioActiveTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
+ detachIfNotNull(mMobileRadioActiveCount);
mMobileRadioActiveCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
}
@@ -7806,27 +7893,22 @@ public class BatteryStatsImpl extends BatteryStats {
active |= mWifiMulticastEnabled;
}
- active |= !resetTimerIfNotNull(mAudioTurnedOnTimer, false);
- active |= !resetTimerIfNotNull(mVideoTurnedOnTimer, false);
- active |= !resetTimerIfNotNull(mFlashlightTurnedOnTimer, false);
- active |= !resetTimerIfNotNull(mCameraTurnedOnTimer, false);
- active |= !resetTimerIfNotNull(mForegroundActivityTimer, false);
- active |= !resetTimerIfNotNull(mForegroundServiceTimer, false);
- active |= !resetTimerIfNotNull(mAggregatedPartialWakelockTimer, false);
- active |= !resetTimerIfNotNull(mBluetoothScanTimer, false);
- active |= !resetTimerIfNotNull(mBluetoothUnoptimizedScanTimer, false);
- if (mBluetoothScanResultCounter != null) {
- mBluetoothScanResultCounter.reset(false);
- }
- if (mBluetoothScanResultBgCounter != null) {
- mBluetoothScanResultBgCounter.reset(false);
- }
+ active |= !resetIfNotNull(mAudioTurnedOnTimer, false);
+ active |= !resetIfNotNull(mVideoTurnedOnTimer, false);
+ active |= !resetIfNotNull(mFlashlightTurnedOnTimer, false);
+ active |= !resetIfNotNull(mCameraTurnedOnTimer, false);
+ active |= !resetIfNotNull(mForegroundActivityTimer, false);
+ active |= !resetIfNotNull(mForegroundServiceTimer, false);
+ active |= !resetIfNotNull(mAggregatedPartialWakelockTimer, false);
+ active |= !resetIfNotNull(mBluetoothScanTimer, false);
+ active |= !resetIfNotNull(mBluetoothUnoptimizedScanTimer, false);
+
+ resetIfNotNull(mBluetoothScanResultCounter, false);
+ resetIfNotNull(mBluetoothScanResultBgCounter, false);
if (mProcessStateTimer != null) {
for (int i = 0; i < NUM_PROCESS_STATE; i++) {
- if (mProcessStateTimer[i] != null) {
- active |= !mProcessStateTimer[i].reset(false);
- }
+ active |= !resetIfNotNull(mProcessStateTimer[i], false);
}
active |= (mProcessState != ActivityManager.PROCESS_STATE_NONEXISTENT);
}
@@ -7839,75 +7921,37 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
- if (mUserActivityCounters != null) {
- for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
- mUserActivityCounters[i].reset(false);
- }
- }
+ resetIfNotNull(mUserActivityCounters, false);
- if (mNetworkByteActivityCounters != null) {
- for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
- mNetworkByteActivityCounters[i].reset(false);
- mNetworkPacketActivityCounters[i].reset(false);
- }
- mMobileRadioActiveTime.reset(false);
- mMobileRadioActiveCount.reset(false);
- }
+ resetIfNotNull(mNetworkByteActivityCounters, false);
+ resetIfNotNull(mNetworkPacketActivityCounters, false);
+ resetIfNotNull(mMobileRadioActiveTime, false);
+ resetIfNotNull(mMobileRadioActiveCount, false);
- if (mWifiControllerActivity != null) {
- mWifiControllerActivity.reset(false);
- }
+ resetIfNotNull(mWifiControllerActivity, false);
+ resetIfNotNull(mBluetoothControllerActivity, false);
+ resetIfNotNull(mModemControllerActivity, false);
- if (mBluetoothControllerActivity != null) {
- mBluetoothControllerActivity.reset(false);
- }
+ resetIfNotNull(mUserCpuTime, false);
+ resetIfNotNull(mSystemCpuTime, false);
- if (mModemControllerActivity != null) {
- mModemControllerActivity.reset(false);
- }
+ resetIfNotNull(mCpuClusterSpeedTimesUs, false);
- mUserCpuTime.reset(false);
- mSystemCpuTime.reset(false);
+ resetIfNotNull(mCpuFreqTimeMs, false);
+ resetIfNotNull(mScreenOffCpuFreqTimeMs, false);
- if (mCpuClusterSpeedTimesUs != null) {
- for (LongSamplingCounter[] speeds : mCpuClusterSpeedTimesUs) {
- if (speeds != null) {
- for (LongSamplingCounter speed : speeds) {
- if (speed != null) {
- speed.reset(false);
- }
- }
- }
- }
- }
- if (mCpuFreqTimeMs != null) {
- mCpuFreqTimeMs.reset(false);
- }
- if (mScreenOffCpuFreqTimeMs != null) {
- mScreenOffCpuFreqTimeMs.reset(false);
- }
+ resetIfNotNull(mCpuActiveTimeMs, false);
+ resetIfNotNull(mCpuClusterTimesMs, false);
- mCpuActiveTimeMs.reset(false);
- mCpuClusterTimesMs.reset(false);
+ resetIfNotNull(mProcStateTimeMs, false);
- if (mProcStateTimeMs != null) {
- for (LongSamplingCounterArray counters : mProcStateTimeMs) {
- if (counters != null) {
- counters.reset(false);
- }
- }
- }
- if (mProcStateScreenOffTimeMs != null) {
- for (LongSamplingCounterArray counters : mProcStateScreenOffTimeMs) {
- if (counters != null) {
- counters.reset(false);
- }
- }
- }
+ resetIfNotNull(mProcStateScreenOffTimeMs, false);
+
+ resetIfNotNull(mMobileRadioApWakeupCount, false);
+
+ resetIfNotNull(mWifiRadioApWakeupCount, false);
- resetLongCounterIfNotNull(mMobileRadioApWakeupCount, false);
- resetLongCounterIfNotNull(mWifiRadioApWakeupCount, false);
final ArrayMap<String, Wakelock> wakeStats = mWakelockStats.getMap();
for (int iw=wakeStats.size()-1; iw>=0; iw--) {
@@ -7943,16 +7987,12 @@ public class BatteryStatsImpl extends BatteryStats {
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);
- }
- }
+ resetIfNotNull(mJobsDeferredEventCount, false);
+ resetIfNotNull(mJobsDeferredCount, false);
+ resetIfNotNull(mJobsFreshnessTimeMs, false);
+ resetIfNotNull(mJobsFreshnessBuckets, false);
- for (int ise=mSensorStats.size()-1; ise>=0; ise--) {
+ for (int ise = mSensorStats.size() - 1; ise >= 0; ise--) {
Sensor s = mSensorStats.valueAt(ise);
if (s.reset()) {
mSensorStats.removeAt(ise);
@@ -7961,173 +8001,135 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
- for (int ip=mProcessStats.size()-1; ip>=0; ip--) {
+ for (int ip = mProcessStats.size() - 1; ip >= 0; ip--) {
Proc proc = mProcessStats.valueAt(ip);
proc.detach();
}
mProcessStats.clear();
- if (mPids.size() > 0) {
- for (int i=mPids.size()-1; i>=0; i--) {
- Pid pid = mPids.valueAt(i);
- if (pid.mWakeNesting > 0) {
- active = true;
- } else {
- mPids.removeAt(i);
- }
+
+ for (int i = mPids.size() - 1; i >= 0; i--) {
+ Pid pid = mPids.valueAt(i);
+ if (pid.mWakeNesting > 0) {
+ active = true;
+ } else {
+ mPids.removeAt(i);
}
}
- if (mPackageStats.size() > 0) {
- Iterator<Map.Entry<String, Pkg>> it = mPackageStats.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry<String, Pkg> pkgEntry = it.next();
- Pkg p = pkgEntry.getValue();
- p.detach();
- if (p.mServiceStats.size() > 0) {
- Iterator<Map.Entry<String, Pkg.Serv>> it2
- = p.mServiceStats.entrySet().iterator();
- while (it2.hasNext()) {
- Map.Entry<String, Pkg.Serv> servEntry = it2.next();
- servEntry.getValue().detach();
- }
- }
- }
- mPackageStats.clear();
+
+
+ for(int i = mPackageStats.size() - 1; i >= 0; i--) {
+ Pkg p = mPackageStats.valueAt(i);
+ p.detach();
}
+ mPackageStats.clear();
mLastStepUserTime = mLastStepSystemTime = 0;
mCurStepUserTime = mCurStepSystemTime = 0;
- if (!active) {
- if (mWifiRunningTimer != null) {
- mWifiRunningTimer.detach();
- }
- if (mFullWifiLockTimer != null) {
- mFullWifiLockTimer.detach();
- }
- if (mWifiScanTimer != null) {
- mWifiScanTimer.detach();
- }
- for (int i = 0; i < NUM_WIFI_BATCHED_SCAN_BINS; i++) {
- if (mWifiBatchedScanTimer[i] != null) {
- mWifiBatchedScanTimer[i].detach();
- }
- }
- if (mWifiMulticastTimer != null) {
- mWifiMulticastTimer.detach();
- }
- if (mAudioTurnedOnTimer != null) {
- mAudioTurnedOnTimer.detach();
- mAudioTurnedOnTimer = null;
- }
- if (mVideoTurnedOnTimer != null) {
- mVideoTurnedOnTimer.detach();
- mVideoTurnedOnTimer = null;
- }
- if (mFlashlightTurnedOnTimer != null) {
- mFlashlightTurnedOnTimer.detach();
- mFlashlightTurnedOnTimer = null;
- }
- if (mCameraTurnedOnTimer != null) {
- mCameraTurnedOnTimer.detach();
- mCameraTurnedOnTimer = null;
- }
- if (mForegroundActivityTimer != null) {
- mForegroundActivityTimer.detach();
- mForegroundActivityTimer = null;
- }
- if (mForegroundServiceTimer != null) {
- mForegroundServiceTimer.detach();
- mForegroundServiceTimer = null;
- }
- if (mAggregatedPartialWakelockTimer != null) {
- mAggregatedPartialWakelockTimer.detach();
- mAggregatedPartialWakelockTimer = null;
- }
- if (mBluetoothScanTimer != null) {
- mBluetoothScanTimer.detach();
- mBluetoothScanTimer = null;
- }
- if (mBluetoothUnoptimizedScanTimer != null) {
- mBluetoothUnoptimizedScanTimer.detach();
- mBluetoothUnoptimizedScanTimer = null;
- }
- if (mBluetoothScanResultCounter != null) {
- mBluetoothScanResultCounter.detach();
- mBluetoothScanResultCounter = null;
- }
- if (mBluetoothScanResultBgCounter != null) {
- mBluetoothScanResultBgCounter.detach();
- mBluetoothScanResultBgCounter = null;
- }
- if (mUserActivityCounters != null) {
- for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
- mUserActivityCounters[i].detach();
- }
- }
- if (mNetworkByteActivityCounters != null) {
- for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
- mNetworkByteActivityCounters[i].detach();
- mNetworkPacketActivityCounters[i].detach();
- }
- }
+ return !active;
+ }
- if (mWifiControllerActivity != null) {
- mWifiControllerActivity.detach();
- }
+ /**
+ * This method MUST be called whenever the Uid object is destructed, otherwise it is a
+ * memory leak in {@link TimeBase#mObservers} list.
+ * Typically the Uid object is destructed when it is removed from
+ * {@link BatteryStatsImpl#mUidStats}
+ */
+ void detachFromTimeBase() {
+ detachIfNotNull(mWifiRunningTimer);
+ detachIfNotNull(mFullWifiLockTimer);
+ detachIfNotNull(mWifiScanTimer);
+ detachIfNotNull(mWifiBatchedScanTimer);
+ detachIfNotNull(mWifiMulticastTimer);
+ detachIfNotNull(mAudioTurnedOnTimer);
+ detachIfNotNull(mVideoTurnedOnTimer);
+ detachIfNotNull(mFlashlightTurnedOnTimer);
- if (mBluetoothControllerActivity != null) {
- mBluetoothControllerActivity.detach();
- }
+ detachIfNotNull(mCameraTurnedOnTimer);
+ detachIfNotNull(mForegroundActivityTimer);
+ detachIfNotNull(mForegroundServiceTimer);
- if (mModemControllerActivity != null) {
- mModemControllerActivity.detach();
- }
+ detachIfNotNull(mAggregatedPartialWakelockTimer);
- mPids.clear();
+ detachIfNotNull(mBluetoothScanTimer);
+ detachIfNotNull(mBluetoothUnoptimizedScanTimer);
+ detachIfNotNull(mBluetoothScanResultCounter);
+ detachIfNotNull(mBluetoothScanResultBgCounter);
- mUserCpuTime.detach();
- mSystemCpuTime.detach();
+ detachIfNotNull(mProcessStateTimer);
- if (mCpuClusterSpeedTimesUs != null) {
- for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeedTimesUs) {
- if (cpuSpeeds != null) {
- for (LongSamplingCounter c : cpuSpeeds) {
- if (c != null) {
- c.detach();
- }
- }
- }
- }
- }
+ detachIfNotNull(mVibratorOnTimer);
- if (mCpuFreqTimeMs != null) {
- mCpuFreqTimeMs.detach();
- }
- if (mScreenOffCpuFreqTimeMs != null) {
- mScreenOffCpuFreqTimeMs.detach();
- }
- mCpuActiveTimeMs.detach();
- mCpuClusterTimesMs.detach();
+ detachIfNotNull(mUserActivityCounters);
- if (mProcStateTimeMs != null) {
- for (LongSamplingCounterArray counters : mProcStateTimeMs) {
- if (counters != null) {
- counters.detach();
- }
- }
- }
- if (mProcStateScreenOffTimeMs != null) {
- for (LongSamplingCounterArray counters : mProcStateScreenOffTimeMs) {
- if (counters != null) {
- counters.detach();
- }
- }
- }
- detachLongCounterIfNotNull(mMobileRadioApWakeupCount);
- detachLongCounterIfNotNull(mWifiRadioApWakeupCount);
+ detachIfNotNull(mNetworkByteActivityCounters);
+ detachIfNotNull(mNetworkPacketActivityCounters);
+
+ detachIfNotNull(mMobileRadioActiveTime);
+ detachIfNotNull(mMobileRadioActiveCount);
+ detachIfNotNull(mMobileRadioApWakeupCount);
+ detachIfNotNull(mWifiRadioApWakeupCount);
+
+ detachIfNotNull(mWifiControllerActivity);
+ detachIfNotNull(mBluetoothControllerActivity);
+ detachIfNotNull(mModemControllerActivity);
+
+ mPids.clear();
+
+ detachIfNotNull(mUserCpuTime);
+ detachIfNotNull(mSystemCpuTime);
+
+ detachIfNotNull(mCpuClusterSpeedTimesUs);
+
+ detachIfNotNull(mCpuActiveTimeMs);
+ detachIfNotNull(mCpuFreqTimeMs);
+
+ detachIfNotNull(mScreenOffCpuFreqTimeMs);
+
+ detachIfNotNull(mCpuClusterTimesMs);
+
+ detachIfNotNull(mProcStateTimeMs);
+
+ detachIfNotNull(mProcStateScreenOffTimeMs);
+
+ final ArrayMap<String, Wakelock> wakeStats = mWakelockStats.getMap();
+ for (int iw = wakeStats.size() - 1; iw >= 0; iw--) {
+ Wakelock wl = wakeStats.valueAt(iw);
+ wl.detachFromTimeBase();
+ }
+ final ArrayMap<String, DualTimer> syncStats = mSyncStats.getMap();
+ for (int is = syncStats.size() - 1; is >= 0; is--) {
+ DualTimer timer = syncStats.valueAt(is);
+ detachIfNotNull(timer);
+ }
+ final ArrayMap<String, DualTimer> jobStats = mJobStats.getMap();
+ for (int ij = jobStats.size() - 1; ij >= 0; ij--) {
+ DualTimer timer = jobStats.valueAt(ij);
+ detachIfNotNull(timer);
}
- return !active;
+ detachIfNotNull(mJobsDeferredEventCount);
+ detachIfNotNull(mJobsDeferredCount);
+ detachIfNotNull(mJobsFreshnessTimeMs);
+ detachIfNotNull(mJobsFreshnessBuckets);
+
+
+ for (int ise = mSensorStats.size() - 1; ise >= 0; ise--) {
+ Sensor s = mSensorStats.valueAt(ise);
+ s.detachFromTimeBase();
+ }
+
+ for (int ip= mProcessStats.size() - 1; ip >= 0; ip--) {
+ Proc proc = mProcessStats.valueAt(ip);
+ proc.detach();
+ }
+ mProcessStats.clear();
+
+ for(int i = mPackageStats.size() - 1; i >= 0; i--) {
+ Pkg p = mPackageStats.valueAt(i);
+ p.detach();
+ }
+ mPackageStats.clear();
}
void writeJobCompletionsToParcelLocked(Parcel out) {
@@ -8850,35 +8852,24 @@ public class BatteryStatsImpl extends BatteryStats {
boolean reset() {
boolean wlactive = false;
- if (mTimerFull != null) {
- wlactive |= !mTimerFull.reset(false);
- }
- if (mTimerPartial != null) {
- wlactive |= !mTimerPartial.reset(false);
- }
- if (mTimerWindow != null) {
- wlactive |= !mTimerWindow.reset(false);
- }
- if (mTimerDraw != null) {
- wlactive |= !mTimerDraw.reset(false);
- }
+
+ wlactive |= !resetIfNotNull(mTimerFull,false);
+ wlactive |= !resetIfNotNull(mTimerPartial,false);
+ wlactive |= !resetIfNotNull(mTimerWindow,false);
+ wlactive |= !resetIfNotNull(mTimerDraw,false);
+
if (!wlactive) {
- if (mTimerFull != null) {
- mTimerFull.detach();
- mTimerFull = null;
- }
- if (mTimerPartial != null) {
- mTimerPartial.detach();
- mTimerPartial = null;
- }
- if (mTimerWindow != null) {
- mTimerWindow.detach();
- mTimerWindow = null;
- }
- if (mTimerDraw != null) {
- mTimerDraw.detach();
- mTimerDraw = null;
- }
+ detachIfNotNull(mTimerFull);
+ mTimerFull = null;
+
+ detachIfNotNull(mTimerPartial);
+ mTimerPartial = null;
+
+ detachIfNotNull(mTimerWindow);
+ mTimerWindow = null;
+
+ detachIfNotNull(mTimerDraw);
+ mTimerDraw = null;
}
return !wlactive;
}
@@ -8912,6 +8903,13 @@ public class BatteryStatsImpl extends BatteryStats {
default: throw new IllegalArgumentException("type = " + type);
}
}
+
+ public void detachFromTimeBase() {
+ detachIfNotNull(mTimerPartial);
+ detachIfNotNull(mTimerFull);
+ detachIfNotNull(mTimerWindow);
+ detachIfNotNull(mTimerDraw);
+ }
}
public static class Sensor extends BatteryStats.Uid.Sensor {
@@ -8981,6 +8979,10 @@ public class BatteryStatsImpl extends BatteryStats {
public int getHandle() {
return mHandle;
}
+
+ public void detachFromTimeBase() {
+ detachIfNotNull(mTimer);
+ }
}
/**
@@ -9112,7 +9114,16 @@ public class BatteryStatsImpl extends BatteryStats {
public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
}
- void detach() {
+ @Override
+ public boolean reset(boolean detachIfReset) {
+ if (detachIfReset) {
+ this.detach();
+ }
+ return true;
+ }
+
+ @Override
+ public void detach() {
mActive = false;
mBsi.mOnBatteryTimeBase.remove(this);
}
@@ -9351,8 +9362,23 @@ public class BatteryStatsImpl extends BatteryStats {
public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
}
- void detach() {
+ @Override
+ public boolean reset(boolean detachIfReset) {
+ if (detachIfReset) {
+ this.detach();
+ }
+ return true;
+ }
+
+ @Override
+ public void detach() {
mBsi.mOnBatteryScreenOffTimeBase.remove(this);
+ for (int j = mWakeupAlarms.size() - 1; j >= 0; j--) {
+ detachIfNotNull(mWakeupAlarms.valueAt(j));
+ }
+ for (int j = mServiceStats.size() - 1; j >= 0; j--) {
+ detachIfNotNull(mServiceStats.valueAt(j));
+ }
}
void readFromParcelLocked(Parcel in) {
@@ -9533,9 +9559,18 @@ public class BatteryStatsImpl extends BatteryStats {
long baseRealtime) {
}
+ @Override
+ public boolean reset(boolean detachIfReset) {
+ if (detachIfReset) {
+ this.detach();
+ }
+ return true;
+ }
+
/**
* Remove this Serv as a listener from the time base.
*/
+ @Override
public void detach() {
mBsi.mOnBatteryTimeBase.remove(this);
}
@@ -9852,6 +9887,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
t = new DualTimer(mBsi.mClocks, this, BatteryStats.SENSOR, timers,
mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase);
+ detachIfNotNull(se.mTimer);
se.mTimer = t;
return t;
}
@@ -10795,6 +10831,7 @@ public class BatteryStatsImpl extends BatteryStats {
for (int i=0; i<mUidStats.size(); i++) {
if (mUidStats.valueAt(i).reset(uptimeMillis * 1000, elapsedRealtimeMillis * 1000)) {
+ mUidStats.valueAt(i).detachFromTimeBase();
mUidStats.remove(mUidStats.keyAt(i));
i--;
}
@@ -12101,11 +12138,13 @@ public class BatteryStatsImpl extends BatteryStats {
}
final Uid u = getUidStatsLocked(uid);
if (u.mCpuFreqTimeMs == null || u.mCpuFreqTimeMs.getSize() != cpuFreqTimeMs.length) {
+ detachIfNotNull(u.mCpuFreqTimeMs);
u.mCpuFreqTimeMs = new LongSamplingCounterArray(mOnBatteryTimeBase);
}
u.mCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs, onBattery);
if (u.mScreenOffCpuFreqTimeMs == null ||
u.mScreenOffCpuFreqTimeMs.getSize() != cpuFreqTimeMs.length) {
+ detachIfNotNull(u.mScreenOffCpuFreqTimeMs);
u.mScreenOffCpuFreqTimeMs = new LongSamplingCounterArray(
mOnBatteryScreenOffTimeBase);
}
@@ -12114,6 +12153,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (perClusterTimesAvailable) {
if (u.mCpuClusterSpeedTimesUs == null ||
u.mCpuClusterSpeedTimesUs.length != numClusters) {
+ detachIfNotNull(u.mCpuClusterSpeedTimesUs);
u.mCpuClusterSpeedTimesUs = new LongSamplingCounter[numClusters][];
}
if (numWakelocks > 0 && mWakeLockAllocationsUs == null) {
@@ -12125,6 +12165,7 @@ public class BatteryStatsImpl extends BatteryStats {
final int speedsInCluster = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
if (u.mCpuClusterSpeedTimesUs[cluster] == null ||
u.mCpuClusterSpeedTimesUs[cluster].length != speedsInCluster) {
+ detachIfNotNull(u.mCpuClusterSpeedTimesUs[cluster]);
u.mCpuClusterSpeedTimesUs[cluster]
= new LongSamplingCounter[speedsInCluster];
}
@@ -12162,6 +12203,7 @@ public class BatteryStatsImpl extends BatteryStats {
final Uid u = partialTimers.get(i).mUid;
if (u.mCpuClusterSpeedTimesUs == null ||
u.mCpuClusterSpeedTimesUs.length != numClusters) {
+ detachIfNotNull(u.mCpuClusterSpeedTimesUs);
u.mCpuClusterSpeedTimesUs = new LongSamplingCounter[numClusters][];
}
@@ -12169,6 +12211,7 @@ public class BatteryStatsImpl extends BatteryStats {
final int speedsInCluster = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
if (u.mCpuClusterSpeedTimesUs[cluster] == null ||
u.mCpuClusterSpeedTimesUs[cluster].length != speedsInCluster) {
+ detachIfNotNull(u.mCpuClusterSpeedTimesUs[cluster]);
u.mCpuClusterSpeedTimesUs[cluster]
= new LongSamplingCounter[speedsInCluster];
}
@@ -13106,6 +13149,12 @@ public class BatteryStatsImpl extends BatteryStats {
mUidStats.put(lastUidForUser, null);
final int firstIndex = mUidStats.indexOfKey(firstUidForUser);
final int lastIndex = mUidStats.indexOfKey(lastUidForUser);
+ for (int i = firstIndex; i <= lastIndex; i++) {
+ final Uid uid = mUidStats.valueAt(i);
+ if (uid != null) {
+ uid.detachFromTimeBase();
+ }
+ }
mUidStats.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
}
@@ -13113,6 +13162,10 @@ public class BatteryStatsImpl extends BatteryStats {
* Remove the statistics object for a particular uid.
*/
public void removeUidStatsLocked(int uid) {
+ final Uid u = mUidStats.get(uid);
+ if (u != null) {
+ u.detachFromTimeBase();
+ }
mUidStats.remove(uid);
mPendingRemovedUids.add(new UidToRemove(uid, mClocks.elapsedRealtime()));
}
@@ -13978,7 +14031,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (mPowerProfile != null && mPowerProfile.getNumCpuClusters() != numClusters) {
throw new ParcelFormatException("Incompatible cpu cluster arrangement");
}
-
+ detachIfNotNull(u.mCpuClusterSpeedTimesUs);
u.mCpuClusterSpeedTimesUs = new LongSamplingCounter[numClusters][];
for (int cluster = 0; cluster < numClusters; cluster++) {
if (in.readInt() != 0) {
@@ -14002,11 +14055,14 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
} else {
+ detachIfNotNull(u.mCpuClusterSpeedTimesUs);
u.mCpuClusterSpeedTimesUs = null;
}
+ detachIfNotNull(u.mCpuFreqTimeMs);
u.mCpuFreqTimeMs = LongSamplingCounterArray.readSummaryFromParcelLocked(
in, mOnBatteryTimeBase);
+ detachIfNotNull(u.mScreenOffCpuFreqTimeMs);
u.mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readSummaryFromParcelLocked(
in, mOnBatteryScreenOffTimeBase);
@@ -14015,6 +14071,7 @@ public class BatteryStatsImpl extends BatteryStats {
int length = in.readInt();
if (length == Uid.NUM_PROCESS_STATE) {
+ detachIfNotNull(u.mProcStateTimeMs);
u.mProcStateTimeMs = new LongSamplingCounterArray[length];
for (int procState = 0; procState < length; ++procState) {
u.mProcStateTimeMs[procState]
@@ -14022,10 +14079,12 @@ public class BatteryStatsImpl extends BatteryStats {
in, mOnBatteryTimeBase);
}
} else {
+ detachIfNotNull(u.mProcStateTimeMs);
u.mProcStateTimeMs = null;
}
length = in.readInt();
if (length == Uid.NUM_PROCESS_STATE) {
+ detachIfNotNull(u.mProcStateScreenOffTimeMs);
u.mProcStateScreenOffTimeMs = new LongSamplingCounterArray[length];
for (int procState = 0; procState < length; ++procState) {
u.mProcStateScreenOffTimeMs[procState]
@@ -14033,20 +14092,25 @@ public class BatteryStatsImpl extends BatteryStats {
in, mOnBatteryScreenOffTimeBase);
}
} else {
+ detachIfNotNull(u.mProcStateScreenOffTimeMs);
u.mProcStateScreenOffTimeMs = null;
}
if (in.readInt() != 0) {
+ detachIfNotNull(u.mMobileRadioApWakeupCount);
u.mMobileRadioApWakeupCount = new LongSamplingCounter(mOnBatteryTimeBase);
u.mMobileRadioApWakeupCount.readSummaryFromParcelLocked(in);
} else {
+ detachIfNotNull(u.mMobileRadioApWakeupCount);
u.mMobileRadioApWakeupCount = null;
}
if (in.readInt() != 0) {
+ detachIfNotNull(u.mWifiRadioApWakeupCount);
u.mWifiRadioApWakeupCount = new LongSamplingCounter(mOnBatteryTimeBase);
u.mWifiRadioApWakeupCount.readSummaryFromParcelLocked(in);
} else {
+ detachIfNotNull(u.mWifiRadioApWakeupCount);
u.mWifiRadioApWakeupCount = null;
}
@@ -14082,6 +14146,7 @@ public class BatteryStatsImpl extends BatteryStats {
u.mJobsDeferredEventCount.readSummaryFromParcelLocked(in);
u.mJobsDeferredCount.readSummaryFromParcelLocked(in);
u.mJobsFreshnessTimeMs.readSummaryFromParcelLocked(in);
+ detachIfNotNull(u.mJobsFreshnessBuckets);
for (int i = 0; i < JOB_FRESHNESS_BUCKETS.length; i++) {
if (in.readInt() != 0) {
u.mJobsFreshnessBuckets[i] = new Counter(u.mBsi.mOnBatteryTimeBase);
@@ -14123,6 +14188,7 @@ public class BatteryStatsImpl extends BatteryStats {
for (int ip = 0; ip < NP; ip++) {
String pkgName = in.readString();
Uid.Pkg p = u.getPackageStatsLocked(pkgName);
+ p.detach();
final int NWA = in.readInt();
if (NWA > 10000) {
throw new ParcelFormatException("File corrupt: too many wakeup alarms " + NWA);