Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0ebc96ce authored by Narayan Kamath's avatar Narayan Kamath
Browse files

BatteryStatsImpl: Support chained attribution for BT events.

These changes are slightly different than the ones for Wifi etc.
We need to keep track of the list of WorkChains attributed to a given
UID in order to log stop events for each of them if the BT process
crashes (or goes away) and we receive a call to noteResetBluetooth..[].

Test: BatteryStatsTests
Bug: 62390666

Change-Id: I4aaa2260cdc509ca08c4fa4838df77cda870ef75
parent 728c8a08
Loading
Loading
Loading
Loading
+133 −26
Original line number Diff line number Diff line
@@ -5274,8 +5274,9 @@ public class BatteryStatsImpl extends BatteryStats {
        }
    }
    private void noteBluetoothScanStartedLocked(int uid, boolean isUnoptimized) {
        uid = mapUid(uid);
    private void noteBluetoothScanStartedLocked(WorkChain workChain, int uid,
            boolean isUnoptimized) {
        uid = getAttributionUid(uid, workChain);
        final long elapsedRealtime = mClocks.elapsedRealtime();
        final long uptime = mClocks.uptimeMillis();
        if (mBluetoothScanNesting == 0) {
@@ -5286,18 +5287,36 @@ public class BatteryStatsImpl extends BatteryStats {
            mBluetoothScanTimer.startRunningLocked(elapsedRealtime);
        }
        mBluetoothScanNesting++;
        // TODO(statsd): Log WorkChain here if it's non-null.
        StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED, uid, 1);
        if (isUnoptimized) {
            StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, 1);
        }
        getUidStatsLocked(uid).noteBluetoothScanStartedLocked(elapsedRealtime, isUnoptimized);
        if (workChain != null) {
            getUidStatsLocked(uid).addBluetoothWorkChain(workChain, isUnoptimized);
        }
    }
    public void noteBluetoothScanStartedFromSourceLocked(WorkSource ws, boolean isUnoptimized) {
        final int N = ws.size();
        for (int i = 0; i < N; i++) {
            noteBluetoothScanStartedLocked(ws.get(i), isUnoptimized);
            noteBluetoothScanStartedLocked(null, ws.get(i), isUnoptimized);
        }
        final List<WorkChain> workChains = ws.getWorkChains();
        if (workChains != null) {
            for (int i = 0; i < workChains.size(); ++i) {
                noteBluetoothScanStartedLocked(workChains.get(i), -1, isUnoptimized);
            }
        }
    }
    private void noteBluetoothScanStoppedLocked(int uid, boolean isUnoptimized) {
        uid = mapUid(uid);
    private void noteBluetoothScanStoppedLocked(WorkChain workChain, int uid,
            boolean isUnoptimized) {
        uid = getAttributionUid(uid, workChain);
        final long elapsedRealtime = mClocks.elapsedRealtime();
        final long uptime = mClocks.uptimeMillis();
        mBluetoothScanNesting--;
@@ -5308,13 +5327,38 @@ public class BatteryStatsImpl extends BatteryStats {
            addHistoryRecordLocked(elapsedRealtime, uptime);
            mBluetoothScanTimer.stopRunningLocked(elapsedRealtime);
        }
        // TODO(statsd): Log WorkChain here if it's non-null.
        StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED, uid, 0);
        if (isUnoptimized) {
            StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, 0);
        }
        getUidStatsLocked(uid).noteBluetoothScanStoppedLocked(elapsedRealtime, isUnoptimized);
        if (workChain != null) {
            getUidStatsLocked(uid).removeBluetoothWorkChain(workChain, isUnoptimized);
        }
    }
    private int getAttributionUid(int uid, WorkChain workChain) {
        if (workChain != null) {
            return mapUid(workChain.getAttributionUid());
        }
        return mapUid(uid);
    }
    public void noteBluetoothScanStoppedFromSourceLocked(WorkSource ws, boolean isUnoptimized) {
        final int N = ws.size();
        for (int i = 0; i < N; i++) {
            noteBluetoothScanStoppedLocked(ws.get(i), isUnoptimized);
            noteBluetoothScanStoppedLocked(null, ws.get(i), isUnoptimized);
        }
        final List<WorkChain> workChains = ws.getWorkChains();
        if (workChains != null) {
            for (int i = 0; i < workChains.size(); ++i) {
                noteBluetoothScanStoppedLocked(workChains.get(i), -1, isUnoptimized);
            }
        }
    }
