Batterystats track background bad ble scan time

Allows tracking ble scan time (total and background) for unoptimized
scans. Whether the scan is unoptimized is provided by the bluetooth
code when calling batterystats.

Bug: 38461344
Test: runtest -x frameworks/base/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
Test: run cts-dev -m CtsIncidentHostTestCases -t com.android.server.cts.BatteryStatsValidationTest#testUnoptimizedBleScans
Test: cts-tradefed run cts-dev -m CtsDumpsysHostTestCases -t android.dumpsys.cts.BatteryStatsDumpsysTest

Change-Id: I814482ff663424170eac4b413464d24c14a5cf91
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 499d6bb..580223d 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -162,6 +162,11 @@
     public static final int AGGREGATED_WAKE_TYPE_PARTIAL = 20;
 
     /**
+     * A constant indicating a bluetooth scan timer for unoptimized scans.
+     */
+    public static final int BLUETOOTH_UNOPTIMIZED_SCAN_ON = 21;
+
+    /**
      * Include all of the data in the stats, including previously saved data.
      */
     public static final int STATS_SINCE_CHARGED = 0;
@@ -191,8 +196,12 @@
      * New in version 21:
      *   - Actual (not just apportioned) Wakelock time is also recorded.
      *   - Aggregated partial wakelock time (per uid, instead of per wakelock) is recorded.
+     *   - BLE scan result count
+     *   - CPU frequency time per uid
+     * New in version 22:
+     *   - BLE scan result background count, BLE unoptimized scan time
      */
