Loading android/app/jni/com_android_bluetooth_gatt.cpp +97 −0 Original line number Diff line number Diff line Loading @@ -198,6 +198,13 @@ static jmethodID method_onPeriodicAdvertisingParametersUpdated; static jmethodID method_onPeriodicAdvertisingDataSet; static jmethodID method_onPeriodicAdvertisingEnabled; /** * Periodic scanner callback methods */ static jmethodID method_onSyncLost; static jmethodID method_onSyncReport; static jmethodID method_onSyncStarted; /** * Static variables */ Loading @@ -205,6 +212,7 @@ static jmethodID method_onPeriodicAdvertisingEnabled; static const btgatt_interface_t* sGattIf = NULL; static jobject mCallbacksObj = NULL; static jobject mAdvertiseCallbacksObj = NULL; static jobject mPeriodicScanCallbacksObj = NULL; /** * BTA client callbacks Loading Loading @@ -1938,6 +1946,83 @@ static void setPeriodicAdvertisingEnableNative(JNIEnv* env, jobject object, base::Bind(&enablePeriodicSetCb, advertiser_id, enable)); } static void periodicScanClassInitNative(JNIEnv* env, jclass clazz) { method_onSyncStarted = env->GetMethodID(clazz, "onSyncStarted", "(IIIILjava/lang/String;III)V"); method_onSyncReport = env->GetMethodID(clazz, "onSyncReport", "(IIII[B)V"); method_onSyncLost = env->GetMethodID(clazz, "onSyncLost", "(I)V"); } static void periodicScanInitializeNative(JNIEnv* env, jobject object) { if (mPeriodicScanCallbacksObj != NULL) { ALOGW("Cleaning up periodic scan callback object"); env->DeleteGlobalRef(mPeriodicScanCallbacksObj); mPeriodicScanCallbacksObj = NULL; } mPeriodicScanCallbacksObj = env->NewGlobalRef(object); } static void periodicScanCleanupNative(JNIEnv* env, jobject object) { if (mPeriodicScanCallbacksObj != NULL) { env->DeleteGlobalRef(mPeriodicScanCallbacksObj); mPeriodicScanCallbacksObj = NULL; } } static void onSyncStarted(int reg_id, uint8_t status, uint16_t sync_handle, uint8_t sid, uint8_t address_type, bt_bdaddr_t address, uint8_t phy, uint16_t interval) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; sCallbackEnv->CallVoidMethod(mPeriodicScanCallbacksObj, method_onSyncStarted, reg_id, sync_handle, sid, address_type, address, phy, interval, status); } static void onSyncReport(uint16_t sync_handle, int8_t tx_power, int8_t rssi, uint8_t data_status, std::vector<uint8_t> data) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; ScopedLocalRef<jbyteArray> jb(sCallbackEnv.get(), sCallbackEnv->NewByteArray(data.size())); sCallbackEnv->SetByteArrayRegion(jb.get(), 0, data.size(), (jbyte*)data.data()); sCallbackEnv->CallVoidMethod(mPeriodicScanCallbacksObj, method_onSyncReport, sync_handle, tx_power, rssi, data_status, jb.get()); } static void onSyncLost(uint16_t sync_handle) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; sCallbackEnv->CallVoidMethod(mPeriodicScanCallbacksObj, method_onSyncLost, sync_handle); } static void startSyncNative(JNIEnv* env, jobject object, jint sid, jstring address, jint skip, jint timeout, jint reg_id) { if (!sGattIf) return; bt_bdaddr_t tmp; jstr2bdaddr(env, &tmp, address); sGattIf->scanner->StartSync( sid, tmp, skip, timeout, base::Bind(&onSyncStarted, reg_id), base::Bind(&onSyncReport), base::Bind(&onSyncLost)); } static void stopSyncNative(int sync_handle) { if (!sGattIf) return; sGattIf->scanner->StopSync(sync_handle); } static void gattTestNative(JNIEnv* env, jobject object, jint command, jlong uuid1_lsb, jlong uuid1_msb, jstring bda1, jint p1, jint p2, jint p3, jint p4, jint p5) { Loading Loading @@ -1989,6 +2074,15 @@ static JNINativeMethod sAdvertiseMethods[] = { (void*)setPeriodicAdvertisingEnableNative}, }; // JNI functions defined in PeriodicScanManager class. static JNINativeMethod sPeriodicScanMethods[] = { {"classInitNative", "()V", (void*)periodicScanClassInitNative}, {"initializeNative", "()V", (void*)periodicScanInitializeNative}, {"cleanupNative", "()V", (void*)periodicScanCleanupNative}, {"startSyncNative", "(ILjava/lang/String;III)V", (void*)startSyncNative}, {"stopSyncNative", "(I)V", (void*)stopSyncNative}, }; // JNI functions defined in ScanManager class. static JNINativeMethod sScanMethods[] = { {"registerScannerNative", "(JJ)V", (void*)registerScannerNative}, Loading Loading @@ -2100,6 +2194,9 @@ int register_com_android_bluetooth_gatt(JNIEnv* env) { register_success &= jniRegisterNativeMethods( env, "com/android/bluetooth/gatt/AdvertiseManager", sAdvertiseMethods, NELEM(sAdvertiseMethods)); register_success &= jniRegisterNativeMethods( env, "com/android/bluetooth/gatt/PeriodicScanManager", sPeriodicScanMethods, NELEM(sPeriodicScanMethods)); return register_success & jniRegisterNativeMethods(env, "com/android/bluetooth/gatt/GattService", sMethods, NELEM(sMethods)); Loading android/app/src/com/android/bluetooth/gatt/GattService.java +8 −2 Original line number Diff line number Diff line Loading @@ -142,6 +142,7 @@ public class GattService extends ProfileService { new HashMap<Integer, List<BluetoothGattService>>(); private AdvertiseManager mAdvertiseManager; private PeriodicScanManager mPeriodicScanManager; private ScanManager mScanManager; private AppOpsManager mAppOps; Loading Loading @@ -172,6 +173,9 @@ public class GattService extends ProfileService { mScanManager = new ScanManager(this); mScanManager.start(); mPeriodicScanManager = new PeriodicScanManager(AdapterService.getAdapterService()); mPeriodicScanManager.start(); return true; } Loading @@ -184,6 +188,7 @@ public class GattService extends ProfileService { mReliableQueue.clear(); if (mAdvertiseManager != null) mAdvertiseManager.cleanup(); if (mScanManager != null) mScanManager.cleanup(); if (mPeriodicScanManager != null) mPeriodicScanManager.cleanup(); return true; } Loading @@ -192,6 +197,7 @@ public class GattService extends ProfileService { cleanupNative(); if (mAdvertiseManager != null) mAdvertiseManager.cleanup(); if (mScanManager != null) mScanManager.cleanup(); if (mPeriodicScanManager != null) mPeriodicScanManager.cleanup(); return true; } Loading Loading @@ -1487,12 +1493,12 @@ public class GattService extends ProfileService { void registerSync( ScanResult scanResult, int skip, int timeout, IPeriodicAdvertisingCallback callback) { enforceAdminPermission(); // TODO(jpawlowski): implement mPeriodicScanManager.startSync(scanResult, skip, timeout, callback); } void unregisterSync(IPeriodicAdvertisingCallback callback) { enforceAdminPermission(); // TODO(jpawlowski): implement mPeriodicScanManager.stopSync(callback); } /************************************************************************** Loading android/app/src/com/android/bluetooth/gatt/PeriodicScanManager.java 0 → 100644 +230 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.bluetooth.gatt; import android.bluetooth.le.AdvertiseData; import android.bluetooth.le.AdvertisingSetParameters; import android.bluetooth.le.IPeriodicAdvertisingCallback; import android.bluetooth.le.PeriodicAdvertisingParameters; import android.bluetooth.le.PeriodicAdvertisingReport; import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; import android.os.IBinder; import android.os.IInterface; import android.os.RemoteException; import android.util.Log; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AdapterService; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Manages Bluetooth LE Periodic scans * * @hide */ class PeriodicScanManager { private static final boolean DBG = GattServiceConfig.DBG; private static final String TAG = GattServiceConfig.TAG_PREFIX + "SyncManager"; private final AdapterService mAdapterService; Map<IBinder, SyncInfo> mSyncs = Collections.synchronizedMap(new HashMap<>()); static int sTempRegistrationId = -1; /** * Constructor of {@link SyncManager}. */ PeriodicScanManager(AdapterService adapterService) { logd("advertise manager created"); mAdapterService = adapterService; } void start() { initializeNative(); } void cleanup() { logd("cleanup()"); cleanupNative(); mSyncs.clear(); sTempRegistrationId = -1; } class SyncInfo { /* When id is negative, the registration is ongoing. When the registration finishes, id * becomes equal to sync_handle */ public Integer id; public SyncDeathRecipient deathRecipient; public IPeriodicAdvertisingCallback callback; SyncInfo(Integer id, SyncDeathRecipient deathRecipient, IPeriodicAdvertisingCallback callback) { this.id = id; this.deathRecipient = deathRecipient; this.callback = callback; } } IBinder toBinder(IPeriodicAdvertisingCallback e) { return ((IInterface) e).asBinder(); } class SyncDeathRecipient implements IBinder.DeathRecipient { IPeriodicAdvertisingCallback callback; public SyncDeathRecipient(IPeriodicAdvertisingCallback callback) { this.callback = callback; } @Override public void binderDied() { if (DBG) Log.d(TAG, "Binder is dead - unregistering advertising set"); stopSync(callback); } } Map.Entry<IBinder, SyncInfo> findSync(int sync_handle) { Map.Entry<IBinder, SyncInfo> entry = null; for (Map.Entry<IBinder, SyncInfo> e : mSyncs.entrySet()) { if (e.getValue().id == sync_handle) { entry = e; break; } } return entry; } void onSyncStarted(int reg_id, int sync_handle, int sid, int address_type, String address, int phy, int interval, int status) throws Exception { logd("onSyncStarted() - reg_id=" + reg_id + ", sync_handle=" + sync_handle + ", status=" + status); Map.Entry<IBinder, SyncInfo> entry = findSync(reg_id); if (entry == null) { Log.i(TAG, "onSyncStarted() - no callback found for reg_id " + reg_id); // Sync was stopped before it was properly registered. stopSyncNative(sync_handle); return; } IPeriodicAdvertisingCallback callback = entry.getValue().callback; if (status == 0) { entry.setValue(new SyncInfo(sync_handle, entry.getValue().deathRecipient, callback)); } else { IBinder binder = entry.getKey(); binder.unlinkToDeath(entry.getValue().deathRecipient, 0); mSyncs.remove(binder); } // TODO: fix callback arguments // callback.onSyncStarted(sync_handle, tx_power, status); } void onSyncReport(int sync_handle, int tx_power, int rssi, int data_status, byte[] data) throws Exception { logd("onSyncReport() - sync_handle=" + sync_handle); Map.Entry<IBinder, SyncInfo> entry = findSync(sync_handle); if (entry == null) { Log.i(TAG, "onSyncReport() - no callback found for sync_handle " + sync_handle); return; } IPeriodicAdvertisingCallback callback = entry.getValue().callback; PeriodicAdvertisingReport report = new PeriodicAdvertisingReport( sync_handle, tx_power, rssi, data_status, ScanRecord.parseFromBytes(data)); callback.onPeriodicAdvertisingReport(report); } void onSyncLost(int sync_handle) throws Exception { logd("onSyncLost() - sync_handle=" + sync_handle); Map.Entry<IBinder, SyncInfo> entry = findSync(sync_handle); if (entry == null) { Log.i(TAG, "onSyncLost() - no callback found for sync_handle " + sync_handle); return; } IPeriodicAdvertisingCallback callback = entry.getValue().callback; mSyncs.remove(entry); callback.onSyncLost(sync_handle); } void startSync( ScanResult scanResult, int skip, int timeout, IPeriodicAdvertisingCallback callback) { SyncDeathRecipient deathRecipient = new SyncDeathRecipient(callback); IBinder binder = toBinder(callback); try { binder.linkToDeath(deathRecipient, 0); } catch (RemoteException e) { throw new IllegalArgumentException("Can't link to periodic scanner death"); } String address = scanResult.getDevice().getAddress(); int sid = scanResult.getAdvertisingSid(); int cb_id = --sTempRegistrationId; mSyncs.put(binder, new SyncInfo(cb_id, deathRecipient, callback)); logd("startSync() - reg_id=" + cb_id + ", callback: " + binder); startSyncNative(sid, address, skip, timeout, cb_id); } void stopSync(IPeriodicAdvertisingCallback callback) { IBinder binder = toBinder(callback); logd("stopSync() " + binder); SyncInfo sync = mSyncs.remove(binder); if (sync == null) { Log.e(TAG, "stopSync() - no client found for callback"); return; } Integer sync_handle = sync.id; binder.unlinkToDeath(sync.deathRecipient, 0); if (sync_handle < 0) { Log.i(TAG, "stopSync() - not finished registration yet"); // Sync will be freed once initiated in onSyncStarted() return; } stopSyncNative(sync_handle); } private void logd(String s) { if (DBG) { Log.d(TAG, s); } } static { classInitNative(); } private native static void classInitNative(); private native void initializeNative(); private native void cleanupNative(); private native void startSyncNative(int sid, String address, int skip, int timeout, int reg_id); private native void stopSyncNative(int sync_handle); } Loading
android/app/jni/com_android_bluetooth_gatt.cpp +97 −0 Original line number Diff line number Diff line Loading @@ -198,6 +198,13 @@ static jmethodID method_onPeriodicAdvertisingParametersUpdated; static jmethodID method_onPeriodicAdvertisingDataSet; static jmethodID method_onPeriodicAdvertisingEnabled; /** * Periodic scanner callback methods */ static jmethodID method_onSyncLost; static jmethodID method_onSyncReport; static jmethodID method_onSyncStarted; /** * Static variables */ Loading @@ -205,6 +212,7 @@ static jmethodID method_onPeriodicAdvertisingEnabled; static const btgatt_interface_t* sGattIf = NULL; static jobject mCallbacksObj = NULL; static jobject mAdvertiseCallbacksObj = NULL; static jobject mPeriodicScanCallbacksObj = NULL; /** * BTA client callbacks Loading Loading @@ -1938,6 +1946,83 @@ static void setPeriodicAdvertisingEnableNative(JNIEnv* env, jobject object, base::Bind(&enablePeriodicSetCb, advertiser_id, enable)); } static void periodicScanClassInitNative(JNIEnv* env, jclass clazz) { method_onSyncStarted = env->GetMethodID(clazz, "onSyncStarted", "(IIIILjava/lang/String;III)V"); method_onSyncReport = env->GetMethodID(clazz, "onSyncReport", "(IIII[B)V"); method_onSyncLost = env->GetMethodID(clazz, "onSyncLost", "(I)V"); } static void periodicScanInitializeNative(JNIEnv* env, jobject object) { if (mPeriodicScanCallbacksObj != NULL) { ALOGW("Cleaning up periodic scan callback object"); env->DeleteGlobalRef(mPeriodicScanCallbacksObj); mPeriodicScanCallbacksObj = NULL; } mPeriodicScanCallbacksObj = env->NewGlobalRef(object); } static void periodicScanCleanupNative(JNIEnv* env, jobject object) { if (mPeriodicScanCallbacksObj != NULL) { env->DeleteGlobalRef(mPeriodicScanCallbacksObj); mPeriodicScanCallbacksObj = NULL; } } static void onSyncStarted(int reg_id, uint8_t status, uint16_t sync_handle, uint8_t sid, uint8_t address_type, bt_bdaddr_t address, uint8_t phy, uint16_t interval) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; sCallbackEnv->CallVoidMethod(mPeriodicScanCallbacksObj, method_onSyncStarted, reg_id, sync_handle, sid, address_type, address, phy, interval, status); } static void onSyncReport(uint16_t sync_handle, int8_t tx_power, int8_t rssi, uint8_t data_status, std::vector<uint8_t> data) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; ScopedLocalRef<jbyteArray> jb(sCallbackEnv.get(), sCallbackEnv->NewByteArray(data.size())); sCallbackEnv->SetByteArrayRegion(jb.get(), 0, data.size(), (jbyte*)data.data()); sCallbackEnv->CallVoidMethod(mPeriodicScanCallbacksObj, method_onSyncReport, sync_handle, tx_power, rssi, data_status, jb.get()); } static void onSyncLost(uint16_t sync_handle) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; sCallbackEnv->CallVoidMethod(mPeriodicScanCallbacksObj, method_onSyncLost, sync_handle); } static void startSyncNative(JNIEnv* env, jobject object, jint sid, jstring address, jint skip, jint timeout, jint reg_id) { if (!sGattIf) return; bt_bdaddr_t tmp; jstr2bdaddr(env, &tmp, address); sGattIf->scanner->StartSync( sid, tmp, skip, timeout, base::Bind(&onSyncStarted, reg_id), base::Bind(&onSyncReport), base::Bind(&onSyncLost)); } static void stopSyncNative(int sync_handle) { if (!sGattIf) return; sGattIf->scanner->StopSync(sync_handle); } static void gattTestNative(JNIEnv* env, jobject object, jint command, jlong uuid1_lsb, jlong uuid1_msb, jstring bda1, jint p1, jint p2, jint p3, jint p4, jint p5) { Loading Loading @@ -1989,6 +2074,15 @@ static JNINativeMethod sAdvertiseMethods[] = { (void*)setPeriodicAdvertisingEnableNative}, }; // JNI functions defined in PeriodicScanManager class. static JNINativeMethod sPeriodicScanMethods[] = { {"classInitNative", "()V", (void*)periodicScanClassInitNative}, {"initializeNative", "()V", (void*)periodicScanInitializeNative}, {"cleanupNative", "()V", (void*)periodicScanCleanupNative}, {"startSyncNative", "(ILjava/lang/String;III)V", (void*)startSyncNative}, {"stopSyncNative", "(I)V", (void*)stopSyncNative}, }; // JNI functions defined in ScanManager class. static JNINativeMethod sScanMethods[] = { {"registerScannerNative", "(JJ)V", (void*)registerScannerNative}, Loading Loading @@ -2100,6 +2194,9 @@ int register_com_android_bluetooth_gatt(JNIEnv* env) { register_success &= jniRegisterNativeMethods( env, "com/android/bluetooth/gatt/AdvertiseManager", sAdvertiseMethods, NELEM(sAdvertiseMethods)); register_success &= jniRegisterNativeMethods( env, "com/android/bluetooth/gatt/PeriodicScanManager", sPeriodicScanMethods, NELEM(sPeriodicScanMethods)); return register_success & jniRegisterNativeMethods(env, "com/android/bluetooth/gatt/GattService", sMethods, NELEM(sMethods)); Loading
android/app/src/com/android/bluetooth/gatt/GattService.java +8 −2 Original line number Diff line number Diff line Loading @@ -142,6 +142,7 @@ public class GattService extends ProfileService { new HashMap<Integer, List<BluetoothGattService>>(); private AdvertiseManager mAdvertiseManager; private PeriodicScanManager mPeriodicScanManager; private ScanManager mScanManager; private AppOpsManager mAppOps; Loading Loading @@ -172,6 +173,9 @@ public class GattService extends ProfileService { mScanManager = new ScanManager(this); mScanManager.start(); mPeriodicScanManager = new PeriodicScanManager(AdapterService.getAdapterService()); mPeriodicScanManager.start(); return true; } Loading @@ -184,6 +188,7 @@ public class GattService extends ProfileService { mReliableQueue.clear(); if (mAdvertiseManager != null) mAdvertiseManager.cleanup(); if (mScanManager != null) mScanManager.cleanup(); if (mPeriodicScanManager != null) mPeriodicScanManager.cleanup(); return true; } Loading @@ -192,6 +197,7 @@ public class GattService extends ProfileService { cleanupNative(); if (mAdvertiseManager != null) mAdvertiseManager.cleanup(); if (mScanManager != null) mScanManager.cleanup(); if (mPeriodicScanManager != null) mPeriodicScanManager.cleanup(); return true; } Loading Loading @@ -1487,12 +1493,12 @@ public class GattService extends ProfileService { void registerSync( ScanResult scanResult, int skip, int timeout, IPeriodicAdvertisingCallback callback) { enforceAdminPermission(); // TODO(jpawlowski): implement mPeriodicScanManager.startSync(scanResult, skip, timeout, callback); } void unregisterSync(IPeriodicAdvertisingCallback callback) { enforceAdminPermission(); // TODO(jpawlowski): implement mPeriodicScanManager.stopSync(callback); } /************************************************************************** Loading
android/app/src/com/android/bluetooth/gatt/PeriodicScanManager.java 0 → 100644 +230 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.bluetooth.gatt; import android.bluetooth.le.AdvertiseData; import android.bluetooth.le.AdvertisingSetParameters; import android.bluetooth.le.IPeriodicAdvertisingCallback; import android.bluetooth.le.PeriodicAdvertisingParameters; import android.bluetooth.le.PeriodicAdvertisingReport; import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; import android.os.IBinder; import android.os.IInterface; import android.os.RemoteException; import android.util.Log; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AdapterService; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Manages Bluetooth LE Periodic scans * * @hide */ class PeriodicScanManager { private static final boolean DBG = GattServiceConfig.DBG; private static final String TAG = GattServiceConfig.TAG_PREFIX + "SyncManager"; private final AdapterService mAdapterService; Map<IBinder, SyncInfo> mSyncs = Collections.synchronizedMap(new HashMap<>()); static int sTempRegistrationId = -1; /** * Constructor of {@link SyncManager}. */ PeriodicScanManager(AdapterService adapterService) { logd("advertise manager created"); mAdapterService = adapterService; } void start() { initializeNative(); } void cleanup() { logd("cleanup()"); cleanupNative(); mSyncs.clear(); sTempRegistrationId = -1; } class SyncInfo { /* When id is negative, the registration is ongoing. When the registration finishes, id * becomes equal to sync_handle */ public Integer id; public SyncDeathRecipient deathRecipient; public IPeriodicAdvertisingCallback callback; SyncInfo(Integer id, SyncDeathRecipient deathRecipient, IPeriodicAdvertisingCallback callback) { this.id = id; this.deathRecipient = deathRecipient; this.callback = callback; } } IBinder toBinder(IPeriodicAdvertisingCallback e) { return ((IInterface) e).asBinder(); } class SyncDeathRecipient implements IBinder.DeathRecipient { IPeriodicAdvertisingCallback callback; public SyncDeathRecipient(IPeriodicAdvertisingCallback callback) { this.callback = callback; } @Override public void binderDied() { if (DBG) Log.d(TAG, "Binder is dead - unregistering advertising set"); stopSync(callback); } } Map.Entry<IBinder, SyncInfo> findSync(int sync_handle) { Map.Entry<IBinder, SyncInfo> entry = null; for (Map.Entry<IBinder, SyncInfo> e : mSyncs.entrySet()) { if (e.getValue().id == sync_handle) { entry = e; break; } } return entry; } void onSyncStarted(int reg_id, int sync_handle, int sid, int address_type, String address, int phy, int interval, int status) throws Exception { logd("onSyncStarted() - reg_id=" + reg_id + ", sync_handle=" + sync_handle + ", status=" + status); Map.Entry<IBinder, SyncInfo> entry = findSync(reg_id); if (entry == null) { Log.i(TAG, "onSyncStarted() - no callback found for reg_id " + reg_id); // Sync was stopped before it was properly registered. stopSyncNative(sync_handle); return; } IPeriodicAdvertisingCallback callback = entry.getValue().callback; if (status == 0) { entry.setValue(new SyncInfo(sync_handle, entry.getValue().deathRecipient, callback)); } else { IBinder binder = entry.getKey(); binder.unlinkToDeath(entry.getValue().deathRecipient, 0); mSyncs.remove(binder); } // TODO: fix callback arguments // callback.onSyncStarted(sync_handle, tx_power, status); } void onSyncReport(int sync_handle, int tx_power, int rssi, int data_status, byte[] data) throws Exception { logd("onSyncReport() - sync_handle=" + sync_handle); Map.Entry<IBinder, SyncInfo> entry = findSync(sync_handle); if (entry == null) { Log.i(TAG, "onSyncReport() - no callback found for sync_handle " + sync_handle); return; } IPeriodicAdvertisingCallback callback = entry.getValue().callback; PeriodicAdvertisingReport report = new PeriodicAdvertisingReport( sync_handle, tx_power, rssi, data_status, ScanRecord.parseFromBytes(data)); callback.onPeriodicAdvertisingReport(report); } void onSyncLost(int sync_handle) throws Exception { logd("onSyncLost() - sync_handle=" + sync_handle); Map.Entry<IBinder, SyncInfo> entry = findSync(sync_handle); if (entry == null) { Log.i(TAG, "onSyncLost() - no callback found for sync_handle " + sync_handle); return; } IPeriodicAdvertisingCallback callback = entry.getValue().callback; mSyncs.remove(entry); callback.onSyncLost(sync_handle); } void startSync( ScanResult scanResult, int skip, int timeout, IPeriodicAdvertisingCallback callback) { SyncDeathRecipient deathRecipient = new SyncDeathRecipient(callback); IBinder binder = toBinder(callback); try { binder.linkToDeath(deathRecipient, 0); } catch (RemoteException e) { throw new IllegalArgumentException("Can't link to periodic scanner death"); } String address = scanResult.getDevice().getAddress(); int sid = scanResult.getAdvertisingSid(); int cb_id = --sTempRegistrationId; mSyncs.put(binder, new SyncInfo(cb_id, deathRecipient, callback)); logd("startSync() - reg_id=" + cb_id + ", callback: " + binder); startSyncNative(sid, address, skip, timeout, cb_id); } void stopSync(IPeriodicAdvertisingCallback callback) { IBinder binder = toBinder(callback); logd("stopSync() " + binder); SyncInfo sync = mSyncs.remove(binder); if (sync == null) { Log.e(TAG, "stopSync() - no client found for callback"); return; } Integer sync_handle = sync.id; binder.unlinkToDeath(sync.deathRecipient, 0); if (sync_handle < 0) { Log.i(TAG, "stopSync() - not finished registration yet"); // Sync will be freed once initiated in onSyncStarted() return; } stopSyncNative(sync_handle); } private void logd(String s) { if (DBG) { Log.d(TAG, s); } } static { classInitNative(); } private native static void classInitNative(); private native void initializeNative(); private native void cleanupNative(); private native void startSyncNative(int sid, String address, int skip, int timeout, int reg_id); private native void stopSyncNative(int sync_handle); }