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

Commit 319aeae6 authored by Vinay Kalia's avatar Vinay Kalia
Browse files

Stop unfiltered BLE scanning on screen off

Stop unfiltered BLE scans when the screen goes off.
These scans will be resumed when the screen turns on
again unless these scans are stopped by application
itself before the screen is turned on.

While screen is off, new scans are allowed to be
created but scan instances will not receive results
until the screen is turned on.

Stats in bluetooth_manager dumpsys are updated to show
suspended time as below:

  LE scans (started/stopped)         : 11 / 10
  Scan time in ms (min/max/avg/total): 1107 / 15003 / 7623 / 83859
  Total time suspended               : 4257ms
  Total number of results            : 14
  Last 5 scans                       :
    2017/07/26 15:41:50 - 14993ms Filter 0 results (6)
    2017/07/26 15:42:05 - 5021ms 0 results (6)
    2017/07/26 15:42:10 - 9908ms Filter 0 results (6)
    2017/07/26 15:42:20 - 5011ms 11 results (6)
      └ Suspended Time: 1193ms
    2017/07/26 15:42:25 - 14990ms Filter 0 results (6)
  Ongoing scans                      :
    2017/07/26 15:42:40 - 1342ms Suspended 0 results (6)
      └ Suspended Time: 1239ms

BUG: 62264269
Test: Tested BLE scanning applications.

Change-Id: I017f0cdc0fc52a0a30d6cf870d7e688e5ff18df9
parent bcdae28f
Loading
Loading
Loading
Loading
+45 −0
Original line number Diff line number Diff line
@@ -50,6 +50,9 @@ import com.android.bluetooth.btservice.BluetoothProto;

    class LastScan {
        long duration;
        long suspendDuration;
        long suspendStartTime;
        boolean isSuspended;
        long timestamp;
        boolean opportunistic;
        boolean timeout;
@@ -67,6 +70,9 @@ import com.android.bluetooth.btservice.BluetoothProto;
            this.filtered = filtered;
            this.results = 0;
            this.scannerId = scannerId;
            this.suspendDuration = 0;
            this.suspendStartTime = 0;
            this.isSuspended = false;
        }
    }

@@ -90,6 +96,7 @@ import com.android.bluetooth.btservice.BluetoothProto;
    long maxScanTime = 0;
    long mScanStartTime = 0;
    long mTotalScanTime = 0;
    long mTotalSuspendTime = 0;
    List<LastScan> lastScans = new ArrayList<LastScan>(NUM_SCAN_DURATIONS_KEPT);
    HashMap<Integer, LastScan> ongoingScans = new HashMap<Integer, LastScan>();
    long startTime = 0;
@@ -168,6 +175,10 @@ import com.android.bluetooth.btservice.BluetoothProto;
        stopTime = SystemClock.elapsedRealtime();
        long scanDuration = stopTime - scan.timestamp;
        scan.duration = scanDuration;
        if (scan.isSuspended) {
            scan.suspendDuration += stopTime - scan.suspendStartTime;
            mTotalSuspendTime += scan.suspendDuration;
        }
        ongoingScans.remove(scannerId);
        if (lastScans.size() >= NUM_SCAN_DURATIONS_KEPT) {
            lastScans.remove(0);
@@ -194,6 +205,26 @@ import com.android.bluetooth.btservice.BluetoothProto;
        }
    }

    synchronized void recordScanSuspend(int scannerId) {
        LastScan scan = getScanFromScannerId(scannerId);
        if (scan == null || scan.isSuspended) {
            return;
        }
        scan.suspendStartTime = SystemClock.elapsedRealtime();
        scan.isSuspended = true;
    }

    synchronized void recordScanResume(int scannerId) {
        LastScan scan = getScanFromScannerId(scannerId);
        if (scan == null || !scan.isSuspended) {
            return;
        }
        scan.isSuspended = false;
        stopTime = SystemClock.elapsedRealtime();
        scan.suspendDuration += stopTime - scan.suspendStartTime;
        mTotalSuspendTime += scan.suspendDuration;
    }

    synchronized void setScanTimeout(int scannerId) {
        if (!isScanning()) return;

@@ -281,6 +312,9 @@ import com.android.bluetooth.btservice.BluetoothProto;
                  maxScan + " / " +
                  avgScan + " / " +
                  totalScanTime + "\n");
        if (mTotalSuspendTime != 0) {
            sb.append("  Total time suspended             : " + mTotalSuspendTime + "ms\n");
        }
        sb.append("  Total number of results            : " +
                  results + "\n");

@@ -301,6 +335,10 @@ import com.android.bluetooth.btservice.BluetoothProto;
                sb.append(scan.results + " results");
                sb.append(" (" + scan.scannerId + ")");
                sb.append("\n");
                if (scan.suspendDuration != 0) {
                    sb.append("      └"
                            + " Suspended Time: " + scan.suspendDuration + "ms\n");
                }
            }
        }

@@ -315,9 +353,16 @@ import com.android.bluetooth.btservice.BluetoothProto;
                if (scan.background) sb.append("Back ");
                if (scan.timeout) sb.append("Forced ");
                if (scan.filtered) sb.append("Filter ");
                if (scan.isSuspended) sb.append("Suspended ");
                sb.append(scan.results + " results");
                sb.append(" (" + scan.scannerId + ")");
                sb.append("\n");
                if (scan.suspendStartTime != 0) {
                    long duration = scan.suspendDuration
                            + (scan.isSuspended ? (elapsedRt - scan.suspendStartTime) : 0);
                    sb.append("      └"
                            + " Suspended Time: " + duration + "ms\n");
                }
            }
        }

