content: package com.adafruit.bluefruit.le.connect.app;
  
  import android.bluetooth.BluetoothGattCharacteristic;
  import android.bluetooth.BluetoothGattDescriptor;
  import android.bluetooth.BluetoothGattService;
  import android.os.Handler;
  import android.support.v7.app.AppCompatActivity;
  import android.util.Log;
  
  import com.adafruit.bluefruit.le.connect.ble.BleManager;
  import com.adafruit.bluefruit.le.connect.ble.BleUtils;
  
  import java.nio.charset.Charset;
  import java.util.Arrays;
  
  public class UartInterfaceActivity extends AppCompatActivity implements BleManager.BleManagerListener {
      // Log
      private final static String TAG = UartInterfaceActivity.class.getSimpleName();
  
      // Service Constants
      public static final String UUID_SERVICE = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";
      public static final String UUID_RX = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";
      public static final String UUID_TX = "6e400002-b5a3-f393-e0a9-e50e24dcca9e";
      public static final String UUID_DFU = "00001530-1212-EFDE-1523-785FEABCD123";
      public static final int kTxMaxCharacters = 20;
  
      // Data
      protected BleManager mBleManager;
      protected BluetoothGattService mUartService;
      private boolean isRxNotificationEnabled = false;
  
  
      // region Send Data to UART
      protected void sendData(String text) {
          final byte[] value = text.getBytes(Charset.forName("UTF-8"));
          sendData(value);
      }
  
      protected void sendData(byte[] data) {
          if (mUartService != null) {
              // Split the value into chunks (UART service has a maximum number of characters that can be written )
              for (int i = 0; i < data.length; i += kTxMaxCharacters) {
                  final byte[] chunk = Arrays.copyOfRange(data, i, Math.min(i + kTxMaxCharacters, data.length));
                  mBleManager.writeService(mUartService, UUID_TX, chunk);
              }
          } else {
              Log.w(TAG, "Uart Service not discovered. Unable to send data");
          }
      }
  
      // Send data to UART and add a byte with a custom CRC
      protected void sendDataWithCRC(byte[] data) {
  
          // Calculate checksum
          byte checksum = 0;
          for (byte aData : data) {
              checksum += aData;
          }
          checksum = (byte) (~checksum);       // Invert
  
          // Add crc to data
          byte dataCrc[] = new byte[data.length + 1];
          System.arraycopy(data, 0, dataCrc, 0, data.length);
          dataCrc[data.length] = checksum;
  
          // Send it
          Log.d(TAG, "Send to UART: " + BleUtils.bytesToHexWithSpaces(dataCrc));
          sendData(dataCrc);
      }
      // endregion
  
      // region SendDataWithCompletionHandler
      protected interface SendDataCompletionHandler {
          void sendDataResponse(String data);
      }
  
      final private Handler sendDataTimeoutHandler = new Handler();
      private Runnable sendDataRunnable = null;
      private SendDataCompletionHandler sendDataCompletionHandler = null;
  
      protected void sendData(byte[] data, SendDataCompletionHandler completionHandler) {
  
          if (completionHandler == null) {
              sendData(data);
              return;
          }
  
          if (!isRxNotificationEnabled) {
              Log.w(TAG, "sendData warning: RX notification not enabled. completionHandler will not be executed");
          }
  
          if (sendDataRunnable != null || sendDataCompletionHandler != null) {
              Log.d(TAG, "sendData error: waiting for a previous response");
              return;
          }
  
          Log.d(TAG, "sendData");
          sendDataCompletionHandler = completionHandler;
          sendDataRunnable = new Runnable() {
              @Override
              public void run() {
                  Log.d(TAG, "sendData timeout");
                  final SendDataCompletionHandler dataCompletionHandler = sendDataCompletionHandler;
  
                  UartInterfaceActivity.this.sendDataRunnable = null;
                  UartInterfaceActivity.this.sendDataCompletionHandler = null;
  
                  dataCompletionHandler.sendDataResponse(null);
              }
          };
  
          sendDataTimeoutHandler.postDelayed(sendDataRunnable, 2 * 1000);
          sendData(data);
  
      }
  
      protected boolean isWaitingForSendDataResponse() {
          return sendDataRunnable != null;
      }
  
      // endregion
  
      // region BleManagerListener  (used to implement sendData with completionHandler)
  
      @Override
      public void onConnected() {
  
      }
  
      @Override
      public void onConnecting() {
  
      }
  
      @Override
      public void onDisconnected() {
  
      }
  
      @Override
      public void onServicesDiscovered() {
          mUartService = mBleManager.getGattService(UUID_SERVICE);
      }
  
      protected void enableRxNotifications() {
          isRxNotificationEnabled = true;
          mBleManager.enableNotification(mUartService, UUID_RX, true);
      }
  
      @Override
      public void onDataAvailable(BluetoothGattCharacteristic characteristic) {
          // Check if there is a pending sendDataRunnable
          if (sendDataRunnable != null) {
              if (characteristic.getService().getUuid().toString().equalsIgnoreCase(UUID_SERVICE)) {
                  if (characteristic.getUuid().toString().equalsIgnoreCase(UUID_RX)) {
  
                      Log.d(TAG, "sendData received data");
                      sendDataTimeoutHandler.removeCallbacks(sendDataRunnable);
                      sendDataRunnable = null;
  
                      if (sendDataCompletionHandler != null) {
                          final byte[] bytes = characteristic.getValue();
                          final String data = new String(bytes, Charset.forName("UTF-8"));
  
                          final SendDataCompletionHandler dataCompletionHandler = sendDataCompletionHandler;
                          sendDataCompletionHandler = null;
                          dataCompletionHandler.sendDataResponse(data);
                      }
                  }
              }
          }
      }
  
      @Override
      public void onDataAvailable(BluetoothGattDescriptor descriptor) {
  
      }
  
      @Override
      public void onReadRemoteRssi(int rssi) {
  
      }
  
      // endregion
  }
  