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

Commit 08da7a11 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

API to discover granted Uri permissions.

Now that granted Uri permissions can be persisted across reboots,
offer APIs to discover them. Returns incoming or outgoing grants
matching the requested flags and mask. Add helper method to discover
"open" documents using this new API and filtering for non-documents.

Require that callers own at least of the filtering packages to avoid
exposing all grants. Switch internal grant tracking to use ArrayMap.

Change-Id: I0a755f221d0d160b411f8d3cfc48279b64345733
parent b9be11c3
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -5587,8 +5587,10 @@ package android.content {
    method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
    method public static deprecated android.content.SyncInfo getCurrentSync();
    method public static java.util.List<android.content.SyncInfo> getCurrentSyncs();
    method public android.net.Uri[] getIncomingUriPermissionGrants(int, int);
    method public static int getIsSyncable(android.accounts.Account, java.lang.String);
    method public static boolean getMasterSyncAutomatically();
    method public android.net.Uri[] getOutgoingUriPermissionGrants(int, int);
    method public static java.util.List<android.content.PeriodicSync> getPeriodicSyncs(android.accounts.Account, java.lang.String);
    method public java.lang.String[] getStreamTypes(android.net.Uri, java.lang.String);
    method public static android.content.SyncAdapterType[] getSyncAdapterTypes();
@@ -20320,6 +20322,7 @@ package android.provider {
    method public static android.net.Uri buildSearchUri(java.lang.String, java.lang.String, java.lang.String, java.lang.String);
    method public static android.net.Uri buildSearchUri(android.net.Uri, java.lang.String);
    method public static java.lang.String getDocId(android.net.Uri);
    method public static android.net.Uri[] getOpenDocuments(android.content.Context);
    method public static java.lang.String getRootId(android.net.Uri);
    method public static java.lang.String getSearchQuery(android.net.Uri);
    method public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point);
@@ -23335,11 +23338,14 @@ package android.test.mock {
    method public java.io.File getDatabasePath(java.lang.String);
    method public java.io.File getDir(java.lang.String, int);
    method public java.io.File getExternalCacheDir();
    method public java.io.File[] getExternalCacheDirs();
    method public java.io.File getExternalFilesDir(java.lang.String);
    method public java.io.File[] getExternalFilesDirs(java.lang.String);
    method public java.io.File getFileStreamPath(java.lang.String);
    method public java.io.File getFilesDir();
    method public android.os.Looper getMainLooper();
    method public java.io.File getObbDir();
    method public java.io.File[] getObbDirs();
    method public java.lang.String getPackageCodePath();
    method public android.content.pm.PackageManager getPackageManager();
    method public java.lang.String getPackageName();
+31 −0
Original line number Diff line number Diff line
@@ -1980,6 +1980,19 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
            reply.writeNoException();
            return true;
        }

        case GET_GRANTED_URI_PERMISSIONS_TRANSACTION: {
            data.enforceInterface(IActivityManager.descriptor);
            final String sourcePackage = data.readString();
            final String targetPackage = data.readString();
            final int modeFlags = data.readInt();
            final int modeMask = data.readInt();
            final Uri[] uris = getGrantedUriPermissions(
                    sourcePackage, targetPackage, modeFlags, modeMask);
            reply.writeNoException();
            reply.writeParcelableArray(uris, 0);
            return true;
        }
        }

        return super.onTransact(code, data, reply, flags);
