From: jihwanseo Date: Mon, 12 Dec 2016 11:25:38 +0000 (+0900) Subject: add new BLE scan through android public API (upper than API level 21) X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ac5629bb713fdd7066d889bbe73ea83286c6b0dc;p=contrib%2Fiotivity.git add new BLE scan through android public API (upper than API level 21) since previous scan api - startLeScan() was deprecated in API level 21. we should use startScan(List, ScanSettings, ScanCallback). https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html Change-Id: Ife58de26f1b132c260b94a6d0ba5c6aa17d1e7f3 Signed-off-by: jihwan.seo Reviewed-on: https://gerrit.iotivity.org/gerrit/17005 Tested-by: jenkins-iotivity Reviewed-by: Dan Mihai --- diff --git a/java/iotivity-android/src/main/java/org/iotivity/ca/CaLeClientInterface.java b/java/iotivity-android/src/main/java/org/iotivity/ca/CaLeClientInterface.java index c3f5262..2b44f99 100644 --- a/java/iotivity-android/src/main/java/org/iotivity/ca/CaLeClientInterface.java +++ b/java/iotivity-android/src/main/java/org/iotivity/ca/CaLeClientInterface.java @@ -38,6 +38,16 @@ import android.content.Intent; import android.content.IntentFilter; import android.util.Log; +// For using bluetooth.le APIs +import android.bluetooth.le.BluetoothLeScanner; +import android.bluetooth.le.ScanCallback; +import android.bluetooth.le.ScanFilter; +import android.bluetooth.le.ScanResult; +import android.bluetooth.le.ScanSettings; +import android.os.ParcelUuid; +import java.util.Iterator; +import android.os.Build; + public class CaLeClientInterface { private static String SERVICE_UUID = "ADE3D529-C784-4F63-A987-EB69F70EE816"; @@ -46,7 +56,7 @@ public class CaLeClientInterface { private static volatile boolean isLeClientInitialized = false; private CaLeClientInterface(Context context) { - caLeRegisterLeScanCallback(mLeScanCallback); + getLeScanCallback(); caLeRegisterGattCallback(mGattCallback); synchronized(CaLeClientInterface.class) { mContext = context; @@ -57,10 +67,12 @@ public class CaLeClientInterface { } } - - public static void getLeScanCallback() { - caLeRegisterLeScanCallback(mLeScanCallback); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + caLeRegisterLeScanCallbackForV21(mLeScanCallbackForV21); + } else { + caLeRegisterLeScanCallback(mLeScanCallback); + } } public static void getLeGattCallback() { @@ -82,13 +94,20 @@ public class CaLeClientInterface { } } + // register scan callback instance into le adapter. private native static void caLeRegisterLeScanCallback(BluetoothAdapter.LeScanCallback callback); + // register scan callback instance for level 21 into le adapter. + private native static void caLeRegisterLeScanCallbackForV21(ScanCallback callback); + private native static void caLeRegisterGattCallback(BluetoothGattCallback callback); // BluetoothAdapter.LeScanCallback private native static void caLeScanCallback(BluetoothDevice device); + // scan failed callback for ca layer + private native static void caLeScanFailedCallback(int errorCode); + // BluetoothGattCallback private native static void caLeGattConnectionStateChangeCallback( BluetoothGatt gatt, int status, int newState); @@ -142,29 +161,58 @@ public class CaLeClientInterface { private native static void caLeGattMtuChangedCallback(BluetoothGatt gatt, int mtu, int status); - // Callback + // Le Scan Callback which lower than API 21 private static BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { + filteringScanResult(device, scanRecord); + } + }; - try { - List uuids = getUuids(scanRecord); - for (UUID uuid : uuids) { - Log.d(TAG, "UUID : " + uuid.toString()); - if(uuid.toString().contains(SERVICE_UUID.toLowerCase())) { - Log.d(TAG, "we found that has the Device"); - Log.d(TAG, "scanned device address : " + device.getAddress()); - caLeScanCallback(device); - } - } - } catch(UnsatisfiedLinkError e) { + // Le Scan Callback which upper than API 21 + private static ScanCallback mLeScanCallbackForV21 = new ScanCallback() { + @Override + public void onScanResult(int callbackType, ScanResult result) { + super.onScanResult(callbackType, result); + Log.d(TAG, "onScanResult from ScanCallback"); + filteringScanResult(result.getDevice(), result.getScanRecord().getBytes()); + } + @Override + public void onBatchScanResults(List results) { + super.onBatchScanResults(results); + Iterator itr = results.iterator(); + while (itr.hasNext()) { + filteringScanResult(itr.next().getDevice(), + itr.next().getScanRecord().getBytes()); } } + + @Override + public void onScanFailed(int errorCode) { + super.onScanFailed(errorCode); + caLeScanFailedCallback(errorCode); + } }; + private static void filteringScanResult(BluetoothDevice device, byte[] scanRecord) { + try { + List uuids = getUuids(scanRecord); + for (UUID uuid : uuids) { + Log.d(TAG, "UUID : " + uuid.toString()); + if(uuid.toString().contains(SERVICE_UUID.toLowerCase())) { + Log.d(TAG, "we found that has the Device"); + Log.d(TAG, "scanned device address : " + device.getAddress()); + caLeScanCallback(device); + } + } + } catch(UnsatisfiedLinkError e) { + e.printStackTrace(); + } + } + private static List getUuids(final byte[] scanRecord) { List uuids = new ArrayList(); diff --git a/resource/csdk/connectivity/src/bt_le_adapter/android/caleclient.c b/resource/csdk/connectivity/src/bt_le_adapter/android/caleclient.c index 56cc314..95860df 100644 --- a/resource/csdk/connectivity/src/bt_le_adapter/android/caleclient.c +++ b/resource/csdk/connectivity/src/bt_le_adapter/android/caleclient.c @@ -55,6 +55,9 @@ #define GATT_REQUEST_NOT_SUPPORTED 6 #define GATT_WRITE_NOT_PERMITTED 3 +#define BLE_SCAN_API_LEVEL (21) +#define BLE_MIN_API_LEVEL (18) + static ca_thread_pool_t g_threadPoolHandle = NULL; JavaVM *g_jvm; @@ -109,8 +112,10 @@ static CALEScanState_t g_curScanningStep = BLE_SCAN_NONE; static CALEScanState_t g_nextScanningStep = BLE_SCAN_ENABLE; static CABLEDataReceivedCallback g_CABLEClientDataReceivedCallback = NULL; +static int32_t g_jniIntSdk = -1; static bool g_setHighQoS = true; +static bool g_setFullScanFlag = true; /** * check if retry logic for connection routine has to be stopped or not. @@ -420,8 +425,8 @@ CAResult_t CALEClientInitialize() isAttached = true; } - CAResult_t ret = CALECheckPlatformVersion(env, 18); - if (CA_STATUS_OK != ret) + g_jniIntSdk = CALEGetBuildVersion(env); + if (g_jniIntSdk < BLE_MIN_API_LEVEL) { OIC_LOG(ERROR, TAG, "it is not supported"); @@ -429,11 +434,10 @@ CAResult_t CALEClientInitialize() { (*g_jvm)->DetachCurrentThread(g_jvm); } - - return ret; + return CA_STATUS_FAILED; } - ret = CALEClientInitGattMutexVaraibles(); + CAResult_t ret = CALEClientInitGattMutexVaraibles(); if (CA_STATUS_OK != ret) { OIC_LOG(ERROR, TAG, "CALEClientInitGattMutexVaraibles has failed!"); @@ -1362,11 +1366,31 @@ CAResult_t CALEClientStartScan() // scan gatt server with UUID if (g_leScanCallback && g_uuidList) { -#ifdef UUID_SCAN - ret = CALEClientStartScanWithUUIDImpl(env, g_uuidList, g_leScanCallback); -#else - ret = CALEClientStartScanImpl(env, g_leScanCallback); -#endif + if (g_jniIntSdk >= BLE_SCAN_API_LEVEL) + { + if (!g_setFullScanFlag) + { + //new uuid scan with callback + ret = CALEClientStartScanWithUUIDImplForV21(env, g_uuidList, g_leScanCallback); + } + else + { + //new full scan with callback + ret = CALEClientStartScanImplForV21(env, g_leScanCallback); + } + } + else + { + if (!g_setFullScanFlag) + { + ret = CALEClientStartScanWithUUIDImpl(env, g_uuidList, g_leScanCallback); + } + else + { + ret = CALEClientStartScanImpl(env, g_leScanCallback); + } + } + if (CA_STATUS_OK != ret) { if (CA_ADAPTER_NOT_ENABLED == ret) @@ -1390,6 +1414,7 @@ CAResult_t CALEClientStartScan() CAResult_t CALEClientStartScanImpl(JNIEnv *env, jobject callback) { + OIC_LOG(DEBUG, TAG, "CALEClientStartScanImpl IN"); VERIFY_NON_NULL(callback, TAG, "callback is null"); VERIFY_NON_NULL(env, TAG, "env is null"); @@ -1462,8 +1487,113 @@ CAResult_t CALEClientStartScanImpl(JNIEnv *env, jobject callback) return CA_STATUS_OK; } +CAResult_t CALEClientStartScanImplForV21(JNIEnv *env, jobject callback) +{ + OIC_LOG(DEBUG, TAG, "CALEClientStartScanImplForV21 IN"); + VERIFY_NON_NULL(callback, TAG, "callback is null"); + VERIFY_NON_NULL(env, TAG, "env is null"); + + if (!CALEIsEnableBTAdapter(env)) + { + OIC_LOG(INFO, TAG, "BT adapter is not enabled"); + return CA_ADAPTER_NOT_ENABLED; + } + + CAResult_t res = CA_STATUS_FAILED; + // get default bt adapter class + jclass jni_cid_BTAdapter = (*env)->FindClass(env, CLASSPATH_BT_ADAPTER); + if (!jni_cid_BTAdapter) + { + OIC_LOG(ERROR, TAG, "getState From BTAdapter: jni_cid_BTAdapter is null"); + CACheckJNIException(env); + return CA_STATUS_FAILED; + } + + jmethodID jni_mid_getDefaultAdapter = (*env)->GetStaticMethodID(env, jni_cid_BTAdapter, + "getDefaultAdapter", + "()Landroid/bluetooth/" + "BluetoothAdapter;"); + if (!jni_mid_getDefaultAdapter) + { + OIC_LOG(ERROR, TAG, "jni_mid_getDefaultAdapter is null"); + (*env)->DeleteLocalRef(env, jni_cid_BTAdapter); + return CA_STATUS_FAILED; + } + + jobject jni_obj_BTAdapter = (*env)->CallStaticObjectMethod(env, jni_cid_BTAdapter, + jni_mid_getDefaultAdapter); + if (!jni_obj_BTAdapter) + { + OIC_LOG(ERROR, TAG, "jni_obj_BTAdapter is null"); + (*env)->DeleteLocalRef(env, jni_cid_BTAdapter); + return CA_STATUS_FAILED; + } + + // get bluetoothLeScanner class + jclass jni_cid_leScanner = (*env)->FindClass(env, CLASSPATH_LE_SCANNER); + if (!jni_cid_leScanner) + { + OIC_LOG(ERROR, TAG, "getState From leScanner: jni_cid_leScanner is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_cid_BTAdapter); + (*env)->DeleteLocalRef(env, jni_obj_BTAdapter); + return CA_STATUS_FAILED; + } + + // get remote bt adapter method + jmethodID jni_mid_getBluetoothLeScanner = (*env)->GetMethodID(env, jni_cid_BTAdapter, + "getBluetoothLeScanner", + "()Landroid/bluetooth/" + "le/BluetoothLeScanner;"); + if (!jni_mid_getBluetoothLeScanner) + { + OIC_LOG(ERROR, TAG, "jni_mid_getBluetoothLeScanner is null"); + CACheckJNIException(env); + goto error_exit; + } + + // get startScan(ScanCallback callback) method + jmethodID jni_mid_startScan = (*env)->GetMethodID(env, jni_cid_leScanner, "startScan", + "(Landroid/bluetooth/le/ScanCallback;)V"); + if (!jni_mid_startScan) + { + OIC_LOG(ERROR, TAG, "startScan: jni_mid_startScan is null"); + CACheckJNIException(env); + goto error_exit; + } + + // gat le scanner object + jobject jni_obj_leScanner = (*env)->CallObjectMethod(env, jni_obj_BTAdapter, + jni_mid_getBluetoothLeScanner); + if (!jni_obj_leScanner) + { + OIC_LOG(ERROR, TAG, "getState From BTAdapter: jni_obj_leScanner is null"); + CACheckJNIException(env); + goto error_exit; + } + + // call startScan method + OIC_LOG(INFO, TAG, "CALL API - startScan(for level21)"); + (*env)->CallVoidMethod(env, jni_obj_leScanner, jni_mid_startScan, callback); + if (CACheckJNIException(env)) + { + OIC_LOG(INFO, TAG, "startScan has failed"); + (*env)->DeleteLocalRef(env, jni_obj_leScanner); + goto error_exit; + } + res = CA_STATUS_OK; + (*env)->DeleteLocalRef(env, jni_obj_leScanner); + +error_exit: + (*env)->DeleteLocalRef(env, jni_cid_BTAdapter); + (*env)->DeleteLocalRef(env, jni_obj_BTAdapter); + (*env)->DeleteLocalRef(env, jni_cid_leScanner); + return res; +} + CAResult_t CALEClientStartScanWithUUIDImpl(JNIEnv *env, jobjectArray uuids, jobject callback) { + OIC_LOG(DEBUG, TAG, "CALEClientStartScanWithUUIDImpl IN"); VERIFY_NON_NULL(callback, TAG, "callback is null"); VERIFY_NON_NULL(uuids, TAG, "uuids is null"); VERIFY_NON_NULL(env, TAG, "env is null"); @@ -1536,6 +1666,379 @@ CAResult_t CALEClientStartScanWithUUIDImpl(JNIEnv *env, jobjectArray uuids, jobj return CA_STATUS_OK; } +CAResult_t CALEClientStartScanWithUUIDImplForV21(JNIEnv *env, jobjectArray uuids, jobject callback) +{ + OIC_LOG(DEBUG, TAG, "CALEClientStartScanWithUUIDImplForV21 IN"); + VERIFY_NON_NULL(callback, TAG, "callback is null"); + VERIFY_NON_NULL(uuids, TAG, "uuids is null"); + VERIFY_NON_NULL(env, TAG, "env is null"); + + if (!CALEIsEnableBTAdapter(env)) + { + OIC_LOG(INFO, TAG, "BT adapter is not enabled"); + return CA_ADAPTER_NOT_ENABLED; + } + + // get bluetoothLeScanner class + jclass jni_cid_leScanner = (*env)->FindClass(env, CLASSPATH_LE_SCANNER); + if (!jni_cid_leScanner) + { + OIC_LOG(ERROR, TAG, "getState From leScanner: jni_cid_leScanner is null"); + CACheckJNIException(env); + return CA_STATUS_FAILED; + } + + // get startScan(with UUID) method + jmethodID jni_mid_startScan = (*env)->GetMethodID(env, jni_cid_leScanner, + "startScan", + "(Ljava/util/List;" + "Landroid/bluetooth/le/ScanSettings;" + "Landroid/bluetooth/le/ScanCallback;" + ")V"); + if (!jni_mid_startScan) + { + OIC_LOG(ERROR, TAG, "startScan: jni_mid_startScan is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_cid_leScanner); + return CA_STATUS_FAILED; + } + (*env)->DeleteLocalRef(env, jni_cid_leScanner); + + // get scanfilter.Builder class id + jclass jni_cid_scanfilterBuilder = (*env)->FindClass(env, + "android/bluetooth/le/" + "ScanFilter$Builder"); + if (!jni_cid_scanfilterBuilder) + { + OIC_LOG(ERROR, TAG, "scanfilter: jni_cid_scanfilterBuilder is null"); + CACheckJNIException(env); + return CA_STATUS_FAILED; + } + + // get scanfilter.Builder(ctor) method id + jmethodID jni_mid_scanfilterBuilderCtor = (*env)->GetMethodID(env, jni_cid_scanfilterBuilder, + "", "()V"); + if (!jni_mid_scanfilterBuilderCtor) + { + OIC_LOG(ERROR, TAG, "scanfilter: jni_cid_scanfilterBuilderCtor is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_cid_scanfilterBuilder); + return CA_STATUS_FAILED; + } + + // call scanfilter.Builder() + jobject jni_obj_scanfilterBuilder = (*env)->NewObject(env, jni_cid_scanfilterBuilder, + jni_mid_scanfilterBuilderCtor); + if (!jni_obj_scanfilterBuilder) + { + OIC_LOG(ERROR, TAG, "scanfilter: jni_obj_scanfilterBuilder is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_cid_scanfilterBuilder); + return CA_STATUS_FAILED; + } + + // get scanfilter.Builder.setServiceUuid method id + jmethodID jni_mid_setServiceUuid = (*env)->GetMethodID(env, jni_cid_scanfilterBuilder, + "setServiceUuid", + "(Landroid/os/ParcelUuid;)Landroid/" + "bluetooth/le/ScanFilter$Builder;"); + if (!jni_mid_setServiceUuid) + { + OIC_LOG(ERROR, TAG, "scanfilter: jni_mid_setServiceUuid is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_cid_scanfilterBuilder); + (*env)->DeleteLocalRef(env, jni_obj_scanfilterBuilder); + return CA_STATUS_FAILED; + } + + // get scanfilter.Builder.build method id + jmethodID jni_mid_build_scanfilterBuilder = (*env)->GetMethodID(env, + jni_cid_scanfilterBuilder, + "build", + "()Landroid/bluetooth/le/" + "ScanFilter;"); + if (!jni_mid_build_scanfilterBuilder) + { + OIC_LOG(ERROR, TAG, "scanfilter: jni_mid_build_scanfilterBuilder is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_cid_scanfilterBuilder); + (*env)->DeleteLocalRef(env, jni_obj_scanfilterBuilder); + return CA_STATUS_FAILED; + } + (*env)->DeleteLocalRef(env, jni_cid_scanfilterBuilder); + + // call ParcelUuid.fromSting(uuid) + jobject jni_obj_parcelUuid = CALEGetParcelUuidFromString(env, OIC_GATT_SERVICE_UUID); + if (!jni_obj_parcelUuid) + { + OIC_LOG(ERROR, TAG, "scanSettings: jni_obj_parcelUuid is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_obj_scanfilterBuilder); + return CA_STATUS_FAILED; + } + + // call setServiceUuid(uuid) + jobject jni_obj_setServiceUuid = (*env)->CallObjectMethod(env, + jni_obj_scanfilterBuilder, + jni_mid_setServiceUuid, + jni_obj_parcelUuid); + if (!jni_obj_setServiceUuid) + { + OIC_LOG(ERROR, TAG, "scanfilter: jni_obj_setServiceUuid is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_obj_scanfilterBuilder); + (*env)->DeleteLocalRef(env, jni_obj_parcelUuid); + return CA_STATUS_FAILED; + } + (*env)->DeleteLocalRef(env, jni_obj_parcelUuid); + (*env)->DeleteLocalRef(env, jni_obj_setServiceUuid); + + // call build() + jobject jni_obj_scanfilter = (*env)->CallObjectMethod(env, + jni_obj_scanfilterBuilder, + jni_mid_build_scanfilterBuilder); + if (!jni_obj_scanfilter) + { + OIC_LOG(ERROR, TAG, "scanfilter: jni_obj_scanfilter is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_obj_scanfilterBuilder); + return CA_STATUS_FAILED; + } + (*env)->DeleteLocalRef(env, jni_obj_scanfilterBuilder); + + // get scanSettings.Builder class id + jclass jni_cid_scanSettingsBuilder = (*env)->FindClass(env, + "android/bluetooth/le/" + "ScanSettings$Builder"); + if (!jni_cid_scanSettingsBuilder) + { + OIC_LOG(ERROR, TAG, "scanSettings: jni_cid_scanSettingsBuilder is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_obj_scanfilter); + return CA_STATUS_FAILED; + } + + // get scanSettings.Builder(ctor) method id + jmethodID jni_mid_scanSettingsBuilderCtor = (*env)->GetMethodID(env, jni_cid_scanSettingsBuilder, + "", "()V"); + if (!jni_mid_scanSettingsBuilderCtor) + { + OIC_LOG(ERROR, TAG, "scanSettings: jni_mid_scanSettingsBuilderCtor is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_obj_scanfilter); + (*env)->DeleteLocalRef(env, jni_cid_scanSettingsBuilder); + return CA_STATUS_FAILED; + } + + // get scanSettings.Builder.setScanMode method id + jmethodID jni_mid_setScanMode = (*env)->GetMethodID(env, jni_cid_scanSettingsBuilder, + "setScanMode", + "(I)Landroid/" + "bluetooth/le/ScanSettings$Builder;"); + if (!jni_mid_setScanMode) + { + OIC_LOG(ERROR, TAG, "scanSettings: jni_mid_setScanMode is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_obj_scanfilter); + (*env)->DeleteLocalRef(env, jni_cid_scanSettingsBuilder); + return CA_STATUS_FAILED; + } + + // get scanSettings.Builder.build method id + jmethodID jni_mid_build_scanSettings = (*env)->GetMethodID(env, + jni_cid_scanSettingsBuilder, + "build", + "()Landroid/bluetooth/le/" + "ScanSettings;"); + if (!jni_mid_build_scanSettings) + { + OIC_LOG(ERROR, TAG, "scanSettings: jni_mid_build_scanSettings is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_obj_scanfilter); + (*env)->DeleteLocalRef(env, jni_cid_scanSettingsBuilder); + return CA_STATUS_FAILED; + } + + // call scanSettings.Builder() + jobject jni_obj_scanSettingBuilder = (*env)->NewObject(env, jni_cid_scanSettingsBuilder, + jni_mid_scanSettingsBuilderCtor); + if (!jni_obj_scanSettingBuilder) + { + OIC_LOG(ERROR, TAG, "scanfilter: jni_obj_scanSettingBuilder is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_obj_scanfilter); + (*env)->DeleteLocalRef(env, jni_cid_scanSettingsBuilder); + return CA_STATUS_FAILED; + } + (*env)->DeleteLocalRef(env, jni_cid_scanSettingsBuilder); + + jclass jni_cid_arrayList = (*env)->FindClass(env, "java/util/ArrayList"); + if (!jni_cid_arrayList) + { + OIC_LOG(ERROR, TAG, "ArrayList: jni_cid_arrayList is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_obj_scanfilter); + (*env)->DeleteLocalRef(env, jni_obj_scanSettingBuilder); + return CA_STATUS_FAILED; + } + + jmethodID jni_mid_arrayListCtor = (*env)->GetMethodID(env, jni_cid_arrayList, "", "()V"); + if (!jni_mid_arrayListCtor) + { + OIC_LOG(ERROR, TAG, "ArrayList: jni_mid_arrayListCtor is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_obj_scanfilter); + (*env)->DeleteLocalRef(env, jni_cid_arrayList); + (*env)->DeleteLocalRef(env, jni_obj_scanSettingBuilder); + return CA_STATUS_FAILED; + } + + jmethodID jni_mid_arrayListAdd = (*env)->GetMethodID(env, jni_cid_arrayList, + "add", "(Ljava/lang/Object;)Z"); + if (!jni_mid_arrayListAdd) + { + OIC_LOG(ERROR, TAG, "ArrayList: jni_mid_arrayListAdd is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_obj_scanfilter); + (*env)->DeleteLocalRef(env, jni_cid_arrayList); + (*env)->DeleteLocalRef(env, jni_obj_scanSettingBuilder); + return CA_STATUS_FAILED; + } + + jobject jni_obj_filterList = (*env)->NewObject(env, jni_cid_arrayList, jni_mid_arrayListCtor); + if (!jni_obj_filterList) + { + OIC_LOG(ERROR, TAG, "ArrayList: jni_obj_filterList is null"); + (*env)->DeleteLocalRef(env, jni_obj_scanfilter); + (*env)->DeleteLocalRef(env, jni_cid_arrayList); + (*env)->DeleteLocalRef(env, jni_obj_scanSettingBuilder); + return CA_STATUS_FAILED; + } + (*env)->DeleteLocalRef(env, jni_cid_arrayList); + + jboolean jni_bool_arrayListIsAdded = (*env)->CallBooleanMethod(env, jni_obj_filterList, + jni_mid_arrayListAdd, + jni_obj_scanfilter); + if (!jni_bool_arrayListIsAdded) + { + OIC_LOG(ERROR, TAG, "ArrayList: jni_bool_arrayListIsAdded is null"); + (*env)->DeleteLocalRef(env, jni_obj_filterList); + (*env)->DeleteLocalRef(env, jni_obj_scanfilter); + (*env)->DeleteLocalRef(env, jni_obj_scanSettingBuilder); + return CA_STATUS_FAILED; + } + (*env)->DeleteLocalRef(env, jni_obj_scanfilter); + + // get ScanSettings.SCAN_MODE_BALANCED jint value + jint jni_int_scanBalancedMode = CALEGetConstantsValue(env, CLASSPATH_LE_SCANSETTINGS, + "SCAN_MODE_BALANCED"); + CACheckJNIException(env); + + // call setScanMode(SCAN_MODE_BALANCED) + jobject jni_obj_setScanMode = (*env)->CallObjectMethod(env, jni_obj_scanSettingBuilder, + jni_mid_setScanMode, + jni_int_scanBalancedMode); + if (!jni_obj_setScanMode) + { + OIC_LOG(ERROR, TAG, "scanfilter: jni_obj_setScanMode is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_obj_scanSettingBuilder); + (*env)->DeleteLocalRef(env, jni_obj_filterList); + return CA_STATUS_FAILED; + } + + // call build + jobject jni_obj_scanSettings = (*env)->CallObjectMethod(env, jni_obj_scanSettingBuilder, + jni_mid_build_scanSettings); + if (!jni_obj_scanSettings) + { + OIC_LOG(ERROR, TAG, "scanfilter: jni_obj_scanSettings is null"); + (*env)->DeleteLocalRef(env, jni_obj_scanSettingBuilder); + (*env)->DeleteLocalRef(env, jni_obj_filterList); + return CA_STATUS_FAILED; + } + (*env)->DeleteLocalRef(env, jni_obj_scanSettingBuilder); + + CAResult_t res = CA_STATUS_FAILED; + // get default bt adapter class + jclass jni_cid_BTAdapter = (*env)->FindClass(env, CLASSPATH_BT_ADAPTER); + if (!jni_cid_BTAdapter) + { + OIC_LOG(ERROR, TAG, "getState From BTAdapter: jni_cid_BTAdapter is null"); + CACheckJNIException(env); + goto error_exit; + } + + jmethodID jni_mid_getDefaultAdapter = (*env)->GetStaticMethodID(env, jni_cid_BTAdapter, + "getDefaultAdapter", + "()Landroid/bluetooth/" + "BluetoothAdapter;"); + if (!jni_mid_getDefaultAdapter) + { + OIC_LOG(ERROR, TAG, "jni_mid_getDefaultAdapter is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_cid_BTAdapter); + goto error_exit; + } + + jobject jni_obj_BTAdapter = (*env)->CallStaticObjectMethod(env, jni_cid_BTAdapter, + jni_mid_getDefaultAdapter); + if (!jni_obj_BTAdapter) + { + OIC_LOG(ERROR, TAG, "jni_obj_BTAdapter is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_cid_BTAdapter); + goto error_exit; + } + + // get remote bt adapter method + jmethodID jni_mid_getBluetoothLeScanner = (*env)->GetMethodID(env, jni_cid_BTAdapter, + "getBluetoothLeScanner", + "()Landroid/bluetooth/" + "le/BluetoothLeScanner;"); + if (!jni_mid_getBluetoothLeScanner) + { + OIC_LOG(ERROR, TAG, "jni_mid_getBluetoothLeScanner is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_cid_BTAdapter); + (*env)->DeleteLocalRef(env, jni_obj_BTAdapter); + goto error_exit; + } + + // get le scanner object + jobject jni_obj_leScanner = (*env)->CallObjectMethod(env, jni_obj_BTAdapter, + jni_mid_getBluetoothLeScanner); + if (!jni_obj_leScanner) + { + OIC_LOG(ERROR, TAG, "jni_obj_leScanner is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_cid_BTAdapter); + (*env)->DeleteLocalRef(env, jni_obj_BTAdapter); + goto error_exit; + } + (*env)->DeleteLocalRef(env, jni_cid_BTAdapter); + (*env)->DeleteLocalRef(env, jni_obj_BTAdapter); + + // call startScan method + OIC_LOG(INFO, TAG, "CALL API - startScanWithUUID(for level 21)"); + (*env)->CallVoidMethod(env, jni_obj_leScanner, jni_mid_startScan, jni_obj_filterList, + jni_obj_scanSettings, callback); + if (CACheckJNIException(env)) + { + OIC_LOG(INFO, TAG, "startScan has failed"); + } + else + { + res = CA_STATUS_OK; + } + (*env)->DeleteLocalRef(env, jni_obj_leScanner); + +error_exit: + (*env)->DeleteLocalRef(env, jni_obj_scanSettings); + (*env)->DeleteLocalRef(env, jni_obj_filterList); + return res; +} + jobject CALEClientGetUUIDObject(JNIEnv *env, const char* uuid) { VERIFY_NON_NULL_RET(uuid, TAG, "uuid is null", NULL); @@ -1597,7 +2100,17 @@ CAResult_t CALEClientStopScan() isAttached = true; } - CAResult_t ret = CALEClientStopScanImpl(env, g_leScanCallback); + CAResult_t ret = CA_STATUS_FAILED; + + if (g_jniIntSdk >= BLE_SCAN_API_LEVEL) + { + ret = CALEClientStopScanImplForV21(env, g_leScanCallback); + } + else + { + ret = CALEClientStopScanImpl(env, g_leScanCallback); + } + if (CA_STATUS_OK != ret) { if (CA_ADAPTER_NOT_ENABLED == ret) @@ -1620,7 +2133,7 @@ CAResult_t CALEClientStopScan() CAResult_t CALEClientStopScanImpl(JNIEnv *env, jobject callback) { - OIC_LOG(DEBUG, TAG, "CALEClientStopScanImpl"); + OIC_LOG(DEBUG, TAG, "CALEClientStopScanImpl IN"); VERIFY_NON_NULL(callback, TAG, "callback is null"); VERIFY_NON_NULL(env, TAG, "env is null"); @@ -1663,7 +2176,7 @@ CAResult_t CALEClientStopScanImpl(JNIEnv *env, jobject callback) return CA_STATUS_FAILED; } - // gat bt adapter object + // get bt adapter object jobject jni_obj_BTAdapter = (*env)->CallStaticObjectMethod(env, jni_cid_BTAdapter, jni_mid_getDefaultAdapter); if (!jni_obj_BTAdapter) @@ -1690,6 +2203,113 @@ CAResult_t CALEClientStopScanImpl(JNIEnv *env, jobject callback) return CA_STATUS_OK; } +CAResult_t CALEClientStopScanImplForV21(JNIEnv *env, jobject callback) +{ + OIC_LOG(DEBUG, TAG, "CALEClientStopScanImplForV21 IN"); + VERIFY_NON_NULL(callback, TAG, "callback is null"); + VERIFY_NON_NULL(env, TAG, "env is null"); + + if (!CALEIsEnableBTAdapter(env)) + { + OIC_LOG(INFO, TAG, "BT adapter is not enabled"); + return CA_ADAPTER_NOT_ENABLED; + } + + // get default bt adapter class + jclass jni_cid_BTAdapter = (*env)->FindClass(env, CLASSPATH_BT_ADAPTER); + if (!jni_cid_BTAdapter) + { + OIC_LOG(ERROR, TAG, "getState From BTAdapter: jni_cid_BTAdapter is null"); + CACheckJNIException(env); + return CA_STATUS_FAILED; + } + + jmethodID jni_mid_getDefaultAdapter = (*env)->GetStaticMethodID(env, jni_cid_BTAdapter, + "getDefaultAdapter", + "()Landroid/bluetooth/" + "BluetoothAdapter;"); + if (!jni_mid_getDefaultAdapter) + { + OIC_LOG(ERROR, TAG, "jni_mid_getDefaultAdapter is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_cid_BTAdapter); + return CA_STATUS_FAILED; + } + + jobject jni_obj_BTAdapter = (*env)->CallStaticObjectMethod(env, jni_cid_BTAdapter, + jni_mid_getDefaultAdapter); + if (!jni_obj_BTAdapter) + { + OIC_LOG(ERROR, TAG, "jni_obj_BTAdapter is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_cid_BTAdapter); + return CA_STATUS_FAILED; + } + + // get bluetoothLeScanner class + jclass jni_cid_leScanner = (*env)->FindClass(env, CLASSPATH_LE_SCANNER); + if (!jni_cid_leScanner) + { + OIC_LOG(ERROR, TAG, "getState From leScanner: jni_cid_leScanner is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_cid_BTAdapter); + (*env)->DeleteLocalRef(env, jni_obj_BTAdapter); + return CA_STATUS_FAILED; + } + + // get remote bt adapter method + jmethodID jni_mid_getBluetoothLeScanner = (*env)->GetMethodID(env, jni_cid_BTAdapter, + "getBluetoothLeScanner", + "()Landroid/bluetooth/" + "le/BluetoothLeScanner;"); + if (!jni_mid_getBluetoothLeScanner) + { + OIC_LOG(ERROR, TAG, "jni_mid_getBluetoothLeScanner is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_cid_BTAdapter); + (*env)->DeleteLocalRef(env, jni_obj_BTAdapter); + (*env)->DeleteLocalRef(env, jni_cid_leScanner); + return CA_STATUS_FAILED; + } + (*env)->DeleteLocalRef(env, jni_cid_BTAdapter); + + // get stopScan(ScanCallback callback) method + jmethodID jni_mid_stopScan = (*env)->GetMethodID(env, jni_cid_leScanner, "stopScan", + "(Landroid/bluetooth/le/ScanCallback;)V"); + if (!jni_mid_stopScan) + { + OIC_LOG(ERROR, TAG, "stopScan: jni_mid_stopScan is null"); + CACheckJNIException(env); + (*env)->DeleteLocalRef(env, jni_obj_BTAdapter); + (*env)->DeleteLocalRef(env, jni_cid_leScanner); + return CA_STATUS_FAILED; + } + (*env)->DeleteLocalRef(env, jni_cid_leScanner); + + // gat le scanner object + jobject jni_obj_leScanner = (*env)->CallObjectMethod(env, jni_obj_BTAdapter, + jni_mid_getBluetoothLeScanner); + if (!jni_obj_leScanner) + { + OIC_LOG(ERROR, TAG, "getState From BTAdapter: jni_obj_leScanner is null"); + CACheckJNIException(env); + return CA_STATUS_FAILED; + } + + // call stopScan method + OIC_LOG(INFO, TAG, "CALL API - stopScan for level 21"); + (*env)->CallVoidMethod(env, jni_obj_leScanner, jni_mid_stopScan, callback); + if (CACheckJNIException(env)) + { + OIC_LOG(INFO, TAG, "stopScan for level 21 has failed"); + (*env)->DeleteLocalRef(env, jni_obj_leScanner); + return CA_STATUS_FAILED; + } + + (*env)->DeleteLocalRef(env, jni_obj_leScanner); + return CA_STATUS_OK; +} + CAResult_t CALEClientDirectConnect(JNIEnv *env, jobject bluetoothDevice, jboolean autoconnect) { OIC_LOG(DEBUG, TAG, "CALEClientDirectConnect"); @@ -4052,6 +4672,20 @@ Java_org_iotivity_ca_CaLeClientInterface_caLeRegisterLeScanCallback(JNIEnv *env, } JNIEXPORT void JNICALL +Java_org_iotivity_ca_CaLeClientInterface_caLeRegisterLeScanCallbackForV21(JNIEnv *env, + jobject obj, + jobject callback) +{ + OIC_LOG(DEBUG, TAG, "caLeRegisterLeScanCallbackForV21"); + VERIFY_NON_NULL_VOID(env, TAG, "env is null"); + VERIFY_NON_NULL_VOID(obj, TAG, "obj is null"); + VERIFY_NON_NULL_VOID(callback, TAG, "callback is null"); + + g_leScanCallback = (*env)->NewGlobalRef(env, callback); + CACheckJNIException(env); +} + +JNIEXPORT void JNICALL Java_org_iotivity_ca_CaLeClientInterface_caLeRegisterGattCallback(JNIEnv *env, jobject obj, jobject callback) { @@ -4079,6 +4713,38 @@ Java_org_iotivity_ca_CaLeClientInterface_caLeScanCallback(JNIEnv *env, jobject o } } +JNIEXPORT void JNICALL +Java_org_iotivity_ca_CaLeClientInterface_caLeScanFailedCallback(JNIEnv *env, jobject obj, + jint errorCode) +{ + VERIFY_NON_NULL_VOID(env, TAG, "env is null"); + VERIFY_NON_NULL_VOID(obj, TAG, "obj is null"); + + switch (errorCode) + { + case 1: + OIC_LOG(ERROR, TAG, "BLE scan has failed, error is SCAN_FAILED_ALREADY_STARTED"); + break; + + case 2: + OIC_LOG(ERROR, TAG, + "BLE scan has failed, error is SCAN_FAILED_APPLICATION_REGISTRATION_FAILED"); + break; + + case 3: + OIC_LOG(ERROR, TAG, "BLE scan has failed, error is SCAN_FAILED_INTERNAL_ERROR"); + break; + + case 4: + OIC_LOG(ERROR, TAG, "BLE scan has failed, error is SCAN_FAILED_FEATURE_UNSUPPORTED"); + break; + + default: + OIC_LOG(ERROR, TAG, "BLE scan has failed with unknown error"); + break; + } +} + /* * Class: org_iotivity_ca_jar_caleinterface * Method: CALeGattConnectionStateChangeCallback diff --git a/resource/csdk/connectivity/src/bt_le_adapter/android/caleclient.h b/resource/csdk/connectivity/src/bt_le_adapter/android/caleclient.h index 5e7fdbf..e05a0d4 100644 --- a/resource/csdk/connectivity/src/bt_le_adapter/android/caleclient.h +++ b/resource/csdk/connectivity/src/bt_le_adapter/android/caleclient.h @@ -226,6 +226,14 @@ CAResult_t CALEClientStartScan(); CAResult_t CALEClientStartScanImpl(JNIEnv *env, jobject callback); /** + * start to scan whole bluetooth devices for android API level 21 (implement). + * @param[in] env JNI interface pointer. + * @param[in] callback callback to receive device object by scanning. + * @return ::CA_STATUS_OK or ERROR CODES (::CAResult_t error codes in cacommon.h). + */ +CAResult_t CALEClientStartScanImplForV21(JNIEnv *env, jobject callback); + +/** * start to scan target bluetooth devices for service uuid (implement). * @param[in] env JNI interface pointer. * @param[in] uuids target UUID. @@ -236,6 +244,16 @@ CAResult_t CALEClientStartScanWithUUIDImpl(JNIEnv *env, jobjectArray uuids, jobject callback); /** + * start to scan target bluetooth devices for service uuid for android API level 21 (implement). + * @param[in] env JNI interface pointer. + * @param[in] uuids target UUID. + * @param[in] callback callback to receive device object by scanning. + * @return ::CA_STATUS_OK or ERROR CODES (::CAResult_t error codes in cacommon.h). + */ +CAResult_t CALEClientStartScanWithUUIDImplForV21(JNIEnv *env, jobjectArray uuids, + jobject callback); + +/** * get uuid object. * @param[in] env JNI interface pointer. * @param[in] uuid uuid. @@ -258,6 +276,14 @@ CAResult_t CALEClientStopScan(); CAResult_t CALEClientStopScanImpl(JNIEnv *env, jobject callback); /** + * stop scan for android API level 21(implement). + * @param[in] env JNI interface pointer. + * @param[in] callback callback to receive device object by scanning. + * @return ::CA_STATUS_OK or ERROR CODES (::CAResult_t error codes in cacommon.h). + */ +CAResult_t CALEClientStopScanImplForV21(JNIEnv *env, jobject callback); + +/** * connect to gatt server hosted. * @param[in] env JNI interface pointer. * @param[in] bluetoothDevice bluetooth Device object. diff --git a/resource/csdk/connectivity/src/bt_le_adapter/android/caleutils.c b/resource/csdk/connectivity/src/bt_le_adapter/android/caleutils.c index 7abefa6..c694152 100644 --- a/resource/csdk/connectivity/src/bt_le_adapter/android/caleutils.c +++ b/resource/csdk/connectivity/src/bt_le_adapter/android/caleutils.c @@ -106,6 +106,50 @@ error_exit: return NULL; } +jobject CALEGetParcelUuidFromString(JNIEnv *env, const char* uuid) +{ + VERIFY_NON_NULL_RET(env, TAG, "env is null", NULL); + VERIFY_NON_NULL_RET(uuid, TAG, "uuid is null", NULL); + + jclass jni_cid_ParcelUuid = (*env)->FindClass(env, "android/os/ParcelUuid"); + if (!jni_cid_ParcelUuid) + { + OIC_LOG(ERROR, TAG, "jni_cid_ParcelUuid is not available"); + goto error_exit; + } + + jmethodID jni_mid_fromString = (*env)->GetStaticMethodID(env, jni_cid_ParcelUuid, + "fromString", + "(Ljava/lang/String;)" + "Landroid/os/ParcelUuid;"); + if (!jni_mid_fromString) + { + OIC_LOG(ERROR, TAG, "jni_mid_fromString is not available"); + goto error_exit; + } + + jstring str_uuid = (*env)->NewStringUTF(env, uuid); + if (!str_uuid) + { + OIC_LOG(ERROR, TAG, "str_uuid is not available"); + goto error_exit; + } + + jobject jni_obj_parcelUuid = (*env)->CallStaticObjectMethod(env, jni_cid_ParcelUuid, + jni_mid_fromString, + str_uuid); + if (!jni_obj_parcelUuid) + { + OIC_LOG(ERROR, TAG, "Fail to get jni uuid object"); + goto error_exit; + } + + return jni_obj_parcelUuid; +error_exit: + CACheckJNIException(env); + return NULL; +} + bool CALEIsBondedDevice(JNIEnv *env, jobject bluetoothDevice) { VERIFY_NON_NULL_RET(env, TAG, "env is null", false); diff --git a/resource/csdk/connectivity/src/bt_le_adapter/android/caleutils.h b/resource/csdk/connectivity/src/bt_le_adapter/android/caleutils.h index b7812e9..fef040f 100644 --- a/resource/csdk/connectivity/src/bt_le_adapter/android/caleutils.h +++ b/resource/csdk/connectivity/src/bt_le_adapter/android/caleutils.h @@ -52,7 +52,8 @@ static const char CLASSPATH_BT_GATT[] = "android/bluetooth/BluetoothGatt"; static const char CLASSPATH_BT_ADAPTER[] = "android/bluetooth/BluetoothAdapter"; static const char CLASSPATH_BT_DEVICE[] = "android/bluetooth/BluetoothDevice"; static const char CLASSPATH_BT_UUID[] = "java/util/UUID"; - +static const char CLASSPATH_LE_SCANNER[] = "android/bluetooth/le/BluetoothLeScanner"; +static const char CLASSPATH_LE_SCANSETTINGS[] = "android/bluetooth/le/ScanSettings"; static const char METHODID_OBJECTNONPARAM[] = "()Landroid/bluetooth/BluetoothAdapter;"; static const char METHODID_BT_DEVICE[] = "()Landroid/bluetooth/BluetoothDevice;"; @@ -95,6 +96,14 @@ jobject CALEGetUuidFromString(JNIEnv *env, const char* uuid); jobject CALEGetParcelUuid(JNIEnv *env, jobject uuid); /** + * get parcel uuid object from uuid string value. + * @param[in] env JNI interface pointer. + * @param[in] uuid uuid (const char*). + * @return parcel uuid object. + */ +jobject CALEGetParcelUuidFromString(JNIEnv *env, const char* uuid); + +/** * get address from a local device. * @param[in] env JNI interface pointer. * @return local address. diff --git a/resource/csdk/connectivity/src/bt_le_adapter/android/org_iotivity_ca_CaLeClientInterface.h b/resource/csdk/connectivity/src/bt_le_adapter/android/org_iotivity_ca_CaLeClientInterface.h index 2b9c21c..6d86fc2 100644 --- a/resource/csdk/connectivity/src/bt_le_adapter/android/org_iotivity_ca_CaLeClientInterface.h +++ b/resource/csdk/connectivity/src/bt_le_adapter/android/org_iotivity_ca_CaLeClientInterface.h @@ -39,6 +39,15 @@ Java_org_iotivity_ca_CaLeClientInterface_caLeRegisterLeScanCallback /* * Class: org_iotivity_ca_CaLeClientInterface + * Method: caLeRegisterLeScanCallback + * Signature: (Landroid/bluetooth/le/ScanCallback;)V + */ +JNIEXPORT void JNICALL +Java_org_iotivity_ca_CaLeClientInterface_caLeRegisterLeScanCallbackForV21 +(JNIEnv *, jobject, jobject); + +/* + * Class: org_iotivity_ca_caLeClientInterface * Method: caLeRegisterGattCallback * Signature: (Landroid/bluetooth/BluetoothGattCallback;)V */ @@ -57,6 +66,15 @@ Java_org_iotivity_ca_CaLeClientInterface_caLeScanCallback /* * Class: org_iotivity_ca_CaLeClientInterface + * Method: caLeScanFailedCallback + * Signature: (I)V + */ +JNIEXPORT void JNICALL +Java_org_iotivity_ca_CaLeClientInterface_caLeScanFailedCallback +(JNIEnv *, jobject, jint); + +/* + * Class: org_iotivity_ca_caLeClientInterface * Method: caLeGattConnectionStateChangeCallback * Signature: (Landroid/bluetooth/BluetoothGatt;II)V */