Loading android/app/src/com/android/bluetooth/gatt/ContextMap.java +28 −5 Original line number Diff line number Diff line Loading @@ -32,12 +32,13 @@ import java.util.HashMap; import java.util.Map; import com.android.bluetooth.btservice.BluetoothProto; /** * Helper class that keeps track of registered GATT applications. * This class manages application callbacks and keeps track of GATT connections. * @hide */ /*package*/ class ContextMap<T> { /*package*/ class ContextMap<C, T> { private static final String TAG = GattServiceConfig.TAG_PREFIX + "ContextMap"; /** Loading Loading @@ -74,8 +75,10 @@ import com.android.bluetooth.btservice.BluetoothProto; AppScanStats appScanStats; /** Application callbacks */ T callback; C callback; /** Context information */ T info; /** Death receipient */ private IBinder.DeathRecipient mDeathRecipient; Loading @@ -88,9 +91,10 @@ import com.android.bluetooth.btservice.BluetoothProto; /** * Creates a new app context. */ App(UUID uuid, T callback, String name, AppScanStats appScanStats) { App(UUID uuid, C callback, T info, String name, AppScanStats appScanStats) { this.uuid = uuid; this.callback = callback; this.info = info; this.name = name; this.appScanStats = appScanStats; } Loading @@ -99,6 +103,8 @@ import com.android.bluetooth.btservice.BluetoothProto; * Link death recipient */ void linkToDeath(IBinder.DeathRecipient deathRecipient) { // It might not be a binder object if (callback == null) return; try { IBinder binder = ((IInterface)callback).asBinder(); binder.linkToDeath(deathRecipient, 0); Loading Loading @@ -144,7 +150,7 @@ import com.android.bluetooth.btservice.BluetoothProto; /** * Add an entry to the application context list. */ void add(UUID uuid, T callback, GattService service) { void add(UUID uuid, C callback, T info, GattService service) { String appName = service.getPackageManager().getNameForUid( Binder.getCallingUid()); if (appName == null) { Loading @@ -157,7 +163,7 @@ import com.android.bluetooth.btservice.BluetoothProto; appScanStats = new AppScanStats(appName, this, service); mAppScanStats.put(appName, appScanStats); } mApps.add(new App(uuid, callback, appName, appScanStats)); mApps.add(new App(uuid, callback, info, appName, appScanStats)); appScanStats.isRegistered = true; } } Loading Loading @@ -297,6 +303,23 @@ import com.android.bluetooth.btservice.BluetoothProto; return null; } /** * Get an application context by the context info object. */ App getByContextInfo(T contextInfo) { synchronized (mApps) { Iterator<App> i = mApps.iterator(); while (i.hasNext()) { App entry = i.next(); if (entry.info != null && entry.info.equals(contextInfo)) { return entry; } } } Log.e(TAG, "Context not found for info " + contextInfo); return null; } /** * Get Logging info by ID */ Loading android/app/src/com/android/bluetooth/gatt/GattService.java +201 −25 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import android.bluetooth.IBluetoothGattCallback; import android.bluetooth.IBluetoothGattServerCallback; import android.bluetooth.le.AdvertiseData; import android.bluetooth.le.AdvertisingSetParameters; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.IAdvertisingSetCallback; import android.bluetooth.le.IPeriodicAdvertisingCallback; import android.bluetooth.le.IScannerCallback; Loading @@ -43,7 +44,6 @@ import android.bluetooth.le.ScanSettings; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.os.IInterface; import android.os.ParcelUuid; import android.os.RemoteException; import android.os.SystemClock; Loading Loading @@ -106,22 +106,39 @@ public class GattService extends ProfileService { UUID.fromString("0000FFFD-0000-1000-8000-00805F9B34FB") // U2F }; /** * Keep the arguments passed in for the PendingIntent. */ class PendingIntentInfo { PendingIntent intent; ScanSettings settings; List<ScanFilter> filters; WorkSource workSource; String callingPackage; @Override public boolean equals(Object other) { if (!(other instanceof PendingIntentInfo)) return false; return intent.equals(((PendingIntentInfo) other).intent); } } /** * List of our registered scanners. */ class ScannerMap extends ContextMap<IScannerCallback> {} class ScannerMap extends ContextMap<IScannerCallback, PendingIntentInfo> {} ScannerMap mScannerMap = new ScannerMap(); /** * List of our registered clients. */ class ClientMap extends ContextMap<IBluetoothGattCallback> {} class ClientMap extends ContextMap<IBluetoothGattCallback, Void> {} ClientMap mClientMap = new ClientMap(); /** * List of our registered server apps. */ class ServerMap extends ContextMap<IBluetoothGattServerCallback> {} class ServerMap extends ContextMap<IBluetoothGattServerCallback, Void> {} ServerMap mServerMap = new ServerMap(); /** Loading Loading @@ -369,13 +386,17 @@ public class GattService extends ProfileService { @Override public void startScanForIntent(PendingIntent intent, ScanSettings settings, List<ScanFilter> filters, String callingPackage) throws RemoteException { // TODO: GattService service = getService(); if (service == null) return; service.registerPiAndStartScan(intent, settings, filters, callingPackage); } @Override public void stopScanForIntent(PendingIntent intent, String callingPackage) throws RemoteException { // TODO: GattService service = getService(); if (service == null) return; service.stopScan(intent, callingPackage); } public void stopScan(int scannerId) { Loading Loading @@ -736,8 +757,16 @@ public class GattService extends ProfileService { try { app.appScanStats.addResult(); if (app.callback != null) { app.callback.onScanResult(result); } catch (RemoteException e) { } else { // Send the PendingIntent ArrayList<ScanResult> results = new ArrayList<>(); results.add(result); sendResultsByPendingIntent(app.info, results, ScanSettings.CALLBACK_TYPE_ALL_MATCHES); } } catch (RemoteException | PendingIntent.CanceledException e) { Log.e(TAG, "Exception: " + e); mScannerMap.remove(client.scannerId); mScanManager.stopScan(client); Loading @@ -745,21 +774,59 @@ public class GattService extends ProfileService { } } private void sendResultByPendingIntent(PendingIntentInfo pii, ScanResult result, int callbackType, ScanClient client) { ArrayList<ScanResult> results = new ArrayList<>(); results.add(result); try { sendResultsByPendingIntent(pii, results, callbackType); } catch (PendingIntent.CanceledException e) { stopScan(client); unregisterScanner(client.scannerId); } } private void sendResultsByPendingIntent(PendingIntentInfo pii, ArrayList<ScanResult> results, int callbackType) throws PendingIntent.CanceledException { Intent extrasIntent = new Intent(); extrasIntent.putParcelableArrayListExtra( BluetoothLeScanner.EXTRA_LIST_SCAN_RESULT, results); extrasIntent.putExtra( BluetoothLeScanner.EXTRA_CALLBACK_TYPE, callbackType); pii.intent.send(this, 0, extrasIntent); } private void sendErrorByPendingIntent(PendingIntentInfo pii, int errorCode) throws PendingIntent.CanceledException { Intent extrasIntent = new Intent(); extrasIntent.putExtra(BluetoothLeScanner.EXTRA_ERROR_CODE, errorCode); pii.intent.send(this, 0, extrasIntent); } void onScannerRegistered(int status, int scannerId, long uuidLsb, long uuidMsb) throws RemoteException { UUID uuid = new UUID(uuidMsb, uuidLsb); if (DBG) Log.d(TAG, "onScannerRegistered() - UUID=" + uuid + ", scannerId=" + scannerId + ", status=" + status); ScannerMap.App app = mScannerMap.getByUuid(uuid); if (app != null) { // First check the callback map ScannerMap.App cbApp = mScannerMap.getByUuid(uuid); if (cbApp != null) { if (status == 0) { app.id = scannerId; app.linkToDeath(new ScannerDeathRecipient(scannerId)); cbApp.id = scannerId; // If app is callback based, setup a death recipient. App will initiate the start. // Otherwise, if PendingIntent based, start the scan directly. if (cbApp.callback != null) { cbApp.linkToDeath(new ScannerDeathRecipient(scannerId)); } else { continuePiStartScan(scannerId, cbApp.info); } } else { mScannerMap.remove(scannerId); } app.callback.onScannerRegistered(status, scannerId); if (cbApp.callback != null) { cbApp.callback.onScannerRegistered(status, scannerId); } } } Loading Loading @@ -1138,7 +1205,16 @@ public class GattService extends ProfileService { // We only support single client for truncated mode. ScannerMap.App app = mScannerMap.getById(scannerId); if (app == null) return; if (app.callback != null) { app.callback.onBatchScanResults(new ArrayList<ScanResult>(results)); } else { // PendingIntent based try { sendResultsByPendingIntent(app.info, new ArrayList<ScanResult>(results), ScanSettings.CALLBACK_TYPE_ALL_MATCHES); } catch (PendingIntent.CanceledException e) { } } } else { for (ScanClient client : mScanManager.getFullBatchScanQueue()) { // Deliver results for each client. Loading @@ -1147,22 +1223,39 @@ public class GattService extends ProfileService { } } private void sendBatchScanResults( ScannerMap.App app, ScanClient client, ArrayList<ScanResult> results) { try { if (app.callback != null) { app.callback.onBatchScanResults(results); } else { sendResultsByPendingIntent(app.info, results, ScanSettings.CALLBACK_TYPE_ALL_MATCHES); } } catch (RemoteException | PendingIntent.CanceledException e) { Log.e(TAG, "Exception: " + e); mScannerMap.remove(client.scannerId); mScanManager.stopScan(client); } } // Check and deliver scan results for different scan clients. private void deliverBatchScan(ScanClient client, Set<ScanResult> allResults) throws RemoteException { ScannerMap.App app = mScannerMap.getById(client.scannerId); if (app == null) return; if (client.filters == null || client.filters.isEmpty()) { app.callback.onBatchScanResults(new ArrayList<ScanResult>(allResults)); sendBatchScanResults(app, client, new ArrayList<ScanResult>(allResults)); // TODO: Question to reviewer: Shouldn't there be a return here? } // Reconstruct the scan results. List<ScanResult> results = new ArrayList<ScanResult>(); ArrayList<ScanResult> results = new ArrayList<ScanResult>(); for (ScanResult scanResult : allResults) { if (matchesFilters(client, scanResult)) { results.add(scanResult); } } app.callback.onBatchScanResults(results); sendBatchScanResults(app, client, results); } private Set<ScanResult> parseBatchScanResults(int numRecords, int reportType, Loading Loading @@ -1281,7 +1374,7 @@ public class GattService extends ProfileService { + " adv_state = " + trackingInfo.getAdvState()); ScannerMap.App app = mScannerMap.getById(trackingInfo.getClientIf()); if (app == null || app.callback == null) { if (app == null || (app.callback == null && app.info == null)) { Log.e(TAG, "app or callback is null"); return; } Loading @@ -1299,11 +1392,21 @@ public class GattService extends ProfileService { if ((advertiserState == ADVT_STATE_ONFOUND) && ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0)) { if (app.callback != null) { app.callback.onFoundOrLost(true, result); } else { sendResultByPendingIntent(app.info, result, ScanSettings.CALLBACK_TYPE_FIRST_MATCH, client); } } else if ((advertiserState == ADVT_STATE_ONLOST) && ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0)) { if (app.callback != null) { app.callback.onFoundOrLost(false, result); } else { sendResultByPendingIntent(app.info, result, ScanSettings.CALLBACK_TYPE_MATCH_LOST, client); } } else { Log.d(TAG, "Not reporting onlost/onfound : " + advertiserState + " scannerId = " + client.scannerId Loading @@ -1325,11 +1428,19 @@ public class GattService extends ProfileService { // callback from ScanManager for dispatch of errors apps. void onScanManagerErrorCallback(int scannerId, int errorCode) throws RemoteException { ScannerMap.App app = mScannerMap.getById(scannerId); if (app == null || app.callback == null) { if (app == null || (app.callback == null && app.info == null)) { Log.e(TAG, "App or callback is null"); return; } if (app.callback != null) { app.callback.onScanManagerErrorCallback(errorCode); } else { try { sendErrorByPendingIntent(app.info, errorCode); } catch (PendingIntent.CanceledException e) { Log.e(TAG, "Error sending error code via PendingIntent:" + e); } } } void onConfigureMTU(int connId, int status, int mtu) throws RemoteException { Loading Loading @@ -1414,7 +1525,7 @@ public class GattService extends ProfileService { UUID uuid = UUID.randomUUID(); if (DBG) Log.d(TAG, "registerScanner() - UUID=" + uuid); mScannerMap.add(uuid, callback, this); mScannerMap.add(uuid, callback, null, this); mScanManager.registerScanner(uuid); } Loading Loading @@ -1466,6 +1577,57 @@ public class GattService extends ProfileService { mScanManager.startScan(scanClient); } void registerPiAndStartScan(PendingIntent pendingIntent, ScanSettings settings, List<ScanFilter> filters, String callingPackage) { if (DBG) Log.d(TAG, "start scan with filters, for PendingIntent"); enforceAdminPermission(); if (needsPrivilegedPermissionForScan(settings)) { enforcePrivilegedPermission(); } // Blame the caller if the work source is unspecified. WorkSource workSource = new WorkSource(Binder.getCallingUid(), callingPackage); UUID uuid = UUID.randomUUID(); if (DBG) Log.d(TAG, "startScan(PI) - UUID=" + uuid); PendingIntentInfo piInfo = new PendingIntentInfo(); piInfo.intent = pendingIntent; piInfo.settings = settings; piInfo.filters = filters; piInfo.workSource = workSource; piInfo.callingPackage = callingPackage; mScannerMap.add(uuid, null, piInfo, this); mScanManager.registerScanner(uuid); } void continuePiStartScan(int scannerId, PendingIntentInfo piInfo) { final ScanClient scanClient = new ScanClient(scannerId, piInfo.settings, piInfo.filters, piInfo.workSource, null); scanClient.hasLocationPermission = true; // Utils.checkCallerHasLocationPermission(this, mAppOps, // piInfo.callingPackage); scanClient.hasPeersMacAddressPermission = true; // Utils.checkCallerHasPeersMacAddressPermission( // this); scanClient.legacyForegroundApp = Utils.isLegacyForegroundApp(this, piInfo.callingPackage); AppScanStats app = null; app = mScannerMap.getAppScanStatsById(scannerId); 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; boolean isFilteredScan = (piInfo.filters != null) && !piInfo.filters.isEmpty(); app.recordScanStart(piInfo.settings, isFilteredScan); } mScanManager.startScan(scanClient); } void flushPendingBatchResults(int scannerId) { if (DBG) Log.d(TAG, "flushPendingBatchResults - scannerId=" + scannerId); mScanManager.flushBatchScanResults(new ScanClient(scannerId)); Loading @@ -1484,6 +1646,20 @@ public class GattService extends ProfileService { mScanManager.stopScan(client); } void stopScan(PendingIntent intent, String callingPackage) { enforceAdminPermission(); PendingIntentInfo pii = new PendingIntentInfo(); pii.intent = intent; ScannerMap.App app = mScannerMap.getByContextInfo(pii); if (VDBG) Log.d(TAG, "stopScan(PendingIntent): app found = " + app); if (app != null) { final int scannerId = app.id; stopScan(new ScanClient(scannerId)); // Also unregister the scanner unregisterScanner(scannerId); } } void disconnectAll() { if (DBG) Log.d(TAG, "disconnectAll()"); Map<Integer, String> connMap = mClientMap.getConnectedMap(); Loading Loading @@ -1576,7 +1752,7 @@ public class GattService extends ProfileService { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); if (DBG) Log.d(TAG, "registerClient() - UUID=" + uuid); mClientMap.add(uuid, callback, this); mClientMap.add(uuid, callback, null, this); gattClientRegisterAppNative(uuid.getLeastSignificantBits(), uuid.getMostSignificantBits()); } Loading Loading @@ -2094,7 +2270,7 @@ public class GattService extends ProfileService { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); if (DBG) Log.d(TAG, "registerServer() - UUID=" + uuid); mServerMap.add(uuid, callback, this); mServerMap.add(uuid, callback, null, this); gattServerRegisterAppNative(uuid.getLeastSignificantBits(), uuid.getMostSignificantBits()); } Loading Loading
android/app/src/com/android/bluetooth/gatt/ContextMap.java +28 −5 Original line number Diff line number Diff line Loading @@ -32,12 +32,13 @@ import java.util.HashMap; import java.util.Map; import com.android.bluetooth.btservice.BluetoothProto; /** * Helper class that keeps track of registered GATT applications. * This class manages application callbacks and keeps track of GATT connections. * @hide */ /*package*/ class ContextMap<T> { /*package*/ class ContextMap<C, T> { private static final String TAG = GattServiceConfig.TAG_PREFIX + "ContextMap"; /** Loading Loading @@ -74,8 +75,10 @@ import com.android.bluetooth.btservice.BluetoothProto; AppScanStats appScanStats; /** Application callbacks */ T callback; C callback; /** Context information */ T info; /** Death receipient */ private IBinder.DeathRecipient mDeathRecipient; Loading @@ -88,9 +91,10 @@ import com.android.bluetooth.btservice.BluetoothProto; /** * Creates a new app context. */ App(UUID uuid, T callback, String name, AppScanStats appScanStats) { App(UUID uuid, C callback, T info, String name, AppScanStats appScanStats) { this.uuid = uuid; this.callback = callback; this.info = info; this.name = name; this.appScanStats = appScanStats; } Loading @@ -99,6 +103,8 @@ import com.android.bluetooth.btservice.BluetoothProto; * Link death recipient */ void linkToDeath(IBinder.DeathRecipient deathRecipient) { // It might not be a binder object if (callback == null) return; try { IBinder binder = ((IInterface)callback).asBinder(); binder.linkToDeath(deathRecipient, 0); Loading Loading @@ -144,7 +150,7 @@ import com.android.bluetooth.btservice.BluetoothProto; /** * Add an entry to the application context list. */ void add(UUID uuid, T callback, GattService service) { void add(UUID uuid, C callback, T info, GattService service) { String appName = service.getPackageManager().getNameForUid( Binder.getCallingUid()); if (appName == null) { Loading @@ -157,7 +163,7 @@ import com.android.bluetooth.btservice.BluetoothProto; appScanStats = new AppScanStats(appName, this, service); mAppScanStats.put(appName, appScanStats); } mApps.add(new App(uuid, callback, appName, appScanStats)); mApps.add(new App(uuid, callback, info, appName, appScanStats)); appScanStats.isRegistered = true; } } Loading Loading @@ -297,6 +303,23 @@ import com.android.bluetooth.btservice.BluetoothProto; return null; } /** * Get an application context by the context info object. */ App getByContextInfo(T contextInfo) { synchronized (mApps) { Iterator<App> i = mApps.iterator(); while (i.hasNext()) { App entry = i.next(); if (entry.info != null && entry.info.equals(contextInfo)) { return entry; } } } Log.e(TAG, "Context not found for info " + contextInfo); return null; } /** * Get Logging info by ID */ Loading
android/app/src/com/android/bluetooth/gatt/GattService.java +201 −25 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import android.bluetooth.IBluetoothGattCallback; import android.bluetooth.IBluetoothGattServerCallback; import android.bluetooth.le.AdvertiseData; import android.bluetooth.le.AdvertisingSetParameters; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.IAdvertisingSetCallback; import android.bluetooth.le.IPeriodicAdvertisingCallback; import android.bluetooth.le.IScannerCallback; Loading @@ -43,7 +44,6 @@ import android.bluetooth.le.ScanSettings; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.os.IInterface; import android.os.ParcelUuid; import android.os.RemoteException; import android.os.SystemClock; Loading Loading @@ -106,22 +106,39 @@ public class GattService extends ProfileService { UUID.fromString("0000FFFD-0000-1000-8000-00805F9B34FB") // U2F }; /** * Keep the arguments passed in for the PendingIntent. */ class PendingIntentInfo { PendingIntent intent; ScanSettings settings; List<ScanFilter> filters; WorkSource workSource; String callingPackage; @Override public boolean equals(Object other) { if (!(other instanceof PendingIntentInfo)) return false; return intent.equals(((PendingIntentInfo) other).intent); } } /** * List of our registered scanners. */ class ScannerMap extends ContextMap<IScannerCallback> {} class ScannerMap extends ContextMap<IScannerCallback, PendingIntentInfo> {} ScannerMap mScannerMap = new ScannerMap(); /** * List of our registered clients. */ class ClientMap extends ContextMap<IBluetoothGattCallback> {} class ClientMap extends ContextMap<IBluetoothGattCallback, Void> {} ClientMap mClientMap = new ClientMap(); /** * List of our registered server apps. */ class ServerMap extends ContextMap<IBluetoothGattServerCallback> {} class ServerMap extends ContextMap<IBluetoothGattServerCallback, Void> {} ServerMap mServerMap = new ServerMap(); /** Loading Loading @@ -369,13 +386,17 @@ public class GattService extends ProfileService { @Override public void startScanForIntent(PendingIntent intent, ScanSettings settings, List<ScanFilter> filters, String callingPackage) throws RemoteException { // TODO: GattService service = getService(); if (service == null) return; service.registerPiAndStartScan(intent, settings, filters, callingPackage); } @Override public void stopScanForIntent(PendingIntent intent, String callingPackage) throws RemoteException { // TODO: GattService service = getService(); if (service == null) return; service.stopScan(intent, callingPackage); } public void stopScan(int scannerId) { Loading Loading @@ -736,8 +757,16 @@ public class GattService extends ProfileService { try { app.appScanStats.addResult(); if (app.callback != null) { app.callback.onScanResult(result); } catch (RemoteException e) { } else { // Send the PendingIntent ArrayList<ScanResult> results = new ArrayList<>(); results.add(result); sendResultsByPendingIntent(app.info, results, ScanSettings.CALLBACK_TYPE_ALL_MATCHES); } } catch (RemoteException | PendingIntent.CanceledException e) { Log.e(TAG, "Exception: " + e); mScannerMap.remove(client.scannerId); mScanManager.stopScan(client); Loading @@ -745,21 +774,59 @@ public class GattService extends ProfileService { } } private void sendResultByPendingIntent(PendingIntentInfo pii, ScanResult result, int callbackType, ScanClient client) { ArrayList<ScanResult> results = new ArrayList<>(); results.add(result); try { sendResultsByPendingIntent(pii, results, callbackType); } catch (PendingIntent.CanceledException e) { stopScan(client); unregisterScanner(client.scannerId); } } private void sendResultsByPendingIntent(PendingIntentInfo pii, ArrayList<ScanResult> results, int callbackType) throws PendingIntent.CanceledException { Intent extrasIntent = new Intent(); extrasIntent.putParcelableArrayListExtra( BluetoothLeScanner.EXTRA_LIST_SCAN_RESULT, results); extrasIntent.putExtra( BluetoothLeScanner.EXTRA_CALLBACK_TYPE, callbackType); pii.intent.send(this, 0, extrasIntent); } private void sendErrorByPendingIntent(PendingIntentInfo pii, int errorCode) throws PendingIntent.CanceledException { Intent extrasIntent = new Intent(); extrasIntent.putExtra(BluetoothLeScanner.EXTRA_ERROR_CODE, errorCode); pii.intent.send(this, 0, extrasIntent); } void onScannerRegistered(int status, int scannerId, long uuidLsb, long uuidMsb) throws RemoteException { UUID uuid = new UUID(uuidMsb, uuidLsb); if (DBG) Log.d(TAG, "onScannerRegistered() - UUID=" + uuid + ", scannerId=" + scannerId + ", status=" + status); ScannerMap.App app = mScannerMap.getByUuid(uuid); if (app != null) { // First check the callback map ScannerMap.App cbApp = mScannerMap.getByUuid(uuid); if (cbApp != null) { if (status == 0) { app.id = scannerId; app.linkToDeath(new ScannerDeathRecipient(scannerId)); cbApp.id = scannerId; // If app is callback based, setup a death recipient. App will initiate the start. // Otherwise, if PendingIntent based, start the scan directly. if (cbApp.callback != null) { cbApp.linkToDeath(new ScannerDeathRecipient(scannerId)); } else { continuePiStartScan(scannerId, cbApp.info); } } else { mScannerMap.remove(scannerId); } app.callback.onScannerRegistered(status, scannerId); if (cbApp.callback != null) { cbApp.callback.onScannerRegistered(status, scannerId); } } } Loading Loading @@ -1138,7 +1205,16 @@ public class GattService extends ProfileService { // We only support single client for truncated mode. ScannerMap.App app = mScannerMap.getById(scannerId); if (app == null) return; if (app.callback != null) { app.callback.onBatchScanResults(new ArrayList<ScanResult>(results)); } else { // PendingIntent based try { sendResultsByPendingIntent(app.info, new ArrayList<ScanResult>(results), ScanSettings.CALLBACK_TYPE_ALL_MATCHES); } catch (PendingIntent.CanceledException e) { } } } else { for (ScanClient client : mScanManager.getFullBatchScanQueue()) { // Deliver results for each client. Loading @@ -1147,22 +1223,39 @@ public class GattService extends ProfileService { } } private void sendBatchScanResults( ScannerMap.App app, ScanClient client, ArrayList<ScanResult> results) { try { if (app.callback != null) { app.callback.onBatchScanResults(results); } else { sendResultsByPendingIntent(app.info, results, ScanSettings.CALLBACK_TYPE_ALL_MATCHES); } } catch (RemoteException | PendingIntent.CanceledException e) { Log.e(TAG, "Exception: " + e); mScannerMap.remove(client.scannerId); mScanManager.stopScan(client); } } // Check and deliver scan results for different scan clients. private void deliverBatchScan(ScanClient client, Set<ScanResult> allResults) throws RemoteException { ScannerMap.App app = mScannerMap.getById(client.scannerId); if (app == null) return; if (client.filters == null || client.filters.isEmpty()) { app.callback.onBatchScanResults(new ArrayList<ScanResult>(allResults)); sendBatchScanResults(app, client, new ArrayList<ScanResult>(allResults)); // TODO: Question to reviewer: Shouldn't there be a return here? } // Reconstruct the scan results. List<ScanResult> results = new ArrayList<ScanResult>(); ArrayList<ScanResult> results = new ArrayList<ScanResult>(); for (ScanResult scanResult : allResults) { if (matchesFilters(client, scanResult)) { results.add(scanResult); } } app.callback.onBatchScanResults(results); sendBatchScanResults(app, client, results); } private Set<ScanResult> parseBatchScanResults(int numRecords, int reportType, Loading Loading @@ -1281,7 +1374,7 @@ public class GattService extends ProfileService { + " adv_state = " + trackingInfo.getAdvState()); ScannerMap.App app = mScannerMap.getById(trackingInfo.getClientIf()); if (app == null || app.callback == null) { if (app == null || (app.callback == null && app.info == null)) { Log.e(TAG, "app or callback is null"); return; } Loading @@ -1299,11 +1392,21 @@ public class GattService extends ProfileService { if ((advertiserState == ADVT_STATE_ONFOUND) && ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0)) { if (app.callback != null) { app.callback.onFoundOrLost(true, result); } else { sendResultByPendingIntent(app.info, result, ScanSettings.CALLBACK_TYPE_FIRST_MATCH, client); } } else if ((advertiserState == ADVT_STATE_ONLOST) && ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0)) { if (app.callback != null) { app.callback.onFoundOrLost(false, result); } else { sendResultByPendingIntent(app.info, result, ScanSettings.CALLBACK_TYPE_MATCH_LOST, client); } } else { Log.d(TAG, "Not reporting onlost/onfound : " + advertiserState + " scannerId = " + client.scannerId Loading @@ -1325,11 +1428,19 @@ public class GattService extends ProfileService { // callback from ScanManager for dispatch of errors apps. void onScanManagerErrorCallback(int scannerId, int errorCode) throws RemoteException { ScannerMap.App app = mScannerMap.getById(scannerId); if (app == null || app.callback == null) { if (app == null || (app.callback == null && app.info == null)) { Log.e(TAG, "App or callback is null"); return; } if (app.callback != null) { app.callback.onScanManagerErrorCallback(errorCode); } else { try { sendErrorByPendingIntent(app.info, errorCode); } catch (PendingIntent.CanceledException e) { Log.e(TAG, "Error sending error code via PendingIntent:" + e); } } } void onConfigureMTU(int connId, int status, int mtu) throws RemoteException { Loading Loading @@ -1414,7 +1525,7 @@ public class GattService extends ProfileService { UUID uuid = UUID.randomUUID(); if (DBG) Log.d(TAG, "registerScanner() - UUID=" + uuid); mScannerMap.add(uuid, callback, this); mScannerMap.add(uuid, callback, null, this); mScanManager.registerScanner(uuid); } Loading Loading @@ -1466,6 +1577,57 @@ public class GattService extends ProfileService { mScanManager.startScan(scanClient); } void registerPiAndStartScan(PendingIntent pendingIntent, ScanSettings settings, List<ScanFilter> filters, String callingPackage) { if (DBG) Log.d(TAG, "start scan with filters, for PendingIntent"); enforceAdminPermission(); if (needsPrivilegedPermissionForScan(settings)) { enforcePrivilegedPermission(); } // Blame the caller if the work source is unspecified. WorkSource workSource = new WorkSource(Binder.getCallingUid(), callingPackage); UUID uuid = UUID.randomUUID(); if (DBG) Log.d(TAG, "startScan(PI) - UUID=" + uuid); PendingIntentInfo piInfo = new PendingIntentInfo(); piInfo.intent = pendingIntent; piInfo.settings = settings; piInfo.filters = filters; piInfo.workSource = workSource; piInfo.callingPackage = callingPackage; mScannerMap.add(uuid, null, piInfo, this); mScanManager.registerScanner(uuid); } void continuePiStartScan(int scannerId, PendingIntentInfo piInfo) { final ScanClient scanClient = new ScanClient(scannerId, piInfo.settings, piInfo.filters, piInfo.workSource, null); scanClient.hasLocationPermission = true; // Utils.checkCallerHasLocationPermission(this, mAppOps, // piInfo.callingPackage); scanClient.hasPeersMacAddressPermission = true; // Utils.checkCallerHasPeersMacAddressPermission( // this); scanClient.legacyForegroundApp = Utils.isLegacyForegroundApp(this, piInfo.callingPackage); AppScanStats app = null; app = mScannerMap.getAppScanStatsById(scannerId); 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; boolean isFilteredScan = (piInfo.filters != null) && !piInfo.filters.isEmpty(); app.recordScanStart(piInfo.settings, isFilteredScan); } mScanManager.startScan(scanClient); } void flushPendingBatchResults(int scannerId) { if (DBG) Log.d(TAG, "flushPendingBatchResults - scannerId=" + scannerId); mScanManager.flushBatchScanResults(new ScanClient(scannerId)); Loading @@ -1484,6 +1646,20 @@ public class GattService extends ProfileService { mScanManager.stopScan(client); } void stopScan(PendingIntent intent, String callingPackage) { enforceAdminPermission(); PendingIntentInfo pii = new PendingIntentInfo(); pii.intent = intent; ScannerMap.App app = mScannerMap.getByContextInfo(pii); if (VDBG) Log.d(TAG, "stopScan(PendingIntent): app found = " + app); if (app != null) { final int scannerId = app.id; stopScan(new ScanClient(scannerId)); // Also unregister the scanner unregisterScanner(scannerId); } } void disconnectAll() { if (DBG) Log.d(TAG, "disconnectAll()"); Map<Integer, String> connMap = mClientMap.getConnectedMap(); Loading Loading @@ -1576,7 +1752,7 @@ public class GattService extends ProfileService { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); if (DBG) Log.d(TAG, "registerClient() - UUID=" + uuid); mClientMap.add(uuid, callback, this); mClientMap.add(uuid, callback, null, this); gattClientRegisterAppNative(uuid.getLeastSignificantBits(), uuid.getMostSignificantBits()); } Loading Loading @@ -2094,7 +2270,7 @@ public class GattService extends ProfileService { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); if (DBG) Log.d(TAG, "registerServer() - UUID=" + uuid); mServerMap.add(uuid, callback, this); mServerMap.add(uuid, callback, null, this); gattServerRegisterAppNative(uuid.getLeastSignificantBits(), uuid.getMostSignificantBits()); } Loading