Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c20c6f65 authored by Garfield, Tan's avatar Garfield, Tan
Browse files

Add free space precondition check for copy job.

Bug: 24948755
Change-Id: I210395bdf339d630604e90e867ffddbbd3cf4bea
parent 386f6928
Loading
Loading
Loading
Loading
+13 −3
Original line number Diff line number Diff line
@@ -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;
+53 −5
Original line number Diff line number Diff line
@@ -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 −3
Original line number Diff line number Diff line
@@ -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)