/**
  * Queries uncompleted partUpload tasks of a multipart upload and constructs a UploadPartRequest
  * for each task. It's used when resuming a multipart upload
  *
  * @param mainUploadId The mainUploadId of a multipart upload task
  * @param multipartId The multipartId of a multipart upload task
  * @return A list of UploadPartRequest
  */
 public List<UploadPartRequest> getNonCompletedPartRequestsFromDB(
     int mainUploadId, String multipartId) {
   ArrayList<UploadPartRequest> list = new ArrayList<UploadPartRequest>();
   Cursor c = transferDBBase.query(getPartUri(mainUploadId), null, null, null, null);
   while (c.moveToNext()) {
     if (TransferState.PART_COMPLETED.equals(
         TransferState.getState(
             c.getString(c.getColumnIndexOrThrow(TransferTable.COLUMN_STATE))))) {
       continue;
     }
     UploadPartRequest putPartRequest =
         new UploadPartRequest()
             .withId(c.getInt(c.getColumnIndexOrThrow(TransferTable.COLUMN_ID)))
             .withMainUploadId(
                 c.getInt(c.getColumnIndexOrThrow(TransferTable.COLUMN_MAIN_UPLOAD_ID)))
             .withBucketName(
                 c.getString(c.getColumnIndexOrThrow(TransferTable.COLUMN_BUCKET_NAME)))
             .withKey(c.getString(c.getColumnIndexOrThrow(TransferTable.COLUMN_KEY)))
             .withUploadId(multipartId)
             .withFile(new File(c.getString(c.getColumnIndexOrThrow(TransferTable.COLUMN_FILE))))
             .withFileOffset(c.getLong(c.getColumnIndexOrThrow(TransferTable.COLUMN_FILE_OFFSET)))
             .withPartNumber(c.getInt(c.getColumnIndexOrThrow(TransferTable.COLUMN_PART_NUM)))
             .withPartSize(c.getLong(c.getColumnIndexOrThrow(TransferTable.COLUMN_BYTES_TOTAL)))
             .withLastPart(
                 1 == c.getInt(c.getColumnIndexOrThrow(TransferTable.COLUMN_IS_LAST_PART)));
     list.add(putPartRequest);
   }
   c.close();
   return list;
 }
 /**
  * Queries the transfer record specified by main upload id.
  *
  * @param mainUploadId The mainUploadId of a multipart upload task
  * @return The bytes already uploaded for this multipart upload task
  */
 public long queryBytesTransferredByMainUploadId(int mainUploadId) {
   Cursor c = transferDBBase.query(getPartUri(mainUploadId), null, null, null, null);
   long bytesTotal = 0;
   while (c.moveToNext()) {
     String state = c.getString(c.getColumnIndexOrThrow(TransferTable.COLUMN_STATE));
     if (TransferState.PART_COMPLETED.equals(TransferState.getState(state))) {
       bytesTotal += c.getLong(c.getColumnIndexOrThrow(TransferTable.COLUMN_BYTES_TOTAL));
     }
   }
   c.close();
   return bytesTotal;
 }
 /**
  * Updates the state but do not notify TransferService to refresh its transfer record list.
  * Therefore, only TransferObserver knows the state change of the transfer record. If the new
  * state is STATE_FAILED, we need to check the original state, because "pause", "cancel" and
  * "disconnect network" actions may also cause failure message of the threads, but these are not
  * actual failure of transfers.
  *
  * @param id The id of the transfer.
  * @param state The new state of the transfer.
  * @return Number of rows updated.
  */
 public int updateState(int id, TransferState state) {
   ContentValues values = new ContentValues();
   values.put(TransferTable.COLUMN_STATE, state.toString());
   if (TransferState.FAILED.equals(state)) {
     return transferDBBase.update(
         getRecordUri(id),
         values,
         TransferTable.COLUMN_STATE + " not in (?,?,?,?,?) ",
         new String[] {
           TransferState.COMPLETED.toString(),
           TransferState.PENDING_NETWORK_DISCONNECT.toString(),
           TransferState.PAUSED.toString(),
           TransferState.CANCELED.toString(),
           TransferState.WAITING_FOR_NETWORK.toString()
         });
   } else {
     return transferDBBase.update(getRecordUri(id), values, null, null);
   }
 }
 /**
  * Gets the Uri of the records that have the given state.
  *
  * @param state The state of transfers
  * @return The Uri that is used to query transfer records with the given state.
  */
 public Uri getStateUri(TransferState state) {
   return Uri.parse(transferDBBase.getContentUri() + "/state/" + state.toString());
 }
 /**
  * Updates the state and also notify TransferService to refresh its transfer record list. The
  * method is called by TransferUtility, more typically, by applications to perform "pause" or
  * "resume" actions, so it needs to explicitly notify the Service after updating the database.
  *
  * @param id The id of the transfer.
  * @param state The new state of the transfer.
  * @return Number of rows updated.
  */
 public int updateStateAndNotifyUpdate(int id, TransferState state) {
   ContentValues values = new ContentValues();
   values.put(TransferTable.COLUMN_STATE, state.toString());
   return transferDBBase.update(
       transferDBBase.getContentUri(), values, TransferTable.COLUMN_ID + "=" + id, null);
 }