@@ -5328,9 +5372,35 @@ public class BatteryStatsImpl extends BatteryStats {
                    + 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);
                StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED, uid.getUid(), 0);
                List<WorkChain> allWorkChains = uid.getAllBluetoothWorkChains();
                if (allWorkChains != null) {
                    for (int j = 0; j < allWorkChains.size(); ++j) {
                        // TODO(statsd) : Log the entire workchain here.
                        StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED,
                                allWorkChains.get(j).getAttributionUid(), 0);
                    }
                    allWorkChains.clear();
                }
                List<WorkChain> unoptimizedWorkChains = uid.getUnoptimizedBluetoothWorkChains();
                if (unoptimizedWorkChains != null) {
                    for (int j = 0; j < unoptimizedWorkChains.size(); ++j) {
                        // TODO(statsd) : Log the entire workchain here.
                        StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
                                unoptimizedWorkChains.get(j).getAttributionUid(), 0);
                    }
                    unoptimizedWorkChains.clear();
                }
            }
        }
    }
@@ -5340,6 +5410,18 @@ public class BatteryStatsImpl extends BatteryStats {
        for (int i = 0; i < N; i++) {
            int uid = mapUid(ws.get(i));
            getUidStatsLocked(uid).noteBluetoothScanResultsLocked(numNewResults);
            StatsLog.write(StatsLog.BLE_SCAN_RESULT_RECEIVED, uid, numNewResults);
        }
        final List<WorkChain> workChains = ws.getWorkChains();
        if (workChains != null) {
            for (int i = 0; i < workChains.size(); ++i) {
                final WorkChain wc = workChains.get(i);
                int uid = mapUid(wc.getAttributionUid());
                getUidStatsLocked(uid).noteBluetoothScanResultsLocked(numNewResults);
                // TODO(statsd): Log the entire WorkChain here.
                StatsLog.write(StatsLog.BLE_SCAN_RESULT_RECEIVED, uid, numNewResults);
            }
        }
    }
@@ -6345,6 +6427,15 @@ public class BatteryStatsImpl extends BatteryStats {
         */
        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;
@@ -6864,44 +6955,63 @@ public class BatteryStatsImpl extends BatteryStats {
            return mBluetoothUnoptimizedScanTimer;
        }
        public void noteBluetoothScanStartedLocked(long elapsedRealtimeMs, boolean isUnoptimized) {
        public void noteBluetoothScanStartedLocked(long elapsedRealtimeMs,
                boolean isUnoptimized) {
            createBluetoothScanTimerLocked().startRunningLocked(elapsedRealtimeMs);
            // TODO(statsd): Possibly use a worksource instead of a uid.
            StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED, getUid(), 1);
            if (isUnoptimized) {
                createBluetoothUnoptimizedScanTimerLocked().startRunningLocked(elapsedRealtimeMs);
                // TODO(statsd): Possibly use a worksource instead of a uid.
                StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, getUid(), 1);
            }
        }
        public void noteBluetoothScanStoppedLocked(long elapsedRealtimeMs, boolean isUnoptimized) {
            if (mBluetoothScanTimer != null) {
                mBluetoothScanTimer.stopRunningLocked(elapsedRealtimeMs);
                if (!mBluetoothScanTimer.isRunningLocked()) { // only tell statsd if truly stopped
                    // TODO(statsd): Possibly use a worksource instead of a uid.
                    StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED, getUid(), 0);
                }
            }
            if (isUnoptimized && mBluetoothUnoptimizedScanTimer != null) {
                mBluetoothUnoptimizedScanTimer.stopRunningLocked(elapsedRealtimeMs);
                if (!mBluetoothUnoptimizedScanTimer.isRunningLocked()) {
                    // TODO(statsd): Possibly use a worksource instead of a uid.
                    StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, getUid(), 0);
            }
        }
        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);
                // TODO(statsd): Possibly use a worksource instead of a uid.
                StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED, getUid(), 0);
            }
            if (mBluetoothUnoptimizedScanTimer != null) {
                mBluetoothUnoptimizedScanTimer.stopAllRunningLocked(elapsedRealtimeMs);
                // TODO(statsd): Possibly use a worksource instead of a uid.
                StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, getUid(), 0);
            }
        }
