Add free space precondition check for copy job.
Bug: 24948755 Change-Id: I210395bdf339d630604e90e867ffddbbd3cf4bea (cherry picked from commit 2a530ab3be585b366d562ea24940dcdd239af6d0)
This commit is contained in:
committed by
Garfield Tan
parent
8f4731db63
commit
71ac8a003e
@@ -36,6 +36,7 @@ import android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.DocumentsContract.Root;
|
||||
import android.provider.DocumentsProvider;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -350,11 +351,20 @@ public class RootsCache {
|
||||
* waiting for all the other roots to come back.
|
||||
*/
|
||||
public RootInfo getRootOneshot(String authority, String rootId) {
|
||||
return getRootOneshot(authority, rootId, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the requested {@link RootInfo}, but only loading the roots of the requested authority.
|
||||
* It always fetches from {@link DocumentsProvider} if forceRefresh is true, which is used to
|
||||
* get the most up-to-date free space before starting copy operations.
|
||||
*/
|
||||
public RootInfo getRootOneshot(String authority, String rootId, boolean forceRefresh) {
|
||||
synchronized (mLock) {
|
||||
RootInfo root = getRootLocked(authority, rootId);
|
||||
RootInfo root = forceRefresh ? null : getRootLocked(authority, rootId);
|
||||
if (root == null) {
|
||||
mRoots.putAll(authority,
|
||||
loadRootsForAuthority(mContext.getContentResolver(), authority, false));
|
||||
mRoots.putAll(authority, loadRootsForAuthority(
|
||||
mContext.getContentResolver(), authority, forceRefresh));
|
||||
root = getRootLocked(authority, rootId);
|
||||
}
|
||||
return root;
|
||||
|
||||
@@ -51,9 +51,11 @@ import android.text.format.DateUtils;
|
||||
import android.util.Log;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import com.android.documentsui.UrisSupplier;
|
||||
import com.android.documentsui.DocumentsApplication;
|
||||
import com.android.documentsui.Metrics;
|
||||
import com.android.documentsui.R;
|
||||
import com.android.documentsui.RootsCache;
|
||||
import com.android.documentsui.UrisSupplier;
|
||||
import com.android.documentsui.model.DocumentInfo;
|
||||
import com.android.documentsui.model.DocumentStack;
|
||||
import com.android.documentsui.model.RootInfo;
|
||||
@@ -210,7 +212,6 @@ class CopyJob extends Job {
|
||||
|
||||
@Override
|
||||
boolean setUp() {
|
||||
|
||||
try {
|
||||
buildDocumentList();
|
||||
} catch (ResourceException e) {
|
||||
@@ -218,6 +219,7 @@ class CopyJob extends Job {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if user has canceled this task.
|
||||
if (isCanceled()) {
|
||||
return false;
|
||||
}
|
||||
@@ -229,7 +231,15 @@ class CopyJob extends Job {
|
||||
mBatchSize = -1;
|
||||
}
|
||||
|
||||
return true;
|
||||
// Check if user has canceled this task. We should check it again here as user cancels
|
||||
// tasks in main thread, but this is running in a worker thread. calculateSize() may
|
||||
// take a long time during which user can cancel this task, and we don't want to waste
|
||||
// resources doing useless large chunk of work.
|
||||
if (isCanceled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return checkSpace();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -286,6 +296,44 @@ class CopyJob extends Job {
|
||||
return !root.isDownloads() || !doc.isDirectory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the destination folder has enough space to take all source files.
|
||||
* @return true if the root has enough space or doesn't provide free space info; otherwise false
|
||||
*/
|
||||
boolean checkSpace() {
|
||||
return checkSpace(mBatchSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the destination folder has enough space to take files of batchSize
|
||||
* @param batchSize the total size of files
|
||||
* @return true if the root has enough space or doesn't provide free space info; otherwise false
|
||||
*/
|
||||
final boolean checkSpace(long batchSize) {
|
||||
// Default to be true because if batchSize or available space is invalid, we still let the
|
||||
// copy start anyway.
|
||||
boolean result = true;
|
||||
if (batchSize >= 0) {
|
||||
RootsCache cache = DocumentsApplication.getRootsCache(appContext);
|
||||
|
||||
// Query root info here instead of using stack.root because the number there may be
|
||||
// stale.
|
||||
RootInfo root = cache.getRootOneshot(stack.root.authority, stack.root.rootId, true);
|
||||
if (root.availableBytes >= 0) {
|
||||
result = (batchSize <= root.availableBytes);
|
||||
} else {
|
||||
Log.w(TAG, root.toString() + " doesn't provide available bytes.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
failedFileCount += mSrcs.size();
|
||||
failedFiles.addAll(mSrcs);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean hasWarnings() {
|
||||
return !convertedFiles.isEmpty();
|
||||
@@ -585,7 +633,7 @@ class CopyJob extends Job {
|
||||
result += calculateFileSizesRecursively(getClient(src), src.derivedUri);
|
||||
} catch (RemoteException e) {
|
||||
throw new ResourceException("Failed to obtain the client for %s.",
|
||||
src.derivedUri);
|
||||
src.derivedUri, e);
|
||||
}
|
||||
} else {
|
||||
result += src.size;
|
||||
@@ -603,7 +651,7 @@ class CopyJob extends Job {
|
||||
*
|
||||
* @throws ResourceException
|
||||
*/
|
||||
private long calculateFileSizesRecursively(
|
||||
long calculateFileSizesRecursively(
|
||||
ContentProviderClient client, Uri uri) throws ResourceException {
|
||||
final String authority = uri.getAuthority();
|
||||
final Uri queryUri = buildChildDocumentsUri(authority, getDocumentId(uri));
|
||||
|
||||
@@ -29,8 +29,8 @@ import android.provider.DocumentsContract;
|
||||
import android.provider.DocumentsContract.Document;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.documentsui.UrisSupplier;
|
||||
import com.android.documentsui.R;
|
||||
import com.android.documentsui.UrisSupplier;
|
||||
import com.android.documentsui.model.DocumentInfo;
|
||||
import com.android.documentsui.model.DocumentStack;
|
||||
|
||||
@@ -96,9 +96,35 @@ final class MoveJob extends CopyJob {
|
||||
return super.setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Only check space for moves across authorities. For now we don't know if the doc in
|
||||
* {@link #mSrcs} is in the same root of destination, and if it's optimized move in the same
|
||||
* root it should succeed regardless of free space, but it's for sure a failure if there is no
|
||||
* enough free space if docs are moved from another authority.
|
||||
*/
|
||||
@Override
|
||||
public void start() {
|
||||
super.start();
|
||||
boolean checkSpace() {
|
||||
long size = 0;
|
||||
for (DocumentInfo src : mSrcs) {
|
||||
if (!src.authority.equals(stack.root.authority)) {
|
||||
if (src.isDirectory()) {
|
||||
try {
|
||||
size += calculateFileSizesRecursively(getClient(src), src.derivedUri);
|
||||
} catch (RemoteException|ResourceException e) {
|
||||
Log.w(TAG, "Failed to obtain client for %s" + src.derivedUri + ".", e);
|
||||
|
||||
// Failed to calculate size, but move may still succeed.
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
size += src.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return checkSpace(size);
|
||||
}
|
||||
|
||||
void processDocument(DocumentInfo src, DocumentInfo srcParent, DocumentInfo dest)
|
||||
|
||||
Reference in New Issue
Block a user