+90 −1
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -34,6 +35,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.util.Log;
import android.view.Display;

import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.AdapterService;
@@ -69,7 +71,8 @@ public class ScanManager {
    private static final int MSG_STOP_BLE_SCAN = 1;
    private static final int MSG_FLUSH_BATCH_RESULTS = 2;
    private static final int MSG_SCAN_TIMEOUT = 3;

    private static final int MSG_SUSPEND_SCANS = 4;
    private static final int MSG_RESUME_SCANS = 5;
    private static final String ACTION_REFRESH_BATCHED_SCAN =
            "com.android.bluetooth.gatt.REFRESH_BATCHED_SCAN";

@@ -89,15 +92,22 @@ public class ScanManager {

    private Set<ScanClient> mRegularScanClients;
    private Set<ScanClient> mBatchClients;
    private Set<ScanClient> mSuspendedScanClients;

    private CountDownLatch mLatch;

    private DisplayManager mDm;

    ScanManager(GattService service) {
        mRegularScanClients = Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>());
        mBatchClients = Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>());
        mSuspendedScanClients =
                Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>());
        mService = service;
        mScanNative = new ScanNative();
        curUsedTrackableAdvertisements = 0;
        mDm = (DisplayManager) mService.getSystemService(Context.DISPLAY_SERVICE);
        mDm.registerDisplayListener(mDisplayListener, null);
    }

    void start() {
@@ -109,6 +119,7 @@ public class ScanManager {
    void cleanup() {
        mRegularScanClients.clear();
        mBatchClients.clear();
        mSuspendedScanClients.clear();
        mScanNative.cleanup();

        if (mHandler != null) {
@@ -215,6 +226,12 @@ public class ScanManager {
                case MSG_SCAN_TIMEOUT:
                    mScanNative.regularScanTimeout(client);
                    break;
                case MSG_SUSPEND_SCANS:
                    handleSuspendScans();
                    break;
                case MSG_RESUME_SCANS:
                    handleResumeScans();
                    break;
                default:
                    // Shouldn't happen.
                    Log.e(TAG, "received an unkown message : " + msg.what);
@@ -223,6 +240,7 @@ public class ScanManager {

        void handleStartScan(ScanClient client) {
            Utils.enforceAdminPermission(mService);
            boolean isFiltered = (client.filters != null) && !client.filters.isEmpty();
            if (DBG) Log.d(TAG, "handling starting scan");

            if (!isScanSupported(client)) {
@@ -234,6 +252,15 @@ public class ScanManager {
                Log.e(TAG, "Scan already started");
                return;
            }

            if (!mScanNative.isOpportunisticScanClient(client) && !isScreenOn() && !isFiltered) {
                Log.e(TAG,
                        "Cannot start unfiltered scan in screen-off. This scan will be resumed later: "
                                + client.scannerId);
                mSuspendedScanClients.add(client);
                return;
            }

            // Begin scan operations.
            if (isBatchClient(client)) {
                mBatchClients.add(client);
@@ -258,6 +285,10 @@ public class ScanManager {
            Utils.enforceAdminPermission(mService);
            if (client == null) return;

            if (mSuspendedScanClients.contains(client)) {
                mSuspendedScanClients.remove(client);
            }

            if (mRegularScanClients.contains(client)) {
                mScanNative.stopRegularScan(client);

@@ -305,6 +336,30 @@ public class ScanManager {
            return settings.getCallbackType() == ScanSettings.CALLBACK_TYPE_ALL_MATCHES &&
                    settings.getReportDelayMillis() == 0;
        }

        void handleSuspendScans() {
            for (ScanClient client : mRegularScanClients) {
                if (!mScanNative.isOpportunisticScanClient(client)
                        && (client.filters == null || client.filters.isEmpty())) {
                    /*Suspend unfiltered scans*/
                    if (client.stats != null) {
                        client.stats.recordScanSuspend(client.scannerId);
                    }
                    handleStopScan(client);
                    mSuspendedScanClients.add(client);
                }
            }
        }

        void handleResumeScans() {
            for (ScanClient client : mSuspendedScanClients) {
                if (client.stats != null) {
                    client.stats.recordScanResume(client.scannerId);
                }
                handleStartScan(client);
            }
            mSuspendedScanClients.clear();
        }
    }

    /**
@@ -1163,4 +1218,38 @@ public class ScanManager {

        private native void gattClientReadScanReportsNative(int client_if, int scan_type);
    }

    private boolean isScreenOn() {
        Display[] displays = mDm.getDisplays();

        if (displays == null) {
            return false;
        }

        for (Display display : displays) {
            if (display.getState() == Display.STATE_ON) {
                return true;
            }
        }

        return false;
    }

    private final DisplayManager.DisplayListener mDisplayListener =
            new DisplayManager.DisplayListener() {
                @Override
                public void onDisplayAdded(int displayId) {}

                @Override
                public void onDisplayRemoved(int displayId) {}

                @Override
                public void onDisplayChanged(int displayId) {
                    if (isScreenOn()) {
                        sendMessage(MSG_RESUME_SCANS, null);
                    } else {
                        sendMessage(MSG_SUSPEND_SCANS, null);
                    }
                }
            };
}