简体   繁体   中英

Write BLE characteristic without discover services Android

Is it possible to write ble characteristic without previous discovering ble services? I want to implement application which connects to peripheral ble device, write characteristic and disconnect, then connects to another device and so on... After disconnection I invoke gatt.close() command to release all resources. On the first connection I create bond. If I wait for callback onServicesDiscovered and write characteristic:

BluetoothGattService mCustomService = mBluetoothGatt.getService(myUUID));
byte[] values = new byte[]{0x00, 0x01};
mWriteCharacteristic.setValue(values);
if(mBluetoothGatt.writeCharacteristic(mWriteCharacteristic) == false) {
    Log.w(TAG, "Failed to write characteristic");
}

everything works fine. But I want to improve and speed up whole process. Is there any way to skip discover services and write ble char after connection with bond ble device?

BluetoothGattCharacteristic mCharacteristic = new BluetoothGattCharacteristic(UUID.fromString(GattAttributes.P1_MINI_POWER_CHARACTERISTIC), (BluetoothGattCharacteristic.PROPERTY_WRITE |BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE | BluetoothGattCharacteristic.PROPERTY_NOTIFY | BluetoothGattCharacteristic.PROPERTY_EXTENDED_PROPS), 0);
mCustomService.addCharacteristic(mCharacteristic);
byte[] values = new byte[]{0x00, 0x01};
mWriteCharacteristic.setValue(values);
        if(mBluetoothGatt.writeCharacteristic(mWriteCharacteristic) == false) {
            Log.w(TAG, "Failed to write characteristic");
        }

But without any success.

No you must use discoverServices. You can't write the definition yourself. One of many reasons is that the BLE stack needs to know the ATT handle of the characteristic value, which is not exposed with BluetoothGattCharacteristic.

It is possible on some versions of Android although the technique requires that you access a grey listed field in BluetoothGattService using the NDK and use some reflection on the BluetoothGatt attached to your BluetoothDevice. Note that it just isn't worth it unless you have a lot of the same bluetooth devices to connect to as it doesn't save that much time - you still need to connect and do discoverServices() on the first one.

In my case I have hundreds to talk to, so caching the GATT is worthwhile. If you only have two, don't waste your time. This was developed for a particular Android device and doesn't work on (say) a Samsung Galaxy S8 although there might be ways around that too.

The steps are: 1. On first device, connect, do discoverServices(), keep a copy of the GattService and GattCharacteristics you want to cache. I have a class (GattWrapper) that has some static fields to cache the service and the characteristics.

When connecting to the 2nd and subsequent devices, check to see if you have already done discoverServices(). If you have, you can copy the GattService and GattCharacteristics and inject your new device into the newly created copies:

ble_inject.c - for setting the device on a BluetoothGattService

    #include   <jni.h>
    #include   <stdio.h>
    #include   <stdlib.h>
    #include   <unistd.h>
    #include   <sys/types.h>
    #include   <sys/stat.h>
    #include   <fcntl.h>
    #include   <termios.h>
    #include   <errno.h>
    #include   <string.h>
    #include <strings.h>
    #include   "android/log.h"

    // inject a bluetooth device into a BluetoothGattService
    JNIEXPORT jint JNICALL Java_com_foo_Utilities_BLEFix_injectdevice
            (JNIEnv *, jobject,jobject, jobject);

    // inject a bluetooth device into a BluetoothGattService
    JNIEXPORT jint JNICALL Java_au_com_smartshepherd_ss2_Utilities_BLEFix_injectservices
            (JNIEnv *, jobject,jobject, jobject);

    JNIEXPORT jint JNICALL
    Java_com_foo_Utilities_Utilities_BLEFix_injectdevice(JNIEnv *env, jobject bleFix,         
    jobject bluetoothGattService, jobject bluetoothDevice)
    {
        jclass bGattServiceClass = (*env)->GetObjectClass(env,bluetoothGattService);
        //jclass bGattServiceClass = (*env)->FindClass(env,"android/bluetooth/BluetoothGattService");
        jfieldID f_setDevice = (*env)->GetFieldID(env,bGattServiceClass,"mDevice","Landroid/bluetooth/BluetoothDevice;");
        (*env)->SetObjectField(env,bluetoothGattService,f_setDevice,bluetoothDevice);
        //env->SetObjectField(instance_CS, f_codeHeight, cs.codeHeight);
        return 0;
    }

Android.mk

     LOCAL_PATH := $(call my-dir)
     include $(CLEAR_VARS)

     TARGET_PLATFORM := android-3
     LOCAL_MODULE    := libblefix
     LOCAL_SRC_FILES := ble_inject.c
     LOCAL_LDLIBS    := -llog
     include $(BUILD_SHARED_LIBRARY)

BLEFix.java

  import android.bluetooth.BluetoothGattService;
  import android.util.Log;

  import java.util.List;

  public class BLEFix {
      private static final String TAG = "ble_fix";
      private static boolean mLoaded = false;

      public void InjectDevice(BluetoothGattService service, BluetoothDevice dev)
      {
          injectdevice(service,dev);
      }

      private native int injectdevice(Object bluetoothGattService, Object bluetoothDevice);

      static {
          try {
              System.loadLibrary("blefix");
              mLoaded = true;
          }
          catch(java.lang.UnsatisfiedLinkError e)
          {
              mLoaded = false;
              Log.e( TAG, "Failed to load blefix library" );
          }
        }
   }

Code for copying BluetoothGattService and BluetoothGattCharacteristic:

    private BluetoothGattCharacteristic         cloneGattCharacteristic(BluetoothGattCharacteristic bgc)
        {
            return new         BluetoothGattCharacteristic(bgc.getUuid(),bgc.getProperties(),bgc.getPermissions());
        }
        static Field mServicesField = null;
        // static initialiser for this field
        static
        {
            Field[] fields = BluetoothGatt.class.getDeclaredFields();
            for (Field fld : fields)
            {
                if (fld.getName() == "mServices")
                {
                    mServicesField = fld;
                    if (Modifier.isPrivate(mServicesField.getModifiers())) {
                        mServicesField.setAccessible(true);
                    }
                }
            }
        }
        private static BluetoothGattService mGattService;
        private static BluetoothGatt mBluetoothGatt; // set this after onConnected()
        private boolean copyCharacteristics()
        {

            mGattService = new         BluetoothGattService(sGattService.getUuid(),BluetoothGattService.SERVICE_TYPE_PRIMARY);
            // FIXME extremely unsafe juse of JNI to inject this device into already discovered characteristics
            bleFix.InjectDevice(mGattService,mBluetoothGatt.getDevice());
            List<BluetoothGattService> services = mBluetoothGatt.getServices();
    if (services.isEmpty()) {
        // use reflection, the mServices value isn't hidden it's private though
        try {

            services.add(mGattService);
            if (mServicesField != null)  // if this doesn't exist you are SOL on this device, it is implementation dependent I think
            {
               mServicesField.set(mBluetoothGatt, services);
            }

        } catch (Exception ex) {
           mBluetoothGatt.discoverServices();
            return false;
         }



        // sMyGattCharacteristic - store it away in a static after the first discoverServices() is successful
        mMyGattCharacteristic = cloneGattCharacteristic(sMyGattCharacteristic);
        mGattService.addCharacteristic(mMyGattCharacteristic);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM