diff options
-rw-r--r-- | api/current.txt | 6 | ||||
-rw-r--r-- | core/java/android/app/ActivityManagerNative.java | 31 | ||||
-rw-r--r-- | core/java/android/app/IActivityManager.java | 5 | ||||
-rw-r--r-- | core/java/android/content/ContentResolver.java | 57 | ||||
-rw-r--r-- | core/java/android/provider/DocumentsContract.java | 46 | ||||
-rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 64 |
6 files changed, 194 insertions, 15 deletions
diff --git a/api/current.txt b/api/current.txt index ed893a2..6026cc5 100644 --- a/api/current.txt +++ b/api/current.txt @@ -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(); @@ -20353,6 +20355,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); @@ -23369,11 +23372,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(); diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index cc964c2..6a29552 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -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; } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 3851eb3..6d1b6fc 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -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; } diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 243c91a..45fed2d 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -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; @@ -1389,6 +1388,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 * types may be used in the extras bundle: diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index acaed73..53f5d58 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -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); } @@ -359,6 +369,36 @@ public final class DocumentsContract { } /** + * 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 * {@link #FLAG_SUPPORTS_THUMBNAIL} set. diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 49e8642..a6d7e3c 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -36,6 +36,7 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.ProcessCpuTracker; import com.android.internal.os.TransferPipe; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.MemInfoReader; @@ -705,8 +706,8 @@ public final class ActivityManagerService extends ActivityManagerNative * to {@link UriPermission#uri} to {@link UriPermission}. */ @GuardedBy("this") - private final SparseArray<HashMap<Uri, UriPermission>> - mGrantedUriPermissions = new SparseArray<HashMap<Uri, UriPermission>>(); + private final SparseArray<ArrayMap<Uri, UriPermission>> + mGrantedUriPermissions = new SparseArray<ArrayMap<Uri, UriPermission>>(); CoreSettingsObserver mCoreSettingsObserver; @@ -5446,9 +5447,9 @@ public final class ActivityManagerService extends ActivityManagerNative private UriPermission findOrCreateUriPermissionLocked( String sourcePkg, String targetPkg, int targetUid, Uri uri) { - HashMap<Uri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid); + ArrayMap<Uri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid); if (targetUris == null) { - targetUris = Maps.newHashMap(); + targetUris = Maps.newArrayMap(); mGrantedUriPermissions.put(targetUid, targetUris); } @@ -5467,7 +5468,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (uid == 0) { return true; } - HashMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(uid); + ArrayMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(uid); if (perms == null) return false; UriPermission perm = perms.get(uri); if (perm == null) return false; @@ -5794,7 +5795,7 @@ public final class ActivityManagerService extends ActivityManagerNative void removeUriPermissionIfNeededLocked(UriPermission perm) { if ((perm.modeFlags&(Intent.FLAG_GRANT_READ_URI_PERMISSION |Intent.FLAG_GRANT_WRITE_URI_PERMISSION)) == 0) { - HashMap<Uri, UriPermission> perms + ArrayMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(perm.targetUid); if (perms != null) { if (DEBUG_URI_PERMISSION) Slog.v(TAG, @@ -5837,7 +5838,7 @@ public final class ActivityManagerService extends ActivityManagerNative final int NS = SEGMENTS.size(); int N = mGrantedUriPermissions.size(); for (int i=0; i<N; i++) { - HashMap<Uri, UriPermission> perms + ArrayMap<Uri, UriPermission> perms = mGrantedUriPermissions.valueAt(i); Iterator<UriPermission> it = perms.values().iterator(); toploop: @@ -6115,6 +6116,51 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override + public Uri[] getGrantedUriPermissions( + String sourcePackage, String targetPackage, int modeFlags, int modeMask) { + enforceNotIsolatedCaller("getGrantedUriPermissions"); + synchronized (this) { + // Verify that caller owns at least one of the requested packages + final int uid = Binder.getCallingUid(); + final IPackageManager pm = AppGlobals.getPackageManager(); + final String[] callerPackages; + try { + callerPackages = pm.getPackagesForUid(uid); + } catch (RemoteException e) { + throw new SecurityException("Failed to find packages for UID " + uid); + } + final boolean callerOwnsSource = sourcePackage != null + && ArrayUtils.contains(callerPackages, sourcePackage); + final boolean callerOwnsTarget = targetPackage != null + && ArrayUtils.contains(callerPackages, targetPackage); + if (!(callerOwnsSource || callerOwnsTarget)) { + throw new SecurityException("Caller " + Arrays.toString(callerPackages) + + " doesn't own " + sourcePackage + " or " + targetPackage); + } + + final ArrayList<Uri> result = Lists.newArrayList(); + final int size = mGrantedUriPermissions.size(); + for (int i = 0; i < size; i++) { + final ArrayMap<Uri, UriPermission> map = mGrantedUriPermissions.valueAt(i); + final int mapSize = map.size(); + for (int j = 0; j < mapSize; j++) { + final UriPermission perm = map.valueAt(j); + final boolean sourceMatch = sourcePackage == null + || sourcePackage.equals(perm.sourcePkg); + final boolean targetMatch = targetPackage == null + || targetPackage.equals(perm.targetPkg); + final boolean modeMatch = (perm.modeFlags & modeMask) == modeFlags; + if (sourceMatch && targetMatch && modeMatch) { + result.add(perm.uri); + } + } + } + + return result.toArray(new Uri[result.size()]); + } + } + + @Override public void showWaitingForDebugger(IApplicationThread who, boolean waiting) { synchronized (this) { ProcessRecord app = @@ -6830,7 +6876,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - HashMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(callingUid); + ArrayMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(callingUid); if (perms != null) { for (Map.Entry<Uri, UriPermission> uri : perms.entrySet()) { if (uri.getKey().getAuthority().equals(cpi.authority)) { @@ -10731,7 +10777,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (dumpUid >= -1 && UserHandle.getAppId(uid) != dumpUid) { continue; } - HashMap<Uri, UriPermission> perms + ArrayMap<Uri, UriPermission> perms = mGrantedUriPermissions.valueAt(i); if (!printed) { if (needSep) pw.println(); |