private void markBatchFailed(int failReason) {
    synchronized (this) {
      try {
        wait(1000);
      } catch (InterruptedException e) {
        if (V) Log.v(TAG, "Interrupted waiting for markBatchFailed");
      }
    }

    if (D) Log.d(TAG, "Mark all ShareInfo in the batch as failed");
    if (mCurrentShare != null) {
      if (V) Log.v(TAG, "Current share has status " + mCurrentShare.mStatus);
      if (BluetoothShare.isStatusError(mCurrentShare.mStatus)) {
        failReason = mCurrentShare.mStatus;
      }
      if (mCurrentShare.mDirection == BluetoothShare.DIRECTION_INBOUND
          && mCurrentShare.mFilename != null) {
        new File(mCurrentShare.mFilename).delete();
      }
    }

    BluetoothOppShareInfo info = null;
    if (mBatch == null) {
      return;
    }
    info = mBatch.getPendingShare();
    while (info != null) {
      if (info.mStatus < 200) {
        info.mStatus = failReason;
        Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + info.mId);
        ContentValues updateValues = new ContentValues();
        updateValues.put(BluetoothShare.STATUS, info.mStatus);
        /* Update un-processed outbound transfer to show some info */
        if (info.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
          BluetoothOppSendFileInfo fileInfo = BluetoothOppUtility.getSendFileInfo(info.mUri);
          BluetoothOppUtility.closeSendFileInfo(info.mUri);
          if (fileInfo.mFileName != null) {
            updateValues.put(BluetoothShare.FILENAME_HINT, fileInfo.mFileName);
            updateValues.put(BluetoothShare.TOTAL_BYTES, fileInfo.mLength);
            updateValues.put(BluetoothShare.MIMETYPE, fileInfo.mMimetype);
          }
        } else {
          if (info.mStatus < 200 && info.mFilename != null) {
            new File(info.mFilename).delete();
          }
        }
        mContext.getContentResolver().update(contentUri, updateValues, null, null);
        Constants.sendIntentIfCompleted(mContext, contentUri, info.mStatus);
      }
      info = mBatch.getPendingShare();
    }
  }
  /** Start the transfer */
  public void start() {
    /* check Bluetooth enable status */
    /*
     * normally it's impossible to reach here if BT is disabled. Just check
     * for safety
     */
    if (!mAdapter.isEnabled()) {
      Log.e(TAG, "Can't start transfer when Bluetooth is disabled for " + mBatch.mId);
      markBatchFailed();
      mBatch.mStatus = Constants.BATCH_STATUS_FAILED;
      return;
    }

    if (mHandlerThread == null) {
      if (V) Log.v(TAG, "Create handler thread for batch " + mBatch.mId);
      mHandlerThread =
          new HandlerThread("BtOpp Transfer Handler", Process.THREAD_PRIORITY_BACKGROUND);
      mHandlerThread.start();
      mSessionHandler = new EventHandler(mHandlerThread.getLooper());

      if (mBatch.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
        /* for outbound transfer, we do connect first */
        startConnectSession();
      } else if (mBatch.mDirection == BluetoothShare.DIRECTION_INBOUND) {
        /*
         * for inbound transfer, it's already connected, so we start
         * OBEX session directly
         */
        startObexSession();
      }
    }
  }
  public BluetoothOppTransfer(
      Context context,
      PowerManager powerManager,
      BluetoothOppBatch batch,
      BluetoothOppObexSession session) {

    mContext = context;
    mBatch = batch;
    mSession = session;

    mBatch.registerListern(this);
    mAdapter = BluetoothAdapter.getDefaultAdapter();
  }
 /** Process when a share is added to current transfer */
 public void onShareAdded(int id) {
   BluetoothOppShareInfo info = mBatch.getPendingShare();
   if (info.mDirection == BluetoothShare.DIRECTION_INBOUND) {
     mCurrentShare = mBatch.getPendingShare();
     /*
      * TODO what if it's not auto confirmed?
      */
     if (mCurrentShare != null
         && (mCurrentShare.mConfirm == BluetoothShare.USER_CONFIRMATION_AUTO_CONFIRMED
             || mCurrentShare.mConfirm == BluetoothShare.USER_CONFIRMATION_HANDOVER_CONFIRMED)) {
       /* have additional auto confirmed share to process */
       if (V)
         Log.v(
             TAG,
             "Transfer continue session for info "
                 + mCurrentShare.mId
                 + " from batch "
                 + mBatch.mId);
       processCurrentShare();
       confirmStatusChanged();
     }
   }
 }
  private void startObexSession() {

    mBatch.mStatus = Constants.BATCH_STATUS_RUNNING;

    mCurrentShare = mBatch.getPendingShare();
    if (mCurrentShare == null) {
      /*
       * TODO catch this error
       */
      Log.e(TAG, "Unexpected error happened !");
      return;
    }
    if (V) Log.v(TAG, "Start session for info " + mCurrentShare.mId + " for batch " + mBatch.mId);

    if (mBatch.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
      if (V) Log.v(TAG, "Create Client session with transport " + mTransport.toString());
      mSession = new BluetoothOppObexClientSession(mContext, mTransport);
    } else if (mBatch.mDirection == BluetoothShare.DIRECTION_INBOUND) {
      /*
       * For inbounds transfer, a server session should already exists
       * before BluetoothOppTransfer is initialized. We should pass in a
       * mSession instance.
       */
      if (mSession == null) {
        /** set current share as error */
        Log.e(TAG, "Unexpected error happened !");
        markBatchFailed();
        mBatch.mStatus = Constants.BATCH_STATUS_FAILED;
        return;
      }
      if (V) Log.v(TAG, "Transfer has Server session" + mSession.toString());
    }

    mSession.start(mSessionHandler, mBatch.getNumShares());
    processCurrentShare();
  }
  /** Process when current transfer is canceled */
  public void onBatchCanceled() {
    if (V) Log.v(TAG, "Transfer on Batch canceled");

    this.stop();
    mBatch.mStatus = Constants.BATCH_STATUS_FINISHED;
  }