aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Garnes <matt@cyngn.com>2015-08-12 11:32:31 -0700
committerMatt Garnes <matt@cyngn.com>2015-08-12 17:49:17 -0700
commit81d466c7eea61897335ccbfa0d06b330e3002bae (patch)
tree1cdd355d8a95371177722fefc1db7282915ea979
parentef030e9b2c80133bbd37d42c526fccf269983bea (diff)
downloadandroid_packages_apps_CMFileManager-81d466c7eea61897335ccbfa0d06b330e3002bae.tar.gz
android_packages_apps_CMFileManager-81d466c7eea61897335ccbfa0d06b330e3002bae.tar.bz2
android_packages_apps_CMFileManager-81d466c7eea61897335ccbfa0d06b330e3002bae.zip
Improve copy/move performance with nio and reintroduce cancel.
Utilize FileChannel.transferFrom to copy files faster. Chunk the file transfer so that we can check between each chunk if the user has cancelled the copy. In my tests copying a large file (650MB), this improved performance by 45%. Also, bring the cancel feature back for non secure storage. Fixes QRDL-976. Change-Id: I112cee7b9dfe682a438516f7f938dfd7538f1efb
-rw-r--r--src/com/cyanogenmod/filemanager/commands/java/CopyCommand.java2
-rw-r--r--src/com/cyanogenmod/filemanager/commands/java/MoveCommand.java4
-rwxr-xr-xsrc/com/cyanogenmod/filemanager/ui/policy/CopyMoveActionPolicy.java4
-rw-r--r--src/com/cyanogenmod/filemanager/util/FileHelper.java84
4 files changed, 56 insertions, 38 deletions
diff --git a/src/com/cyanogenmod/filemanager/commands/java/CopyCommand.java b/src/com/cyanogenmod/filemanager/commands/java/CopyCommand.java
index 5d8073fd..60ce55d9 100644
--- a/src/com/cyanogenmod/filemanager/commands/java/CopyCommand.java
+++ b/src/com/cyanogenmod/filemanager/commands/java/CopyCommand.java
@@ -83,7 +83,7 @@ public class CopyCommand extends Program implements CopyExecutable {
}
//Copy recursively
- if (!FileHelper.copyRecursive(s, d, getBufferSize(), this)) {
+ if (!FileHelper.copyRecursive(s, d, this)) {
if (isTrace()) {
Log.v(TAG, "Result: FAIL. InsufficientPermissionsException"); //$NON-NLS-1$
}
diff --git a/src/com/cyanogenmod/filemanager/commands/java/MoveCommand.java b/src/com/cyanogenmod/filemanager/commands/java/MoveCommand.java
index c4943b27..a8ac7eb6 100644
--- a/src/com/cyanogenmod/filemanager/commands/java/MoveCommand.java
+++ b/src/com/cyanogenmod/filemanager/commands/java/MoveCommand.java
@@ -83,7 +83,7 @@ public class MoveCommand extends Program implements MoveExecutable {
//Move or copy recursively
if (d.exists()) {
- if (!FileHelper.copyRecursive(s, d, getBufferSize(), this)) {
+ if (!FileHelper.copyRecursive(s, d, this)) {
if (isTrace()) {
Log.v(TAG, "Result: FAIL. InsufficientPermissionsException"); //$NON-NLS-1$
}
@@ -97,7 +97,7 @@ public class MoveCommand extends Program implements MoveExecutable {
} else {
// Move between filesystem is not allow. If rename fails then use copy operation
if (!s.renameTo(d)) {
- if (!FileHelper.copyRecursive(s, d, getBufferSize(), this)) {
+ if (!FileHelper.copyRecursive(s, d, this)) {
if (isTrace()) {
Log.v(TAG, "Result: FAIL. InsufficientPermissionsException"); //$NON-NLS-1$
}
diff --git a/src/com/cyanogenmod/filemanager/ui/policy/CopyMoveActionPolicy.java b/src/com/cyanogenmod/filemanager/ui/policy/CopyMoveActionPolicy.java
index de4cf75f..73d08ee2 100755
--- a/src/com/cyanogenmod/filemanager/ui/policy/CopyMoveActionPolicy.java
+++ b/src/com/cyanogenmod/filemanager/ui/policy/CopyMoveActionPolicy.java
@@ -26,6 +26,7 @@ import com.cyanogenmod.filemanager.R;
import com.cyanogenmod.filemanager.console.Console;
import com.cyanogenmod.filemanager.console.NoSuchFileOrDirectory;
import com.cyanogenmod.filemanager.console.RelaunchableException;
+import com.cyanogenmod.filemanager.console.secure.SecureConsole;
import com.cyanogenmod.filemanager.listeners.OnRequestRefreshListener;
import com.cyanogenmod.filemanager.listeners.OnSelectionListener;
import com.cyanogenmod.filemanager.model.FileSystemObject;
@@ -282,7 +283,8 @@ public final class CopyMoveActionPolicy extends ActionsPolicy {
}
@Override
public boolean isDialogCancellable() {
- return false;
+ return !(mSrcConsole instanceof SecureConsole)
+ && !(mDstConsole instanceof SecureConsole);
}
@Override
diff --git a/src/com/cyanogenmod/filemanager/util/FileHelper.java b/src/com/cyanogenmod/filemanager/util/FileHelper.java
index 174fae61..48a3be09 100644
--- a/src/com/cyanogenmod/filemanager/util/FileHelper.java
+++ b/src/com/cyanogenmod/filemanager/util/FileHelper.java
@@ -22,7 +22,6 @@ import android.content.res.Resources;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.util.Log;
-
import com.cyanogenmod.filemanager.FileManagerApplication;
import com.cyanogenmod.filemanager.R;
import com.cyanogenmod.filemanager.commands.SyncResultExecutable;
@@ -57,12 +56,12 @@ import com.cyanogenmod.filemanager.preferences.ObjectStringIdentifier;
import com.cyanogenmod.filemanager.preferences.Preferences;
import com.cyanogenmod.filemanager.util.MimeTypeHelper.MimeTypeCategory;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.nio.channels.ClosedByInterruptException;
+import java.nio.channels.FileChannel;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Collections;
@@ -118,6 +117,13 @@ public final class FileHelper {
*/
public static final String NEWLINE = System.getProperty("line.separator"); //$NON-NLS-1$
+ /**
+ * The size of chunks that we will copy with nio during a copy operation.
+ *
+ * Set to 1MiB.
+ */
+ public final static long NIO_COPY_CHUNK_SIZE = 1024000L;
+
// The date/time formats objects
/**
* @hide
@@ -1087,12 +1093,11 @@ public final class FileHelper {
*
* @param src The source file or folder
* @param dst The destination file or folder
- * @param bufferSize The buffer size for the operation
* @return boolean If the operation complete successfully
* @throws ExecutionException If a problem was detected in the operation
*/
public static boolean copyRecursive(
- final File src, final File dst, int bufferSize, Program program)
+ final File src, final File dst, Program program)
throws ExecutionException, CancelledOperationException {
if (src.isDirectory()) {
// Create the directory
@@ -1115,7 +1120,7 @@ public final class FileHelper {
throw new CancelledOperationException();
}
- if (!copyRecursive(files[i], new File(dst, files[i].getName()), bufferSize,
+ if (!copyRecursive(files[i], new File(dst, files[i].getName()),
program)) {
return false;
}
@@ -1123,7 +1128,7 @@ public final class FileHelper {
}
} else {
// Copy the directory
- if (!bufferedCopy(src, dst,bufferSize, program)) {
+ if (!copyFileWithNio(src, dst, program)) {
return false;
}
}
@@ -1131,40 +1136,46 @@ public final class FileHelper {
}
/**
- * Method that copies a file
+ * Method that copies a file, using FileChannel.transferFrom from
+ * the nio package.
+ *
+ * The file is chunked into chunks of size {@link #NIO_COPY_CHUNK_SIZE}
+ * and each chunk is transferred until the file is completely copied. This
+ * allows us to cancel the file transfer at any time.
*
* @param src The source file
* @param dst The destination file
- * @param bufferSize The buffer size for the operation
- * @return boolean If the operation complete successfully
+ * @return boolean Whether the operation completed successfully
*/
- public static boolean bufferedCopy(final File src, final File dst,
- int bufferSize, Program program)
- throws ExecutionException, CancelledOperationException {
- BufferedInputStream bis = null;
- BufferedOutputStream bos = null;
+ public static boolean copyFileWithNio(final File src, final File dst,
+ Program program) throws CancelledOperationException, ExecutionException {
+ FileChannel inputChannel = null;
+ FileChannel outputChannel = null;
+ long currentPosition = 0;
+ long count = NIO_COPY_CHUNK_SIZE;
try {
- bis = new BufferedInputStream(new FileInputStream(src), bufferSize);
- bos = new BufferedOutputStream(new FileOutputStream(dst), bufferSize);
- int read = 0;
- byte[] data = new byte[bufferSize];
- while ((read = bis.read(data, 0, bufferSize)) != -1) {
+ inputChannel = new FileInputStream(src).getChannel();
+ outputChannel = new FileOutputStream(dst).getChannel();
+ while (currentPosition < inputChannel.size()) {
// Short circuit if we've been cancelled. Show's over :(
if (program.isCancelled()) {
throw new CancelledOperationException();
}
- bos.write(data, 0, read);
- }
- return true;
+ if ((currentPosition + count) > inputChannel.size()) {
+ count = (inputChannel.size() - currentPosition);
+ }
+ outputChannel.transferFrom(inputChannel, currentPosition, count);
+ currentPosition = currentPosition + count;
+ }
} catch (Throwable e) {
Log.e(TAG,
String.format(TAG, "Failed to copy from %s to %d", src, dst), e); //$NON-NLS-1$
try {
- // delete the destination file if it exists since the operation failed
- if (dst.exists()) {
- dst.delete();
+ // Delete the destination file upon failure
+ if (!dst.delete()) {
+ Log.e(TAG, "Failed to delete the dest file: " + dst);
}
} catch (Throwable t) {/**NON BLOCK**/}
@@ -1173,24 +1184,29 @@ public final class FileHelper {
if (e.getCause() instanceof ErrnoException
&& ((ErrnoException)e.getCause()).errno == OsConstants.ENOSPC) {
throw new ExecutionException(R.string.msgs_no_disk_space);
- } if (e instanceof CancelledOperationException) {
+ } else if ((e instanceof CancelledOperationException)
+ || (e instanceof ClosedByInterruptException)) {
// If the user cancelled this operation, let it through.
throw (CancelledOperationException)e;
}
-
return false;
} finally {
try {
- if (bis != null) {
- bis.close();
+ if (inputChannel != null) {
+ inputChannel.close();
}
- } catch (Throwable e) {/**NON BLOCK**/}
+ } catch (IOException e) {
+ Log.e(TAG, "Error while closing input channel during copyFileWithNio");
+ }
try {
- if (bos != null) {
- bos.close();
+ if (outputChannel != null) {
+ outputChannel.close();
}
- } catch (Throwable e) {/**NON BLOCK**/}
+ } catch (IOException e) {
+ Log.e(TAG, "Error while closing output channel during copyFileWithNio");
+ }
}
+ return true;
}
/**