private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 176 + (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;
}
}
+ @VisibleForTesting
public static class LongSamplingCounter extends LongCounter implements TimeBaseObs {
final TimeBase mTimeBase;
- long mCount;
- long mLoadedCount;
- long mUnpluggedCount;
+ 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;
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 logState(Printer pw, String prefix) {
pw.println(prefix + "mCount=" + mCount
+ + " mCurrentCount=" + mCurrentCount
+ " mLoadedCount=" + mLoadedCount
+ " mUnpluggedCount=" + mUnpluggedCount);
}
- void addCountLocked(long count) {
- addCountLocked(count, mTimeBase.isRunning());
+ 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());
}
- void addCountLocked(long count, boolean isRunning) {
+ public void update(long count, boolean isRunning) {
+ if (count < mCurrentCount) {
+ mCurrentCount = 0;
+ }
if (isRunning) {
- mCount += count;
+ mCount += count - mCurrentCount;
}
+ mCurrentCount = count;
}
/**
* Clear state of this counter.
*/
- void reset(boolean detachIfReset) {
+ public void reset(boolean detachIfReset) {
mCount = 0;
mLoadedCount = mUnpluggedCount = 0;
if (detachIfReset) {
}
}
- 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 = mLoadedCount;
+ public void readSummaryFromParcelLocked(Parcel in) {
+ mCount = mUnpluggedCount= mLoadedCount = in.readLong();
}
}
}
}
- // Cache last value for comparison.
- private BluetoothActivityEnergyInfo mLastBluetoothActivityEnergyInfo =
- new BluetoothActivityEnergyInfo(0, 0, 0, 0, 0, 0);
-
/**
* Add modem tx power to history
* Device is said to be in high cellular transmit power when it has spent most of the transmit
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) {
mHasBluetoothReporting = true;
final long elapsedRealtimeMs = mClocks.elapsedRealtime();
- final long rxTimeMs = info.getControllerRxTimeMillis() -
- mLastBluetoothActivityEnergyInfo.getControllerRxTimeMillis();
- final long txTimeMs = info.getControllerTxTimeMillis() -
- mLastBluetoothActivityEnergyInfo.getControllerTxTimeMillis();
- final long idleTimeMs = info.getControllerIdleTimeMillis() -
- mLastBluetoothActivityEnergyInfo.getControllerIdleTimeMillis();
+ 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");
}
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);
}
//
long totalRxBytes = 0;
final UidTraffic[] uidTraffic = info.getUidTraffic();
- final UidTraffic[] lastUidTraffic = mLastBluetoothActivityEnergyInfo.getUidTraffic();
- final ArrayList<UidTraffic> deltaTraffic = new ArrayList<>();
- int m = 0, n = 0;
- for (; m < uidTraffic.length && n < lastUidTraffic.length; m++) {
- final UidTraffic traffic = uidTraffic[m];
- final UidTraffic lastTraffic = lastUidTraffic[n];
- if (traffic.getUid() == lastTraffic.getUid()) {
- deltaTraffic.add(new UidTraffic(traffic.getUid(),
- traffic.getRxBytes() - lastTraffic.getRxBytes(),
- traffic.getTxBytes() - lastTraffic.getTxBytes()));
- n++;
- }
- }
- for (; m < uidTraffic.length; m ++) {
- deltaTraffic.add(uidTraffic[m]);
- }
-
- for (int i = 0, j = 0; i < deltaTraffic.size(); i++) {
- final UidTraffic traffic = deltaTraffic.get(i);
+ 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)) {
- for (int i = 0; i < deltaTraffic.size(); i++) {
- final UidTraffic traffic = deltaTraffic.get(i);
+ 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;
}
if (opVolt != 0) {
// We store the power drain as mAms.
mBluetoothActivity.getPowerCounter().addCountLocked(
- (long) ((info.getControllerEnergyUsed() -
- mLastBluetoothActivityEnergyInfo.getControllerEnergyUsed() )/ opVolt));
+ (long) ((info.getControllerEnergyUsed() - mLastBluetoothActivityInfo.energy)
+ / opVolt));
}
- mLastBluetoothActivityEnergyInfo = info;
+ mLastBluetoothActivityInfo.set(info);
}
/**
KernelUidCpuActiveTimeReaderTest.class,
KernelUidCpuClusterTimeReaderTest.class,
KernelWakelockReaderTest.class,
+ LongSamplingCounterTest.class,
LongSamplingCounterArrayTest.class,
PowerCalculatorTest.class,
PowerProfileTest.class
--- /dev/null
+/*
+ * 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 static android.os.BatteryStats.STATS_SINCE_CHARGED;
+
+import static com.android.internal.os.BatteryStatsImpl.LongSamplingCounter;
+import static com.android.internal.os.BatteryStatsImpl.TimeBase;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Test class for {@link LongSamplingCounter}.
+ *
+ * To run the tests, use
+ *
+ * bit FrameworksCoreTests:com.android.internal.os.LongSamplingCounterTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LongSamplingCounterTest {
+
+ private static final long COUNT = 1111;
+ private static final long CURRENT_COUNT = 5555;
+
+ @Mock
+ private TimeBase mTimeBase;
+ private LongSamplingCounter mCounter;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mCounter = new LongSamplingCounter(mTimeBase);
+ Mockito.reset(mTimeBase);
+ }
+
+ @Test
+ public void testReadWriteParcel() {
+ final Parcel parcel = Parcel.obtain();
+ updateCounts(COUNT, CURRENT_COUNT);
+ mCounter.writeToParcel(parcel);
+ parcel.setDataPosition(0);
+
+ // Now clear counterArray and verify values are read from parcel correctly.
+ updateCounts(0, 0);
+
+ mCounter = new LongSamplingCounter(mTimeBase, parcel);
+ assertEquals(COUNT, mCounter.mCount);
+ assertEquals(CURRENT_COUNT, mCounter.mCurrentCount);
+ parcel.recycle();
+ }
+
+ @Test
+ public void testReadWriteSummaryParcel() {
+ final Parcel parcel = Parcel.obtain();
+ updateCounts(COUNT, CURRENT_COUNT);
+ mCounter.writeSummaryFromParcelLocked(parcel);
+ parcel.setDataPosition(0);
+
+ // Now clear counterArray and verify values are read from parcel correctly.
+ updateCounts(0, 0);
+
+ mCounter.readSummaryFromParcelLocked(parcel);
+ assertEquals(COUNT, mCounter.mCount);
+ parcel.recycle();
+ }
+
+ @Test
+ public void testOnTimeStarted() {
+ updateCounts(COUNT, CURRENT_COUNT);
+ mCounter.onTimeStarted(0, 0, 0);
+ assertEquals(COUNT, mCounter.mCount);
+ assertEquals(COUNT, mCounter.mUnpluggedCount);
+ }
+
+ @Test
+ public void testOnTimeStopped() {
+ updateCounts(COUNT, CURRENT_COUNT);
+ mCounter.onTimeStopped(0, 0, 0);
+ assertEquals(COUNT, mCounter.mCount);
+ }
+
+ @Test
+ public void testAddCountLocked() {
+ updateCounts(0, 0);
+ assertEquals(0, mCounter.getCountLocked(0));
+ when(mTimeBase.isRunning()).thenReturn(true);
+ mCounter.addCountLocked(111);
+ assertEquals(111, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+ assertEquals(111, mCounter.mCurrentCount);
+ mCounter.addCountLocked(222);
+ assertEquals(333, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+ assertEquals(333, mCounter.mCurrentCount);
+
+ when(mTimeBase.isRunning()).thenReturn(false);
+ mCounter.addCountLocked(456);
+ assertEquals(333, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+ assertEquals(789, mCounter.mCurrentCount);
+
+ mCounter.addCountLocked(444, true);
+ assertEquals(777, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+ assertEquals(1233, mCounter.mCurrentCount);
+ mCounter.addCountLocked(567, false);
+ assertEquals(777, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+ assertEquals(1800, mCounter.mCurrentCount);
+ }
+
+ @Test
+ public void testUpdate() {
+ updateCounts(0, 0);
+ assertEquals(0, mCounter.getCountLocked(0));
+ when(mTimeBase.isRunning()).thenReturn(true);
+ mCounter.update(111);
+ assertEquals(111, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+ assertEquals(111, mCounter.mCurrentCount);
+ mCounter.update(333);
+ assertEquals(333, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+ assertEquals(333, mCounter.mCurrentCount);
+
+ when(mTimeBase.isRunning()).thenReturn(false);
+ mCounter.update(789);
+ assertEquals(333, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+ assertEquals(789, mCounter.mCurrentCount);
+ mCounter.update(100);
+ assertEquals(333, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+ assertEquals(100, mCounter.mCurrentCount);
+
+ mCounter.update(544, true);
+ assertEquals(777, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+ assertEquals(544, mCounter.mCurrentCount);
+ mCounter.update(1544, false);
+ assertEquals(777, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+ assertEquals(1544, mCounter.mCurrentCount);
+ }
+
+ @Test
+ public void testReset() {
+ updateCounts(COUNT, CURRENT_COUNT);
+ // Test with detachIfReset=false
+ mCounter.reset(false /* detachIfReset */);
+ assertEquals(0, mCounter.mCount);
+ assertEquals(CURRENT_COUNT, mCounter.mCurrentCount);
+ verifyZeroInteractions(mTimeBase);
+
+ updateCounts(COUNT, CURRENT_COUNT);
+ // Test with detachIfReset=true
+ mCounter.reset(true /* detachIfReset */);
+ assertEquals(0, mCounter.mCount);
+ assertEquals(CURRENT_COUNT, mCounter.mCurrentCount);
+ verify(mTimeBase).remove(mCounter);
+ verifyNoMoreInteractions(mTimeBase);
+ }
+
+ @Test
+ public void testDetach() {
+ mCounter.detach();
+ verify(mTimeBase).remove(mCounter);
+ verifyNoMoreInteractions(mTimeBase);
+ }
+
+ private void updateCounts(long total, long current) {
+ mCounter.mCount = total;
+ mCounter.mCurrentCount = current;
+ }
+}
mUseLatestStates = true;
}
- synchronized (mWorkerLock) {
- if (DEBUG) {
- Slog.d(TAG, "begin updateExternalStatsSync reason=" + reason);
- }
- try {
- updateExternalStatsLocked(reason, updateFlags, onBattery,
- onBatteryScreenOff, useLatestStates);
- } finally {
+ try {
+ synchronized (mWorkerLock) {
if (DEBUG) {
- Slog.d(TAG, "end updateExternalStatsSync");
+ Slog.d(TAG, "begin updateExternalStatsSync reason=" + reason);
+ }
+ try {
+ updateExternalStatsLocked(reason, updateFlags, onBattery,
+ onBatteryScreenOff, useLatestStates);
+ } finally {
+ if (DEBUG) {
+ Slog.d(TAG, "end updateExternalStatsSync");
+ }
}
}
- }
- if ((updateFlags & UPDATE_CPU) != 0) {
- mStats.copyFromAllUidsCpuTimes();
- }
+ if ((updateFlags & UPDATE_CPU) != 0) {
+ mStats.copyFromAllUidsCpuTimes();
+ }
- // Clean up any UIDs if necessary.
- synchronized (mStats) {
- for (int uid : uidsToRemove) {
- mStats.removeIsolatedUidLocked(uid);
+ // Clean up any UIDs if necessary.
+ synchronized (mStats) {
+ for (int uid : uidsToRemove) {
+ mStats.removeIsolatedUidLocked(uid);
+ }
+ mStats.clearPendingRemovedUids();
}
- mStats.clearPendingRemovedUids();
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Error updating external stats: ", e);
}
}
};
if (bluetoothInfo.isValid()) {
mStats.updateBluetoothStateLocked(bluetoothInfo);
} else {
- Slog.e(TAG, "bluetooth info is invalid: " + bluetoothInfo);
+ Slog.w(TAG, "bluetooth info is invalid: " + bluetoothInfo);
}
}
}
if (wifiInfo.isValid()) {
mStats.updateWifiState(extractDeltaLocked(wifiInfo));
} else {
- Slog.e(TAG, "wifi info is invalid: " + wifiInfo);
+ Slog.w(TAG, "wifi info is invalid: " + wifiInfo);
}
}
if (modemInfo.isValid()) {
mStats.updateMobileRadioState(modemInfo);
} else {
- Slog.e(TAG, "modem info is invalid: " + modemInfo);
+ Slog.w(TAG, "modem info is invalid: " + modemInfo);
}
}
}