-    static final String CHECKIN_VERSION = "21";
+    static final String CHECKIN_VERSION = "22";
 
     /**
      * Old version, we hit 9 and ran out of room, need to remove.
@@ -565,7 +574,10 @@
         public abstract Timer getForegroundActivityTimer();
         public abstract Timer getBluetoothScanTimer();
         public abstract Timer getBluetoothScanBackgroundTimer();
+        public abstract Timer getBluetoothUnoptimizedScanTimer();
+        public abstract Timer getBluetoothUnoptimizedScanBackgroundTimer();
         public abstract Counter getBluetoothScanResultCounter();
+        public abstract Counter getBluetoothScanResultBgCounter();
 
         public abstract long[] getCpuFreqTimes(int which);
         public abstract long[] getScreenOffCpuFreqTimes(int which);
@@ -3429,10 +3441,29 @@
                     final long actualTime = bleTimer.getTotalDurationMsLocked(rawRealtimeMs);
                     final long actualTimeBg = bleTimerBg != null ?
                             bleTimerBg.getTotalDurationMsLocked(rawRealtimeMs) : 0;
+                    // Result counters
                     final int resultCount = u.getBluetoothScanResultCounter() != null ?
                             u.getBluetoothScanResultCounter().getCountLocked(which) : 0;
+                    final int resultCountBg = u.getBluetoothScanResultBgCounter() != null ?
+                            u.getBluetoothScanResultBgCounter().getCountLocked(which) : 0;
+                    // Unoptimized scan timer. Unpooled and since reset (regardless of 'which').
+                    final Timer unoptimizedScanTimer = u.getBluetoothUnoptimizedScanTimer();
+                    final long unoptimizedScanTotalTime = unoptimizedScanTimer != null ?
+                            unoptimizedScanTimer.getTotalDurationMsLocked(rawRealtimeMs) : 0;
+                    final long unoptimizedScanMaxTime = unoptimizedScanTimer != null ?
+                            unoptimizedScanTimer.getMaxDurationMsLocked(rawRealtimeMs) : 0;
+                    // Unoptimized bg scan timer. Unpooled and since reset (regardless of 'which').
+                    final Timer unoptimizedScanTimerBg =
+                            u.getBluetoothUnoptimizedScanBackgroundTimer();
+                    final long unoptimizedScanTotalTimeBg = unoptimizedScanTimerBg != null ?
+                            unoptimizedScanTimerBg.getTotalDurationMsLocked(rawRealtimeMs) : 0;
+                    final long unoptimizedScanMaxTimeBg = unoptimizedScanTimerBg != null ?
+                            unoptimizedScanTimerBg.getMaxDurationMsLocked(rawRealtimeMs) : 0;
+
                     dumpLine(pw, uid, category, BLUETOOTH_MISC_DATA, totalTime, count,
-                            countBg, actualTime, actualTimeBg, resultCount);
+                            countBg, actualTime, actualTimeBg, resultCount, resultCountBg,
+                            unoptimizedScanTotalTime, unoptimizedScanTotalTimeBg,
+                            unoptimizedScanMaxTime, unoptimizedScanMaxTimeBg);
                 }
             }
 
@@ -4625,34 +4656,94 @@
                     final long actualTimeMs = bleTimer.getTotalDurationMsLocked(rawRealtimeMs);
                     final long actualTimeMsBg = bleTimerBg != null ?
                             bleTimerBg.getTotalDurationMsLocked(rawRealtimeMs) : 0;
+                    // Result counters
                     final int resultCount = u.getBluetoothScanResultCounter() != null ?
                             u.getBluetoothScanResultCounter().getCountLocked(which) : 0;
+                    final int resultCountBg = u.getBluetoothScanResultBgCounter() != null ?
+                            u.getBluetoothScanResultBgCounter().getCountLocked(which) : 0;
+                    // Unoptimized scan timer. Unpooled and since reset (regardless of 'which').
+                    final Timer unoptimizedScanTimer = u.getBluetoothUnoptimizedScanTimer();
+                    final long unoptimizedScanTotalTime = unoptimizedScanTimer != null ?
+                            unoptimizedScanTimer.getTotalDurationMsLocked(rawRealtimeMs) : 0;
+                    final long unoptimizedScanMaxTime = unoptimizedScanTimer != null ?
+                            unoptimizedScanTimer.getMaxDurationMsLocked(rawRealtimeMs) : 0;
+                    // Unoptimized bg scan timer. Unpooled and since reset (regardless of 'which').
+                    final Timer unoptimizedScanTimerBg =
+                            u.getBluetoothUnoptimizedScanBackgroundTimer();
+                    final long unoptimizedScanTotalTimeBg = unoptimizedScanTimerBg != null ?
+                            unoptimizedScanTimerBg.getTotalDurationMsLocked(rawRealtimeMs) : 0;
+                    final long unoptimizedScanMaxTimeBg = unoptimizedScanTimerBg != null ?
+                            unoptimizedScanTimerBg.getMaxDurationMsLocked(rawRealtimeMs) : 0;
 
                     sb.setLength(0);
-                    sb.append(prefix);
-                    sb.append("    ");
-                    sb.append("Bluetooth Scan");
-                    sb.append(": ");
                     if (actualTimeMs != totalTimeMs) {
+                        sb.append(prefix);
+                        sb.append("    Bluetooth Scan (total blamed realtime): ");
                         formatTimeMs(sb, totalTimeMs);
-                        sb.append("blamed realtime, ");
+                        sb.append(" (");
+                        sb.append(count);
+                        sb.append(" times)");
+                        if (bleTimer.isRunningLocked()) {
+                            sb.append(" (currently running)");
+                        }
+                        sb.append("\n");
                     }
-                    formatTimeMs(sb, actualTimeMs); // since reset, regardless of 'which'
-                    sb.append("realtime (");
+
+                    sb.append(prefix);
+                    sb.append("    Bluetooth Scan (total actual realtime): ");
+                    formatTimeMs(sb, actualTimeMs); // since reset, ignores 'which'
+                    sb.append(" (");
                     sb.append(count);
                     sb.append(" times)");
                     if (bleTimer.isRunningLocked()) {
-                            sb.append(" (running)");
+                            sb.append(" (currently running)");
                     }
-                    if (actualTimeMsBg != 0 || countBg > 0) {
-                        sb.append(", ");
-                        formatTimeMs(sb, actualTimeMsBg); // since reset, regardless of 'which'
-                        sb.append("background (");
+                    sb.append("\n");
+                    if (actualTimeMsBg > 0 || countBg > 0) {
+                        sb.append(prefix);
+                        sb.append("    Bluetooth Scan (background realtime): ");
+                        formatTimeMs(sb, actualTimeMsBg); // since reset, ignores 'which'
+                        sb.append(" (");
                         sb.append(countBg);
                         sb.append(" times)");
+                        if (bleTimerBg != null && bleTimerBg.isRunningLocked()) {
+                            sb.append(" (currently running in background)");
+                        }
+                        sb.append("\n");
                     }
-                    sb.append("; Results count ");
+
+                    sb.append(prefix);
+                    sb.append("    Bluetooth Scan Results: ");
                     sb.append(resultCount);
+                    sb.append(" (");
+                    sb.append(resultCountBg);
+                    sb.append(" in background)");
+
+                    if (unoptimizedScanTotalTime > 0 || unoptimizedScanTotalTimeBg > 0) {
+                        sb.append("\n");
+                        sb.append(prefix);
+                        sb.append("    Unoptimized Bluetooth Scan (realtime): ");
+                        formatTimeMs(sb, unoptimizedScanTotalTime); // since reset, ignores 'which'
+                        sb.append(" (max ");
+                        formatTimeMs(sb, unoptimizedScanMaxTime); // since reset, ignores 'which'
+                        sb.append(")");
+                        if (unoptimizedScanTimer != null
+                                && unoptimizedScanTimer.isRunningLocked()) {
+                            sb.append(" (currently running unoptimized)");
+                        }
+                        if (unoptimizedScanTimerBg != null && unoptimizedScanTotalTimeBg > 0) {
+                            sb.append("\n");
+                            sb.append(prefix);
+                            sb.append("    Unoptimized Bluetooth Scan (background realtime): ");
+                            formatTimeMs(sb, unoptimizedScanTotalTimeBg); // since reset
+                            sb.append(" (max ");
+                            formatTimeMs(sb, unoptimizedScanMaxTimeBg); // since reset
+                            sb.append(")");
+                            if (unoptimizedScanTimerBg.isRunningLocked()) {
+                                sb.append(" (currently running unoptimized in background)");
+                            }
+                        }
+                    }
                     pw.println(sb.toString());
                     uidActivity = true;
                 }
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 78566df..04f7c76 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -129,7 +129,7 @@
     long getAwakeTimeBattery();
     long getAwakeTimePlugged();
 
-    void noteBleScanStarted(in WorkSource ws);
+    void noteBleScanStarted(in WorkSource ws, boolean isUnoptimized);
     void noteBleScanStopped(in WorkSource ws);
     void noteResetBleScan();
     void noteBleScanResults(in WorkSource ws, int numNewResults);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index f28e862..e4d8525 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -4821,7 +4821,7 @@
         }
     }
 
-    private void noteBluetoothScanStartedLocked(int uid) {
+    private void noteBluetoothScanStartedLocked(int uid, boolean isUnoptimized) {
         uid = mapUid(uid);
         final long elapsedRealtime = mClocks.elapsedRealtime();
         final long uptime = mClocks.uptimeMillis();
@@ -4833,13 +4833,13 @@
             mBluetoothScanTimer.startRunningLocked(elapsedRealtime);
         }
         mBluetoothScanNesting++;
-        getUidStatsLocked(uid).noteBluetoothScanStartedLocked(elapsedRealtime);
+        getUidStatsLocked(uid).noteBluetoothScanStartedLocked(elapsedRealtime, isUnoptimized);
     }
 
-    public void noteBluetoothScanStartedFromSourceLocked(WorkSource ws) {
+    public void noteBluetoothScanStartedFromSourceLocked(WorkSource ws, boolean isUnoptimized) {
         final int N = ws.size();
         for (int i = 0; i < N; i++) {
-            noteBluetoothScanStartedLocked(ws.get(i));
+            noteBluetoothScanStartedLocked(ws.get(i), isUnoptimized);
         }
     }
 
@@ -5611,7 +5611,9 @@
         /** Total time spent by the uid holding any partial wakelocks. */
         DualTimer mAggregatedPartialWakelockTimer;
         DualTimer mBluetoothScanTimer;