@@ -4540,5 +4553,23 @@ class ActivityManagerProxy implements IActivityManager
        reply.recycle();
    }

    public Uri[] getGrantedUriPermissions(
            String sourcePackage, String targetPackage, int modeFlags, int modeMask)
            throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeString(sourcePackage);
        data.writeString(targetPackage);
        data.writeInt(modeFlags);
        data.writeInt(modeMask);
        mRemote.transact(GET_GRANTED_URI_PERMISSIONS_TRANSACTION, data, reply, 0);
        reply.readException();
        final Uri[] uris = (Uri[]) reply.readParcelableArray(null);
        data.recycle();
        reply.recycle();
        return uris;
    }

    private IBinder mRemote;
}
+5 −0
Original line number Diff line number Diff line
@@ -399,6 +399,10 @@ public interface IActivityManager extends IInterface {

    public void restart() throws RemoteException;

    public Uri[] getGrantedUriPermissions(
            String sourcePackage, String targetPackage, int modeFlags, int modeMask)
            throws RemoteException;

    /*
     * Private non-Binder interfaces
     */
@@ -680,4 +684,5 @@ public interface IActivityManager extends IInterface {
    int NOTIFY_ACTIVITY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+175;
    int REPORT_ACTIVITY_FULLY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+176;
    int RESTART_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+177;
    int GET_GRANTED_URI_PERMISSIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+178;
}
+54 −3
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package android.content;

import dalvik.system.CloseGuard;

import android.accounts.Account;
import android.app.ActivityManagerNative;
import android.app.ActivityThread;
@@ -44,7 +42,8 @@ import android.os.UserHandle;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
import android.util.Pair;

import dalvik.system.CloseGuard;

import java.io.File;
import java.io.FileInputStream;
@@ -1388,6 +1387,58 @@ public abstract class ContentResolver {
        }
    }

    /**
     * Return list of all Uri permissions that have been granted <em>to</em> the
     * calling package, and which exactly match the requested flags. For
     * example, to return all Uris that the calling application has
     * <em>non-persistent</em> read access to:
     *
     * <pre class="prettyprint">
     * getIncomingUriPermissionGrants(Intent.FLAG_GRANT_READ_URI_PERMISSION,
     *         Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
     * </pre>
     *
     * @param modeFlags any combination of
     *            {@link Intent#FLAG_GRANT_READ_URI_PERMISSION},
     *            {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, or
     *            {@link Intent#FLAG_PERSIST_GRANT_URI_PERMISSION}.
     * @param modeMask mask indicating which flags must match.
     */
    public Uri[] getIncomingUriPermissionGrants(int modeFlags, int modeMask) {
        try {
            return ActivityManagerNative.getDefault()
                    .getGrantedUriPermissions(null, getPackageName(), modeFlags, modeMask);
        } catch (RemoteException e) {
            return new Uri[0];
        }
    }

    /**
     * Return list of all Uri permissions that have been granted <em>from</em> the
     * calling package, and which exactly match the requested flags. For
     * example, to return all Uris that the calling application has granted
     * <em>non-persistent</em> read access to:
     *
     * <pre class="prettyprint">
     * getOutgoingUriPermissionGrants(Intent.FLAG_GRANT_READ_URI_PERMISSION,
     *         Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
     * </pre>
     *
     * @param modeFlags any combination of
     *            {@link Intent#FLAG_GRANT_READ_URI_PERMISSION},
     *            {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, or
     *            {@link Intent#FLAG_PERSIST_GRANT_URI_PERMISSION}.
     * @param modeMask mask indicating which flags must match.
     */
    public Uri[] getOutgoingUriPermissionGrants(int modeFlags, int modeMask) {
        try {
            return ActivityManagerNative.getDefault()
                    .getGrantedUriPermissions(getPackageName(), null, modeFlags, modeMask);
        } catch (RemoteException e) {
            return new Uri[0];
        }
    }

    /**
     * Start an asynchronous sync operation. If you want to monitor the progress
     * of the sync you may register a SyncObserver. Only values of the following
+43 −3
Original line number Diff line number Diff line
@@ -19,7 +19,9 @@ package android.provider;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
@@ -30,6 +32,8 @@ import android.net.Uri;
import android.os.Bundle;
import android.util.Log;

import com.google.android.collect.Lists;

import libcore.io.IoUtils;

import java.io.IOException;
@@ -201,19 +205,25 @@ public final class DocumentsContract {

    public static String getRootId(Uri documentUri) {
        final List<String> paths = documentUri.getPathSegments();
        if (paths.size() < 2) {
            throw new IllegalArgumentException("Not a root: " + documentUri);
        }
        if (!PATH_ROOTS.equals(paths.get(0))) {
            throw new IllegalArgumentException();
            throw new IllegalArgumentException("Not a root: " + documentUri);
        }
        return paths.get(1);
    }

    public static String getDocId(Uri documentUri) {
        final List<String> paths = documentUri.getPathSegments();
        if (paths.size() < 4) {
            throw new IllegalArgumentException("Not a document: " + documentUri);
        }
        if (!PATH_ROOTS.equals(paths.get(0))) {
            throw new IllegalArgumentException();
            throw new IllegalArgumentException("Not a document: " + documentUri);
        }
        if (!PATH_DOCS.equals(paths.get(2))) {
            throw new IllegalArgumentException();
            throw new IllegalArgumentException("Not a document: " + documentUri);
        }
        return paths.get(3);
    }
@@ -358,6 +368,36 @@ public final class DocumentsContract {
        public static final String AVAILABLE_BYTES = "available_bytes";
    }

    /**
     * Return list of all documents that the calling package has "open." These
     * are Uris matching {@link DocumentsContract} to which persistent
     * read/write access has been granted, usually through
     * {@link Intent#ACTION_OPEN_DOCUMENT} or
     * {@link Intent#ACTION_CREATE_DOCUMENT}.
     *
     * @see Context#grantUriPermission(String, Uri, int)
     * @see ContentResolver#getIncomingUriPermissionGrants(int, int)
     */
    public static Uri[] getOpenDocuments(Context context) {
        final int openedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION
                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION;
        final Uri[] uris = context.getContentResolver()
                .getIncomingUriPermissionGrants(openedFlags, openedFlags);

        // Filter to only include document providers
        final PackageManager pm = context.getPackageManager();
        final List<Uri> result = Lists.newArrayList();
        for (Uri uri : uris) {
            final ProviderInfo info = pm.resolveContentProvider(
                    uri.getAuthority(), PackageManager.GET_META_DATA);
            if (info.metaData.containsKey(META_DATA_DOCUMENT_PROVIDER)) {
                result.add(uri);
            }
        }

        return result.toArray(new Uri[result.size()]);
    }

    /**
     * Return thumbnail representing the document at the given URI. Callers are
     * responsible for their own caching. Given document must have
Loading