org.bcsphere.bluetooth.BluetoothSam42.java Source code

Java tutorial

Introduction

Here is the source code for org.bcsphere.bluetooth.BluetoothSam42.java

Source

/*
   Copyright 2013-2014, JUMA Technology
    
   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 org.bcsphere.bluetooth;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;

import org.apache.cordova.CallbackContext;
import org.apache.cordova.PluginResult;
import org.bcsphere.bluetooth.tools.Tools;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProfile.ServiceListener;
import android.content.Context;
import android.util.Log;

import com.samsung.android.sdk.bt.gatt.BluetoothGatt;
import com.samsung.android.sdk.bt.gatt.BluetoothGattAdapter;
import com.samsung.android.sdk.bt.gatt.BluetoothGattCallback;
import com.samsung.android.sdk.bt.gatt.BluetoothGattCharacteristic;
import com.samsung.android.sdk.bt.gatt.BluetoothGattDescriptor;
import com.samsung.android.sdk.bt.gatt.BluetoothGattServer;
import com.samsung.android.sdk.bt.gatt.BluetoothGattServerCallback;
import com.samsung.android.sdk.bt.gatt.BluetoothGattService;
import com.samsung.android.sdk.bt.gatt.MutableBluetoothGattCharacteristic;
import com.samsung.android.sdk.bt.gatt.MutableBluetoothGattDescriptor;
import com.samsung.android.sdk.bt.gatt.MutableBluetoothGattService;

@SuppressLint("NewApi")
public class BluetoothSam42 implements IBluetooth {

    private static final String TAG = "BluetoothSam42";
    private static int serviceNumber = -1;
    private static int addedServiceNumber = 0;
    private boolean scanning = false;
    private BluetoothGatt bluetoothGatt;
    private BluetoothAdapter bluetoothAdapter;
    private BluetoothGattServer bluetoothGattServer;
    private CallbackContext addServiceCallBack;
    private Map<String, CallbackContext> mapGetRSSICallBack;
    private Map<String, CallbackContext> mapConnectCallBack;
    private Map<String, CallbackContext> mapDisconnectCallBack;
    private Map<Object, CallbackContext> mapReadValueCallBack;
    private Map<Object, CallbackContext> mapWriteValueCallBack;
    private Map<String, CallbackContext> mapGetServicesCallBack;
    private Map<String, CallbackContext> mapAddListenerCallBack;
    private Map<Object, CallbackContext> mapSetNotificationCallBack;
    private Map<String, CallbackContext> mapGetDeviceAllDataCallBack;
    private Map<String, BluetoothGattService> mapRemoteServices;
    private Map<String, List<BluetoothGattService>> mapDeviceServices;

    @Override
    public void setContext(Context context) {
        Log.i(TAG, "setContext");
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        BluetoothGattAdapter.getProfileProxy(context, serviceListener, BluetoothGattAdapter.GATT);
        BluetoothGattAdapter.getProfileProxy(context, serviceListener, BluetoothGattAdapter.GATT_SERVER);
        while (bluetoothAdapter != null && bluetoothAdapter.isEnabled()) {
            if (bluetoothGatt != null) {
                break;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void startScan(JSONArray json, CallbackContext callbackContext) {
        Log.i(TAG, "startScan");
        if (!isInitialized(callbackContext)) {
            return;
        }
        UUID[] uuids = Tools.getUUIDs(json);
        boolean result = false;
        if (uuids == null || uuids.length < 1) {
            result = bluetoothGatt.startScan();
        } else {
            result = bluetoothGatt.startScan(uuids);
        }
        if (!result) {
            Tools.sendErrorMsg(callbackContext);
            return;
        }
        scanning = true;
        Tools.sendSuccessMsg(callbackContext);
    }

    @Override
    public void stopScan(JSONArray json, CallbackContext callbackContext) {
        Log.i(TAG, "stopScan");
        if (!isInitialized(callbackContext)) {
            return;
        }
        if (scanning) {
            bluetoothGatt.stopScan();
            scanning = false;
        }
        Tools.sendSuccessMsg(callbackContext);
    }

    @Override
    public void connect(JSONArray json, CallbackContext callbackContext) {
        Log.i(TAG, "connect");
        if (!isInitialized(callbackContext)) {
            return;
        }
        String deviceAddress = Tools.getData(json, Tools.DEVICE_ADDRESS);
        if (deviceAddress == null) {
            Tools.sendErrorMsg(callbackContext);
            return;
        }
        BluetoothDevice device = bluetoothAdapter.getRemoteDevice(deviceAddress);
        if (isConnected(device)) {
            Tools.sendSuccessMsg(callbackContext);
            return;
        }
        if (mapConnectCallBack == null) {
            mapConnectCallBack = new HashMap<String, CallbackContext>();
        }
        mapConnectCallBack.put(deviceAddress, callbackContext);
        bluetoothGatt.connect(device, false);
    }

    @Override
    public void disconnect(JSONArray json, CallbackContext callbackContext) {
        Log.i(TAG, "disconnect");
        String deviceAddress = Tools.getData(json, Tools.DEVICE_ADDRESS);
        if (deviceAddress == null) {
            Tools.sendErrorMsg(callbackContext);
            return;
        }
        BluetoothDevice device = bluetoothAdapter.getRemoteDevice(deviceAddress);
        if (!isConnected(device)) {
            Tools.sendSuccessMsg(callbackContext);
            return;
        }
        if (mapDisconnectCallBack == null) {
            mapDisconnectCallBack = new HashMap<String, CallbackContext>();
        }
        mapDisconnectCallBack.put(deviceAddress, callbackContext);
        bluetoothGatt.cancelConnection(device);
    }

    @Override
    public void getConnectedDevices(JSONArray json, CallbackContext callbackContext) {
        Log.i(TAG, "getConnectedDevices");
        if (!isInitialized(callbackContext)) {
            return;
        }
        @SuppressWarnings("unchecked")
        List<BluetoothDevice> bluetoothDevices = bluetoothGatt.getConnectedDevices();
        JSONArray jsonDevices = new JSONArray();
        for (BluetoothDevice device : bluetoothDevices) {
            JSONObject jsonDevice = new JSONObject();
            Tools.addProperty(jsonDevice, Tools.DEVICE_ADDRESS, device.getAddress());
            Tools.addProperty(jsonDevice, Tools.DEVICE_NAME, device.getName());
            jsonDevices.put(jsonDevice);
        }
        callbackContext.success(jsonDevices);
    }

    @Override
    public void getServices(JSONArray json, CallbackContext callbackContext) {
        Log.i(TAG, "getServices");
        if (!isInitialized(callbackContext)) {
            return;
        }
        String deviceAddress = Tools.getData(json, Tools.DEVICE_ADDRESS);
        if (deviceAddress == null) {
            Tools.sendErrorMsg(callbackContext);
            return;
        }
        BluetoothDevice device = bluetoothAdapter.getRemoteDevice(deviceAddress);
        if (!isConnected(device)) {
            Tools.sendErrorMsg(callbackContext);
            return;
        }
        if (mapGetServicesCallBack == null) {
            mapGetServicesCallBack = new HashMap<String, CallbackContext>();
        }
        mapGetServicesCallBack.put(deviceAddress, callbackContext);
        bluetoothGatt.discoverServices(device);
    }

    @Override
    public void getCharacteristics(JSONArray json, CallbackContext callbackContext) {
        Log.i(TAG, "getCharacteristics");
        if (!isInitialized(callbackContext)) {
            return;
        }
        String deviceAddress = Tools.getData(json, Tools.DEVICE_ADDRESS);
        String serviceIndex = Tools.getData(json, Tools.SERVICE_INDEX);
        String[] args = new String[] { deviceAddress, serviceIndex };
        if (!isNullOrEmpty(args, callbackContext)) {
            return;
        }
        BluetoothDevice device = bluetoothAdapter.getRemoteDevice(deviceAddress);
        if (!isConnected(device)) {
            Tools.sendErrorMsg(callbackContext);
            return;
        }

        if (serviceIndex == null) {
            Tools.sendErrorMsg(callbackContext);
            return;
        }
        JSONObject jsonObject = new JSONObject();
        Tools.addProperty(jsonObject, Tools.DEVICE_ADDRESS, deviceAddress);
        JSONArray characteristics = new JSONArray();
        int size = getService(deviceAddress, serviceIndex).getCharacteristics().size();
        for (int i = 0; i < size; i++) {
            BluetoothGattCharacteristic bluetoothGattCharacteristic = getCharacteristic(deviceAddress, serviceIndex,
                    String.valueOf(i));
            UUID charateristicUUID = bluetoothGattCharacteristic.getUuid();
            JSONObject characteristic = new JSONObject();
            Tools.addProperty(characteristic, Tools.CHARACTERISTIC_INDEX, i);
            Tools.addProperty(characteristic, Tools.CHARACTERISTIC_UUID, charateristicUUID);
            Tools.addProperty(characteristic, Tools.CHARACTERISTIC_NAME, Tools.lookup(charateristicUUID));
            Tools.addProperty(characteristic, Tools.CHARACTERISTIC_PROPERTY,
                    Tools.decodeProperty(bluetoothGattCharacteristic.getProperties()));
            characteristics.put(characteristic);
        }
        Tools.addProperty(jsonObject, Tools.CHARACTERISTICS, characteristics);
        callbackContext.success(jsonObject);
    }

    @Override
    public void getDescriptors(JSONArray json, CallbackContext callbackContext) {
        Log.i(TAG, "getDescriptors");
        if (!isInitialized(callbackContext)) {
            return;
        }
        String deviceAddress = Tools.getData(json, Tools.DEVICE_ADDRESS);
        String serviceIndex = Tools.getData(json, Tools.SERVICE_INDEX);
        String characteristicIndex = Tools.getData(json, Tools.CHARACTERISTIC_INDEX);
        String[] args = new String[] { deviceAddress, serviceIndex, characteristicIndex };
        if (!isNullOrEmpty(args, callbackContext)) {
            return;
        }
        BluetoothDevice device = bluetoothAdapter.getRemoteDevice(deviceAddress);
        if (!isConnected(device)) {
            Tools.sendErrorMsg(callbackContext);
            return;
        }
        JSONObject jsonObject = new JSONObject();
        Tools.addProperty(jsonObject, Tools.DEVICE_ADDRESS, deviceAddress);
        JSONArray descriptors = new JSONArray();
        @SuppressWarnings("unchecked")
        List<BluetoothGattDescriptor> listBluetoothGattDescriptors = getCharacteristic(deviceAddress, serviceIndex,
                characteristicIndex).getDescriptors();
        int length = listBluetoothGattDescriptors.size();
        for (int i = 0; i < length; i++) {
            UUID uuid = listBluetoothGattDescriptors.get(i).getUuid();
            JSONObject descriptor = new JSONObject();
            Tools.addProperty(descriptor, Tools.DESCRIPTOR_INDEX, i);
            Tools.addProperty(descriptor, Tools.DESCRIPTOR_UUID, uuid);
            Tools.addProperty(descriptor, Tools.DESCRIPTOR_NAME, Tools.lookup(uuid));
            descriptors.put(descriptor);
        }
        Tools.addProperty(jsonObject, Tools.DESCRIPTORS, descriptors);
        callbackContext.success(jsonObject);
    }

    @Override
    public void writeValue(JSONArray json, CallbackContext callbackContext) {
        Log.i(TAG, "writeValue");
        if (!isInitialized(callbackContext)) {
            return;
        }
        String deviceAddress = Tools.getData(json, Tools.DEVICE_ADDRESS);
        String serviceIndex = Tools.getData(json, Tools.SERVICE_INDEX);
        String characteristicIndex = Tools.getData(json, Tools.CHARACTERISTIC_INDEX);
        String descriptorIndex = Tools.getData(json, Tools.DESCRIPTOR_INDEX);
        String writeValue = Tools.getData(json, Tools.WRITE_VALUE);
        String[] args = new String[] { deviceAddress, serviceIndex, characteristicIndex, writeValue };
        if (!isNullOrEmpty(args, callbackContext)) {
            return;
        }
        if (descriptorIndex == null) {
            Tools.sendErrorMsg(callbackContext);
            return;
        }
        BluetoothDevice device = bluetoothAdapter.getRemoteDevice(deviceAddress);
        if (!isConnected(device)) {
            Tools.sendErrorMsg(callbackContext);
            return;
        }
        byte[] value = Tools.decodeBase64(writeValue);
        ;
        if (mapWriteValueCallBack == null) {
            mapWriteValueCallBack = new HashMap<Object, CallbackContext>();
        }
        if ("".equals(descriptorIndex)) {
            writeCharacteristic(deviceAddress, serviceIndex, characteristicIndex, value, callbackContext);
        } else {
            writeDescriptor(deviceAddress, serviceIndex, characteristicIndex, descriptorIndex, value,
                    callbackContext);
        }
    }

    @Override
    public void readValue(JSONArray json, CallbackContext callbackContext) {
        Log.i(TAG, "readValue");
        if (!isInitialized(callbackContext)) {
            return;
        }
        String deviceAddress = Tools.getData(json, Tools.DEVICE_ADDRESS);
        String serviceIndex = Tools.getData(json, Tools.SERVICE_INDEX);
        String characteristicIndex = Tools.getData(json, Tools.CHARACTERISTIC_INDEX);
        String descriptorIndex = Tools.getData(json, Tools.DESCRIPTOR_INDEX);
        String[] args = new String[] { deviceAddress, serviceIndex, characteristicIndex };
        if (!isNullOrEmpty(args, callbackContext)) {
            return;
        }
        if (descriptorIndex == null) {
            Tools.sendErrorMsg(callbackContext);
            return;
        }
        BluetoothDevice device = bluetoothAdapter.getRemoteDevice(deviceAddress);
        if (!isConnected(device)) {
            Tools.sendErrorMsg(callbackContext);
            return;
        }
        if (mapReadValueCallBack == null) {
            mapReadValueCallBack = new HashMap<Object, CallbackContext>();
        }
        if ("".equals(descriptorIndex)) {
            readCharacteristic(deviceAddress, serviceIndex, characteristicIndex, callbackContext);
        } else {
            readDescriptor(deviceAddress, serviceIndex, characteristicIndex, descriptorIndex, callbackContext);
        }
    }

    @Override
    public void setNotification(JSONArray json, CallbackContext callbackContext) {
        Log.i(TAG, "setNotification");
        if (!isInitialized(callbackContext)) {
            return;
        }
        String deviceAddress = Tools.getData(json, Tools.DEVICE_ADDRESS);
        String serviceIndex = Tools.getData(json, Tools.SERVICE_INDEX);
        String characteristicIndex = Tools.getData(json, Tools.CHARACTERISTIC_INDEX);
        String enable = Tools.getData(json, Tools.ENABLE);
        String[] args = new String[] { deviceAddress, serviceIndex, characteristicIndex, enable };
        if (!isNullOrEmpty(args, callbackContext)) {
            return;
        }
        BluetoothDevice device = bluetoothAdapter.getRemoteDevice(deviceAddress);
        if (!isConnected(device)) {
            Tools.sendErrorMsg(callbackContext);
            return;
        }
        BluetoothGattCharacteristic bluetoothGattCharacteristic = getCharacteristic(deviceAddress, serviceIndex,
                characteristicIndex);
        BluetoothGattDescriptor bluetoothGattDescriptor = bluetoothGattCharacteristic
                .getDescriptor(Tools.NOTIFICATION_UUID);
        if ("true".equalsIgnoreCase(enable)) {
            bluetoothGatt.setCharacteristicNotification(bluetoothGattCharacteristic, true);
            if (Tools.lookup(bluetoothGattCharacteristic.getProperties(),
                    BluetoothGattCharacteristic.PROPERTY_NOTIFY) != null) {
                bluetoothGattDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            } else {
                bluetoothGattDescriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
            }
        } else {
            bluetoothGatt.setCharacteristicNotification(bluetoothGattCharacteristic, false);
            bluetoothGattDescriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
        }
        if (mapSetNotificationCallBack == null) {
            mapSetNotificationCallBack = new HashMap<Object, CallbackContext>();
        }
        mapSetNotificationCallBack.put(bluetoothGattCharacteristic, callbackContext);
        boolean result = bluetoothGatt.writeDescriptor(bluetoothGattDescriptor);
        Log.i(TAG, "setNotification is " + result);
    }

    @Override
    public void getDeviceAllData(JSONArray json, CallbackContext callbackContext) {
        Log.i(TAG, "getDeviceAllData");
        if (!isInitialized(callbackContext)) {
            return;
        }
        String deviceAddress = Tools.getData(json, Tools.DEVICE_ADDRESS);
        if (deviceAddress == null) {
            Tools.sendErrorMsg(callbackContext);
            return;
        }
        BluetoothDevice device = bluetoothAdapter.getRemoteDevice(deviceAddress);
        if (!isConnected(device)) {
            Tools.sendErrorMsg(callbackContext);
            return;
        }
        if (mapDeviceServices == null) {
            mapDeviceServices = new HashMap<String, List<BluetoothGattService>>();
        }
        if (mapGetDeviceAllDataCallBack == null) {
            mapGetDeviceAllDataCallBack = new HashMap<String, CallbackContext>();
        }
        mapGetDeviceAllDataCallBack.put(deviceAddress, callbackContext);
        bluetoothGatt.discoverServices(device);
    }

    @Override
    public void addServices(JSONArray json, CallbackContext callbackContext) {
        Log.i(TAG, "addService");
        JSONObject jsonServices = Tools.getObjectFromArray(json);
        JSONArray jsonArray = Tools.getArray(json, Tools.SERVICES);
        if (mapRemoteServices == null) {
            mapRemoteServices = new HashMap<String, BluetoothGattService>();
        }
        addServiceCallBack = callbackContext;
        serviceNumber = jsonServices.length();
        for (int i = 0; i < jsonServices.length(); i++) {
            String serviceIndex = Tools.getData(jsonArray, Tools.UINQUE_ID);
            String serviceType = Tools.getData(jsonArray, Tools.SERVICE_TYPE);
            String strServiceUUID = Tools.getData(jsonArray, Tools.SERVICE_UUID);
            String[] args = new String[] { serviceIndex, serviceType, strServiceUUID };
            if (!isNullOrEmpty(args, callbackContext)) {
                return;
            }
            UUID serviceUUID = UUID.fromString(strServiceUUID);
            MutableBluetoothGattService bluetoothGattService = createService(serviceUUID, serviceType);
            JSONArray jsonCharacteristics = Tools.getArray(jsonArray, Tools.CHARACTERISTICS);
            addCharacteristics(bluetoothGattService, jsonCharacteristics, callbackContext);
            if (bluetoothGattServer.addService(bluetoothGattService)) {
                mapRemoteServices.put(serviceIndex, bluetoothGattService);
            }
        }

    }

    private void addCharacteristics(MutableBluetoothGattService bluetoothGattService, JSONArray jsonCharacteristics,
            CallbackContext callbackContext) {
        int characterLength = jsonCharacteristics.length();
        for (int j = 0; j < characterLength; j++) {
            String characteristicValueType = Tools.getData(jsonCharacteristics, j, Tools.CHARACTERISTIC_VALUE_TYPE);
            String characteristicValue = Tools.getData(jsonCharacteristics, j, Tools.CHARACTERISTIC_VALUE);
            String strCharacteristicUUID = Tools.getData(jsonCharacteristics, j, Tools.CHARACTERISTIC_UUID);
            String[] args = new String[] { characteristicValueType, characteristicValue, strCharacteristicUUID };
            if (!isNullOrEmpty(args, callbackContext)) {
                return;
            }
            UUID characteristicUUID = UUID.fromString(strCharacteristicUUID);
            int characterProperty = Tools
                    .encodeProperty(Tools.getArray(jsonCharacteristics, j, Tools.CHARACTERISTIC_PROPERTY));
            int characterPermission = Tools
                    .encodePermission(Tools.getArray(jsonCharacteristics, j, Tools.CHARACTERISTIC_PERMISSION));
            MutableBluetoothGattCharacteristic bluetoothGattCharacteristic = createCharacteristic(
                    characteristicUUID, characterProperty, characterPermission, characteristicValueType,
                    characteristicValue);
            JSONArray jsonDescriptors = Tools.getArray(jsonCharacteristics, j, Tools.DESCRIPTORS);
            addDescriptors(bluetoothGattCharacteristic, jsonDescriptors, callbackContext);
            bluetoothGattService.addCharacteristic(bluetoothGattCharacteristic);
        }
    }

    private void addDescriptors(MutableBluetoothGattCharacteristic bluetoothGattCharacteristic,
            JSONArray jsonDescriptors, CallbackContext callbackContext) {
        int descLength = jsonDescriptors.length();
        for (int k = 0; k < descLength; k++) {
            String descriptorValue = Tools.getData(jsonDescriptors, k, Tools.DESCRIPTOR_VALUE);
            String strDescriptorUUID = Tools.getData(jsonDescriptors, k, Tools.DESCRIPTOR_UUID);
            String descriptorValueType = Tools.getData(jsonDescriptors, k, Tools.DESCRIPTOR_VALUE_TYPE);
            UUID descriptorsUUID = UUID.fromString(strDescriptorUUID);
            int descriptorsPermission = Tools
                    .encodePermission(Tools.getArray(jsonDescriptors, k, Tools.DESCRIPTOR_PERMISSION));
            MutableBluetoothGattDescriptor bluetoothGattDescriptor = createDescriptor(descriptorsUUID,
                    descriptorsPermission, descriptorValueType, descriptorValue);
            bluetoothGattCharacteristic.addDescriptor(bluetoothGattDescriptor);
        }
    }

    @Override
    public void removeServices(JSONArray json, CallbackContext callbackContext) {
        Log.i(TAG, "removeServices");
        if (bluetoothGattServer == null) {
            Tools.sendErrorMsg(callbackContext);
            return;
        }
        String serviceIndex = Tools.getData(json, Tools.SERVICE_INDEX);
        if ("".equals(serviceIndex)) {
            bluetoothGattServer.clearServices();
            bluetoothGattServer = null;
            Tools.sendSuccessMsg(callbackContext);
        } else {
            bluetoothGattServer.removeService(mapRemoteServices.get(serviceIndex));
            Tools.sendSuccessMsg(callbackContext);
        }
    }

    @Override
    public void getRSSI(JSONArray json, CallbackContext callbackContext) {
        Log.i(TAG, "getRSSI");
        String deviceAddress = Tools.getData(json, Tools.DEVICE_ADDRESS);
        if (deviceAddress == null) {
            Tools.sendErrorMsg(callbackContext);
            return;
        }
        BluetoothDevice device = bluetoothAdapter.getRemoteDevice(deviceAddress);
        if (!isConnected(device)) {
            Tools.sendErrorMsg(callbackContext);
            return;
        }
        if (mapGetRSSICallBack == null) {
            mapGetRSSICallBack = new HashMap<String, CallbackContext>();
        }
        mapGetRSSICallBack.put(deviceAddress, callbackContext);
        bluetoothGatt.readRemoteRssi(device);
    }

    private MutableBluetoothGattService createService(UUID uuid, String serviceType) {
        int type = -1;
        if ("0".equals(serviceType)) {
            type = BluetoothGattService.SERVICE_TYPE_PRIMARY;
        } else {
            type = BluetoothGattService.SERVICE_TYPE_SECONDARY;
        }
        return new MutableBluetoothGattService(uuid, type);
    }

    private MutableBluetoothGattCharacteristic createCharacteristic(UUID uuid, int property, int permission,
            String valueType, String value) {
        MutableBluetoothGattCharacteristic bluetoothGattCharacteristic = new MutableBluetoothGattCharacteristic(
                uuid, property, permission);
        byte[] charValue = Tools.decodeBase64(value);
        ;
        bluetoothGattCharacteristic.setValue(charValue);
        return bluetoothGattCharacteristic;
    }

    private MutableBluetoothGattDescriptor createDescriptor(UUID uuid, int permission, String valueType,
            String value) {
        MutableBluetoothGattDescriptor bluetoothGattDescriptor = new MutableBluetoothGattDescriptor(uuid,
                permission);
        byte[] desValue = Tools.decodeBase64(value);
        bluetoothGattDescriptor.setValue(desValue);
        return bluetoothGattDescriptor;
    }

    private boolean writeCharacteristic(String deviceAddress, String serviceIndex, String characteristicIndex,
            byte[] value, CallbackContext callbackContext) {
        BluetoothGattCharacteristic bluetoothGattCharacteristic = getCharacteristic(deviceAddress, serviceIndex,
                characteristicIndex);
        mapWriteValueCallBack.put(bluetoothGattCharacteristic, callbackContext);
        bluetoothGattCharacteristic.setValue(value);
        return bluetoothGatt.writeCharacteristic(bluetoothGattCharacteristic);
    }

    private boolean writeDescriptor(String deviceAddress, String serviceIndex, String characteristicIndex,
            String descriptorIndex, byte[] value, CallbackContext callbackContext) {
        BluetoothGattDescriptor bluetoothGattDescriptor = getDescriptor(deviceAddress, serviceIndex,
                characteristicIndex, descriptorIndex);
        mapWriteValueCallBack.put(bluetoothGattDescriptor, callbackContext);
        bluetoothGattDescriptor.setValue(value);
        return bluetoothGatt.writeDescriptor(bluetoothGattDescriptor);
    }

    private boolean readCharacteristic(String deviceAddress, String serviceIndex, String characteristicIndex,
            CallbackContext callbackContext) {
        BluetoothGattCharacteristic bluetoothGattCharacteristic = getCharacteristic(deviceAddress, serviceIndex,
                characteristicIndex);
        mapReadValueCallBack.put(bluetoothGattCharacteristic, callbackContext);
        return bluetoothGatt.readCharacteristic(bluetoothGattCharacteristic);
    }

    private boolean readDescriptor(String deviceAddress, String serviceIndex, String characteristicIndex,
            String descriptorIndex, CallbackContext callbackContext) {
        BluetoothGattDescriptor bluetoothGattDescriptor = getDescriptor(deviceAddress, serviceIndex,
                characteristicIndex, descriptorIndex);
        mapReadValueCallBack.put(bluetoothGattDescriptor, callbackContext);
        return bluetoothGatt.readDescriptor(bluetoothGattDescriptor);
    }

    private BluetoothGattDescriptor getDescriptor(String deviceAddress, String serviceIndex,
            String characteristicIndex, String descriptorIndex) {
        return (BluetoothGattDescriptor) getCharacteristic(deviceAddress, serviceIndex, characteristicIndex)
                .getDescriptors().get(Integer.parseInt(descriptorIndex));
    }

    private BluetoothGattCharacteristic getCharacteristic(String deviceAddress, String serviceIndex,
            String characteristicIndex) {
        return (BluetoothGattCharacteristic) getService(deviceAddress, serviceIndex).getCharacteristics()
                .get(Integer.parseInt(characteristicIndex));
    }

    private BluetoothGattService getService(String deviceAddress, String serviceIndex) {
        return mapDeviceServices.get(deviceAddress).get(Integer.parseInt(serviceIndex));
    }

    private String getdeviceAddress(BluetoothGattService service) {
        String deviceAddress = "";
        Iterator<Entry<String, List<BluetoothGattService>>> it = mapDeviceServices.entrySet().iterator();
        while (it.hasNext()) {
            Entry<String, List<BluetoothGattService>> bluetoothEntry = it.next();
            if (bluetoothEntry.getValue().contains(service)) {
                deviceAddress = bluetoothEntry.getKey();
                break;
            }
        }
        return deviceAddress;
    }

    private String getServiceIndex(BluetoothGattService service) {
        String serviceIndex = "";
        Iterator<Entry<String, List<BluetoothGattService>>> it = mapDeviceServices.entrySet().iterator();
        while (it.hasNext()) {
            List<BluetoothGattService> bluetoothGattServices = it.next().getValue();
            int index = bluetoothGattServices.indexOf(service);
            if (index != -1) {
                serviceIndex = String.valueOf(index);
                break;
            }
        }
        return serviceIndex;
    }

    private String getCharacteristicIndex(BluetoothGattService service,
            BluetoothGattCharacteristic characteristic) {
        return String.valueOf(service.getCharacteristics().indexOf(characteristic));
    }

    private BluetoothProfile.ServiceListener serviceListener = new ServiceListener() {

        @Override
        public void onServiceDisconnected(int profile) {
            if (null != bluetoothGatt) {
                bluetoothGatt.unregisterApp();
                bluetoothGatt = null;
            }
            if (null != bluetoothGattServer) {
                bluetoothGattServer.unregisterApp();
                bluetoothGattServer = null;
            }
        }

        @Override
        public void onServiceConnected(int profile, BluetoothProfile proxy) {
            if ((proxy instanceof BluetoothGatt) && bluetoothGatt == null) {
                bluetoothGatt = (BluetoothGatt) proxy;
                bluetoothGatt.registerApp(bluetoothGattCallback);
            }
            if ((proxy instanceof BluetoothGattServer) && bluetoothGattServer == null) {
                bluetoothGattServer = (BluetoothGattServer) proxy;
                bluetoothGattServer.registerApp(bluetoothGattServerCallback);
            }
        }
    };

    private BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {

        @Override
        public void onCharacteristicChanged(BluetoothGattCharacteristic characteristic) {
            Log.i(TAG, "onCharacteristicChanged");
            super.onCharacteristicChanged(characteristic);
            String deviceAddress = getdeviceAddress(characteristic.getService());
            CallbackContext callbackContext = null;
            if (mapSetNotificationCallBack != null) {
                callbackContext = mapSetNotificationCallBack.get(characteristic);
            }
            if (callbackContext != null) {
                JSONObject jsonObject = new JSONObject();
                Tools.addProperty(jsonObject, Tools.DEVICE_ADDRESS, deviceAddress);
                Tools.addProperty(jsonObject, Tools.SERVICE_INDEX, getServiceIndex(characteristic.getService()));
                Tools.addProperty(jsonObject, Tools.CHARACTERISTIC_INDEX,
                        getCharacteristicIndex(characteristic.getService(), characteristic));
                Tools.addProperty(jsonObject, Tools.VALUE, Tools.encodeBase64(characteristic.getValue()));
                Tools.addProperty(jsonObject, Tools.DATE, Tools.getDateString());
                PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, jsonObject);
                pluginResult.setKeepCallback(true);
                callbackContext.sendPluginResult(pluginResult);
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGattCharacteristic characteristic, int status) {
            Log.i(TAG, "onCharacteristicRead");
            super.onCharacteristicRead(characteristic, status);
            String deviceAddress = getdeviceAddress(characteristic.getService());
            CallbackContext callbackContext = null;
            if (mapReadValueCallBack != null) {
                callbackContext = mapReadValueCallBack.get(characteristic);
            }
            if (callbackContext != null) {
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    JSONObject jsonObject = new JSONObject();
                    Tools.addProperty(jsonObject, Tools.DEVICE_ADDRESS, deviceAddress);
                    Tools.addProperty(jsonObject, Tools.VALUE, Tools.encodeBase64(characteristic.getValue()));
                    Tools.addProperty(jsonObject, Tools.DATE, Tools.getDateString());
                    callbackContext.success(jsonObject);
                } else {
                    Tools.sendErrorMsg(callbackContext);
                }
                mapReadValueCallBack.remove(characteristic);
            }
        }

        @Override
        public void onCharacteristicWrite(BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(characteristic, status);
            Log.i(TAG, "onCharacteristicWrite");
            CallbackContext callbackContext = mapWriteValueCallBack.get(characteristic);
            if (callbackContext != null) {
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    Tools.sendSuccessMsg(callbackContext);
                } else {
                    Tools.sendErrorMsg(callbackContext);
                }
                mapWriteValueCallBack.remove(characteristic);
            }
        }

        @Override
        public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
            super.onConnectionStateChange(device, status, newState);
            Log.i(TAG, "onConnectionStateChange");
            String deviceAddress = device.getAddress();
            CallbackContext connectCallbackContext = null;
            CallbackContext disConnectCallbackContext = null;
            JSONObject jsonObject = new JSONObject();
            if (mapConnectCallBack != null) {
                connectCallbackContext = mapConnectCallBack.get(deviceAddress);
            }
            if (mapDisconnectCallBack != null) {
                disConnectCallbackContext = mapDisconnectCallBack.get(deviceAddress);
            }
            if (disConnectCallbackContext == null && mapAddListenerCallBack != null) {
                disConnectCallbackContext = mapAddListenerCallBack.get(Tools.DISCONNECT);
            }
            if (connectCallbackContext != null) {
                if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) {
                    Tools.addProperty(jsonObject, Tools.DEVICE_ADDRESS, deviceAddress);
                    Tools.addProperty(jsonObject, Tools.MES, Tools.SUCCESS);
                    connectCallbackContext.success(jsonObject);
                } else {
                    Tools.sendErrorMsg(connectCallbackContext);
                }
                mapConnectCallBack.remove(deviceAddress);
                return;
            }
            if (disConnectCallbackContext != null) {
                Log.i(TAG, "device: " + deviceAddress + " disconnect!");
                if (newState == BluetoothProfile.STATE_DISCONNECTED && status == BluetoothGatt.GATT_SUCCESS) {
                    Tools.addProperty(jsonObject, Tools.DEVICE_ADDRESS, deviceAddress);
                    Tools.addProperty(jsonObject, Tools.MES, Tools.SUCCESS);
                    PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, jsonObject);
                    pluginResult.setKeepCallback(true);
                    disConnectCallbackContext.sendPluginResult(pluginResult);
                } else {
                    Tools.sendErrorMsg(disConnectCallbackContext);
                }
                mapDisconnectCallBack.remove(deviceAddress);
            }
        }

        @Override
        public void onReadRemoteRssi(BluetoothDevice device, int rssi, int status) {
            super.onReadRemoteRssi(device, rssi, status);
            Log.i(TAG, "onReadRemoteRssi");
            String deviceAddress = device.getAddress();
            CallbackContext callbackContext = null;
            if (mapGetRSSICallBack != null) {
                callbackContext = mapGetRSSICallBack.get(deviceAddress);
            }
            if (callbackContext != null) {
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    JSONObject jsonObject = new JSONObject();
                    Tools.addProperty(jsonObject, Tools.DEVICE_ADDRESS, deviceAddress);
                    Tools.addProperty(jsonObject, Tools.RSSI, Integer.toString(rssi));
                    callbackContext.success(jsonObject);
                } else {
                    Tools.sendErrorMsg(callbackContext);
                }
                mapGetRSSICallBack.remove(deviceAddress);
            }
        }

        @Override
        public void onDescriptorRead(BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorRead(descriptor, status);
            Log.i(TAG, "onDescriptorRead");
            String deviceAddress = getdeviceAddress(descriptor.getCharacteristic().getService());
            CallbackContext callbackContext = mapReadValueCallBack.get(descriptor);
            if (callbackContext != null) {
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    JSONObject jsonObject = new JSONObject();
                    Tools.addProperty(jsonObject, Tools.DEVICE_ADDRESS, deviceAddress);
                    Tools.addProperty(jsonObject, Tools.VALUE, Tools.encodeBase64(descriptor.getValue()));
                    Tools.addProperty(jsonObject, Tools.DATE, Tools.getDateString());
                    callbackContext.success(jsonObject);
                } else {
                    Tools.sendErrorMsg(callbackContext);
                }
                mapReadValueCallBack.remove(descriptor);
            }
        }

        @Override
        public void onDescriptorWrite(BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorWrite(descriptor, status);
            Log.i(TAG, "onDescriptorWrite");
            CallbackContext writeValueCallbackContext = null;
            if (mapWriteValueCallBack != null) {
                writeValueCallbackContext = mapWriteValueCallBack.get(descriptor);
            }
            if (writeValueCallbackContext != null) {
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    Tools.sendSuccessMsg(writeValueCallbackContext);
                } else {
                    Tools.sendErrorMsg(writeValueCallbackContext);
                }
                mapWriteValueCallBack.remove(descriptor);
            }
            CallbackContext setNotificationCallbackContext = null;
            if (mapSetNotificationCallBack != null) {
                setNotificationCallbackContext = mapSetNotificationCallBack.get(descriptor.getCharacteristic());
            }

            if (setNotificationCallbackContext != null
                    && (descriptor.getValue() == BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE)) {
                JSONObject jsonObject = new JSONObject();
                Tools.addProperty(jsonObject, Tools.MES, Tools.SUCCESS);
                PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, jsonObject);
                pluginResult.setKeepCallback(true);
                setNotificationCallbackContext.sendPluginResult(pluginResult);
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothDevice device, int status) {
            super.onServicesDiscovered(device, status);
            Log.i(TAG, "onServicesDiscovered");
            String deviceAddress = device.getAddress();
            @SuppressWarnings("unchecked")
            List<BluetoothGattService> listServices = bluetoothGatt.getServices(device);
            JSONObject jsonObject = new JSONObject();
            Tools.addProperty(jsonObject, Tools.DEVICE_ADDRESS, deviceAddress);
            JSONArray services = new JSONArray();
            int index = 0;
            for (Iterator<BluetoothGattService> it = listServices.iterator(); it.hasNext();) {
                BluetoothGattService bluetoothGattService = it.next();
                UUID uuid = bluetoothGattService.getUuid();
                JSONObject service = new JSONObject();
                Tools.addProperty(service, Tools.SERVICE_INDEX, index++);
                Tools.addProperty(service, Tools.SERVICE_UUID, uuid);
                Tools.addProperty(service, Tools.SERVICE_NAME, Tools.lookup(uuid));
                services.put(service);
            }
            Tools.addProperty(jsonObject, Tools.SERVICES, services);
            if (mapDeviceServices == null) {
                mapDeviceServices = new HashMap<String, List<BluetoothGattService>>();
            }
            mapDeviceServices.put(deviceAddress, listServices);
            CallbackContext getServicesCallback = null;
            CallbackContext getDeviceAllDataCallback = null;
            if (mapGetServicesCallBack != null) {
                getServicesCallback = mapGetServicesCallBack.get(deviceAddress);
            }
            if (mapGetDeviceAllDataCallBack != null) {
                getDeviceAllDataCallback = mapGetDeviceAllDataCallBack.get(deviceAddress);
            }
            int serviceLength = listServices.size();
            if (getServicesCallback != null) {
                getServicesCallback.success(jsonObject);
                mapGetServicesCallBack.remove(deviceAddress);
            }
            if (getDeviceAllDataCallback != null) {
                JSONArray deviceAllData = null;
                try {
                    deviceAllData = jsonObject.getJSONArray(Tools.SERVICES);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                for (int i = 0; i < serviceLength; i++) {
                    JSONObject service = null;
                    try {
                        service = (JSONObject) deviceAllData.get(i);
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                    JSONArray characteristics = new JSONArray();
                    @SuppressWarnings("unchecked")
                    List<BluetoothGattCharacteristic> bluetoothGattCharacteristics = getService(deviceAddress,
                            Integer.toString(i)).getCharacteristics();
                    int charaLength = bluetoothGattCharacteristics.size();
                    for (int j = 0; j < charaLength; j++) {
                        JSONObject characteristic = new JSONObject();
                        BluetoothGattCharacteristic bluetoothGattCharacteristic = bluetoothGattCharacteristics
                                .get(j);
                        UUID uuid = bluetoothGattCharacteristic.getUuid();
                        Tools.addProperty(characteristic, Tools.CHARACTERISTIC_UUID, uuid);
                        Tools.addProperty(characteristic, Tools.CHARACTERISTIC_INDEX, j);
                        Tools.addProperty(characteristic, Tools.CHARACTERISTIC_NAME, Tools.lookup(uuid));
                        Tools.addProperty(characteristic, Tools.CHARACTERISTIC_PROPERTY,
                                Tools.decodeProperty(bluetoothGattCharacteristic.getProperties()));
                        JSONArray descriptors = new JSONArray();
                        @SuppressWarnings("unchecked")
                        List<BluetoothGattDescriptor> listDescriptors = bluetoothGattCharacteristic
                                .getDescriptors();
                        for (int k = 0; k < listDescriptors.size(); k++) {
                            JSONObject descriptor = new JSONObject();
                            BluetoothGattDescriptor bluetoothGattDescriptor = listDescriptors.get(k);
                            UUID descriptorUUID = bluetoothGattDescriptor.getUuid();
                            Tools.addProperty(descriptor, Tools.DESCRIPTOR_UUID, descriptorUUID);
                            Tools.addProperty(descriptor, Tools.DESCRIPTOR_INDEX, k);
                            Tools.addProperty(descriptor, Tools.DESCRIPTOR_NAME, Tools.lookup(descriptorUUID));
                            descriptors.put(descriptor);
                        }
                        Tools.addProperty(characteristic, Tools.DESCRIPTORS, descriptors);
                        characteristics.put(characteristic);
                    }
                    Tools.addProperty(service, Tools.CHARACTERISTICS, characteristics);
                }
                Tools.addProperty(jsonObject, Tools.SERVICES, deviceAllData);
                getDeviceAllDataCallback.success(jsonObject);
                mapGetDeviceAllDataCallBack.remove(deviceAddress);
            }
        }

        @Override
        public void onScanResult(BluetoothDevice device, int rssi, byte[] scanRecord) {
            super.onScanResult(device, rssi, scanRecord);
            Log.i(TAG, "onScanResult");
            String deviceAddress = device.getAddress();
            JSONObject jsonDevice = new JSONObject();
            Tools.addProperty(jsonDevice, Tools.DEVICE_ADDRESS, deviceAddress);
            Tools.addProperty(jsonDevice, Tools.DEVICE_NAME, device.getName());
            Tools.addProperty(jsonDevice, Tools.IS_CONNECTED, bluetoothGatt.getConnectedDevices().contains(device));
            Tools.addProperty(jsonDevice, Tools.RSSI, rssi);
            Tools.addProperty(jsonDevice, Tools.ADVERTISEMENT_DATA, Tools.decodeAdvData(scanRecord));
            Tools.addProperty(jsonDevice, Tools.TYPE, "BLE");
            PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, jsonDevice);
            pluginResult.setKeepCallback(true);
            mapAddListenerCallBack.get(Tools.NEW_ADV_PACKET).sendPluginResult(pluginResult);
        }
    };

    private BluetoothGattServerCallback bluetoothGattServerCallback = new BluetoothGattServerCallback() {

        @Override
        public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset,
                BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
            Log.i(TAG, "onCharacteristicReadRequest");
        }

        @Override
        public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId,
                BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded,
                int offset, byte[] value) {
            super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded,
                    offset, value);
            Log.i(TAG, "onCharacteristicWriteRequest");
        }

        @Override
        public void onServiceAdded(int status, BluetoothGattService bluetoothGattService) {
            super.onServiceAdded(status, bluetoothGattService);
            Log.i(TAG, "onServiceAdded");
            if (status == BluetoothGatt.GATT_SUCCESS) {
                addedServiceNumber++;
                if (addedServiceNumber == serviceNumber) {
                    if (addServiceCallBack != null) {
                        Tools.sendSuccessMsg(addServiceCallBack);
                    }
                    addedServiceNumber = 0;
                }
            }
        }
    };

    private boolean isInitialized(CallbackContext callbackContext) {
        if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled() || bluetoothGatt == null) {
            Tools.sendErrorMsg(callbackContext);
            return false;
        }
        return true;
    }

    private boolean isConnected(BluetoothDevice device) {
        if (device == null) {
            return false;
        }
        return bluetoothGatt.getConnectedDevices().contains(device)
                && bluetoothGatt.getConnectionState(device) == BluetoothProfile.STATE_CONNECTED;
    }

    @Override
    public void addEventListener(JSONArray json, CallbackContext callbackContext) {
        String eventName = Tools.getData(json, Tools.EVENT_NAME);
        if (eventName == null) {
            Tools.sendErrorMsg(callbackContext);
            return;
        }
        if (mapAddListenerCallBack == null) {
            mapAddListenerCallBack = new HashMap<String, CallbackContext>();
        }
        mapAddListenerCallBack.put(eventName, callbackContext);
    }

    private boolean isNullOrEmpty(String[] args, CallbackContext callbackContext) {
        if (args == null) {
            Tools.sendErrorMsg(callbackContext);
            return false;
        }
        int length = args.length;
        for (int i = 0; i < length; i++) {
            String str = args[i];
            if (str == null || str.isEmpty()) {
                Tools.sendErrorMsg(callbackContext);
                return false;
            }
        }
        return true;
    }

}