+        DualTimer mBluetoothUnoptimizedScanTimer;
         Counter mBluetoothScanResultCounter;
+        Counter mBluetoothScanResultBgCounter;
 
         int mProcessState = ActivityManager.PROCESS_STATE_NONEXISTENT;
         StopwatchTimer[] mProcessStateTimer;
@@ -6096,20 +6098,41 @@
             return mBluetoothScanTimer;
         }
 
-        public void noteBluetoothScanStartedLocked(long elapsedRealtimeMs) {
+        public DualTimer createBluetoothUnoptimizedScanTimerLocked() {
+            if (mBluetoothUnoptimizedScanTimer == null) {
+                mBluetoothUnoptimizedScanTimer = new DualTimer(mBsi.mClocks, Uid.this,
+                        BLUETOOTH_UNOPTIMIZED_SCAN_ON, null,
+                        mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase);
+            }
+            return mBluetoothUnoptimizedScanTimer;
+        }
+
+        public void noteBluetoothScanStartedLocked(long elapsedRealtimeMs, boolean isUnoptimized) {
             createBluetoothScanTimerLocked().startRunningLocked(elapsedRealtimeMs);
+            if (isUnoptimized) {
+                createBluetoothUnoptimizedScanTimerLocked().startRunningLocked(elapsedRealtimeMs);
+            }
         }
 
         public void noteBluetoothScanStoppedLocked(long elapsedRealtimeMs) {
             if (mBluetoothScanTimer != null) {
                 mBluetoothScanTimer.stopRunningLocked(elapsedRealtimeMs);
             }
+            // In the ble code, a scan cannot change types and nested starts are not possible.
+            // So if an unoptimizedScan is running, it is now being stopped.
+            if (mBluetoothUnoptimizedScanTimer != null
+                    && mBluetoothUnoptimizedScanTimer.isRunningLocked()) {
+                mBluetoothUnoptimizedScanTimer.stopRunningLocked(elapsedRealtimeMs);
+            }
         }
 
         public void noteResetBluetoothScanLocked(long elapsedRealtimeMs) {
             if (mBluetoothScanTimer != null) {
                 mBluetoothScanTimer.stopAllRunningLocked(elapsedRealtimeMs);
             }
+            if (mBluetoothUnoptimizedScanTimer != null) {
+                mBluetoothUnoptimizedScanTimer.stopAllRunningLocked(elapsedRealtimeMs);
+            }
         }
 
         public Counter createBluetoothScanResultCounterLocked() {
@@ -6119,8 +6142,17 @@
             return mBluetoothScanResultCounter;
         }
 
+        public Counter createBluetoothScanResultBgCounterLocked() {
+            if (mBluetoothScanResultBgCounter == null) {
+                mBluetoothScanResultBgCounter = new Counter(mOnBatteryBackgroundTimeBase);
+            }
+            return mBluetoothScanResultBgCounter;
+        }
+
         public void noteBluetoothScanResultsLocked(int numNewResults) {
             createBluetoothScanResultCounterLocked().addAtomic(numNewResults);
+            // Uses background timebase, so the count will only be incremented if uid in background.
+            createBluetoothScanResultBgCounterLocked().addAtomic(numNewResults);
         }
 
         @Override
@@ -6277,10 +6309,28 @@
         }
 
         @Override
