OSDN Git Service

Separate LE scanner from GATT client (2/4)
authorJakub Pawlowski <jpawlowski@google.com>
Wed, 26 Oct 2016 21:47:47 +0000 (14:47 -0700)
committerAndre Eisenbach <eisenbach@google.com>
Thu, 27 Oct 2016 23:08:32 +0000 (23:08 +0000)
Right now, LE scanning functionality is combined with the GATT client.
This is the source of various bugs, like scans suddenly stoppinging when
a GATT client is killed. It also increases memory consumption, because
we associate many structures with a GATT client, which are not necessary
when just scanning.

Test: sl4a BleScanApiTest ConcurrentBleScanTest
Bug: 30622771
Change-Id: Ie4a20abe7fa112bf501619651e288ae46d28e209

jni/com_android_bluetooth_gatt.cpp
src/com/android/bluetooth/gatt/GattService.java
src/com/android/bluetooth/gatt/ScanClient.java
src/com/android/bluetooth/gatt/ScanManager.java

index 433e9aa..2546695 100644 (file)
@@ -135,6 +135,7 @@ namespace android {
  */
 
 static jmethodID method_onClientRegistered;
+static jmethodID method_onScannerRegistered;
 static jmethodID method_onScanResult;
 static jmethodID method_onConnected;
 static jmethodID method_onDisconnected;
@@ -205,6 +206,14 @@ void btgattc_register_app_cb(int status, int clientIf, bt_uuid_t *app_uuid)
         clientIf, UUID_PARAMS(app_uuid));
 }
 