@@ -6923,9 +7033,6 @@ public class BatteryStatsImpl extends BatteryStats {
            createBluetoothScanResultCounterLocked().addAtomic(numNewResults);
            // Uses background timebase, so the count will only be incremented if uid in background.
            createBluetoothScanResultBgCounterLocked().addAtomic(numNewResults);
            // TODO(statsd): Possibly use a worksource instead of a uid.
            // TODO(statsd): This could be in AppScanStats instead, if desired.
            StatsLog.write(StatsLog.BLE_SCAN_RESULT_RECEIVED, getUid(), numNewResults);
        }
        @Override
+58 −1
Original line number Diff line number Diff line
@@ -194,9 +194,19 @@ public class BatteryStatsBackgroundStatsTest extends TestCase {

    @SmallTest
    public void testAppBluetoothScan() throws Exception {
        doTestAppBluetoothScanInternal(new WorkSource(UID));
    }

    @SmallTest
    public void testAppBluetoothScan_workChain() throws Exception {
        WorkSource ws = new WorkSource();
        ws.createWorkChain().addNode(UID, "foo");
        doTestAppBluetoothScanInternal(ws);
    }

    private void doTestAppBluetoothScanInternal(WorkSource ws) throws Exception {
        final MockClocks clocks = new MockClocks();
        MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
        WorkSource ws = new WorkSource(UID); // needed for bluetooth
        long curr = 0; // realtime in us

        // On battery
@@ -262,6 +272,53 @@ public class BatteryStatsBackgroundStatsTest extends TestCase {
        assertEquals((3004 - 2001) * 1000, badBgTime);
    }

    @SmallTest
    public void testAppBluetoothScan_workChainAccounting() throws Exception {
        final MockClocks clocks = new MockClocks();MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
        long curr = 0; // realtime in us

        // On battery
        curr = 1000 * (clocks.realtime = clocks.uptime = 100);
        bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr); // on battery

        // App in foreground
        bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);

        WorkSource ws = new WorkSource();
        ws.createWorkChain().addNode(500, "foo");
        ws.createWorkChain().addNode(500, "bar");

        // Test start / stop and reset with isUnoptimized == false.
        bi.noteBluetoothScanStartedFromSourceLocked(ws, false);
        BatteryStatsImpl.Uid stats = (BatteryStatsImpl.Uid) bi.getUidStats().get(500);
        assertEquals(ws.getWorkChains(), stats.getAllBluetoothWorkChains());
        assertNull(stats.getUnoptimizedBluetoothWorkChains());

        bi.noteBluetoothScanStoppedFromSourceLocked(ws, false);
        assertTrue(stats.getAllBluetoothWorkChains().isEmpty());
        assertNull(stats.getUnoptimizedBluetoothWorkChains());

        bi.noteBluetoothScanStartedFromSourceLocked(ws, false);
        bi.noteResetBluetoothScanLocked();
        assertTrue(stats.getAllBluetoothWorkChains().isEmpty());
        assertNull(stats.getUnoptimizedBluetoothWorkChains());

        // Test start / stop  and reset with isUnoptimized == true.
        bi.noteBluetoothScanStartedFromSourceLocked(ws, true);
        stats = (BatteryStatsImpl.Uid) bi.getUidStats().get(500);
        assertEquals(ws.getWorkChains(), stats.getAllBluetoothWorkChains());
        assertEquals(ws.getWorkChains(), stats.getUnoptimizedBluetoothWorkChains());

        bi.noteBluetoothScanStoppedFromSourceLocked(ws, true);
        assertTrue(stats.getAllBluetoothWorkChains().isEmpty());
        assertTrue(stats.getUnoptimizedBluetoothWorkChains().isEmpty());

        bi.noteBluetoothScanStartedFromSourceLocked(ws, true);
        bi.noteResetBluetoothScanLocked();
        assertTrue(stats.getAllBluetoothWorkChains().isEmpty());
        assertTrue(stats.getUnoptimizedBluetoothWorkChains().isEmpty());
    }

    @SmallTest
    public void testJob() throws Exception {
        final MockClocks clocks = new MockClocks();