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

Commit 788ec75b authored by Anton Hansson's avatar Anton Hansson
Browse files

Optimize FileSystemProvider.includeFile

When traversing a file tree using SAF, this method is called for
every file in the tree, so wasted cycles add up to quite a lot
of total time for large trees.

Optimizations:
- don't look up info not necessary by the cursor, by inspecting
  the projection columns
- remove a few redundant double-lookups, of e.g. the File path
- mark variables final where possible

On my device/file tree, this reduces the total time spent in includeFile
from ~33 seconds to ~22 seconds. This is where the majority of cycles
are spent when traversing a dir with SAF.

Bug: 130276310
Test: SAF test app
Change-Id: Ibdfc335253a90eb87795a7a4eecb8b7b89601f09
parent 46669aa7
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.database;

import android.annotation.UnsupportedAppUsage;
import android.os.Build;

import java.util.ArrayList;

/**
@@ -240,6 +241,12 @@ public class MatrixCursor extends AbstractCursor {
            }
            return this;
        }

        /** @hide */
        public final RowBuilder add(int columnIndex, Object value) {
            data[(row * columnCount) + columnIndex] = value;
            return this;
        }
    }

    // AbstractCursor implementation.
+49 −32
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import android.util.Log;
import android.webkit.MimeTypeMap;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;

import libcore.io.IoUtils;

@@ -450,7 +451,11 @@ public abstract class FileSystemProvider extends DocumentsProvider {

    @Override
    public String getDocumentType(String documentId) throws FileNotFoundException {
        final File file = getFileForDocId(documentId);
        return getDocumentType(documentId, getFileForDocId(documentId));
    }

    private String getDocumentType(final String documentId, final File file)
            throws FileNotFoundException {
        if (file.isDirectory()) {
            return Document.MIME_TYPE_DIR;
        } else {
@@ -532,18 +537,25 @@ public abstract class FileSystemProvider extends DocumentsProvider {
        return DocumentsContract.openImageThumbnail(file);
    }

    protected RowBuilder includeFile(MatrixCursor result, String docId, File file)
    protected RowBuilder includeFile(final MatrixCursor result, String docId, File file)
            throws FileNotFoundException {
        final String[] columns = result.getColumnNames();
        final RowBuilder row = result.newRow();

        if (docId == null) {
            docId = getDocIdForFile(file);
        } else {
            file = getFileForDocId(docId);
        }
        final String mimeType = getDocumentType(docId, file);
        row.add(Document.COLUMN_DOCUMENT_ID, docId);
        row.add(Document.COLUMN_MIME_TYPE, mimeType);

        final int flagIndex = ArrayUtils.indexOf(columns, Document.COLUMN_FLAGS);
        if (flagIndex != -1) {
            int flags = 0;

            if (file.canWrite()) {
            if (file.isDirectory()) {
                if (mimeType.equals(Document.MIME_TYPE_DIR)) {
                    flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
                    flags |= Document.FLAG_SUPPORTS_DELETE;
                    flags |= Document.FLAG_SUPPORTS_RENAME;
@@ -556,8 +568,6 @@ public abstract class FileSystemProvider extends DocumentsProvider {
                }
            }

        final String mimeType = getDocumentType(docId);
        final String displayName = file.getName();
            if (mimeType.startsWith("image/")) {
                flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
            }
@@ -565,18 +575,25 @@ public abstract class FileSystemProvider extends DocumentsProvider {
            if (typeSupportsMetadata(mimeType)) {
                flags |= Document.FLAG_SUPPORTS_METADATA;
            }
            row.add(flagIndex, flags);
        }

        final RowBuilder row = result.newRow();
        row.add(Document.COLUMN_DOCUMENT_ID, docId);
        row.add(Document.COLUMN_DISPLAY_NAME, displayName);
        row.add(Document.COLUMN_SIZE, file.length());
        row.add(Document.COLUMN_MIME_TYPE, mimeType);
        row.add(Document.COLUMN_FLAGS, flags);
        final int displayNameIndex = ArrayUtils.indexOf(columns, Document.COLUMN_DISPLAY_NAME);
        if (displayNameIndex != -1) {
            row.add(displayNameIndex, file.getName());
        }

        final int lastModifiedIndex = ArrayUtils.indexOf(columns, Document.COLUMN_LAST_MODIFIED);
        if (lastModifiedIndex != -1) {
            final long lastModified = file.lastModified();
            // Only publish dates reasonably after epoch
        long lastModified = file.lastModified();
            if (lastModified > 31536000000L) {
            row.add(Document.COLUMN_LAST_MODIFIED, lastModified);
                row.add(lastModifiedIndex, lastModified);
            }
        }
        final int sizeIndex = ArrayUtils.indexOf(columns, Document.COLUMN_SIZE);
        if (sizeIndex != -1) {
            row.add(sizeIndex, file.length());
        }

        // Return the row builder just in case any subclass want to add more stuff to it.