+        public Timer getBluetoothUnoptimizedScanTimer() {
+            return mBluetoothUnoptimizedScanTimer;
+        }
+
+        @Override
+        public Timer getBluetoothUnoptimizedScanBackgroundTimer() {
+            if (mBluetoothUnoptimizedScanTimer == null) {
+                return null;
+            }
+            return mBluetoothUnoptimizedScanTimer.getSubTimer();
+        }
+
+        @Override
         public Counter getBluetoothScanResultCounter() {
             return mBluetoothScanResultCounter;
         }
 
+        @Override
+        public Counter getBluetoothScanResultBgCounter() {
+            return mBluetoothScanResultBgCounter;
+        }
+
         void makeProcessState(int i, Parcel in) {
             if (i < 0 || i >= NUM_PROCESS_STATE) return;
 
@@ -6531,9 +6581,13 @@
             active |= !resetTimerIfNotNull(mForegroundActivityTimer, 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);
+            }
 
             if (mProcessStateTimer != null) {
                 for (int i = 0; i < NUM_PROCESS_STATE; i++) {
@@ -6731,10 +6785,18 @@
                     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();
@@ -6919,12 +6981,24 @@
             } else {
                 out.writeInt(0);
             }
+            if (mBluetoothUnoptimizedScanTimer != null) {
+                out.writeInt(1);
+                mBluetoothUnoptimizedScanTimer.writeToParcel(out, elapsedRealtimeUs);
+            } else {
+                out.writeInt(0);
+            }
             if (mBluetoothScanResultCounter != null) {
                 out.writeInt(1);
                 mBluetoothScanResultCounter.writeToParcel(out);
             } else {
                 out.writeInt(0);
             }
+            if (mBluetoothScanResultBgCounter != null) {
+                out.writeInt(1);
+                mBluetoothScanResultBgCounter.writeToParcel(out);
+            } else {
+                out.writeInt(0);
+            }
             for (int i = 0; i < NUM_PROCESS_STATE; i++) {
                 if (mProcessStateTimer[i] != null) {
                     out.writeInt(1);
@@ -7168,10 +7242,22 @@
                 mBluetoothScanTimer = null;
             }
             if (in.readInt() != 0) {
+                mBluetoothUnoptimizedScanTimer = new DualTimer(mBsi.mClocks, Uid.this,
+                        BLUETOOTH_UNOPTIMIZED_SCAN_ON, null,
+                        mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase, in);
+            } else {
+                mBluetoothUnoptimizedScanTimer = null;
+            }
+            if (in.readInt() != 0) {
                 mBluetoothScanResultCounter = new Counter(mBsi.mOnBatteryTimeBase, in);
             } else {
                 mBluetoothScanResultCounter = null;
             }
+            if (in.readInt() != 0) {
+                mBluetoothScanResultBgCounter = new Counter(mOnBatteryBackgroundTimeBase, in);
+            } else {
+                mBluetoothScanResultBgCounter = null;
+            }
             mProcessState = ActivityManager.PROCESS_STATE_NONEXISTENT;
             for (int i = 0; i < NUM_PROCESS_STATE; i++) {
                 if (in.readInt() != 0) {
@@ -11378,8 +11464,14 @@
                 u.createBluetoothScanTimerLocked().readSummaryFromParcelLocked(in);
             }
             if (in.readInt() != 0) {
+                u.createBluetoothUnoptimizedScanTimerLocked().readSummaryFromParcelLocked(in);
+            }
+            if (in.readInt() != 0) {
                 u.createBluetoothScanResultCounterLocked().readSummaryFromParcelLocked(in);
             }
+            if (in.readInt() != 0) {
+                u.createBluetoothScanResultBgCounterLocked().readSummaryFromParcelLocked(in);
+            }
             u.mProcessState = ActivityManager.PROCESS_STATE_NONEXISTENT;
             for (int i = 0; i < Uid.NUM_PROCESS_STATE; i++) {
                 if (in.readInt() != 0) {
@@ -11787,12 +11879,24 @@
             } else {
                 out.writeInt(0);
             }
+            if (u.mBluetoothUnoptimizedScanTimer != null) {
+                out.writeInt(1);
+                u.mBluetoothUnoptimizedScanTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+            } else {
+                out.writeInt(0);
+            }
             if (u.mBluetoothScanResultCounter != null) {
                 out.writeInt(1);
                 u.mBluetoothScanResultCounter.writeSummaryFromParcelLocked(out);
             } else {
                 out.writeInt(0);
             }
+            if (u.mBluetoothScanResultBgCounter != null) {
+                out.writeInt(1);
+                u.mBluetoothScanResultBgCounter.writeSummaryFromParcelLocked(out);
+            } else {
+                out.writeInt(0);
+            }
             for (int i = 0; i < Uid.NUM_PROCESS_STATE; i++) {
                 if (u.mProcessStateTimer[i] != null) {
                     out.writeInt(1);
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
index 9aabdbb..c539f78 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
@@ -205,9 +205,9 @@
         // App in foreground
         bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
 
-        // Start timer
+        // Start timer (optimized)
         curr = 1000 * (clocks.realtime = clocks.uptime = 202);
-        bi.noteBluetoothScanStartedFromSourceLocked(ws);
+        bi.noteBluetoothScanStartedFromSourceLocked(ws, false);
 
         // Move to background
         curr = 1000 * (clocks.realtime = clocks.uptime = 254);
@@ -221,21 +221,44 @@
         curr = 1000 * (clocks.realtime = clocks.uptime = 409);
         bi.noteBluetoothScanStoppedFromSourceLocked(ws);
 
+        // Start timer (unoptimized)
+        curr = 1000 * (clocks.realtime = clocks.uptime = 1000);
+        bi.noteBluetoothScanStartedFromSourceLocked(ws, true);
+
+        // On battery
+        curr = 1000 * (clocks.realtime = clocks.uptime = 2001);
+        bi.updateTimeBasesLocked(true, false, curr, curr); // on battery
+
+        // Move to foreground
+        curr = 1000 * (clocks.realtime = clocks.uptime = 3004);
+        bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP);
+
+        // Stop timer
+        curr = 1000 * (clocks.realtime = clocks.uptime = 4008);
+        bi.noteBluetoothScanStoppedFromSourceLocked(ws);
+
         // Test
-        curr = 1000 * (clocks.realtime = clocks.uptime = 657);
+        curr = 1000 * (clocks.realtime = clocks.uptime = 5000);
         BatteryStats.Timer timer = bi.getUidStats().get(UID).getBluetoothScanTimer();
         BatteryStats.Timer bgTimer = bi.getUidStats().get(UID).getBluetoothScanBackgroundTimer();
+        BatteryStats.Timer badTimer = bi.getUidStats().get(UID).getBluetoothUnoptimizedScanTimer();
+        BatteryStats.Timer badBgTimer = bi.getUidStats().get(UID)
+                .getBluetoothUnoptimizedScanBackgroundTimer();
 
         long time = timer.getTotalTimeLocked(curr, STATS_SINCE_CHARGED);
         int count = timer.getCountLocked(STATS_SINCE_CHARGED);
         int bgCount = bgTimer.getCountLocked(STATS_SINCE_CHARGED);
         long actualTime = timer.getTotalDurationMsLocked(clocks.realtime) * 1000;
         long bgTime = bgTimer.getTotalDurationMsLocked(clocks.realtime) * 1000;
-        assertEquals((305 - 202) * 1000, time);
-        assertEquals(1, count);
-        assertEquals(0, bgCount);
-        assertEquals((305 - 202) * 1000, actualTime);
-        assertEquals((305 - 254) * 1000, bgTime);
+        long badTime = badTimer.getTotalDurationMsLocked(clocks.realtime) * 1000;
+        long badBgTime = badBgTimer.getTotalDurationMsLocked(clocks.realtime) * 1000;
+        assertEquals((305 - 202 + 4008 - 2001) * 1000, time);
+        assertEquals(1, count); // second scan starts off-battery
+        assertEquals(0, bgCount); // first scan starts in fg, second starts off-battery
+        assertEquals((305 - 202 + 4008 - 2001) * 1000, actualTime);
+        assertEquals((305 - 254 + 3004 - 2001) * 1000, bgTime);
+        assertEquals((4008 - 2001) * 1000, badTime);
+        assertEquals((3004 - 2001) * 1000, badBgTime);
     }
 
     @SmallTest
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index 06ca18d..4e8ab31 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -37,12 +37,28 @@
     public void testNoteBluetoothScanResultLocked() throws Exception {
         MockBatteryStatsImpl bi = new MockBatteryStatsImpl(new MockClocks());
         bi.updateTimeBasesLocked(true, true, 0, 0);
+        bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP);
 
         bi.noteBluetoothScanResultsFromSourceLocked(WS, 1);
         bi.noteBluetoothScanResultsFromSourceLocked(WS, 100);
         assertEquals(101,
                 bi.getUidStats().get(UID).getBluetoothScanResultCounter()
                         .getCountLocked(STATS_SINCE_CHARGED));
+        // TODO: remove next line when Counter misreporting values when plugged-in bug is fixed.
+        bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+        BatteryStats.Counter bgCntr = bi.getUidStats().get(UID).getBluetoothScanResultBgCounter();
+        if (bgCntr != null) {
+            assertEquals(0, bgCntr.getCountLocked(STATS_SINCE_CHARGED));
+        }
+
+        bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+        bi.noteBluetoothScanResultsFromSourceLocked(WS, 17);
+        assertEquals(101 + 17,
+                bi.getUidStats().get(UID).getBluetoothScanResultCounter()
+                        .getCountLocked(STATS_SINCE_CHARGED));
+        assertEquals(17,
+                bi.getUidStats().get(UID).getBluetoothScanResultBgCounter()
+                        .getCountLocked(STATS_SINCE_CHARGED));
     }
 
     /** Test BatteryStatsImpl.Uid.noteStartWakeLocked. */
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 11fc40b..e8c8f74 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -942,10 +942,10 @@
     }
 
     @Override
-    public void noteBleScanStarted(WorkSource ws) {
+    public void noteBleScanStarted(WorkSource ws, boolean isUnoptimized) {
         enforceCallingPermission();
         synchronized (mStats) {
-            mStats.noteBluetoothScanStartedFromSourceLocked(ws);
+            mStats.noteBluetoothScanStartedFromSourceLocked(ws, isUnoptimized);
         }
     }