@Override
 public void onDescriptorWrite(
     BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
   super.onDescriptorWrite(gatt, descriptor, status);
   Log.d(TAG, "onDescriptorWrite");
   switch (status) {
     case BluetoothGatt.GATT_SUCCESS:
       getBluetoothLe()
           .getBleEventCallback()
           .onCharacteristicNotificationEnabled(gatt, descriptor);
       break;
     case BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION:
     case BluetoothGatt.GATT_INSUFFICIENT_ENCRYPTION:
       // bonds automatically
       Log.w(
           TAG,
           "Failed to write descriptor because of "
               + "GATT_INSUFFICIENT_AUTHENTICATION"
               + " and "
               + "GATT_INSUFFICIENT_ENCRYPTION: "
               + descriptor.getUuid());
       getBluetoothLe()
           .getBleEventCallback()
           .onCharacteristicNotificationEnablingFail(gatt, descriptor, status);
       break;
     default:
       Log.w(TAG, "Failed to write descriptor: " + descriptor.getUuid());
       getBluetoothLe()
           .getBleEventCallback()
           .onCharacteristicNotificationEnablingFail(gatt, descriptor, status);
       break;
   }
 }
    @Override
    public final void onDescriptorWrite(
        final BluetoothGatt gatt, final BluetoothGattDescriptor descriptor, final int status) {
      if (status == BluetoothGatt.GATT_SUCCESS) {
        Logger.i(
            mLogSession,
            "Data written to descr. "
                + descriptor.getUuid()
                + ", value: "
                + ParserUtils.parse(descriptor));

        if (isServiceChangedCCCD(descriptor)) {
          Logger.a(mLogSession, "Service Changed notifications enabled");
          if (!readBatteryLevel()) nextRequest();
        } else if (isBatteryLevelCCCD(descriptor)) {
          final byte[] value = descriptor.getValue();
          if (value != null && value.length > 0 && value[0] == 0x01) {
            Logger.a(mLogSession, "Battery Level notifications enabled");
            nextRequest();
          } else Logger.a(mLogSession, "Battery Level notifications disabled");
        } else {
          nextRequest();
        }
      } else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) {
        if (gatt.getDevice().getBondState() != BluetoothDevice.BOND_NONE) {
          DebugLogger.w(TAG, ERROR_AUTH_ERROR_WHILE_BONDED);
          mCallbacks.onError(ERROR_AUTH_ERROR_WHILE_BONDED, status);
        }
      } else {
        DebugLogger.e(TAG, "onDescriptorWrite error " + status);
        onError(ERROR_WRITE_DESCRIPTOR, status);
      }
    }
 @NonNull
 @Override
 public List<UUID> getDescriptors() {
   final List<UUID> identifiers = new ArrayList<>();
   for (final BluetoothGattDescriptor descriptor : wrappedCharacteristic.getDescriptors()) {
     identifiers.add(descriptor.getUuid());
   }
   return identifiers;
 }
  public void listenToCaliperMeasurements(final BluetoothGattCharacteristic characteristic) {
    if (characteristic == null) {
      Log.e(
          TAG,
          "Haven't discovered this feature yet, please wait until it has connected once, and or try disconnecting and reconnecting.");
      return;
    }

    Log.d(TAG, "Starting to listen to notifications from " + characteristic.getUuid());
    final int charaProp = characteristic.getProperties();
    if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
      // If there is an active notification on a characteristic, clear
      // it first so it doesn't update the data field on the user interface.
      if (mNotifyCharacteristic != null) {
        mBluetoothLeService.setCharacteristicNotification(mNotifyCharacteristic, false);
        mNotifyCharacteristic = null;
      }
      mBluetoothLeService.readCharacteristic(characteristic);
    }
    if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
      mNotifyCharacteristic = characteristic;
      mBluetoothLeService.setCharacteristicNotification(characteristic, true);

      // Set descriptor to enable notifications
      BluetoothGattDescriptor descriptor =
          characteristic.getDescriptor(
              UUID.fromString(SCalEvoBluetoothSpecifications.CLIENT_CHARACTERISTIC_CONFIG));
      if (descriptor == null) {
        Log.e(
            TAG,
            "descriptor "
                + SCalEvoBluetoothSpecifications.CLIENT_CHARACTERISTIC_CONFIG
                + "was null, looking for others.");

        List<BluetoothGattDescriptor> descriptors = characteristic.getDescriptors();
        String uuid;
        for (BluetoothGattDescriptor aDescriptor : descriptors) {
          uuid = aDescriptor.getUuid().toString();
          Log.e(TAG, "BluetoothGattDescriptor " + uuid);
          descriptor = aDescriptor;
        }
        if (descriptor == null) {
          Log.e(
              TAG,
              "Couldn't find any descriptors for this characteristic, cant enable notifications");
          return;
        } else {
          Log.e(TAG, "Trying the last descriptor to enable notifications");
        }
      }
      descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
      mBluetoothLeService.writeDescriptor(descriptor);
      Toast.makeText(this, "Ready", Toast.LENGTH_SHORT).show();
      Log.e(TAG, "Ready to read from characteristic " + characteristic.getUuid());
    }
  }
  public JSONObject asJSONObject(BluetoothGatt gatt) {

    JSONObject json = asJSONObject();

    try {
      JSONArray servicesArray = new JSONArray();
      JSONArray characteristicsArray = new JSONArray();
      json.put("services", servicesArray);
      json.put("characteristics", characteristicsArray);

      if (connected && gatt != null) {
        for (BluetoothGattService service : gatt.getServices()) {
          servicesArray.put(UUIDHelper.uuidToString(service.getUuid()));

          for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {
            JSONObject characteristicsJSON = new JSONObject();
            characteristicsArray.put(characteristicsJSON);

            characteristicsJSON.put("service", UUIDHelper.uuidToString(service.getUuid()));
            characteristicsJSON.put(
                "characteristic", UUIDHelper.uuidToString(characteristic.getUuid()));
            // characteristicsJSON.put("instanceId", characteristic.getInstanceId());

            characteristicsJSON.put("properties", Helper.decodeProperties(characteristic));
            // characteristicsJSON.put("propertiesValue", characteristic.getProperties());

            if (characteristic.getPermissions() > 0) {
              characteristicsJSON.put("permissions", Helper.decodePermissions(characteristic));
              // characteristicsJSON.put("permissionsValue", characteristic.getPermissions());
            }

            JSONArray descriptorsArray = new JSONArray();

            for (BluetoothGattDescriptor descriptor : characteristic.getDescriptors()) {
              JSONObject descriptorJSON = new JSONObject();
              descriptorJSON.put("uuid", UUIDHelper.uuidToString(descriptor.getUuid()));
              descriptorJSON.put("value", descriptor.getValue()); // always blank

              if (descriptor.getPermissions() > 0) {
                descriptorJSON.put("permissions", Helper.decodePermissions(descriptor));
                // descriptorJSON.put("permissionsValue", descriptor.getPermissions());
              }
              descriptorsArray.put(descriptorJSON);
            }
            if (descriptorsArray.length() > 0) {
              characteristicsJSON.put("descriptors", descriptorsArray);
            }
          }
        }
      }
    } catch (JSONException e) { // TODO better error handling
      e.printStackTrace();
    }

    return json;
  }
  public void showServicesAndCharacteristics() {
    for (BluetoothGattService service : this.io.gatt.getServices()) {
      Log.d(TAG, "onServicesDiscovered:" + service.getUuid());

      for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {
        Log.d(TAG, "  char:" + characteristic.getUuid());

        for (BluetoothGattDescriptor descriptor : characteristic.getDescriptors()) {
          Log.d(TAG, "    descriptor:" + descriptor.getUuid());
        }
      }
    }
  }
 private void onHeartRateMeasurementCharacteristicFound(
     BluetoothGattCharacteristic characteristic) {
   Log.d();
   boolean found = false;
   for (BluetoothGattDescriptor descriptor : characteristic.getDescriptors()) {
     Log.d("descriptor=" + descriptor.getUuid());
     if (getAssignedNumber(descriptor.getUuid())
         == GATT_DESCRIPTOR_CLIENT_CHARACTERISTIC_CONFIGURATION) {
       onClientCharacteristicConfigurationDescriptorFound(characteristic, descriptor);
       found = true;
       break;
     }
   }
   if (!found) {
     onClientCharacteristicConfigurationDescriptorNotFound();
   }
 }
  private void displayGattServices(List<BluetoothGattService> gattServices) {
    if (gattServices == null) return;

    for (BluetoothGattService gattService : gattServices) {
      // -----Service的字段信息-----//
      int type = gattService.getType();
      Log.e(TAG, "-->service type:" + Utils.getServiceType(type));
      Log.e(TAG, "-->includedServices size:" + gattService.getIncludedServices().size());
      Log.e(TAG, "-->service uuid:" + gattService.getUuid());

      // -----Characteristics的字段信息-----//
      List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
      for (final BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
        Log.e(TAG, "---->char uuid:" + gattCharacteristic.getUuid());

        int permission = gattCharacteristic.getPermissions();
        Log.e(TAG, "---->char permission:" + Utils.getCharPermission(permission));

        int property = gattCharacteristic.getProperties();
        Log.e(TAG, "---->char property:" + Utils.getCharPropertie(property));

        byte[] data = gattCharacteristic.getValue();
        if (data != null && data.length > 0) {
          Log.e(TAG, "---->char value:" + Utils.bytesToHexString(data));
        }

        if (gattService.getUuid().toString().equals(UUID_KEY_SERVICE)) { // 如果是温度计的service
          // UUID_KEY_DATA是可以跟蓝牙模块串口通信的Characteristic
          if (gattCharacteristic.getUuid().toString().equals(UUID_KEY_DATA_RECIV)) {
            // 测试读取当前Characteristic数据,会触发mOnDataAvailable.onCharacteristicRead()
            mHandler.postDelayed(
                new Runnable() {
                  @Override
                  public void run() {
                    mBLE_reciv.readCharacteristic(gattCharacteristic);
                  }
                },
                500);

            // 接受Characteristic被写的通知,收到蓝牙模块的数据后会触发mOnDataAvailable.onCharacteristicWrite()
            mBLE_reciv.setCharacteristicNotification(gattCharacteristic, true);
            BluetoothGattDescriptor localBluetoothGattDescriptor =
                gattCharacteristic.getDescriptor(UUID.fromString(CCC));
            localBluetoothGattDescriptor.setValue(
                BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            mBluetoothGatt.writeDescriptor(localBluetoothGattDescriptor);
            appState.gattCharacteristic_reciv_dianzichen = gattCharacteristic;
            // 设置数据内容
            //            			gattCharacteristic.setValue("0");
            // 往蓝牙模块写入数据
            //            			appState.mBLE_reciv_dianzichen.writeCharacteristic(gattCharacteristic);
          }

          // UUID_KEY_DATA是可以跟蓝牙模块串口通信的Characteristic
          if (gattCharacteristic.getUuid().toString().equals(UUID_KEY_DATA_SEND)) {
            // 测试读取当前Characteristic数据,会触发mOnDataAvailable.onCharacteristicRead()
            mHandler.postDelayed(
                new Runnable() {
                  @Override
                  public void run() {
                    mBLE_send.readCharacteristic(gattCharacteristic);
                    // Log.i("info", "appState.mBLE_send_dianzichen 尝试读数据");
                  }
                },
                500);

            // 接受Characteristic被写的通知,收到蓝牙模块的数据后会触发mOnDataAvailable.onCharacteristicWrite()
            mBLE_send.setCharacteristicNotification(gattCharacteristic, true);
            // 设置数据内容
            // gattCharacteristic.setValue("send data->");
            // 往蓝牙模块写入数据
            // mBLE_send.writeCharacteristic(gattCharacteristic);

            if (!appState.firstActivityRunning) {
              appState.gattCharacteristic_send_dianzichen = gattCharacteristic;

              appState.file.write2SDFromInput("inurse/", "Dianzichen.txt", appState.deviceAddress);

              Intent it = new Intent(this, DianzichenActivity.class);
              startActivityForResult(it, 0); // 配合onActivityResult,当FirstActivity退出时获得一个0值,然后自己也退出
              //            				moveTaskToBack(true); //这一句是整个程序返回桌面
              appState.firstActivityRunning = true;
            }
          }
        } // 结束 if ( gattService.getUuid().toString().equals(UUID_KEY_SERVICE) )

        // -----Descriptors的字段信息-----//
        List<BluetoothGattDescriptor> gattDescriptors = gattCharacteristic.getDescriptors();
        for (BluetoothGattDescriptor gattDescriptor : gattDescriptors) {
          Log.e(TAG, "-------->desc uuid:" + gattDescriptor.getUuid());
          int descPermission = gattDescriptor.getPermissions();
          Log.e(TAG, "-------->desc permission:" + Utils.getDescPermission(descPermission));

          byte[] desData = gattDescriptor.getValue();
          if (desData != null && desData.length > 0) {
            Log.e(TAG, "-------->desc value:" + Utils.bytesToHexString(data));
          }

          // soloman 接收的uuid要设置CCC通知enable,并回写到BluetoothGatt
          //					if (gattDescriptor.getUuid().toString().equals(CCC)){
          //						gattDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
          //						mBluetoothGatt.writeDescriptor(gattDescriptor);
          //					}
        }
      }
    } //
  }
  // Demonstrates how to iterate through the supported GATT Services/Characteristics.
  // In this sample, we populate the data structure that is bound to the ExpandableListView
  // on the UI.
  private void displayGattServices(List<BluetoothGattService> gattServices) {
    if (gattServices == null) return;

    if (mDataReceivedCharacteristic != null) {
      Log.e(TAG, "Dont need to re list the characteristics, we already found them.");
      return;
    }
    String uuid = null;
    String unknownServiceString = getResources().getString(R.string.unknown_service);
    String unknownCharaString = getResources().getString(R.string.unknown_characteristic);
    ArrayList<HashMap<String, String>> gattServiceData = new ArrayList<HashMap<String, String>>();
    ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData =
        new ArrayList<ArrayList<HashMap<String, String>>>();
    mGattCharacteristics = new ArrayList<ArrayList<BluetoothGattCharacteristic>>();

    // Loops through available GATT Services.
    for (BluetoothGattService gattService : gattServices) {
      HashMap<String, String> currentServiceData = new HashMap<String, String>();
      uuid = gattService.getUuid().toString();
      Log.e("BluetoothService", uuid);

      currentServiceData.put(
          LIST_NAME, SCalEvoBluetoothSpecifications.lookup(uuid, unknownServiceString));
      currentServiceData.put(LIST_UUID, uuid);
      gattServiceData.add(currentServiceData);

      ArrayList<HashMap<String, String>> gattCharacteristicGroupData =
          new ArrayList<HashMap<String, String>>();
      List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
      ArrayList<BluetoothGattCharacteristic> charas = new ArrayList<BluetoothGattCharacteristic>();

      // Loops through available Characteristics.
      for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
        charas.add(gattCharacteristic);
        HashMap<String, String> currentCharaData = new HashMap<String, String>();
        uuid = gattCharacteristic.getUuid().toString();
        Log.e("BluetoothCharacteristic", uuid);
        currentCharaData.put(
            LIST_NAME, SCalEvoBluetoothSpecifications.lookup(uuid, unknownCharaString));
        currentCharaData.put(LIST_UUID, uuid);
        gattCharacteristicGroupData.add(currentCharaData);
        if (SCalEvoBluetoothSpecifications.DATA_RECEIVED.equals(uuid)) {
          mDataReceivedCharacteristic = gattCharacteristic;
        } else if (SCalEvoBluetoothSpecifications.ANSWER_TO_REQUEST_OR_COMMAND.equals(uuid)) {
          mAnswerToDataRequestOrCommandCharacteristic = gattCharacteristic;
        } else if (SCalEvoBluetoothSpecifications.DATA_REQUEST_OR_COMMAND.equals(uuid)) {
          mDataRequestOrCommandCharacteristic = gattCharacteristic;
        }

        List<BluetoothGattDescriptor> descriptors = gattCharacteristic.getDescriptors();
        for (BluetoothGattDescriptor aDescriptor : descriptors) {
          uuid = aDescriptor.getUuid().toString();
          Log.e(TAG, "BluetoothGattDescriptor " + uuid);
        }
      }
      mGattCharacteristics.add(charas);
      gattCharacteristicData.add(gattCharacteristicGroupData);
    }

    SimpleExpandableListAdapter gattServiceAdapter =
        new SimpleExpandableListAdapter(
            this,
            gattServiceData,
            android.R.layout.simple_expandable_list_item_2,
            new String[] {LIST_NAME, LIST_UUID},
            new int[] {android.R.id.text1, android.R.id.text2},
            gattCharacteristicData,
            android.R.layout.simple_expandable_list_item_2,
            new String[] {LIST_NAME, LIST_UUID},
            new int[] {android.R.id.text1, android.R.id.text2});
    mGattServicesList.setAdapter(gattServiceAdapter);

    listenToCalipersDataButton();
  }
  /**
   * @Title: displayGattServices @Description: TODO(处理蓝牙服务)
   *
   * @param 无
   * @return void
   * @throws
   */
  @SuppressLint("NewApi")
  private void displayGattServices(List<BluetoothGattService> gattServices) {

    if (gattServices == null) return;
    String uuid = null;
    //		String unknownServiceString = "unknown_service";
    //		String unknownCharaString = "unknown_characteristic";

    // 服务数据,可扩展下拉列表的第一级数据
    ArrayList<HashMap<String, String>> gattServiceData = new ArrayList<HashMap<String, String>>();

    // 特征数据(隶属于某一级服务下面的特征值集合)
    ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData =
        new ArrayList<ArrayList<HashMap<String, String>>>();

    // 部分层次,所有特征值集合
    mGattCharacteristics = new ArrayList<ArrayList<BluetoothGattCharacteristic>>();

    // Loops through available GATT Services.
    for (BluetoothGattService gattService : gattServices) {

      // 获取服务列表
      HashMap<String, String> currentServiceData = new HashMap<String, String>();
      uuid = gattService.getUuid().toString();

      // 查表,根据该uuid获取对应的服务名称。SampleGattAttributes这个表需要自定义。

      gattServiceData.add(currentServiceData);

      System.out.println("Service uuid:" + uuid);

      ArrayList<HashMap<String, String>> gattCharacteristicGroupData =
          new ArrayList<HashMap<String, String>>();

      // 从当前循环所指向的服务中读取特征值列表
      List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();

      ArrayList<BluetoothGattCharacteristic> charas = new ArrayList<BluetoothGattCharacteristic>();

      // Loops through available Characteristics.
      // 对于当前循环所指向的服务中的每一个特征值
      for (final BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
        charas.add(gattCharacteristic);
        HashMap<String, String> currentCharaData = new HashMap<String, String>();
        uuid = gattCharacteristic.getUuid().toString();

        if (gattCharacteristic.getUuid().toString().equals(HEART_RATE_MEASUREMENT)) {
          // 测试读取当前Characteristic数据,会触发mOnDataAvailable.onCharacteristicRead()
          mhandler.postDelayed(
              new Runnable() {

                @Override
                public void run() {
                  // TODO Auto-generated method stub
                  mBluetoothLeService.readCharacteristic(gattCharacteristic);
                }
              },
              200);

          // 接受Characteristic被写的通知,收到蓝牙模块的数据后会触发mOnDataAvailable.onCharacteristicWrite()
          mBluetoothLeService.setCharacteristicNotification(gattCharacteristic, true);
          target_chara = gattCharacteristic;
          // 设置数据内容
          // 往蓝牙模块写入数据
          // mBluetoothLeService.writeCharacteristic(gattCharacteristic);
        }
        List<BluetoothGattDescriptor> descriptors = gattCharacteristic.getDescriptors();
        for (BluetoothGattDescriptor descriptor : descriptors) {
          System.out.println("---descriptor UUID:" + descriptor.getUuid());
          // 获取特征值的描述
          mBluetoothLeService.getCharacteristicDescriptor(descriptor);
        }

        gattCharacteristicGroupData.add(currentCharaData);
      }
      // 按先后顺序,分层次放入特征值集合中,只有特征值
      mGattCharacteristics.add(charas);
      // 构件第二级扩展列表(服务下面的特征值)
      gattCharacteristicData.add(gattCharacteristicGroupData);
    }
  }