private void startProgramming() {
    mLog.append("Programming started\n");
    mProgramming = true;
    updateGui();

    // Prepare image notification
    byte[] buf = new byte[OAD_IMG_HDR_SIZE + 2 + 2];
    buf[0] = Conversion.loUint16(mFileImgHdr.ver);
    buf[1] = Conversion.hiUint16(mFileImgHdr.ver);
    buf[2] = Conversion.loUint16(mFileImgHdr.len);
    buf[3] = Conversion.hiUint16(mFileImgHdr.len);
    System.arraycopy(mFileImgHdr.uid, 0, buf, 4, 4);

    // Send image notification
    mCharIdentify.setValue(buf);
    mLeService.writeCharacteristic(mCharIdentify);

    // Initialize stats
    mProgInfo.reset();

    // Start the packet timer
    mTimer = null;
    mTimer = new Timer();
    mTimerTask = new ProgTimerTask();
    mTimer.scheduleAtFixedRate(mTimerTask, 0, PKT_INTERVAL);
  }
        @Override
        public void onReceive(Context context, Intent intent) {

          final String action = intent.getAction();
          Log.d(TAG, "action: " + action);

          if (BluetoothLeService.ACTION_DATA_NOTIFY.equals(action)) {
            byte[] value = intent.getByteArrayExtra(BluetoothLeService.EXTRA_DATA);
            String uuidStr = intent.getStringExtra(BluetoothLeService.EXTRA_UUID);
            if (uuidStr.equals(mCharIdentify.getUuid().toString())) {
              // Image info notification
              mTargImgHdr.ver = Conversion.buildUint16(value[1], value[0]);
              mTargImgHdr.imgType = ((mTargImgHdr.ver & 1) == 1) ? 'B' : 'A';
              mTargImgHdr.len = Conversion.buildUint16(value[3], value[2]);
              displayImageInfo(mTargImage, mTargImgHdr);
            }
          } else if (BluetoothLeService.ACTION_DATA_WRITE.equals(action)) {
            int status =
                intent.getIntExtra(BluetoothLeService.EXTRA_STATUS, BluetoothGatt.GATT_SUCCESS);
            if (status != BluetoothGatt.GATT_SUCCESS) {
              Log.e(TAG, "Write failed: " + status);
              Toast.makeText(context, "GATT error: status=" + status, Toast.LENGTH_SHORT).show();
            }
          }
        }
  private boolean loadFile(String filepath, boolean isAsset) {
    boolean fSuccess = false;

    // Load binary file
    try {
      // Read the file raw into a buffer
      InputStream stream;
      if (isAsset) {
        stream = getAssets().open(filepath);
      } else {
        File f = new File(filepath);
        stream = new FileInputStream(f);
      }
      stream.read(mFileBuffer, 0, mFileBuffer.length);
      stream.close();
    } catch (IOException e) {
      // Handle exceptions here
      mLog.setText("File open failed: " + filepath + "\n");
      return false;
    }

    // Show image info
    mFileImgHdr.ver = Conversion.buildUint16(mFileBuffer[5], mFileBuffer[4]);
    mFileImgHdr.len = Conversion.buildUint16(mFileBuffer[7], mFileBuffer[6]);
    mFileImgHdr.imgType = ((mFileImgHdr.ver & 1) == 1) ? 'B' : 'A';
    System.arraycopy(mFileBuffer, 8, mFileImgHdr.uid, 0, 4);
    displayImageInfo(mFileImage, mFileImgHdr);

    // Verify image types
    boolean ready = mFileImgHdr.imgType != mTargImgHdr.imgType;
    int resid = ready ? R.style.dataStyle1 : R.style.dataStyle2;
    mFileImage.setTextAppearance(this, resid);

    // Enable programming button only if image types differ
    mBtnStart.setEnabled(ready);

    // Expected duration
    mEstDuration = ((PKT_INTERVAL * mFileImgHdr.len * 4) / OAD_BLOCK_SIZE) / 1000;
    displayStats();

    // Log
    mLog.setText("Image " + mFileImgHdr.imgType + " selected.\n");
    mLog.append(ready ? "Ready to program device!\n" : "Incompatible image, select alternative!\n");

    updateGui();

    return fSuccess;
  }
  private void onBlockTimer() {

    if (mProgInfo.iBlocks < mProgInfo.nBlocks) {
      mProgramming = true;

      // Prepare block
      mOadBuffer[0] = Conversion.loUint16(mProgInfo.iBlocks);
      mOadBuffer[1] = Conversion.hiUint16(mProgInfo.iBlocks);
      System.arraycopy(mFileBuffer, mProgInfo.iBytes, mOadBuffer, 2, OAD_BLOCK_SIZE);

      // Send block
      mCharBlock.setValue(mOadBuffer);
      boolean success = mLeService.writeCharacteristic(mCharBlock);

      if (success) {
        // Update stats
        mProgInfo.iBlocks++;
        mProgInfo.iBytes += OAD_BLOCK_SIZE;
        mProgressBar.setProgress((mProgInfo.iBlocks * 100) / mProgInfo.nBlocks);
      } else {
        // Check if the device has been prematurely disconnected
        if (BluetoothLeService.getBtGatt() == null) mProgramming = false;
      }
    } else {
      mProgramming = false;
    }
    mProgInfo.iTimeElapsed += PKT_INTERVAL;

    if (!mProgramming) {
      runOnUiThread(
          new Runnable() {
            public void run() {
              displayStats();
              stopProgramming();
            }
          });
    }
  }
 private void setConnectionParameters() {
   // Make sure connection interval is long enough for OAD
   byte[] value = {
     Conversion.loUint16(OAD_CONN_INTERVAL),
     Conversion.hiUint16(OAD_CONN_INTERVAL),
     Conversion.loUint16(OAD_CONN_INTERVAL),
     Conversion.hiUint16(OAD_CONN_INTERVAL),
     0,
     0,
     Conversion.loUint16(OAD_SUPERVISION_TIMEOUT),
     Conversion.hiUint16(OAD_SUPERVISION_TIMEOUT)
   };
   mCharConnReq.setValue(value);
   boolean ok = mLeService.writeCharacteristic(mCharConnReq);
   if (ok) ok = mLeService.waitIdle(GATT_WRITE_TIMEOUT);
 }