/**
  * Updates states of all transfer records with the specified which are "running" and "waiting" to
  * "pending cancel"
  *
  * @param TransferType The type of transfers to cancel
  * @return Number of rows updated.
  */
 public int cancelAllWithType(TransferType type) {
   ContentValues values = new ContentValues();
   values.put(TransferTable.COLUMN_STATE, TransferState.PENDING_CANCEL.toString());
   String selection = null;
   String[] selectionArgs = null;
   if (type == TransferType.ANY) {
     selection = TransferTable.COLUMN_STATE + " in (?,?,?,?,?)";
     selectionArgs =
         new String[] {
           TransferState.IN_PROGRESS.toString(),
           TransferState.RESUMED_WAITING.toString(),
           TransferState.WAITING.toString(),
           TransferState.PAUSED.toString(),
           TransferState.WAITING_FOR_NETWORK.toString()
         };
   } else {
     selection =
         TransferTable.COLUMN_STATE + " in (?,?,?,?,?) and " + TransferTable.COLUMN_TYPE + "=?";
     selectionArgs =
         new String[] {
           TransferState.IN_PROGRESS.toString(),
           TransferState.RESUMED_WAITING.toString(),
           TransferState.WAITING.toString(),
           TransferState.PAUSED.toString(),
           TransferState.WAITING_FOR_NETWORK.toString(),
           type.toString()
         };
   }
   return transferDBBase.update(transferDBBase.getContentUri(), values, selection, selectionArgs);
 }
 /**
  * Queries all the records which have the given type.
  *
  * @param TransferType The type of transfers to query for.
  * @return A Cursor pointing to records in the database with the given type.
  */
 public Cursor queryAllTransfersWithType(TransferType type) {
   if (type == TransferType.ANY) {
     return transferDBBase.query(transferDBBase.getContentUri(), null, null, null, null);
   } else {
     return transferDBBase.query(
         transferDBBase.getContentUri(),
         null,
         TransferTable.COLUMN_TYPE + "=?",
         new String[] {type.toString()},
         null);
   }
 }
 /**
  * Updates states of all transfer records which are "waiting for network" to "waiting to resume"
  *
  * @return Number of rows updated.
  */
 public int updateNetworkConnected() {
   ContentValues values = new ContentValues();
   values.put(TransferTable.COLUMN_STATE, TransferState.RESUMED_WAITING.toString());
   return transferDBBase.update(
       transferDBBase.getContentUri(),
       values,
       TransferTable.COLUMN_STATE + " in (?,?)",
       new String[] {
         TransferState.PENDING_NETWORK_DISCONNECT.toString(),
         TransferState.WAITING_FOR_NETWORK.toString()
       });
 }
 /**
  * Queries all the records which have the given type and state.
  *
  * @param TransferType The type of transfers to query for.
  * @param TransferState The state of the transfer.
  * @return A Cursor pointing to records in the database with the given type and state.
  */
 public Cursor queryTransfersWithTypeAndState(TransferType type, TransferState state) {
   if (type == TransferType.ANY) {
     return transferDBBase.query(getStateUri(state), null, null, null, null);
   } else {
     return transferDBBase.query(
         getStateUri(state),
         null,
         TransferTable.COLUMN_TYPE + "=?",
         new String[] {type.toString()},
         null);
   }
 }
 /**
  * Updates states of all transfer records which are "running" and "waiting" to "paused"
  *
  * @return Number of rows updated.
  */
 public int setAllRunningRecordsToPausedBeforeShutdownService() {
   ContentValues values = new ContentValues();
   values.put(TransferTable.COLUMN_STATE, TransferState.PAUSED.toString());
   return transferDBBase.update(
       transferDBBase.getContentUri(),
       values,
       TransferTable.COLUMN_STATE + " in (?,?,?,?)",
       new String[] {
         TransferState.IN_PROGRESS.toString(),
         TransferState.PENDING_PAUSE.toString(),
         TransferState.RESUMED_WAITING.toString(),
         TransferState.WAITING.toString()
       });
 }
 /**
  * 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;
 }
 /**
  * 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);
   }
 }
 /**
  * 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;
 }
 /**
  * Inserts a part upload record into database with the given values.
  *
  * @param bucket The name of the bucket to upload to.
  * @param key The key in the specified bucket by which to store the new object.
  * @param file The file to upload.
  * @param fileOffset The byte offset for the file to upload.
  * @param partNumber The part number of this part.
  * @param uploadId The multipart upload id of the upload.
  * @param bytesTotal The Total bytes of the file.
  * @param isLastPart Whether this part is the last part of the upload.
  * @return An Uri of the record inserted.
  */
 public Uri insertMultipartUploadRecord(
     String bucket,
     String key,
     File file,
     long fileOffset,
     int partNumber,
     String uploadId,
     long bytesTotal,
     int isLastPart) {
   ContentValues values =
       generateContentValuesForMultiPartUpload(
           bucket,
           key,
           file,
           fileOffset,
           partNumber,
           uploadId,
           bytesTotal,
           isLastPart,
           new ObjectMetadata());
   return transferDBBase.insert(transferDBBase.getContentUri(), values);
 }
 /**
  * Queries all the PartETags of completed parts from the multipart upload specified by the
  * mainUploadId. The list of PartETags is used to complete a multiart upload, so it's usually
  * called after all partUpload tasks are finished.
  *
  * @param mainUploadId The mainUploadId of a multipart upload task
  * @return A list of PartEtag of completed parts
  */
 public List<PartETag> queryPartETagsOfUpload(int mainUploadId) {
   List<PartETag> partETags = new ArrayList<PartETag>();
   Cursor c = transferDBBase.query(getPartUri(mainUploadId), null, null, null, null);
   int partNum = 0;
   String eTag = null;
   while (c.moveToNext()) {
     partNum = c.getInt(c.getColumnIndexOrThrow(TransferTable.COLUMN_PART_NUM));
     eTag = c.getString(c.getColumnIndexOrThrow(TransferTable.COLUMN_ETAG));
     partETags.add(new PartETag(partNum, eTag));
   }
   c.close();
   return partETags;
 }
 /**
  * Updates the current bytes of a transfer record, but calling this method may not cause the
  * actual update. If last updated time is less than 1 second from now, or change of bytes is less
  * than 10*1024, the update request will be discarded. If forceUpdate is set to true, then it will
  * update the DB despite the limitation above.
  *
  * @param id The id of the transfer
  * @param bytes The bytes currently transferred
  * @param forceUpdate Whether to update now
  * @return Number of rows updated.
  */
 public int updateBytesTransferred(int id, long bytes, boolean forceUpdate) {
   long timeInMillis = System.currentTimeMillis();
   if (forceUpdate
       || !mapLastBytes.containsKey(id)
       || !mapLastUploadTime.containsKey(id)
       || (bytes - mapLastBytes.get(id) > 10 * 1024
           && timeInMillis - mapLastUploadTime.get(id) > 1000)) {
     ContentValues values = new ContentValues();
     values.put(TransferTable.COLUMN_BYTES_CURRENT, bytes);
     mapLastBytes.put(id, bytes);
     mapLastUploadTime.put(id, timeInMillis);
     return transferDBBase.update(getRecordUri(id), values, null, null);
   }
   return 0;
 }
 /**
  * Inserts a transfer record into database with the given values.
  *
  * @param type The type of the transfer, can be "upload" or "download".
  * @param bucket The name of the bucket to upload to.
  * @param key The key in the specified bucket by which to store the new object.
  * @param file The file to upload.
  * @param metadata The S3 Object metadata associated with this object
  * @return An Uri of the record inserted.
  */
 public Uri insertSingleTransferRecord(
     TransferType type, String bucket, String key, File file, ObjectMetadata metadata) {
   ContentValues values =
       generateContentValuesForSinglePartTransfer(type, bucket, key, file, metadata);
   return transferDBBase.insert(transferDBBase.getContentUri(), values);
 }
 /**
  * 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());
 }
 /**
  * Gets the Uri of part records of a multipart upload.
  *
  * @param mainUploadId The main upload id of the transfer.
  * @return The Uri of the part upload records that have the given mainUploadId value.
  */
 public Uri getPartUri(int mainUploadId) {
   return Uri.parse(transferDBBase.getContentUri() + "/part/" + mainUploadId);
 }
 /**
  * Gets the Uri of a record.
  *
  * @param id The id of the transfer.
  * @return The Uri of the record specified by the id.
  */
 public Uri getRecordUri(int id) {
   return Uri.parse(transferDBBase.getContentUri() + "/" + id);
 }
 /**
  * Gets the Uri of the transfer record table.
  *
  * @return The Uri of a table.
  */
 public Uri getContentUri() {
   return transferDBBase.getContentUri();
 }
 /** Closes the DB Connection */
 public void closeDB() {
   if (transferDBBase != null) {
     transferDBBase.closeDBHelper();
   }
 }
 /**
  * Updates the total bytes of a download record.
  *
  * @param id The id of the transfer
  * @param bytes The total bytes of the download.
  * @return Number of rows updated.
  */
 public int updateBytesTotalForDownload(int id, long bytes) {
   ContentValues values = new ContentValues();
   values.put(TransferTable.COLUMN_BYTES_TOTAL, bytes);
   return transferDBBase.update(getRecordUri(id), values, null, null);
 }
 /**
  * Deletes the record with the given id.
  *
  * @param id The id of the transfer to be deleted.
  * @return Number of rows deleted.
  */
 public int deleteTransferRecords(int id) {
   return transferDBBase.delete(getRecordUri(id), null, null);
 }
 /**
  * Queries the transfer record specified by id.
  *
  * @param id The id of the transfer.
  * @return The result Cursor of the query.
  */
 public Cursor queryTransferById(int id) {
   return transferDBBase.query(getRecordUri(id), null, null, null, null);
 }
 /**
  * Inserts multiple records at a time.
  *
  * @param valuesArray An array of values to insert.
  * @return The mainUploadId of the multipart records
  */
 public int bulkInsertTransferRecords(ContentValues[] valuesArray) {
   return transferDBBase.bulkInsert(transferDBBase.getContentUri(), valuesArray);
 }
 /**
  * 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);
 }
 /**
  * Updates the multipart id of the transfer record.
  *
  * @param id The id of the transfer.
  * @param multipartId The multipart id of the transfer.
  * @return Number of rows updated.
  */
 public int updateMultipartId(int id, String multipartId) {
   ContentValues values = new ContentValues();
   values.put(TransferTable.COLUMN_MULTIPART_ID, multipartId);
   return transferDBBase.update(getRecordUri(id), values, null, null);
 }
 /**
  * Updates the Etag of the transfer record.
  *
  * @param id The id of the transfer.
  * @param etag The Etag of the transfer.
  * @return Number of rows updated.
  */
 public int updateETag(int id, String etag) {
   ContentValues values = new ContentValues();
   values.put(TransferTable.COLUMN_ETAG, etag);
   return transferDBBase.update(getRecordUri(id), values, null, null);
 }