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

Commit 680b2ea6 authored by /e/ robot's avatar /e/ robot
Browse files

Merge remote-tracking branch 'origin/lineage-18.1' into v1-r

parents 67889067 ceec5980
Loading
Loading
Loading
Loading
+9 −0
Original line number Original line Diff line number Diff line
@@ -242,6 +242,7 @@ public abstract class Context {
            BIND_IMPORTANT,
            BIND_IMPORTANT,
            BIND_ADJUST_WITH_ACTIVITY,
            BIND_ADJUST_WITH_ACTIVITY,
            BIND_NOT_PERCEPTIBLE,
            BIND_NOT_PERCEPTIBLE,
            BIND_DENY_ACTIVITY_STARTS,
            BIND_INCLUDE_CAPABILITIES
            BIND_INCLUDE_CAPABILITIES
    })
    })
    @Retention(RetentionPolicy.SOURCE)
    @Retention(RetentionPolicy.SOURCE)
@@ -355,6 +356,14 @@ public abstract class Context {
    /***********    Public flags above this line ***********/
    /***********    Public flags above this line ***********/
    /***********    Hidden flags below this line ***********/
    /***********    Hidden flags below this line ***********/


    /**
     * Flag for {@link #bindService}: If binding from an app that is visible, the bound service is
     * allowed to start an activity from background. Add a flag so that this behavior can be opted
     * out.
     * @hide
     */
    public static final int BIND_DENY_ACTIVITY_STARTS = 0X000004000;

    /**
    /**
     * Flag for {@link #bindService}: This flag is intended to be used only by the system to adjust
     * Flag for {@link #bindService}: This flag is intended to be used only by the system to adjust
     * the scheduling policy for IMEs (and any other out-of-process user-visible components that
     * the scheduling policy for IMEs (and any other out-of-process user-visible components that
+92 −76
Original line number Original line Diff line number Diff line
@@ -63,13 +63,13 @@ import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.LinkedList;
import java.util.List;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Predicate;
import java.util.regex.Pattern;


/**
/**
 * A helper class for {@link android.provider.DocumentsProvider} to perform file operations on local
 * A helper class for {@link android.provider.DocumentsProvider} to perform file operations on local
@@ -87,6 +87,8 @@ public abstract class FileSystemProvider extends DocumentsProvider {
            DocumentsContract.QUERY_ARG_LAST_MODIFIED_AFTER,
            DocumentsContract.QUERY_ARG_LAST_MODIFIED_AFTER,
            DocumentsContract.QUERY_ARG_MIME_TYPES);
            DocumentsContract.QUERY_ARG_MIME_TYPES);


    private static final int MAX_RESULTS_NUMBER = 23;

    private static String joinNewline(String... args) {
    private static String joinNewline(String... args) {
        return TextUtils.join("\n", args);
        return TextUtils.join("\n", args);
    }
    }
@@ -394,56 +396,53 @@ public abstract class FileSystemProvider extends DocumentsProvider {
    }
    }


    /**
    /**
     * This method is similar to
     * WARNING: this method should really be {@code final}, but for the backward compatibility it's
     * {@link DocumentsProvider#queryChildDocuments(String, String[], String)}. This method returns
     * not; new classes that extend {@link FileSystemProvider} should override
     * all children documents including hidden directories/files.
     * {@link #queryChildDocuments(String, String[], String, boolean)}, not this method.
     *
     * <p>
     * In a scoped storage world, access to "Android/data" style directories are hidden for privacy
     * reasons. This method may show privacy sensitive data, so its usage should only be in
     * restricted modes.
     *
     * @param parentDocumentId the directory to return children for.
     * @param projection list of {@link Document} columns to put into the
     *            cursor. If {@code null} all supported columns should be
     *            included.
     * @param sortOrder how to order the rows, formatted as an SQL
     *            {@code ORDER BY} clause (excluding the ORDER BY itself).
     *            Passing {@code null} will use the default sort order, which
     *            may be unordered. This ordering is a hint that can be used to
     *            prioritize how data is fetched from the network, but UI may
     *            always enforce a specific ordering
     * @throws FileNotFoundException when parent document doesn't exist or query fails
     */
     */
    protected Cursor queryChildDocumentsShowAll(
    @Override
            String parentDocumentId, String[] projection, String sortOrder)
    public Cursor queryChildDocuments(String documentId, String[] projection, String sortOrder)
            throws FileNotFoundException {
            throws FileNotFoundException {
        return queryChildDocuments(parentDocumentId, projection, sortOrder, File -> true);
        return queryChildDocuments(documentId, projection, sortOrder, /* includeHidden */ false);
    }
    }


    /**
     * This method is similar to {@link #queryChildDocuments(String, String[], String)}, however, it
     * could return <b>all</b> content of the directory, <b>including restricted (hidden)
     * directories and files</b>.
     * <p>
     * In the scoped storage world, some directories and files (e.g. {@code Android/data/} and
     * {@code Android/obb/} on the external storage) are hidden for privacy reasons.
     * Hence, this method may reveal privacy-sensitive data, thus should be used with extra care.
     */
    @Override
    @Override
    public Cursor queryChildDocuments(
    public final Cursor queryChildDocumentsForManage(String documentId, String[] projection,
            String parentDocumentId, String[] projection, String sortOrder)
            String sortOrder) throws FileNotFoundException {
            throws FileNotFoundException {
        return queryChildDocuments(documentId, projection, sortOrder, /* includeHidden */ true);
        // Access to some directories is hidden for privacy reasons.
        return queryChildDocuments(parentDocumentId, projection, sortOrder, this::shouldShow);
    }
    }


    private Cursor queryChildDocuments(
    protected Cursor queryChildDocuments(String documentId, String[] projection, String sortOrder,
            String parentDocumentId, String[] projection, String sortOrder,
            boolean includeHidden) throws FileNotFoundException {
            @NonNull Predicate<File> filter) throws FileNotFoundException {
        final File parent = getFileForDocId(documentId);
        final File parent = getFileForDocId(parentDocumentId);
        final MatrixCursor result = new DirectoryCursor(
        final MatrixCursor result = new DirectoryCursor(
                resolveProjection(projection), parentDocumentId, parent);
                resolveProjection(projection), documentId, parent);
        if (parent.isDirectory()) {

            for (File file : FileUtils.listFilesOrEmpty(parent)) {
        if (!parent.isDirectory()) {
                if (filter.test(file)) {
            Log.w(TAG, '"' + documentId + "\" is not a directory");
                    includeFile(result, null, file);
            return result;
        }
        }

        if (!includeHidden && shouldHideDocument(documentId)) {
            Log.w(TAG, "Queried directory \"" + documentId + "\" is hidden");
            return result;
        }
        }
        } else {

            Log.w(TAG, "parentDocumentId '" + parentDocumentId + "' is not Directory");
        for (File file : FileUtils.listFilesOrEmpty(parent)) {
            if (!includeHidden && shouldHideDocument(file)) continue;

            includeFile(result, null, file);
        }
        }

        return result;
        return result;
    }
    }


@@ -465,23 +464,29 @@ public abstract class FileSystemProvider extends DocumentsProvider {
     *
     *
     * @see ContentResolver#EXTRA_HONORED_ARGS
     * @see ContentResolver#EXTRA_HONORED_ARGS
     */
     */
    protected final Cursor querySearchDocuments(
    protected final Cursor querySearchDocuments(File folder, String[] projection,
            File folder, String[] projection, Set<String> exclusion, Bundle queryArgs)
            Set<String> exclusion, Bundle queryArgs) throws FileNotFoundException {
            throws FileNotFoundException {
        final MatrixCursor result = new MatrixCursor(resolveProjection(projection));
        final MatrixCursor result = new MatrixCursor(resolveProjection(projection));
        final LinkedList<File> pending = new LinkedList<>();

        pending.add(folder);
        // We'll be a running a BFS here.
        while (!pending.isEmpty() && result.getCount() < 24) {
        final Queue<File> pending = new ArrayDeque<>();
            final File file = pending.removeFirst();
        pending.offer(folder);
            if (shouldHide(file)) continue;

        while (!pending.isEmpty() && result.getCount() < MAX_RESULTS_NUMBER) {
            final File file = pending.poll();

            // Skip hidden documents (both files and directories)
            if (shouldHideDocument(file)) continue;


            if (file.isDirectory()) {
            if (file.isDirectory()) {
                for (File child : FileUtils.listFilesOrEmpty(file)) {
                for (File child : FileUtils.listFilesOrEmpty(file)) {
                    pending.add(child);
                    pending.offer(child);
                }
                }
            }
            }
            if (!exclusion.contains(file.getAbsolutePath()) && matchSearchQueryArguments(file,

                    queryArgs)) {
            if (exclusion.contains(file.getAbsolutePath())) continue;

            if (matchSearchQueryArguments(file, queryArgs)) {
                includeFile(result, null, file);
                includeFile(result, null, file);
            }
            }
        }
        }
@@ -600,26 +605,23 @@ public abstract class FileSystemProvider extends DocumentsProvider {


        final int flagIndex = ArrayUtils.indexOf(columns, Document.COLUMN_FLAGS);
        final int flagIndex = ArrayUtils.indexOf(columns, Document.COLUMN_FLAGS);
        if (flagIndex != -1) {
        if (flagIndex != -1) {
            final boolean isDir = mimeType.equals(Document.MIME_TYPE_DIR);
            int flags = 0;
            int flags = 0;
            if (file.canWrite()) {
            if (file.canWrite()) {
                if (mimeType.equals(Document.MIME_TYPE_DIR)) {
                    flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
                flags |= Document.FLAG_SUPPORTS_DELETE;
                flags |= Document.FLAG_SUPPORTS_DELETE;
                flags |= Document.FLAG_SUPPORTS_RENAME;
                flags |= Document.FLAG_SUPPORTS_RENAME;
                flags |= Document.FLAG_SUPPORTS_MOVE;
                flags |= Document.FLAG_SUPPORTS_MOVE;

                if (isDir) {
                    if (shouldBlockFromTree(docId)) {
                    flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
                        flags |= Document.FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE;
                    }

                } else {
                } else {
                    flags |= Document.FLAG_SUPPORTS_WRITE;
                    flags |= Document.FLAG_SUPPORTS_WRITE;
                    flags |= Document.FLAG_SUPPORTS_DELETE;
                    flags |= Document.FLAG_SUPPORTS_RENAME;
                    flags |= Document.FLAG_SUPPORTS_MOVE;
                }
                }
            }
            }


            if (isDir && shouldBlockDirectoryFromTree(docId)) {
                flags |= Document.FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE;
            }

            if (mimeType.startsWith("image/")) {
            if (mimeType.startsWith("image/")) {
                flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
                flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
            }
            }
@@ -652,22 +654,36 @@ public abstract class FileSystemProvider extends DocumentsProvider {
        return row;
        return row;
    }
    }


    private static final Pattern PATTERN_HIDDEN_PATH = Pattern.compile(
            "(?i)^/storage/[^/]+/(?:[0-9]+/)?Android/(?:data|obb|sandbox)$");

    /**
    /**
     * In a scoped storage world, access to "Android/data" style directories are
     * Some providers may want to restrict access to certain directories and files,
     * hidden for privacy reasons.
     * e.g. <i>"Android/data"</i> and <i>"Android/obb"</i> on the shared storage for
     * privacy reasons.
     * Such providers should override this method.
     */
     */
    protected boolean shouldHide(@NonNull File file) {
    protected boolean shouldHideDocument(@NonNull String documentId)
        return (PATTERN_HIDDEN_PATH.matcher(file.getAbsolutePath()).matches());
            throws FileNotFoundException {
        return false;
    }
    }


    private boolean shouldShow(@NonNull File file) {
    /**
        return !shouldHide(file);
     * A variant of the {@link #shouldHideDocument(String)} that takes a {@link File} instead of
     * a {@link String} {@code documentId}.
     *
     * @see #shouldHideDocument(String)
     */
    protected final boolean shouldHideDocument(@NonNull File document)
            throws FileNotFoundException {
        return shouldHideDocument(getDocIdForFile(document));
    }
    }


    protected boolean shouldBlockFromTree(@NonNull String docId) {
    /**
     * @return if the directory that should be blocked from being selected when the user launches
     * an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} intent.
     *
     * @see Document#FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE
     */
    protected boolean shouldBlockDirectoryFromTree(@NonNull String documentId)
            throws FileNotFoundException {
        return false;
        return false;
    }
    }


+1 −0
Original line number Original line Diff line number Diff line
@@ -524,6 +524,7 @@ message ConnectionRecordProto {
        DEAD = 15;
        DEAD = 15;
        NOT_PERCEPTIBLE = 16;
        NOT_PERCEPTIBLE = 16;
        INCLUDE_CAPABILITIES = 17;
        INCLUDE_CAPABILITIES = 17;
        DENY_ACTIVITY_STARTS = 18;
    }
    }
    repeated Flag flags = 3;
    repeated Flag flags = 3;
    optional string service_name = 4;
    optional string service_name = 4;
+109 −53
Original line number Original line Diff line number Diff line
@@ -16,6 +16,8 @@


package com.android.externalstorage;
package com.android.externalstorage;


import static java.util.regex.Pattern.CASE_INSENSITIVE;

import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.app.usage.StorageStatsManager;
import android.app.usage.StorageStatsManager;
@@ -61,9 +63,22 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.Collections;
import java.util.List;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Objects;
import java.util.UUID;
import java.util.UUID;
import java.util.regex.Pattern;


/**
 * Presents content of the shared (a.k.a. "external") storage.
 * <p>
 * Starting with Android 11 (R), restricts access to the certain sections of the shared storage:
 * {@code Android/data/}, {@code Android/obb/} and {@code Android/sandbox/}, that will be hidden in
 * the DocumentsUI by default.
 * See <a href="https://developer.android.com/about/versions/11/privacy/storage">
 * Storage updates in Android 11</a>.
 * <p>
 * Documents ID format: {@code root:path/to/file}.
 */
public class ExternalStorageProvider extends FileSystemProvider {
public class ExternalStorageProvider extends FileSystemProvider {
    private static final String TAG = "ExternalStorage";
    private static final String TAG = "ExternalStorage";


@@ -74,7 +89,12 @@ public class ExternalStorageProvider extends FileSystemProvider {
    private static final Uri BASE_URI =
    private static final Uri BASE_URI =
            new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY).build();
            new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY).build();


    // docId format: root:path/to/file
    /**
     * Regex for detecting {@code /Android/data/}, {@code /Android/obb/} and
     * {@code /Android/sandbox/} along with all their subdirectories and content.
     */
    private static final Pattern PATTERN_RESTRICTED_ANDROID_SUBTREES =
            Pattern.compile("^Android/(?:data|obb|sandbox)(?:/.+)?", CASE_INSENSITIVE);


    private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
    private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
            Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
            Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
@@ -276,70 +296,91 @@ public class ExternalStorageProvider extends FileSystemProvider {
        return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
        return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
    }
    }


    /**
     * Mark {@code Android/data/}, {@code Android/obb/} and {@code Android/sandbox/} on the
     * integrated shared ("external") storage along with all their content and subdirectories as
     * hidden.
     */
    @Override
    @Override
    public Cursor queryChildDocumentsForManage(
    protected boolean shouldHideDocument(@NonNull String documentId) {
            String parentDocId, String[] projection, String sortOrder)
        // Don't need to hide anything on USB drives.
            throws FileNotFoundException {
        if (isOnRemovableUsbStorage(documentId)) {
        return queryChildDocumentsShowAll(parentDocId, projection, sortOrder);
            return false;
        }

        final String path = getPathFromDocId(documentId);
        return PATTERN_RESTRICTED_ANDROID_SUBTREES.matcher(path).matches();
    }
    }


    /**
    /**
     * Check that the directory is the root of storage or blocked file from tree.
     * Check that the directory is the root of storage or blocked file from tree.
     * <p>
     * Note, that this is different from hidden documents: blocked documents <b>WILL</b> appear
     * the UI, but the user <b>WILL NOT</b> be able to select them.
     *
     *
     * @param docId the docId of the directory to be checked
     * @param documentId the docId of the directory to be checked
     * @return true, should be blocked from tree. Otherwise, false.
     * @return true, should be blocked from tree. Otherwise, false.
     *
     * @see Document#FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE
     */
     */
    @Override
    @Override
    protected boolean shouldBlockFromTree(@NonNull String docId) {
    protected boolean shouldBlockDirectoryFromTree(@NonNull String documentId)
        try {
            throws FileNotFoundException {
            final File dir = getFileForDocId(docId, false /* visible */);
        final File dir = getFileForDocId(documentId, false);

        // The file is null or it is not a directory
            // the file is null or it is not a directory
        if (dir == null || !dir.isDirectory()) {
        if (dir == null || !dir.isDirectory()) {
            return false;
            return false;
        }
        }


        // Allow all directories on USB, including the root.
        // Allow all directories on USB, including the root.
            try {
        if (isOnRemovableUsbStorage(documentId)) {
                RootInfo rootInfo = getRootFromDocId(docId);
                if ((rootInfo.flags & Root.FLAG_REMOVABLE_USB) == Root.FLAG_REMOVABLE_USB) {
            return false;
            return false;
        }
        }
            } catch (FileNotFoundException e) {
                Log.e(TAG, "Failed to determine rootInfo for docId");
            }


            final String path = getPathFromDocId(docId);
        // Get canonical(!) path. Note that this path will have neither leading nor training "/".
        // This the root's path will be just an empty string.
        final String path = getPathFromDocId(documentId);


        // Block the root of the storage
        // Block the root of the storage
        if (path.isEmpty()) {
        if (path.isEmpty()) {
            return true;
            return true;
        }
        }


            // Block Download folder from tree
        // Block /Download/ and /Android/ folders from the tree.
            if (TextUtils.equals(Environment.DIRECTORY_DOWNLOADS.toLowerCase(),
        if (equalIgnoringCase(path, Environment.DIRECTORY_DOWNLOADS) ||
                    path.toLowerCase())) {
                equalIgnoringCase(path, Environment.DIRECTORY_ANDROID)) {
            return true;
            return true;
        }
        }


            if (TextUtils.equals(Environment.DIRECTORY_ANDROID.toLowerCase(),
        // This shouldn't really make a difference, but just in case - let's block hidden
                    path.toLowerCase())) {
        // directories as well.
        if (shouldHideDocument(documentId)) {
            return true;
            return true;
        }
        }


        return false;
        return false;
        } catch (IOException e) {
            throw new IllegalArgumentException(
                    "Failed to determine if " + docId + " should block from tree " + ": " + e);
    }
    }

    private boolean isOnRemovableUsbStorage(@NonNull String documentId) {
        final RootInfo rootInfo;
        try {
            rootInfo = getRootFromDocId(documentId);
        } catch (FileNotFoundException e) {
            Log.e(TAG, "Failed to determine rootInfo for docId\"" + documentId + '"');
            return false;
        }
        }


        return (rootInfo.flags & Root.FLAG_REMOVABLE_USB) != 0;
    }

    @NonNull
    @Override
    @Override
    protected String getDocIdForFile(File file) throws FileNotFoundException {
    protected String getDocIdForFile(@NonNull File file) throws FileNotFoundException {
        return getDocIdForFileMaybeCreate(file, false);
        return getDocIdForFileMaybeCreate(file, false);
    }
    }


    private String getDocIdForFileMaybeCreate(File file, boolean createNewDir)
    @NonNull
    private String getDocIdForFileMaybeCreate(@NonNull File file, boolean createNewDir)
            throws FileNotFoundException {
            throws FileNotFoundException {
        String path = file.getAbsolutePath();
        String path = file.getAbsolutePath();


@@ -409,26 +450,30 @@ public class ExternalStorageProvider extends FileSystemProvider {
    private File getFileForDocId(String docId, boolean visible, boolean mustExist)
    private File getFileForDocId(String docId, boolean visible, boolean mustExist)
            throws FileNotFoundException {
            throws FileNotFoundException {
        RootInfo root = getRootFromDocId(docId);
        RootInfo root = getRootFromDocId(docId);
        return buildFile(root, docId, visible, mustExist);
        return buildFile(root, docId, mustExist);
    }
    }


    private Pair<RootInfo, File> resolveDocId(String docId, boolean visible)
    private Pair<RootInfo, File> resolveDocId(String docId) throws FileNotFoundException {
            throws FileNotFoundException {
        RootInfo root = getRootFromDocId(docId);
        RootInfo root = getRootFromDocId(docId);
        return Pair.create(root, buildFile(root, docId, visible, true));
        return Pair.create(root, buildFile(root, docId, /* mustExist */ true));
    }
    }


    @VisibleForTesting
    @VisibleForTesting
    static String getPathFromDocId(String docId) {
    static String getPathFromDocId(String docId) {
        final int splitIndex = docId.indexOf(':', 1);
        final int splitIndex = docId.indexOf(':', 1);
        final String path = docId.substring(splitIndex + 1);
        final String docIdPath = docId.substring(splitIndex + 1);


        if (path.isEmpty()) {
        // Canonicalize path and strip the leading "/"
            return path;
        final String path;
        try {
            path = new File(docIdPath).getCanonicalPath().substring(1);
        } catch (IOException e) {
            Log.w(TAG, "Could not canonicalize \"" + docIdPath + '"');
            return "";
        }
        }


        // remove trailing "/"
        // Remove the trailing "/" as well.
        if (path.charAt(path.length() - 1) == '/') {
        if (!path.isEmpty() && path.charAt(path.length() - 1) == '/') {
            return path.substring(0, path.length() - 1);
            return path.substring(0, path.length() - 1);
        } else {
        } else {
            return path;
            return path;
@@ -450,7 +495,7 @@ public class ExternalStorageProvider extends FileSystemProvider {
        return root;
        return root;
    }
    }


    private File buildFile(RootInfo root, String docId, boolean visible, boolean mustExist)
    private File buildFile(RootInfo root, String docId, boolean mustExist)
            throws FileNotFoundException {
            throws FileNotFoundException {
        final int splitIndex = docId.indexOf(':', 1);
        final int splitIndex = docId.indexOf(':', 1);
        final String path = docId.substring(splitIndex + 1);
        final String path = docId.substring(splitIndex + 1);
@@ -529,7 +574,7 @@ public class ExternalStorageProvider extends FileSystemProvider {
    @Override
    @Override
    public Path findDocumentPath(@Nullable String parentDocId, String childDocId)
    public Path findDocumentPath(@Nullable String parentDocId, String childDocId)
            throws FileNotFoundException {
            throws FileNotFoundException {
        final Pair<RootInfo, File> resolvedDocId = resolveDocId(childDocId, false);
        final Pair<RootInfo, File> resolvedDocId = resolveDocId(childDocId);
        final RootInfo root = resolvedDocId.first;
        final RootInfo root = resolvedDocId.first;
        File child = resolvedDocId.second;
        File child = resolvedDocId.second;


@@ -633,6 +678,13 @@ public class ExternalStorageProvider extends FileSystemProvider {
        }
        }
    }
    }


    /**
     * Print the state into the given stream.
     * Gets invoked when you run:
     * <pre>
     * adb shell dumpsys activity provider com.android.externalstorage/.ExternalStorageProvider
     * </pre>
     */
    @Override
    @Override
    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
        final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ", 160);
        final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ", 160);
@@ -716,4 +768,8 @@ public class ExternalStorageProvider extends FileSystemProvider {
        }
        }
        return bundle;
        return bundle;
    }
    }

    private static boolean equalIgnoringCase(@NonNull String a, @NonNull String b) {
        return TextUtils.equals(a.toLowerCase(Locale.ROOT), b.toLowerCase(Locale.ROOT));
    }
}
}
+5 −0
Original line number Original line Diff line number Diff line
@@ -220,6 +220,11 @@ public class TileLifecycleManager extends BroadcastReceiver implements
        handlePendingMessages();
        handlePendingMessages();
    }
    }


    @Override
    public void onNullBinding(ComponentName name) {
        setBindService(false);
    }

    @Override
    @Override
    public void onServiceDisconnected(ComponentName name) {
    public void onServiceDisconnected(ComponentName name) {
        if (DEBUG) Log.d(TAG, "onServiceDisconnected " + name);
        if (DEBUG) Log.d(TAG, "onServiceDisconnected " + name);
Loading