+void btgattc_register_scanner_cb(int status, int scannerId, bt_uuid_t *app_uuid)
+{
+    CallbackEnv sCallbackEnv(__func__);
+    if (!sCallbackEnv.valid()) return;
+    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onScannerRegistered, status,
+        scannerId, UUID_PARAMS(app_uuid));
+}
+
 void btgattc_scan_result_cb(bt_bdaddr_t* bda, int rssi, vector<uint8_t> adv_data)
 {
     CallbackEnv sCallbackEnv(__func__);
@@ -555,6 +564,7 @@ void btgattc_get_gatt_db_cb(int conn_id, btgatt_db_element_t *db, int count)
 
 static const btgatt_client_callbacks_t sGattClientCallbacks = {
     btgattc_register_app_cb,
+    btgattc_register_scanner_cb,
     btgattc_scan_result_cb,
     btgattc_open_cb,
     btgattc_close_cb,
@@ -811,6 +821,7 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
     // Client callbacks
 
     method_onClientRegistered = env->GetMethodID(clazz, "onClientRegistered", "(IIJJ)V");
+    method_onScannerRegistered = env->GetMethodID(clazz, "onScannerRegistered", "(IIJJ)V");
     method_onScanResult = env->GetMethodID(clazz, "onScanResult", "(Ljava/lang/String;I[B)V");
     method_onConnected   = env->GetMethodID(clazz, "onConnected", "(IIILjava/lang/String;)V");
     method_onDisconnected = env->GetMethodID(clazz, "onDisconnected", "(IIILjava/lang/String;)V");
@@ -946,6 +957,24 @@ static void gattClientUnregisterAppNative(JNIEnv* env, jobject object, jint clie
     sGattIf->client->unregister_client(clientIf);
 }
 
+static void registerScannerNative(JNIEnv* env, jobject object,
+                                     jlong app_uuid_lsb, jlong app_uuid_msb)
+{
+    bt_uuid_t uuid;
+
+    if (!sGattIf) return;
+
+    set_uuid(uuid.uu, app_uuid_msb, app_uuid_lsb);
+    sGattIf->client->register_scanner(&uuid);
+}
+
+static void unregisterScannerNative(JNIEnv* env, jobject object, jint scanner_id)
+{
+    if (!sGattIf) return;
+
+    sGattIf->client->unregister_scanner(scanner_id);
+}
+
 static void gattClientScanNative(JNIEnv* env, jobject object, jboolean start)
 {
     if (!sGattIf) return;
@@ -1630,6 +1659,8 @@ static JNINativeMethod sAdvertiseMethods[] = {
 
 // JNI functions defined in ScanManager class.
 static JNINativeMethod sScanMethods[] = {
+    {"registerScannerNative", "(JJ)V", (void *) registerScannerNative},
+    {"unregisterScannerNative", "(I)V", (void *) unregisterScannerNative},
     {"gattClientScanNative", "(Z)V", (void *) gattClientScanNative},
     // Batch scan JNI functions.
     {"gattClientConfigBatchScanStorageNative", "(IIII)V",(void *) gattClientConfigBatchScanStorageNative},
index a59bac2..3817938 100644 (file)
@@ -32,6 +32,7 @@ import android.bluetooth.le.AdvertiseCallback;
 import android.bluetooth.le.AdvertiseData;
 import android.bluetooth.le.AdvertiseSettings;
 import android.bluetooth.le.IAdvertiserCallback;
+import android.bluetooth.le.IScannerCallback;
 import android.bluetooth.le.ResultStorageDescriptor;
 import android.bluetooth.le.ScanFilter;
 import android.bluetooth.le.ScanRecord;
@@ -107,6 +108,12 @@ public class GattService extends ProfileService {
     AdvertiserMap mAdvertiserMap = new AdvertiserMap();
 
     /**
+     * List of our registered advertisers.
+     */
+    class ScannerMap extends ContextMap<IScannerCallback> {}
+    ScannerMap mScannerMap = new ScannerMap();
+
+    /**
      * List of our registered clients.
      */
     class ClientMap extends ContextMap<IBluetoothGattCallback> {}
@@ -172,6 +179,8 @@ public class GattService extends ProfileService {
 
     protected boolean stop() {
         if (DBG) Log.d(TAG, "stop()");
+        mAdvertiserMap.clear();
+        mScannerMap.clear();
         mClientMap.clear();
         mServerMap.clear();
         mHandleMap.clear();
@@ -233,19 +242,19 @@ public class GattService extends ProfileService {
      * disconnect ungracefully (ie. crash or forced close).
      */
 
-    class ClientDeathRecipient implements IBinder.DeathRecipient {
-        int mAppIf;
+    class ScannerDeathRecipient implements IBinder.DeathRecipient {
+        int mScannerId;
 
-        public ClientDeathRecipient(int appIf) {
-            mAppIf = appIf;
+        public ScannerDeathRecipient(int scannerId) {
+            mScannerId = scannerId;
         }
 
         @Override
         public void binderDied() {
-            if (DBG) Log.d(TAG, "Binder is dead - unregistering client (" + mAppIf + ")!");
+            if (DBG) Log.d(TAG, "Binder is dead - unregistering scanner (" + mScannerId + ")!");
 
-            if (isScanClient(mAppIf)) {
-                ScanClient client = new ScanClient(mAppIf, false);
+            if (isScanClient(mScannerId)) {
+                ScanClient client = new ScanClient(mScannerId);
                 client.appDied = true;
                 stopScan(client);
             }
@@ -253,12 +262,12 @@ public class GattService extends ProfileService {
 
         private boolean isScanClient(int clientIf) {
             for (ScanClient client : mScanManager.getRegularScanQueue()) {
-                if (client.clientIf == clientIf) {
+                if (client.scannerId == clientIf) {
                     return true;
                 }
             }
             for (ScanClient client : mScanManager.getBatchScanQueue()) {
-                if (client.clientIf == clientIf) {
+                if (client.scannerId == clientIf) {
                     return true;
                 }
             }
@@ -335,27 +344,39 @@ public class GattService extends ProfileService {
             service.unregisterClient(clientIf);
         }
 
+        public void registerScanner(IScannerCallback callback) {
+            GattService service = getService();
+            if (service == null) return;
+            service.registerScanner(callback);
+        }
+
+        public void unregisterScanner(int scannerId) {
+            GattService service = getService();
+            if (service == null) return;
+            service.unregisterScanner(scannerId);
+        }
+
         @Override
-        public void startScan(int appIf, boolean isServer, ScanSettings settings,
+        public void startScan(int scannerId, ScanSettings settings,
                 List<ScanFilter> filters, WorkSource workSource, List storages,
                 String callingPackage) {
             GattService service = getService();
             if (service == null) return;
-            service.startScan(appIf, isServer, settings, filters, workSource, storages,
+            service.startScan(scannerId, settings, filters, workSource, storages,
                     callingPackage);
         }
 
-        public void stopScan(int appIf, boolean isServer) {
+        public void stopScan(int scannerId) {
             GattService service = getService();
             if (service == null) return;
-            service.stopScan(new ScanClient(appIf, isServer));
+            service.stopScan(new ScanClient(scannerId));
         }
 
         @Override
-        public void flushPendingBatchResults(int appIf, boolean isServer) {
+        public void flushPendingBatchResults(int scannerId) {
             GattService service = getService();
             if (service == null) return;
-            service.flushPendingBatchResults(appIf, isServer);
+            service.flushPendingBatchResults(scannerId);
         }
 
         public void clientConnect(int clientIf, String address, boolean isDirect, int transport) {
@@ -576,38 +597,25 @@ public class GattService extends ProfileService {
                 if (matches < client.uuids.length) continue;
             }
 
-            if (!client.isServer) {
-                ClientMap.App app = mClientMap.getById(client.clientIf);
-                if (app != null) {
-                    BluetoothDevice device = BluetoothAdapter.getDefaultAdapter()
-                            .getRemoteDevice(address);
-                    ScanResult result = new ScanResult(device, ScanRecord.parseFromBytes(adv_data),
-                            rssi, SystemClock.elapsedRealtimeNanos());
-                    // Do no report if location mode is OFF or the client has no location permission
-                    // PEERS_MAC_ADDRESS permission holders always get results
-                    if (hasScanResultPermission(client) && matchesFilters(client, result)) {
-                        try {
-                            ScanSettings settings = client.settings;
-                            if ((settings.getCallbackType() &
-                                    ScanSettings.CALLBACK_TYPE_ALL_MATCHES) != 0) {
-                                app.appScanStats.addResult();
-                                app.callback.onScanResult(result);
-                            }
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "Exception: " + e);
-                            mClientMap.remove(client.clientIf);
-                            mScanManager.stopScan(client);
-                        }
-                    }
-                }
-            } else {
-                ServerMap.App app = mServerMap.getById(client.clientIf);
-                if (app != null) {
+            ScannerMap.App app = mScannerMap.getById(client.scannerId);
+            if (app != null) {
+                BluetoothDevice device = BluetoothAdapter.getDefaultAdapter()
+                        .getRemoteDevice(address);
+                ScanResult result = new ScanResult(device, ScanRecord.parseFromBytes(adv_data),
+                        rssi, SystemClock.elapsedRealtimeNanos());
+                // Do no report if location mode is OFF or the client has no location permission
+                // PEERS_MAC_ADDRESS permission holders always get results
+                if (hasScanResultPermission(client) && matchesFilters(client, result)) {
                     try {
-                        app.callback.onScanResult(address, rssi, adv_data);
+                        ScanSettings settings = client.settings;
+                        if ((settings.getCallbackType() &
+                                ScanSettings.CALLBACK_TYPE_ALL_MATCHES) != 0) {
+                            app.appScanStats.addResult();
+                            app.callback.onScanResult(result);
+                        }
                     } catch (RemoteException e) {
                         Log.e(TAG, "Exception: " + e);
-                        mServerMap.remove(client.clientIf);
+                        mScannerMap.remove(client.scannerId);
                         mScanManager.stopScan(client);
                     }
                 }
@@ -615,6 +623,24 @@ public class GattService extends ProfileService {
         }
     }
 
+    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) {
+            if (status == 0) {
+                app.id = scannerId;
+                app.linkToDeath(new ScannerDeathRecipient(scannerId));
+            } else {
+                mScannerMap.remove(scannerId);
+            }
+            app.callback.onScannerRegistered(status, scannerId);
+        }
+    }
+
     /** Determines if the given scan client has the appropriate permissions to receive callbacks. */
     private boolean hasScanResultPermission(final ScanClient client) {
         final boolean requiresLocationEnabled =
@@ -649,7 +675,6 @@ public class GattService extends ProfileService {
         if (app != null) {
             if (status == 0) {
                 app.id = clientIf;
-                app.linkToDeath(new ClientDeathRecipient(clientIf));
             } else {
                 mClientMap.remove(uuid);
             }
@@ -904,17 +929,17 @@ public class GattService extends ProfileService {
         mScanManager.callbackDone(clientIf, status);
     }
 
-    void onBatchScanReports(int status, int clientIf, int reportType, int numRecords,
+    void onBatchScanReports(int status, int scannerId, int reportType, int numRecords,
             byte[] recordData) throws RemoteException {
         if (DBG) {
-            Log.d(TAG, "onBatchScanReports() - clientIf=" + clientIf + ", status=" + status
+            Log.d(TAG, "onBatchScanReports() - scannerId=" + scannerId + ", status=" + status
                     + ", reportType=" + reportType + ", numRecords=" + numRecords);
         }
-        mScanManager.callbackDone(clientIf, status);
+        mScanManager.callbackDone(scannerId, status);
         Set<ScanResult> results = parseBatchScanResults(numRecords, reportType, recordData);
         if (reportType == ScanManager.SCAN_RESULT_TYPE_TRUNCATED) {
             // We only support single client for truncated mode.
-            ClientMap.App app = mClientMap.getById(clientIf);
+            ScannerMap.App app = mScannerMap.getById(scannerId);
             if (app == null) return;
             app.callback.onBatchScanResults(new ArrayList<ScanResult>(results));
         } else {
@@ -928,7 +953,7 @@ public class GattService extends ProfileService {
     // Check and deliver scan results for different scan clients.
     private void deliverBatchScan(ScanClient client, Set<ScanResult> allResults) throws
             RemoteException {
-        ClientMap.App app = mClientMap.getById(client.clientIf);
+        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));
@@ -1039,8 +1064,7 @@ public class GattService extends ProfileService {
         if (DBG) {
             Log.d(TAG, "onBatchScanThresholdCrossed() - clientIf=" + clientIf);
         }
-        boolean isServer = false;
-        flushPendingBatchResults(clientIf, isServer);
+        flushPendingBatchResults(clientIf);
     }
 
     AdvtFilterOnFoundOnLostInfo CreateonTrackAdvFoundLostObject(int client_if, int adv_pkt_len,
@@ -1055,11 +1079,11 @@ public class GattService extends ProfileService {
     }
 
     void onTrackAdvFoundLost(AdvtFilterOnFoundOnLostInfo trackingInfo) throws RemoteException {
-        if (DBG) Log.d(TAG, "onTrackAdvFoundLost() - clientIf= " + trackingInfo.getClientIf()
+        if (DBG) Log.d(TAG, "onTrackAdvFoundLost() - scannerId= " + trackingInfo.getClientIf()
                     + " address = " + trackingInfo.getAddress()
                     + " adv_state = " + trackingInfo.getAdvState());
 
-        ClientMap.App app = mClientMap.getById(trackingInfo.getClientIf());
+        ScannerMap.App app = mScannerMap.getById(trackingInfo.getClientIf());
         if (app == null || app.callback == null) {
             Log.e(TAG, "app or callback is null");
             return;
@@ -1073,7 +1097,7 @@ public class GattService extends ProfileService {
                         trackingInfo.getRSSIValue(), SystemClock.elapsedRealtimeNanos());
 
         for (ScanClient client : mScanManager.getRegularScanQueue()) {
-            if (client.clientIf == trackingInfo.getClientIf()) {
+            if (client.scannerId == trackingInfo.getClientIf()) {
                 ScanSettings settings = client.settings;
                 if ((advertiserState == ADVT_STATE_ONFOUND)
                         && ((settings.getCallbackType()
@@ -1085,15 +1109,15 @@ public class GattService extends ProfileService {
                     app.callback.onFoundOrLost(false, result);
                 } else {
                     Log.d(TAG, "Not reporting onlost/onfound : " + advertiserState
-                                + " clientIf = " + client.clientIf
+                                + " scannerId = " + client.scannerId
                                 + " callbackType " + settings.getCallbackType());
                 }
             }
         }
     }
 
-    void onScanParamSetupCompleted(int status, int clientIf) throws RemoteException {
-        ClientMap.App app = mClientMap.getById(clientIf);
+    void onScanParamSetupCompleted(int status, int scannerId) throws RemoteException {
+        ScannerMap.App app = mScannerMap.getById(scannerId);
         if (app == null || app.callback == null) {
             Log.e(TAG, "Advertise app or callback is null");
             return;
@@ -1115,8 +1139,8 @@ public class GattService extends ProfileService {
     }
 
     // callback from ScanManager for dispatch of errors apps.
-    void onScanManagerErrorCallback(int clientIf, int errorCode) throws RemoteException {
-        ClientMap.App app = mClientMap.getById(clientIf);
+    void onScanManagerErrorCallback(int scannerId, int errorCode) throws RemoteException {
+        ScannerMap.App app = mScannerMap.getById(scannerId);
         if (app == null || app.callback == null) {
             Log.e(TAG, "App or callback is null");
             return;
@@ -1267,7 +1291,24 @@ public class GattService extends ProfileService {
         return deviceList;
     }
 
-    void startScan(int appIf, boolean isServer, ScanSettings settings,
+    void registerScanner(IScannerCallback callback) {
+        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+
+        UUID uuid = UUID.randomUUID();
+        if (DBG) Log.d(TAG, "registerScanner() - UUID=" + uuid);
+        mScannerMap.add(uuid, callback, this);
+        mScanManager.registerScanner(uuid);
+    }
+
+    void unregisterScanner(int scannerId) {
+        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+
+        if (DBG) Log.d(TAG, "unregisterScanner() - scannerId=" + scannerId);
+        mScannerMap.remove(scannerId);
+        mScanManager.unregisterScanner(scannerId);
+    }
+
+    void startScan(int scannerId, ScanSettings settings,
             List<ScanFilter> filters, WorkSource workSource,
             List<List<ResultStorageDescriptor>> storages, String callingPackage) {
         if (DBG) Log.d(TAG, "start scan with filters");
@@ -1281,7 +1322,7 @@ public class GattService extends ProfileService {
             // Blame the caller if the work source is unspecified.
             workSource = new WorkSource(Binder.getCallingUid(), callingPackage);
         }
-        final ScanClient scanClient = new ScanClient(appIf, isServer, settings, filters, workSource,
+        final ScanClient scanClient = new ScanClient(scannerId, settings, filters, workSource,
                 storages);
         scanClient.hasLocationPermission = Utils.checkCallerHasLocationPermission(this, mAppOps,
                 callingPackage);
@@ -1290,11 +1331,7 @@ public class GattService extends ProfileService {
         scanClient.legacyForegroundApp = Utils.isLegacyForegroundApp(this, callingPackage);
 
         AppScanStats app = null;
-        if (isServer) {
-            app = mServerMap.getAppScanStatsById(appIf);
-        } else {
-            app = mClientMap.getAppScanStatsById(appIf);
-        }
+        app = mClientMap.getAppScanStatsById(scannerId);
 
         if (app != null) {
             if (app.isScanningTooFrequently() &&
@@ -1309,10 +1346,9 @@ public class GattService extends ProfileService {
         mScanManager.startScan(scanClient);
     }
 
-    void flushPendingBatchResults(int clientIf, boolean isServer) {
-        if (DBG) Log.d(TAG, "flushPendingBatchResults - clientIf=" + clientIf +
-                ", isServer=" + isServer);
-        mScanManager.flushBatchScanResults(new ScanClient(clientIf, isServer));
+    void flushPendingBatchResults(int scannerId) {
+        if (DBG) Log.d(TAG, "flushPendingBatchResults - scannerId=" + scannerId);
+        mScanManager.flushBatchScanResults(new ScanClient(scannerId));
     }
 
     void stopScan(ScanClient client) {
@@ -1322,11 +1358,7 @@ public class GattService extends ProfileService {
         if (DBG) Log.d(TAG, "stopScan() - queue size =" + scanQueueSize);
 
         AppScanStats app = null;
-        if (client.isServer) {
-            app = mServerMap.getAppScanStatsById(client.clientIf);
-        } else {
-            app = mClientMap.getAppScanStatsById(client.clientIf);
-        }
+        app = mScannerMap.getAppScanStatsById(client.scannerId);
         if (app != null) app.recordScanStop();
 
         mScanManager.stopScan(client);
index a620436..bbd6c18 100644 (file)
@@ -31,8 +31,7 @@ import java.util.UUID;
  * @hide
  */
 /* package */class ScanClient {
-    int clientIf;
-    boolean isServer;
+    int scannerId;
     UUID[] uuids;
     ScanSettings settings;
     List<ScanFilter> filters;
@@ -52,35 +51,34 @@ import java.util.UUID;
     private static final ScanSettings DEFAULT_SCAN_SETTINGS = new ScanSettings.Builder()
             .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
 
-    ScanClient(int appIf, boolean isServer) {
-        this(appIf, isServer, new UUID[0], DEFAULT_SCAN_SETTINGS, null, null, null);
+    ScanClient(int scannerId) {
+        this(scannerId, new UUID[0], DEFAULT_SCAN_SETTINGS, null, null, null);
     }
 
-    ScanClient(int appIf, boolean isServer, UUID[] uuids) {
-        this(appIf, isServer, uuids, DEFAULT_SCAN_SETTINGS, null, null, null);
+    ScanClient(int scannerId, UUID[] uuids) {
+        this(scannerId, uuids, DEFAULT_SCAN_SETTINGS, null, null, null);
     }
 
-    ScanClient(int appIf, boolean isServer, ScanSettings settings,
+    ScanClient(int scannerId, ScanSettings settings,
             List<ScanFilter> filters) {
-        this(appIf, isServer, new UUID[0], settings, filters, null, null);
+        this(scannerId, new UUID[0], settings, filters, null, null);
     }
 
-    ScanClient(int appIf, boolean isServer, ScanSettings settings,
+    ScanClient(int scannerId, ScanSettings settings,
             List<ScanFilter> filters, List<List<ResultStorageDescriptor>> storages) {
-        this(appIf, isServer, new UUID[0], settings, filters, null, storages);
+        this(scannerId, new UUID[0], settings, filters, null, storages);
     }
 
-    ScanClient(int appIf, boolean isServer, ScanSettings settings,
+    ScanClient(int scannerId, ScanSettings settings,
                List<ScanFilter> filters, WorkSource workSource,
                List<List<ResultStorageDescriptor>> storages) {
-        this(appIf, isServer, new UUID[0], settings, filters, workSource, storages);
+        this(scannerId, new UUID[0], settings, filters, workSource, storages);
     }
 
-    private ScanClient(int appIf, boolean isServer, UUID[] uuids, ScanSettings settings,
+    private ScanClient(int scannerId, UUID[] uuids, ScanSettings settings,
             List<ScanFilter> filters, WorkSource workSource,
             List<List<ResultStorageDescriptor>> storages) {
-        this.clientIf = appIf;
-        this.isServer = isServer;
+        this.scannerId = scannerId;
         this.uuids = uuids;
         this.settings = settings;
         this.filters = filters;
@@ -97,11 +95,11 @@ import java.util.UUID;
             return false;
         }
         ScanClient other = (ScanClient) obj;
-        return clientIf == other.clientIf;
+        return scannerId == other.scannerId;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(clientIf);
+        return Objects.hash(scannerId);
     }
 }
index 6213597..6811009 100644 (file)
@@ -46,6 +46,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -127,6 +128,15 @@ public class ScanManager {
         }
     }
 
+    void registerScanner(UUID uuid) {
+        mScanNative.registerScannerNative(
+            uuid.getLeastSignificantBits(), uuid.getMostSignificantBits());
+    }
+
+    void unregisterScanner(int scannerId) {
+        mScanNative.unregisterScannerNative(scannerId);
+    }
+
     /**
      * Returns the regular scan queue.
      */
@@ -168,8 +178,8 @@ public class ScanManager {
         sendMessage(MSG_FLUSH_BATCH_RESULTS, client);
     }
 
-    void callbackDone(int clientIf, int status) {
-        logd("callback done for clientIf - " + clientIf + " status - " + status);
+    void callbackDone(int scannerId, int status) {
+        logd("callback done for scannerId - " + scannerId + " status - " + status);
         if (status == 0) {
             mLatch.countDown();
         }
@@ -276,9 +286,9 @@ public class ScanManager {
 
                 // Update BatteryStats with this workload.
                 try {
-                    // The ScanClient passed in just holds the clientIf. We retrieve the real client,
+                    // The ScanClient passed in just holds the scannerId. We retrieve the real client,
                     // which may have workSource set.
-                    ScanClient workClient = mScanNative.getRegularScanClient(client.clientIf);
+                    ScanClient workClient = mScanNative.getRegularScanClient(client.scannerId);
                     if (workClient != null)
                         mBatteryStats.noteBleScanStopped(workClient.workSource);
                 } catch (RemoteException e) {
@@ -288,8 +298,8 @@ public class ScanManager {
                 mScanNative.stopBatchScan(client);
             }
             if (client.appDied) {
-                logd("app died, unregister client - " + client.clientIf);
-                mService.unregisterClient(client.clientIf);
+                logd("app died, unregister client - " + client.scannerId);
+                mService.unregisterClient(client.scannerId);
             }
         }
 
@@ -298,7 +308,7 @@ public class ScanManager {
             if (!mBatchClients.contains(client)) {
                 return;
             }
-            mScanNative.flushBatchResults(client.clientIf);
+            mScanNative.flushBatchResults(client.scannerId);
         }
 
         private boolean isBatchClient(ScanClient client) {
@@ -328,13 +338,13 @@ public class ScanManager {
      */
     class BatchScanParams {
         int scanMode;
-        int fullScanClientIf;
-        int truncatedScanClientIf;
+        int fullScanscannerId;
+        int truncatedScanscannerId;
 
         BatchScanParams() {
             scanMode = -1;
-            fullScanClientIf = -1;
-            truncatedScanClientIf = -1;
+            fullScanscannerId = -1;
+            truncatedScanscannerId = -1;
         }
 
         @Override
@@ -346,8 +356,8 @@ public class ScanManager {
                 return false;
             }
             BatchScanParams other = (BatchScanParams) obj;
-            return scanMode == other.scanMode && fullScanClientIf == other.fullScanClientIf
-                    && truncatedScanClientIf == other.truncatedScanClientIf;
+            return scanMode == other.scanMode && fullScanscannerId == other.fullScanscannerId
+                    && truncatedScanscannerId == other.truncatedScanscannerId;
 
         }
     }
@@ -405,7 +415,7 @@ public class ScanManager {
         private static final int FILTER_LOGIC_TYPE = 1;
         // Filter indices that are available to user. It's sad we need to maintain filter index.
         private final Deque<Integer> mFilterIndexStack;
-        // Map of clientIf and Filter indices used by client.
+        // Map of scannerId and Filter indices used by client.
         private final Map<Integer, Deque<Integer>> mClientFilterIndexMap;
         // Keep track of the clients that uses ALL_PASS filters.
         private final Set<Integer> mAllPassRegularClients = new HashSet<>();
@@ -477,7 +487,7 @@ public class ScanManager {
                     gattClientScanNative(false);
                     logd("configureRegularScanParams - scanInterval = " + scanInterval +
                         "configureRegularScanParams - scanWindow = " + scanWindow);
-                    gattSetScanParametersNative(client.clientIf, scanInterval, scanWindow);
+                    gattSetScanParametersNative(client.scannerId, scanInterval, scanWindow);
                     gattClientScanNative(true);
                     mLastConfiguredScanSetting = curScanSetting;
                 }
@@ -551,17 +561,17 @@ public class ScanManager {
         }
 
         private void resetBatchScan(ScanClient client) {
-            int clientIf = client.clientIf;
+            int scannerId = client.scannerId;
             BatchScanParams batchScanParams = getBatchScanParams();
             // Stop batch if batch scan params changed and previous params is not null.
             if (mBatchScanParms != null && (!mBatchScanParms.equals(batchScanParams))) {
                 logd("stopping BLe Batch");
                 resetCountDownLatch();
-                gattClientStopBatchScanNative(clientIf);
+                gattClientStopBatchScanNative(scannerId);
                 waitForCallback();
                 // Clear pending results as it's illegal to config storage if there are still
                 // pending results.
-                flushBatchResults(clientIf);
+                flushBatchResults(scannerId);
             }
             // Start batch if batchScanParams changed and current params is not null.
             if (batchScanParams != null && (!batchScanParams.equals(mBatchScanParms))) {
@@ -570,8 +580,8 @@ public class ScanManager {
                 int resultType = getResultType(batchScanParams);
                 int fullScanPercent = getFullScanStoragePercent(resultType);
                 resetCountDownLatch();
-                logd("configuring batch scan storage, appIf " + client.clientIf);
-                gattClientConfigBatchScanStorageNative(client.clientIf, fullScanPercent,
+                logd("configuring batch scan storage, appIf " + client.scannerId);
+                gattClientConfigBatchScanStorageNative(client.scannerId, fullScanPercent,
                         100 - fullScanPercent, notifyThreshold);
                 waitForCallback();
                 resetCountDownLatch();
@@ -579,7 +589,7 @@ public class ScanManager {
                         Utils.millsToUnit(getBatchScanIntervalMillis(batchScanParams.scanMode));
                 int scanWindow =
                         Utils.millsToUnit(getBatchScanWindowMillis(batchScanParams.scanMode));
-                gattClientStartBatchScanNative(clientIf, resultType, scanInterval,
+                gattClientStartBatchScanNative(scannerId, resultType, scanInterval,
                         scanWindow, 0, DISCARD_OLDEST_WHEN_BUFFER_FULL);
                 waitForCallback();
             }
@@ -610,9 +620,9 @@ public class ScanManager {
             for (ScanClient client : mBatchClients) {
                 params.scanMode = Math.max(params.scanMode, client.settings.getScanMode());
                 if (client.settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL) {
-                    params.fullScanClientIf = client.clientIf;
+                    params.fullScanscannerId = client.scannerId;
                 } else {
-                    params.truncatedScanClientIf = client.clientIf;
+                    params.truncatedScanscannerId = client.scannerId;
                 }
             }
             return params;
@@ -673,7 +683,7 @@ public class ScanManager {
                         Log.e(TAG, "Error freeing for onfound/onlost filter resources "
                                     + entriesToFree);
                         try {
-                            mService.onScanManagerErrorCallback(client.clientIf,
+                            mService.onScanManagerErrorCallback(client.scannerId,
                                             ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
                         } catch (RemoteException e) {
                             Log.e(TAG, "failed on onScanManagerCallback at freeing", e);
@@ -686,14 +696,14 @@ public class ScanManager {
                 logd("stop scan");
                 gattClientScanNative(false);
             }
-            removeScanFilters(client.clientIf);
+            removeScanFilters(client.scannerId);
         }
 
         void regularScanTimeout() {
             for (ScanClient client : mRegularScanClients) {
                 if (!isExemptFromScanDowngrade(client)) {
-                    Log.w(TAG, "Moving scan client to opportunistic (clientIf "
-                          + client.clientIf + ")");
+                    Log.w(TAG, "Moving scan client to opportunistic (scannerId "
+                          + client.scannerId + ")");
                     setOpportunisticScanClient(client);
                     client.stats.setScanTimeout();
                 }
@@ -721,32 +731,32 @@ public class ScanManager {
         }
 
         // Find the regular scan client information.
-        ScanClient getRegularScanClient(int clientIf) {
+        ScanClient getRegularScanClient(int scannerId) {
             for (ScanClient client : mRegularScanClients) {
-              if (client.clientIf == clientIf) return client;
+              if (client.scannerId == scannerId) return client;
             }
             return null;
         }
 
         void stopBatchScan(ScanClient client) {
             mBatchClients.remove(client);
-            removeScanFilters(client.clientIf);
+            removeScanFilters(client.scannerId);
             if (!isOpportunisticScanClient(client)) {
                 resetBatchScan(client);
             }
         }
 
-        void flushBatchResults(int clientIf) {
-            logd("flushPendingBatchResults - clientIf = " + clientIf);
-            if (mBatchScanParms.fullScanClientIf != -1) {
+        void flushBatchResults(int scannerId) {
+            logd("flushPendingBatchResults - scannerId = " + scannerId);
+            if (mBatchScanParms.fullScanscannerId != -1) {
                 resetCountDownLatch();
-                gattClientReadScanReportsNative(mBatchScanParms.fullScanClientIf,
+                gattClientReadScanReportsNative(mBatchScanParms.fullScanscannerId,
                         SCAN_RESULT_TYPE_FULL);
                 waitForCallback();
             }
-            if (mBatchScanParms.truncatedScanClientIf != -1) {
+            if (mBatchScanParms.truncatedScanscannerId != -1) {
                 resetCountDownLatch();
-                gattClientReadScanReportsNative(mBatchScanParms.truncatedScanClientIf,
+                gattClientReadScanReportsNative(mBatchScanParms.truncatedScanscannerId,
                         SCAN_RESULT_TYPE_TRUNCATED);
                 waitForCallback();
             }
@@ -777,7 +787,7 @@ public class ScanManager {
         // If no offload filter can/needs to be set, set ALL_PASS filter.
         // Otherwise offload all filters to hardware and enable all filters.
         private void configureScanFilters(ScanClient client) {
-            int clientIf = client.clientIf;
+            int scannerId = client.scannerId;
             int deliveryMode = getDeliveryMode(client);
             int trackEntries = 0;
             if (!shouldAddAllPassFilterToController(client, deliveryMode)) {
@@ -785,7 +795,7 @@ public class ScanManager {
             }
 
             resetCountDownLatch();
-            gattClientScanFilterEnableNative(clientIf, true);
+            gattClientScanFilterEnableNative(scannerId, true);
             waitForCallback();
 
             if (shouldUseAllPassFilter(client)) {
@@ -793,7 +803,7 @@ public class ScanManager {
                         ALL_PASS_FILTER_INDEX_BATCH_SCAN : ALL_PASS_FILTER_INDEX_REGULAR_SCAN;
                 resetCountDownLatch();
                 // Don't allow Onfound/onlost with all pass
-                configureFilterParamter(clientIf, client, ALL_PASS_FILTER_SELECTION,
+                configureFilterParamter(scannerId, client, ALL_PASS_FILTER_SELECTION,
                                 filterIndex, 0);
                 waitForCallback();
             } else {
@@ -805,7 +815,7 @@ public class ScanManager {
                     int filterIndex = mFilterIndexStack.pop();
                     while (!queue.isEmpty()) {
                         resetCountDownLatch();
-                        addFilterToController(clientIf, queue.pop(), filterIndex);
+                        addFilterToController(scannerId, queue.pop(), filterIndex);
                         waitForCallback();
                     }
                     resetCountDownLatch();
@@ -815,19 +825,19 @@ public class ScanManager {
                             Log.e(TAG, "No hardware resources for onfound/onlost filter " +
                                     trackEntries);
                             try {
-                                mService.onScanManagerErrorCallback(clientIf,
+                                mService.onScanManagerErrorCallback(scannerId,
                                             ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
                             } catch (RemoteException e) {
                                 Log.e(TAG, "failed on onScanManagerCallback", e);
                             }
                         }
                     }
-                    configureFilterParamter(clientIf, client, featureSelection, filterIndex,
+                    configureFilterParamter(scannerId, client, featureSelection, filterIndex,
                                             trackEntries);
                     waitForCallback();
                     clientFilterIndices.add(filterIndex);
                 }
-                mClientFilterIndexMap.put(clientIf, clientFilterIndices);
+                mClientFilterIndexMap.put(scannerId, clientFilterIndices);
             }
         }
 
@@ -840,47 +850,47 @@ public class ScanManager {
             }
 
             if (deliveryMode == DELIVERY_MODE_BATCH) {
-                mAllPassBatchClients.add(client.clientIf);
+                mAllPassBatchClients.add(client.scannerId);
                 return mAllPassBatchClients.size() == 1;
             } else {
-                mAllPassRegularClients.add(client.clientIf);
+                mAllPassRegularClients.add(client.scannerId);
                 return mAllPassRegularClients.size() == 1;
             }
         }
 
-        private void removeScanFilters(int clientIf) {
-            Deque<Integer> filterIndices = mClientFilterIndexMap.remove(clientIf);
+        private void removeScanFilters(int scannerId) {
+            Deque<Integer> filterIndices = mClientFilterIndexMap.remove(scannerId);
             if (filterIndices != null) {
                 mFilterIndexStack.addAll(filterIndices);
                 for (Integer filterIndex : filterIndices) {
                     resetCountDownLatch();
-                    gattClientScanFilterParamDeleteNative(clientIf, filterIndex);
+                    gattClientScanFilterParamDeleteNative(scannerId, filterIndex);
                     waitForCallback();
                 }
             }
             // Remove if ALL_PASS filters are used.
-            removeFilterIfExisits(mAllPassRegularClients, clientIf,
+            removeFilterIfExisits(mAllPassRegularClients, scannerId,
                     ALL_PASS_FILTER_INDEX_REGULAR_SCAN);
-            removeFilterIfExisits(mAllPassBatchClients, clientIf,
+            removeFilterIfExisits(mAllPassBatchClients, scannerId,
                     ALL_PASS_FILTER_INDEX_BATCH_SCAN);
         }
 
-        private void removeFilterIfExisits(Set<Integer> clients, int clientIf, int filterIndex) {
-            if (!clients.contains(clientIf)) {
+        private void removeFilterIfExisits(Set<Integer> clients, int scannerId, int filterIndex) {
+            if (!clients.contains(scannerId)) {
                 return;
             }
-            clients.remove(clientIf);
+            clients.remove(scannerId);
             // Remove ALL_PASS filter iff no app is using it.
             if (clients.isEmpty()) {
                 resetCountDownLatch();
-                gattClientScanFilterParamDeleteNative(clientIf, filterIndex);
+                gattClientScanFilterParamDeleteNative(scannerId, filterIndex);
                 waitForCallback();
             }
         }
 
-        private ScanClient getBatchScanClient(int clientIf) {
+        private ScanClient getBatchScanClient(int scannerId) {
             for (ScanClient client : mBatchClients) {
-                if (client.clientIf == clientIf) {
+                if (client.scannerId == scannerId) {
                     return client;
                 }
             }
@@ -891,13 +901,13 @@ public class ScanManager {
          * Return batch scan result type value defined in bt stack.
          */
         private int getResultType(BatchScanParams params) {
-            if (params.fullScanClientIf != -1 && params.truncatedScanClientIf != -1) {
+            if (params.fullScanscannerId != -1 && params.truncatedScanscannerId != -1) {
                 return SCAN_RESULT_TYPE_BOTH;
             }
-            if (params.truncatedScanClientIf != -1) {
+            if (params.truncatedScanscannerId != -1) {
                 return SCAN_RESULT_TYPE_TRUNCATED;
             }
-            if (params.fullScanClientIf != -1) {
+            if (params.fullScanscannerId != -1) {
                 return SCAN_RESULT_TYPE_FULL;
             }
             return -1;
@@ -914,26 +924,26 @@ public class ScanManager {
             return client.filters.size() > mFilterIndexStack.size();
         }
 
-        private void addFilterToController(int clientIf, ScanFilterQueue.Entry entry,
+        private void addFilterToController(int scannerId, ScanFilterQueue.Entry entry,
                 int filterIndex) {
             logd("addFilterToController: " + entry.type);
             switch (entry.type) {
                 case ScanFilterQueue.TYPE_DEVICE_ADDRESS:
                     logd("add address " + entry.address);
-                    gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0, 0, 0, 0,
+                    gattClientScanFilterAddNative(scannerId, entry.type, filterIndex, 0, 0, 0, 0, 0,
                             0,
                             "", entry.address, (byte) entry.addr_type, new byte[0], new byte[0]);
                     break;
 
                 case ScanFilterQueue.TYPE_SERVICE_DATA:
-                    gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0, 0, 0, 0,
+                    gattClientScanFilterAddNative(scannerId, entry.type, filterIndex, 0, 0, 0, 0, 0,
                             0,
                             "", "", (byte) 0, entry.data, entry.data_mask);
                     break;
 
                 case ScanFilterQueue.TYPE_SERVICE_UUID:
                 case ScanFilterQueue.TYPE_SOLICIT_UUID:
-                    gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0,
+                    gattClientScanFilterAddNative(scannerId, entry.type, filterIndex, 0, 0,
                             entry.uuid.getLeastSignificantBits(),
                             entry.uuid.getMostSignificantBits(),
                             entry.uuid_mask.getLeastSignificantBits(),
@@ -943,7 +953,7 @@ public class ScanManager {
 
                 case ScanFilterQueue.TYPE_LOCAL_NAME:
                     logd("adding filters: " + entry.name);
-                    gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0, 0, 0, 0,
+                    gattClientScanFilterAddNative(scannerId, entry.type, filterIndex, 0, 0, 0, 0, 0,
                             0,
                             entry.name, "", (byte) 0, new byte[0], new byte[0]);
                     break;
@@ -952,7 +962,7 @@ public class ScanManager {
                     int len = entry.data.length;
                     if (entry.data_mask.length != len)
                         return;
-                    gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, entry.company,
+                    gattClientScanFilterAddNative(scannerId, entry.type, filterIndex, entry.company,
                             entry.company_mask, 0, 0, 0, 0, "", "", (byte) 0,
                             entry.data, entry.data_mask);
                     break;
@@ -972,7 +982,7 @@ public class ScanManager {
         }
 
         // Configure filter parameters.
-        private void configureFilterParamter(int clientIf, ScanClient client, int featureSelection,
+        private void configureFilterParamter(int scannerId, ScanClient client, int featureSelection,
                 int filterIndex, int numOfTrackingEntries) {
             int deliveryMode = getDeliveryMode(client);
             int rssiThreshold = Byte.MIN_VALUE;
@@ -983,7 +993,7 @@ public class ScanManager {
             onLostTimeout = 10000;
             logd("configureFilterParamter " + onFoundTimeout + " " + onLostTimeout + " "
                     + onFoundCount + " " + numOfTrackingEntries);
-            FilterParams FiltValue = new FilterParams(clientIf, filterIndex, featureSelection,
+            FilterParams FiltValue = new FilterParams(scannerId, filterIndex, featureSelection,
                     LIST_LOGIC_TYPE, FILTER_LOGIC_TYPE, rssiThreshold, rssiThreshold, deliveryMode,
                     onFoundTimeout, onLostTimeout, onFoundCount, numOfTrackingEntries);
             gattClientScanFilterParamAddNative(FiltValue);
@@ -1114,6 +1124,9 @@ public class ScanManager {
 
 
         /************************** Regular scan related native methods **************************/
+        private native void registerScannerNative(long app_uuid_lsb, long app_uuid_msb);
+        private native void unregisterScannerNative(int scannerId);
+
         private native void gattClientScanNative(boolean start);
 
         private native void gattSetScanParametersNative(int client_if, int scan_interval,