Loading android/app/src/com/android/bluetooth/gatt/AppScanStats.java +28 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import com.android.bluetooth.btservice.BluetoothProto; long duration; long timestamp; boolean opportunistic; boolean timeout; boolean background; int results; Loading @@ -57,6 +58,12 @@ import com.android.bluetooth.btservice.BluetoothProto; static final int NUM_SCAN_DURATIONS_KEPT = 5; // This constant defines the time window an app can scan multiple times. // Any single app can scan up to |NUM_SCAN_DURATIONS_KEPT| times during // this window. Once they reach this limit, they must wait until their // earliest recorded scan exits this window. static final long EXCESSIVE_SCANNING_PERIOD_MS = 30 * 1000; String appName; int scansStarted = 0; int scansStopped = 0; Loading Loading @@ -134,6 +141,25 @@ import com.android.bluetooth.btservice.BluetoothProto; gattService.addScanEvent(scanEvent); } synchronized void setScanTimeout() { if (!isScanning) return; if (!lastScans.isEmpty()) { LastScan curr = lastScans.get(lastScans.size() - 1); curr.timeout = true; } } synchronized boolean isScanningTooFrequently() { if (lastScans.size() < NUM_SCAN_DURATIONS_KEPT) { return false; } return (System.currentTimeMillis() - lastScans.get(0).timestamp) < EXCESSIVE_SCANNING_PERIOD_MS; } // This function truncates the app name for privacy reasons. Apps with // four part package names or more get truncated to three parts, and apps // with three part package names names get truncated to two. Apps with two Loading Loading @@ -183,6 +209,7 @@ import com.android.bluetooth.btservice.BluetoothProto; if (isRegistered) sb.append(" (Registered)"); if (lastScan.opportunistic) sb.append(" (Opportunistic)"); if (lastScan.background) sb.append(" (Background)"); if (lastScan.timeout) sb.append(" (Forced-Opportunistic)"); sb.append("\n"); sb.append(" LE scans (started/stopped) : " + Loading @@ -209,6 +236,7 @@ import com.android.bluetooth.btservice.BluetoothProto; sb.append(scan.duration + "ms "); if (scan.opportunistic) sb.append("Opp "); if (scan.background) sb.append("Back "); if (scan.timeout) sb.append("Forced "); sb.append(scan.results + " results"); sb.append("\n"); } Loading android/app/src/com/android/bluetooth/gatt/GattService.java +11 −1 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; import static android.content.pm.PackageManager.PERMISSION_GRANTED; /** * Provides Bluetooth Gatt profile, as a service in * the Bluetooth application. Loading Loading @@ -1309,7 +1310,16 @@ public class GattService extends ProfileService { } else { app = mClientMap.getAppScanStatsById(appIf); } if (app != null) app.recordScanStart(settings); if (app != null) { if (app.isScanningTooFrequently() && checkCallingOrSelfPermission(BLUETOOTH_PRIVILEGED) != PERMISSION_GRANTED) { Log.e(TAG, "App '" + app.appName + "' is scanning too frequently"); return; } scanClient.stats = app; app.recordScanStart(settings); } mScanManager.startScan(scanClient); } Loading android/app/src/com/android/bluetooth/gatt/ScanClient.java +2 −0 Original line number Diff line number Diff line Loading @@ -47,6 +47,8 @@ import java.util.UUID; // Who is responsible for this scan. WorkSource workSource; AppScanStats stats = null; private static final ScanSettings DEFAULT_SCAN_SETTINGS = new ScanSettings.Builder() .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build(); Loading android/app/src/com/android/bluetooth/gatt/ScanManager.java +54 −0 Original line number Diff line number Diff line Loading @@ -66,6 +66,10 @@ public class ScanManager { private static final int MSG_START_BLE_SCAN = 0; 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; // Maximum msec before scan gets downgraded to opportunistic private static final int SCAN_TIMEOUT_MS = 5 * 60 * 1000; private static final String ACTION_REFRESH_BATCHED_SCAN = "com.android.bluetooth.gatt.REFRESH_BATCHED_SCAN"; Loading Loading @@ -192,6 +196,9 @@ public class ScanManager { case MSG_FLUSH_BATCH_RESULTS: handleFlushBatchResults(client); break; case MSG_SCAN_TIMEOUT: mScanNative.regularScanTimeout(); break; default: // Shouldn't happen. Log.e(TAG, "received an unkown message : " + msg.what); Loading Loading @@ -220,6 +227,14 @@ public class ScanManager { mScanNative.startRegularScan(client); if (!mScanNative.isOpportunisticScanClient(client)) { mScanNative.configureRegularScanParams(); if (!mScanNative.isFirstMatchScanClient(client)) { Message msg = mHandler.obtainMessage(MSG_SCAN_TIMEOUT); msg.obj = client; // Only one timeout message should exist at any time mHandler.removeMessages(SCAN_TIMEOUT_MS); mHandler.sendMessageDelayed(msg, SCAN_TIMEOUT_MS); } } // Update BatteryStats with this workload. Loading @@ -242,6 +257,11 @@ public class ScanManager { if (client == null) return; mScanNative.stopRegularScan(client); if (mScanNative.numRegularScanClients() == 0) { mHandler.removeMessages(MSG_SCAN_TIMEOUT); } if (!mScanNative.isOpportunisticScanClient(client)) { mScanNative.configureRegularScanParams(); } Loading Loading @@ -508,6 +528,10 @@ public class ScanManager { return client.settings.getScanMode() == ScanSettings.SCAN_MODE_OPPORTUNISTIC; } private boolean isFirstMatchScanClient(ScanClient client) { return (client.settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0; } private void resetBatchScan(ScanClient client) { int clientIf = client.clientIf; BatchScanParams batchScanParams = getBatchScanParams(); Loading Loading @@ -647,6 +671,36 @@ public class ScanManager { removeScanFilters(client.clientIf); } void regularScanTimeout() { for (ScanClient client : mRegularScanClients) { if (!isOpportunisticScanClient(client) && !isFirstMatchScanClient(client)) { logd("clientIf set to scan opportunisticly: " + client.clientIf); setOpportunisticScanClient(client); client.stats.setScanTimeout(); } } // The scan should continue for background scans configureRegularScanParams(); if (numRegularScanClients() == 0) { logd("stop scan"); gattClientScanNative(false); } } void setOpportunisticScanClient(ScanClient client) { // TODO: Add constructor to ScanSettings.Builder // that can copy values from an existing ScanSettings object ScanSettings.Builder builder = new ScanSettings.Builder(); ScanSettings settings = client.settings; builder.setScanMode(ScanSettings.SCAN_MODE_OPPORTUNISTIC); builder.setCallbackType(settings.getCallbackType()); builder.setScanResultType(settings.getScanResultType()); builder.setReportDelay(settings.getReportDelayMillis()); builder.setNumOfMatches(settings.getNumOfMatches()); client.settings = builder.build(); } // Find the regular scan client information. ScanClient getRegularScanClient(int clientIf) { for (ScanClient client : mRegularScanClients) { Loading Loading
android/app/src/com/android/bluetooth/gatt/AppScanStats.java +28 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import com.android.bluetooth.btservice.BluetoothProto; long duration; long timestamp; boolean opportunistic; boolean timeout; boolean background; int results; Loading @@ -57,6 +58,12 @@ import com.android.bluetooth.btservice.BluetoothProto; static final int NUM_SCAN_DURATIONS_KEPT = 5; // This constant defines the time window an app can scan multiple times. // Any single app can scan up to |NUM_SCAN_DURATIONS_KEPT| times during // this window. Once they reach this limit, they must wait until their // earliest recorded scan exits this window. static final long EXCESSIVE_SCANNING_PERIOD_MS = 30 * 1000; String appName; int scansStarted = 0; int scansStopped = 0; Loading Loading @@ -134,6 +141,25 @@ import com.android.bluetooth.btservice.BluetoothProto; gattService.addScanEvent(scanEvent); } synchronized void setScanTimeout() { if (!isScanning) return; if (!lastScans.isEmpty()) { LastScan curr = lastScans.get(lastScans.size() - 1); curr.timeout = true; } } synchronized boolean isScanningTooFrequently() { if (lastScans.size() < NUM_SCAN_DURATIONS_KEPT) { return false; } return (System.currentTimeMillis() - lastScans.get(0).timestamp) < EXCESSIVE_SCANNING_PERIOD_MS; } // This function truncates the app name for privacy reasons. Apps with // four part package names or more get truncated to three parts, and apps // with three part package names names get truncated to two. Apps with two Loading Loading @@ -183,6 +209,7 @@ import com.android.bluetooth.btservice.BluetoothProto; if (isRegistered) sb.append(" (Registered)"); if (lastScan.opportunistic) sb.append(" (Opportunistic)"); if (lastScan.background) sb.append(" (Background)"); if (lastScan.timeout) sb.append(" (Forced-Opportunistic)"); sb.append("\n"); sb.append(" LE scans (started/stopped) : " + Loading @@ -209,6 +236,7 @@ import com.android.bluetooth.btservice.BluetoothProto; sb.append(scan.duration + "ms "); if (scan.opportunistic) sb.append("Opp "); if (scan.background) sb.append("Back "); if (scan.timeout) sb.append("Forced "); sb.append(scan.results + " results"); sb.append("\n"); } Loading
android/app/src/com/android/bluetooth/gatt/GattService.java +11 −1 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; import static android.content.pm.PackageManager.PERMISSION_GRANTED; /** * Provides Bluetooth Gatt profile, as a service in * the Bluetooth application. Loading Loading @@ -1309,7 +1310,16 @@ public class GattService extends ProfileService { } else { app = mClientMap.getAppScanStatsById(appIf); } if (app != null) app.recordScanStart(settings); if (app != null) { if (app.isScanningTooFrequently() && checkCallingOrSelfPermission(BLUETOOTH_PRIVILEGED) != PERMISSION_GRANTED) { Log.e(TAG, "App '" + app.appName + "' is scanning too frequently"); return; } scanClient.stats = app; app.recordScanStart(settings); } mScanManager.startScan(scanClient); } Loading
android/app/src/com/android/bluetooth/gatt/ScanClient.java +2 −0 Original line number Diff line number Diff line Loading @@ -47,6 +47,8 @@ import java.util.UUID; // Who is responsible for this scan. WorkSource workSource; AppScanStats stats = null; private static final ScanSettings DEFAULT_SCAN_SETTINGS = new ScanSettings.Builder() .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build(); Loading
android/app/src/com/android/bluetooth/gatt/ScanManager.java +54 −0 Original line number Diff line number Diff line Loading @@ -66,6 +66,10 @@ public class ScanManager { private static final int MSG_START_BLE_SCAN = 0; 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; // Maximum msec before scan gets downgraded to opportunistic private static final int SCAN_TIMEOUT_MS = 5 * 60 * 1000; private static final String ACTION_REFRESH_BATCHED_SCAN = "com.android.bluetooth.gatt.REFRESH_BATCHED_SCAN"; Loading Loading @@ -192,6 +196,9 @@ public class ScanManager { case MSG_FLUSH_BATCH_RESULTS: handleFlushBatchResults(client); break; case MSG_SCAN_TIMEOUT: mScanNative.regularScanTimeout(); break; default: // Shouldn't happen. Log.e(TAG, "received an unkown message : " + msg.what); Loading Loading @@ -220,6 +227,14 @@ public class ScanManager { mScanNative.startRegularScan(client); if (!mScanNative.isOpportunisticScanClient(client)) { mScanNative.configureRegularScanParams(); if (!mScanNative.isFirstMatchScanClient(client)) { Message msg = mHandler.obtainMessage(MSG_SCAN_TIMEOUT); msg.obj = client; // Only one timeout message should exist at any time mHandler.removeMessages(SCAN_TIMEOUT_MS); mHandler.sendMessageDelayed(msg, SCAN_TIMEOUT_MS); } } // Update BatteryStats with this workload. Loading @@ -242,6 +257,11 @@ public class ScanManager { if (client == null) return; mScanNative.stopRegularScan(client); if (mScanNative.numRegularScanClients() == 0) { mHandler.removeMessages(MSG_SCAN_TIMEOUT); } if (!mScanNative.isOpportunisticScanClient(client)) { mScanNative.configureRegularScanParams(); } Loading Loading @@ -508,6 +528,10 @@ public class ScanManager { return client.settings.getScanMode() == ScanSettings.SCAN_MODE_OPPORTUNISTIC; } private boolean isFirstMatchScanClient(ScanClient client) { return (client.settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0; } private void resetBatchScan(ScanClient client) { int clientIf = client.clientIf; BatchScanParams batchScanParams = getBatchScanParams(); Loading Loading @@ -647,6 +671,36 @@ public class ScanManager { removeScanFilters(client.clientIf); } void regularScanTimeout() { for (ScanClient client : mRegularScanClients) { if (!isOpportunisticScanClient(client) && !isFirstMatchScanClient(client)) { logd("clientIf set to scan opportunisticly: " + client.clientIf); setOpportunisticScanClient(client); client.stats.setScanTimeout(); } } // The scan should continue for background scans configureRegularScanParams(); if (numRegularScanClients() == 0) { logd("stop scan"); gattClientScanNative(false); } } void setOpportunisticScanClient(ScanClient client) { // TODO: Add constructor to ScanSettings.Builder // that can copy values from an existing ScanSettings object ScanSettings.Builder builder = new ScanSettings.Builder(); ScanSettings settings = client.settings; builder.setScanMode(ScanSettings.SCAN_MODE_OPPORTUNISTIC); builder.setCallbackType(settings.getCallbackType()); builder.setScanResultType(settings.getScanResultType()); builder.setReportDelay(settings.getReportDelayMillis()); builder.setNumOfMatches(settings.getNumOfMatches()); client.settings = builder.build(); } // Find the regular scan client information. ScanClient getRegularScanClient(int clientIf) { for (ScanClient client : mRegularScanClients) { Loading