diff options
author | Jeff Sharkey <jsharkey@android.com> | 2014-04-04 12:12:41 -0700 |
---|---|---|
committer | Jeff Sharkey <jsharkey@android.com> | 2014-04-22 21:06:49 -0700 |
commit | 846318a3250fa95f47a9decfbffb05a31dbd0006 (patch) | |
tree | c5d28f5d77e1fd625117e65bc9d24acfdfb6b94e | |
parent | fb5d9f338153f569775bbc76177cab05441999c1 (diff) | |
download | frameworks_base-846318a3250fa95f47a9decfbffb05a31dbd0006.zip frameworks_base-846318a3250fa95f47a9decfbffb05a31dbd0006.tar.gz frameworks_base-846318a3250fa95f47a9decfbffb05a31dbd0006.tar.bz2 |
Allow prefix-based Uri permission grants.
Define new FLAG_GRANT_PREFIX_URI_PERMISSION which indicates that a
Uri permission grant should also apply to any other Uris that have
matching scheme, authority, and path segments. For example, a prefix
grant for /foo/ would allow /foo/bar/ but not /foo2/.
Allow persistable and prefix grants to be issued directly through
grantUriPermission(). Relaxing persistable is fine, since it still
requires the receiver to actively take the permission.
Since exact- and prefix-match grants for the same Uri can coexist,
we track them separately using a new UriGrant key. (Consider the
case where an app separately extends READ|PREFIX and WRITE for
the same Uri: we can't let that become READ|WRITE|PREFIX.)
Fix revoke to always take away persisted permissions. Move prefix
matching logic to Uri and add tests. Add new flags to "am" tool, and
various internal uses around Intent and Context. Switch some lagging
users to ArraySet.
Bug: 10607375
Change-Id: Ia8ce2b88421ff9f2fe5a979a27a026fc445d46f1
-rw-r--r-- | api/current.txt | 1 | ||||
-rw-r--r-- | cmds/am/src/com/android/commands/am/Am.java | 5 | ||||
-rw-r--r-- | core/java/android/app/ContextImpl.java | 29 | ||||
-rw-r--r-- | core/java/android/content/ContentResolver.java | 4 | ||||
-rw-r--r-- | core/java/android/content/Context.java | 29 | ||||
-rw-r--r-- | core/java/android/content/Intent.java | 54 | ||||
-rw-r--r-- | core/java/android/net/Uri.java | 28 | ||||
-rw-r--r-- | core/tests/coretests/src/android/net/UriTest.java | 54 | ||||
-rw-r--r-- | services/core/java/com/android/server/am/ActivityManagerService.java | 311 | ||||
-rwxr-xr-x | services/core/java/com/android/server/am/ActivityRecord.java | 9 | ||||
-rw-r--r-- | services/core/java/com/android/server/am/ServiceRecord.java | 9 | ||||
-rw-r--r-- | services/core/java/com/android/server/am/UriPermission.java | 50 | ||||
-rw-r--r-- | services/core/java/com/android/server/am/UriPermissionOwner.java | 89 |
13 files changed, 435 insertions, 237 deletions
diff --git a/api/current.txt b/api/current.txt index 4e2a3bb..6c51201 100644 --- a/api/current.txt +++ b/api/current.txt @@ -7075,6 +7075,7 @@ package android.content { field public static final int FLAG_EXCLUDE_STOPPED_PACKAGES = 16; // 0x10 field public static final int FLAG_FROM_BACKGROUND = 4; // 0x4 field public static final int FLAG_GRANT_PERSISTABLE_URI_PERMISSION = 64; // 0x40 + field public static final int FLAG_GRANT_PREFIX_URI_PERMISSION = 128; // 0x80 field public static final int FLAG_GRANT_READ_URI_PERMISSION = 1; // 0x1 field public static final int FLAG_GRANT_WRITE_URI_PERMISSION = 2; // 0x2 field public static final int FLAG_INCLUDE_STOPPED_PACKAGES = 32; // 0x20 diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index 92cb52c..7df55a5 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -240,6 +240,7 @@ public class Am extends BaseCommand { " (to embed a comma into a string escape it using \"\\,\")\n" + " [-n <COMPONENT>] [-f <FLAGS>]\n" + " [--grant-read-uri-permission] [--grant-write-uri-permission]\n" + + " [--grant-persistable-uri-permission] [--grant-prefix-uri-permission]\n" + " [--debug-log-resolution] [--exclude-stopped-packages]\n" + " [--include-stopped-packages]\n" + " [--activity-brought-to-front] [--activity-clear-top]\n" + @@ -455,6 +456,10 @@ public class Am extends BaseCommand { intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } else if (opt.equals("--grant-write-uri-permission")) { intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + } else if (opt.equals("--grant-persistable-uri-permission")) { + intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); + } else if (opt.equals("--grant-prefix-uri-permission")) { + intent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION); } else if (opt.equals("--exclude-stopped-packages")) { intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES); } else if (opt.equals("--include-stopped-packages")) { diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index f1ce54a..fe532bf 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1865,17 +1865,26 @@ class ContextImpl extends Context { } private String uriModeFlagToString(int uriModeFlags) { - switch (uriModeFlags) { - case Intent.FLAG_GRANT_READ_URI_PERMISSION | - Intent.FLAG_GRANT_WRITE_URI_PERMISSION: - return "read and write"; - case Intent.FLAG_GRANT_READ_URI_PERMISSION: - return "read"; - case Intent.FLAG_GRANT_WRITE_URI_PERMISSION: - return "write"; + StringBuilder builder = new StringBuilder(); + if ((uriModeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) { + builder.append("read and "); + } + if ((uriModeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) { + builder.append("write and "); + } + if ((uriModeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0) { + builder.append("persistable and "); + } + if ((uriModeFlags & Intent.FLAG_GRANT_PREFIX_URI_PERMISSION) != 0) { + builder.append("prefix and "); + } + + if (builder.length() > 5) { + builder.setLength(builder.length() - 5); + return builder.toString(); + } else { + throw new IllegalArgumentException("Unknown permission mode flags: " + uriModeFlags); } - throw new IllegalArgumentException( - "Unknown permission mode flags: " + uriModeFlags); } private void enforceForUri( diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index f3c803d..5b41394 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -1641,7 +1641,7 @@ public abstract class ContentResolver { * * @see #getPersistedUriPermissions() */ - public void takePersistableUriPermission(Uri uri, int modeFlags) { + public void takePersistableUriPermission(Uri uri, @Intent.AccessUriMode int modeFlags) { try { ActivityManagerNative.getDefault().takePersistableUriPermission(uri, modeFlags); } catch (RemoteException e) { @@ -1656,7 +1656,7 @@ public abstract class ContentResolver { * * @see #getPersistedUriPermissions() */ - public void releasePersistableUriPermission(Uri uri, int modeFlags) { + public void releasePersistableUriPermission(Uri uri, @Intent.AccessUriMode int modeFlags) { try { ActivityManagerNative.getDefault().releasePersistableUriPermission(uri, modeFlags); } catch (RemoteException e) { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 906484a..cbb6cf5 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2791,9 +2791,13 @@ public abstract class Context { * @param uri The Uri you would like to grant access to. * @param modeFlags The desired access modes. Any combination of * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION - * Intent.FLAG_GRANT_READ_URI_PERMISSION} or + * Intent.FLAG_GRANT_READ_URI_PERMISSION}, * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION - * Intent.FLAG_GRANT_WRITE_URI_PERMISSION}. + * Intent.FLAG_GRANT_WRITE_URI_PERMISSION}, + * {@link Intent#FLAG_GRANT_PERSISTABLE_URI_PERMISSION + * Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION}, or + * {@link Intent#FLAG_GRANT_PREFIX_URI_PERMISSION + * Intent.FLAG_GRANT_PREFIX_URI_PERMISSION}. * * @see #revokeUriPermission */ @@ -2806,7 +2810,8 @@ public abstract class Context { * Uri will match all previously granted Uris that are the same or a * sub-path of the given Uri. That is, revoking "content://foo/target" will * revoke both "content://foo/target" and "content://foo/target/sub", but not - * "content://foo". + * "content://foo". It will not remove any prefix grants that exist at a + * higher level. * * @param uri The Uri you would like to revoke access to. * @param modeFlags The desired access modes. Any combination of @@ -2817,7 +2822,7 @@ public abstract class Context { * * @see #grantUriPermission */ - public abstract void revokeUriPermission(Uri uri, @Intent.GrantUriMode int modeFlags); + public abstract void revokeUriPermission(Uri uri, @Intent.AccessUriMode int modeFlags); /** * Determine whether a particular process and user ID has been granted @@ -2841,7 +2846,7 @@ public abstract class Context { * @see #checkCallingUriPermission */ public abstract int checkUriPermission(Uri uri, int pid, int uid, - @Intent.GrantUriMode int modeFlags); + @Intent.AccessUriMode int modeFlags); /** * Determine whether the calling process and user ID has been @@ -2864,7 +2869,7 @@ public abstract class Context { * * @see #checkUriPermission(Uri, int, int, int) */ - public abstract int checkCallingUriPermission(Uri uri, @Intent.GrantUriMode int modeFlags); + public abstract int checkCallingUriPermission(Uri uri, @Intent.AccessUriMode int modeFlags); /** * Determine whether the calling process of an IPC <em>or you</em> has been granted @@ -2884,7 +2889,7 @@ public abstract class Context { * @see #checkCallingUriPermission */ public abstract int checkCallingOrSelfUriPermission(Uri uri, - @Intent.GrantUriMode int modeFlags); + @Intent.AccessUriMode int modeFlags); /** * Check both a Uri and normal permission. This allows you to perform @@ -2910,7 +2915,7 @@ public abstract class Context { */ public abstract int checkUriPermission(@Nullable Uri uri, @Nullable String readPermission, @Nullable String writePermission, int pid, int uid, - @Intent.GrantUriMode int modeFlags); + @Intent.AccessUriMode int modeFlags); /** * If a particular process and user ID has not been granted @@ -2932,7 +2937,7 @@ public abstract class Context { * @see #checkUriPermission(Uri, int, int, int) */ public abstract void enforceUriPermission( - Uri uri, int pid, int uid, @Intent.GrantUriMode int modeFlags, String message); + Uri uri, int pid, int uid, @Intent.AccessUriMode int modeFlags, String message); /** * If the calling process and user ID has not been granted @@ -2954,7 +2959,7 @@ public abstract class Context { * @see #checkCallingUriPermission(Uri, int) */ public abstract void enforceCallingUriPermission( - Uri uri, @Intent.GrantUriMode int modeFlags, String message); + Uri uri, @Intent.AccessUriMode int modeFlags, String message); /** * If the calling process of an IPC <em>or you</em> has not been @@ -2973,7 +2978,7 @@ public abstract class Context { * @see #checkCallingOrSelfUriPermission(Uri, int) */ public abstract void enforceCallingOrSelfUriPermission( - Uri uri, @Intent.GrantUriMode int modeFlags, String message); + Uri uri, @Intent.AccessUriMode int modeFlags, String message); /** * Enforce both a Uri and normal permission. This allows you to perform @@ -2998,7 +3003,7 @@ public abstract class Context { */ public abstract void enforceUriPermission( @Nullable Uri uri, @Nullable String readPermission, - @Nullable String writePermission, int pid, int uid, @Intent.GrantUriMode int modeFlags, + @Nullable String writePermission, int pid, int uid, @Intent.AccessUriMode int modeFlags, @Nullable String message); /** @hide */ diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 421956b..67b6737 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -18,6 +18,7 @@ package android.content; import android.content.pm.ApplicationInfo; import android.util.ArraySet; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -864,8 +865,9 @@ public class Intent implements Parcelable, Cloneable { } // Migrate any clip data and flags from target. - int permFlags = target.getFlags() - & (FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION); + int permFlags = target.getFlags() & (FLAG_GRANT_READ_URI_PERMISSION + | FLAG_GRANT_WRITE_URI_PERMISSION | FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | FLAG_GRANT_PREFIX_URI_PERMISSION); if (permFlags != 0) { ClipData targetClipData = target.getClipData(); if (targetClipData == null && target.getData() != null) { @@ -3425,11 +3427,29 @@ public class Intent implements Parcelable, Cloneable { // Intent flags (see mFlags variable). /** @hide */ - @IntDef(flag = true, - value = {FLAG_GRANT_READ_URI_PERMISSION, FLAG_GRANT_WRITE_URI_PERMISSION}) + @IntDef(flag = true, value = { + FLAG_GRANT_READ_URI_PERMISSION, FLAG_GRANT_WRITE_URI_PERMISSION, + FLAG_GRANT_PERSISTABLE_URI_PERMISSION, FLAG_GRANT_PREFIX_URI_PERMISSION }) @Retention(RetentionPolicy.SOURCE) public @interface GrantUriMode {} + /** @hide */ + @IntDef(flag = true, value = { + FLAG_GRANT_READ_URI_PERMISSION, FLAG_GRANT_WRITE_URI_PERMISSION }) + @Retention(RetentionPolicy.SOURCE) + public @interface AccessUriMode {} + + /** + * Test if given mode flags specify an access mode, which must be at least + * read and/or write. + * + * @hide + */ + public static boolean isAccessUriMode(int modeFlags) { + return (modeFlags & (Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION)) != 0; + } + /** * If set, the recipient of this Intent will be granted permission to * perform read operations on the URI in the Intent's data and any URIs @@ -3491,6 +3511,17 @@ public class Intent implements Parcelable, Cloneable { public static final int FLAG_GRANT_PERSISTABLE_URI_PERMISSION = 0x00000040; /** + * When combined with {@link #FLAG_GRANT_READ_URI_PERMISSION} and/or + * {@link #FLAG_GRANT_WRITE_URI_PERMISSION}, the URI permission grant + * applies to any URI that is a prefix match against the original granted + * URI. (Without this flag, the URI must match exactly for access to be + * granted.) Another URI is considered a prefix match only when scheme, + * authority, and all path segments defined by the prefix are an exact + * match. + */ + public static final int FLAG_GRANT_PREFIX_URI_PERMISSION = 0x00000080; + + /** * If set, the new activity is not kept in the history stack. As soon as * the user navigates away from it, the activity is finished. This may also * be set with the {@link android.R.styleable#AndroidManifestActivity_noHistory @@ -3810,9 +3841,9 @@ public class Intent implements Parcelable, Cloneable { /** * @hide Flags that can't be changed with PendingIntent. */ - public static final int IMMUTABLE_FLAGS = - FLAG_GRANT_READ_URI_PERMISSION - | FLAG_GRANT_WRITE_URI_PERMISSION; + public static final int IMMUTABLE_FLAGS = FLAG_GRANT_READ_URI_PERMISSION + | FLAG_GRANT_WRITE_URI_PERMISSION | FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | FLAG_GRANT_PREFIX_URI_PERMISSION; // --------------------------------------------------------------------- // --------------------------------------------------------------------- @@ -6350,6 +6381,8 @@ public class Intent implements Parcelable, Cloneable { * * @see #FLAG_GRANT_READ_URI_PERMISSION * @see #FLAG_GRANT_WRITE_URI_PERMISSION + * @see #FLAG_GRANT_PERSISTABLE_URI_PERMISSION + * @see #FLAG_GRANT_PREFIX_URI_PERMISSION * @see #FLAG_DEBUG_LOG_RESOLUTION * @see #FLAG_FROM_BACKGROUND * @see #FLAG_ACTIVITY_BROUGHT_TO_FRONT @@ -7381,9 +7414,10 @@ public class Intent implements Parcelable, Cloneable { // Since we migrated in child, we need to promote ClipData // and flags to ourselves to grant. setClipData(target.getClipData()); - addFlags(target.getFlags() - & (FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION - | FLAG_GRANT_PERSISTABLE_URI_PERMISSION)); + addFlags(target.getFlags() & (FLAG_GRANT_READ_URI_PERMISSION + | FLAG_GRANT_WRITE_URI_PERMISSION + | FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | FLAG_GRANT_PREFIX_URI_PERMISSION)); return true; } else { return false; diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index a7a8a0a..ce70455 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -21,6 +21,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.StrictMode; import android.util.Log; + import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; @@ -32,8 +33,10 @@ import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; +import java.util.Objects; import java.util.RandomAccess; import java.util.Set; + import libcore.net.UriCodec; /** @@ -2338,4 +2341,29 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { StrictMode.onFileUriExposed(location); } } + + /** + * Test if this is a path prefix match against the given Uri. Verifies that + * scheme, authority, and atomic path segments match. + * + * @hide + */ + public boolean isPathPrefixMatch(Uri prefix) { + if (!Objects.equals(getScheme(), prefix.getScheme())) return false; + if (!Objects.equals(getAuthority(), prefix.getAuthority())) return false; + + List<String> seg = getPathSegments(); + List<String> prefixSeg = prefix.getPathSegments(); + + final int prefixSize = prefixSeg.size(); + if (seg.size() < prefixSize) return false; + + for (int i = 0; i < prefixSize; i++) { + if (!Objects.equals(seg.get(i), prefixSeg.get(i))) { + return false; + } + } + + return true; + } } diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java index 6fb8946..cd45017 100644 --- a/core/tests/coretests/src/android/net/UriTest.java +++ b/core/tests/coretests/src/android/net/UriTest.java @@ -19,12 +19,14 @@ package android.net; import android.content.ContentUris; import android.os.Parcel; import android.test.suitebuilder.annotation.SmallTest; + +import junit.framework.TestCase; + import java.io.File; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Set; -import junit.framework.TestCase; public class UriTest extends TestCase { @@ -752,4 +754,54 @@ public class UriTest extends TestCase { assertEquals("d e", Uri.parse("http://a/b?c=d%20e").getQueryParameter("c")); assertEquals("d e", Uri.parse("http://a/b?c=d+e").getQueryParameter("c")); } + + public void testPathPrefixMatch() { + // Exact match + assertTrue(Uri.parse("content://com.example/path").isPathPrefixMatch( + Uri.parse("content://com.example/path/"))); + assertTrue(Uri.parse("content://com.example/path").isPathPrefixMatch( + Uri.parse("content://com.example/path"))); + assertTrue(Uri.parse("content://com.example///path///").isPathPrefixMatch( + Uri.parse("content://com.example/path/"))); + assertTrue(Uri.parse("content://com.example/path").isPathPrefixMatch( + Uri.parse("content://com.example///path///"))); + + // Child match + assertTrue(Uri.parse("content://com.example/path/to/child").isPathPrefixMatch( + Uri.parse("content://com.example/path/"))); + assertTrue(Uri.parse("content://com.example/path/to/child").isPathPrefixMatch( + Uri.parse("content://com.example/path"))); + + // Extra parameters + assertTrue(Uri.parse("content://com.example/path#fragment").isPathPrefixMatch( + Uri.parse("content://com.example/path/"))); + assertTrue(Uri.parse("content://com.example/path?q=v").isPathPrefixMatch( + Uri.parse("content://com.example/path/"))); + assertTrue(Uri.parse("content://com.example/path/?q=v").isPathPrefixMatch( + Uri.parse("content://com.example/path/"))); + + // Different path + assertFalse(Uri.parse("content://com.example/path").isPathPrefixMatch( + Uri.parse("content://com.example/path/deeper/"))); + assertFalse(Uri.parse("content://com.example/path2").isPathPrefixMatch( + Uri.parse("content://com.example/path"))); + + // Top-level match + assertTrue(Uri.parse("content://com.example/path/").isPathPrefixMatch( + Uri.parse("content://com.example/"))); + assertTrue(Uri.parse("content://com.example/path/").isPathPrefixMatch( + Uri.parse("content://com.example"))); + + // Different prefixes + assertFalse(Uri.parse("content://com.example/path/").isPathPrefixMatch( + Uri.parse("file://com.example/path/"))); + assertFalse(Uri.parse("content://com.example/path/").isPathPrefixMatch( + Uri.parse("content://org.example/path/"))); + + // Escaping + assertTrue(Uri.parse("content://com.example/path path/").isPathPrefixMatch( + Uri.parse("content://com.example/path%20path/"))); + assertFalse(Uri.parse("content://com.example/path/path").isPathPrefixMatch( + Uri.parse("content://com.example/path%2Fpath"))); + } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ecbb0d9..1bbdf3b 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -17,8 +17,10 @@ package com.android.server.am; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static com.android.internal.util.XmlUtils.readBooleanAttribute; import static com.android.internal.util.XmlUtils.readIntAttribute; import static com.android.internal.util.XmlUtils.readLongAttribute; +import static com.android.internal.util.XmlUtils.writeBooleanAttribute; import static com.android.internal.util.XmlUtils.writeIntAttribute; import static com.android.internal.util.XmlUtils.writeLongAttribute; import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST; @@ -724,6 +726,7 @@ public final class ActivityManagerService extends ActivityManagerNative private static final String ATTR_URI = "uri"; private static final String ATTR_MODE_FLAGS = "modeFlags"; private static final String ATTR_CREATED_TIME = "createdTime"; + private static final String ATTR_PREFIX = "prefix"; /** * Global set of specific {@link Uri} permissions that have been granted. @@ -731,8 +734,41 @@ public final class ActivityManagerService extends ActivityManagerNative * to {@link UriPermission#uri} to {@link UriPermission}. */ @GuardedBy("this") - private final SparseArray<ArrayMap<Uri, UriPermission>> - mGrantedUriPermissions = new SparseArray<ArrayMap<Uri, UriPermission>>(); + private final SparseArray<ArrayMap<GrantUri, UriPermission>> + mGrantedUriPermissions = new SparseArray<ArrayMap<GrantUri, UriPermission>>(); + + public static class GrantUri { + public final Uri uri; + public final boolean prefix; + + public GrantUri(Uri uri, boolean prefix) { + this.uri = uri; + this.prefix = prefix; + } + + @Override + public int hashCode() { + return toString().hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof GrantUri) { + GrantUri other = (GrantUri) o; + return uri.equals(other.uri) && prefix == other.prefix; + } + return false; + } + + @Override + public String toString() { + if (prefix) { + return uri.toString() + " [prefix]"; + } else { + return uri.toString(); + } + } + } CoreSettingsObserver mCoreSettingsObserver; @@ -5822,7 +5858,7 @@ public final class ActivityManagerService extends ActivityManagerNative * in {@link ContentProvider}. */ private final boolean checkHoldingPermissionsLocked( - IPackageManager pm, ProviderInfo pi, Uri uri, int uid, int modeFlags) { + IPackageManager pm, ProviderInfo pi, Uri uri, int uid, final int modeFlags) { if (DEBUG_URI_PERMISSION) Slog.v(TAG, "checkHoldingPermissionsLocked: uri=" + uri + " uid=" + uid); @@ -5918,18 +5954,17 @@ public final class ActivityManagerService extends ActivityManagerNative return pi; } - private UriPermission findUriPermissionLocked(int targetUid, Uri uri) { - ArrayMap<Uri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid); + private UriPermission findUriPermissionLocked(int targetUid, GrantUri uri) { + final ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid); if (targetUris != null) { return targetUris.get(uri); - } else { - return null; } + return null; } - private UriPermission findOrCreateUriPermissionLocked( - String sourcePkg, String targetPkg, int targetUid, Uri uri) { - ArrayMap<Uri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid); + private UriPermission findOrCreateUriPermissionLocked(String sourcePkg, + String targetPkg, int targetUid, GrantUri uri) { + ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid); if (targetUris == null) { targetUris = Maps.newArrayMap(); mGrantedUriPermissions.put(targetUid, targetUris); @@ -5945,20 +5980,36 @@ public final class ActivityManagerService extends ActivityManagerNative } private final boolean checkUriPermissionLocked( - Uri uri, int uid, int modeFlags, int minStrength) { + Uri uri, int uid, final int modeFlags, int minStrength) { // Root gets to do everything. if (uid == 0) { return true; } - ArrayMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(uid); + + final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(uid); if (perms == null) return false; - UriPermission perm = perms.get(uri); - if (perm == null) return false; - return perm.getStrength(modeFlags) >= minStrength; + + // First look for exact match + final UriPermission exactPerm = perms.get(new GrantUri(uri, false)); + if (exactPerm != null && exactPerm.getStrength(modeFlags) >= minStrength) { + return true; + } + + // No exact match, look for prefixes + final int N = perms.size(); + for (int i = 0; i < N; i++) { + final UriPermission perm = perms.valueAt(i); + if (perm.uri.prefix && uri.isPathPrefixMatch(perm.uri.uri) + && perm.getStrength(modeFlags) >= minStrength) { + return true; + } + } + + return false; } @Override - public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) { + public int checkUriPermission(Uri uri, int pid, int uid, final int modeFlags) { enforceNotIsolatedCaller("checkUriPermission"); // Another redirected-binder-call permissions check as in @@ -5990,11 +6041,8 @@ public final class ActivityManagerService extends ActivityManagerNative * lastTargetUid else set that to -1. */ int checkGrantUriPermissionLocked(int callingUid, String targetPkg, - Uri uri, int modeFlags, int lastTargetUid) { - final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0; - modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION - | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - if (modeFlags == 0) { + Uri uri, final int modeFlags, int lastTargetUid) { + if (!Intent.isAccessUriMode(modeFlags)) { return -1; } @@ -6089,6 +6137,8 @@ public final class ActivityManagerService extends ActivityManagerNative if (callingUid != Process.myUid()) { if (!checkHoldingPermissionsLocked(pm, pi, uri, callingUid, modeFlags)) { // Require they hold a strong enough Uri permission + final boolean persistable = + (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0; final int minStrength = persistable ? UriPermission.STRENGTH_PERSISTABLE : UriPermission.STRENGTH_OWNED; if (!checkUriPermissionLocked(uri, callingUid, modeFlags, minStrength)) { @@ -6103,19 +6153,16 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public int checkGrantUriPermission(int callingUid, String targetPkg, - Uri uri, int modeFlags) { + Uri uri, final int modeFlags) { enforceNotIsolatedCaller("checkGrantUriPermission"); synchronized(this) { return checkGrantUriPermissionLocked(callingUid, targetPkg, uri, modeFlags, -1); } } - void grantUriPermissionUncheckedLocked( - int targetUid, String targetPkg, Uri uri, int modeFlags, UriPermissionOwner owner) { - final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0; - modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION - | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - if (modeFlags == 0) { + void grantUriPermissionUncheckedLocked(int targetUid, String targetPkg, Uri uri, + final int modeFlags, UriPermissionOwner owner) { + if (!Intent.isAccessUriMode(modeFlags)) { return; } @@ -6133,13 +6180,14 @@ public final class ActivityManagerService extends ActivityManagerNative return; } + final boolean prefix = (modeFlags & Intent.FLAG_GRANT_PREFIX_URI_PERMISSION) != 0; final UriPermission perm = findOrCreateUriPermissionLocked( - pi.packageName, targetPkg, targetUid, uri); - perm.grantModes(modeFlags, persistable, owner); + pi.packageName, targetPkg, targetUid, new GrantUri(uri, prefix)); + perm.grantModes(modeFlags, owner); } void grantUriPermissionLocked(int callingUid, String targetPkg, Uri uri, - int modeFlags, UriPermissionOwner owner) { + final int modeFlags, UriPermissionOwner owner) { if (targetPkg == null) { throw new NullPointerException("targetPkg"); } @@ -6253,7 +6301,7 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public void grantUriPermission(IApplicationThread caller, String targetPkg, - Uri uri, int modeFlags) { + Uri uri, final int modeFlags) { enforceNotIsolatedCaller("grantUriPermission"); synchronized(this) { final ProcessRecord r = getRecordForAppLocked(caller); @@ -6269,32 +6317,32 @@ public final class ActivityManagerService extends ActivityManagerNative throw new IllegalArgumentException("null uri"); } - // Persistable only supported through Intents - Preconditions.checkFlagsArgument(modeFlags, - Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + Preconditions.checkFlagsArgument(modeFlags, Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION); - grantUriPermissionLocked(r.uid, targetPkg, uri, modeFlags, - null); + grantUriPermissionLocked(r.uid, targetPkg, uri, modeFlags, null); } } void removeUriPermissionIfNeededLocked(UriPermission perm) { - if ((perm.modeFlags&(Intent.FLAG_GRANT_READ_URI_PERMISSION - |Intent.FLAG_GRANT_WRITE_URI_PERMISSION)) == 0) { - ArrayMap<Uri, UriPermission> perms - = mGrantedUriPermissions.get(perm.targetUid); + if (perm.modeFlags == 0) { + final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get( + perm.targetUid); if (perms != null) { if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Removing " + perm.targetUid + " permission to " + perm.uri); + perms.remove(perm.uri); - if (perms.size() == 0) { + if (perms.isEmpty()) { mGrantedUriPermissions.remove(perm.targetUid); } } } } - private void revokeUriPermissionLocked(int callingUid, Uri uri, int modeFlags) { + private void revokeUriPermissionLocked(int callingUid, Uri uri, final int modeFlags) { if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Revoking all granted permissions to " + uri); final IPackageManager pm = AppGlobals.getPackageManager(); @@ -6318,46 +6366,29 @@ public final class ActivityManagerService extends ActivityManagerNative boolean persistChanged = false; // Go through all of the permissions and remove any that match. - final List<String> SEGMENTS = uri.getPathSegments(); - if (SEGMENTS != null) { - final int NS = SEGMENTS.size(); - int N = mGrantedUriPermissions.size(); - for (int i=0; i<N; i++) { - ArrayMap<Uri, UriPermission> perms - = mGrantedUriPermissions.valueAt(i); - Iterator<UriPermission> it = perms.values().iterator(); - toploop: - while (it.hasNext()) { - UriPermission perm = it.next(); - Uri targetUri = perm.uri; - if (!authority.equals(targetUri.getAuthority())) { - continue; - } - List<String> targetSegments = targetUri.getPathSegments(); - if (targetSegments == null) { - continue; - } - if (targetSegments.size() < NS) { - continue; - } - for (int j=0; j<NS; j++) { - if (!SEGMENTS.get(j).equals(targetSegments.get(j))) { - continue toploop; - } - } - if (DEBUG_URI_PERMISSION) Slog.v(TAG, - "Revoking " + perm.targetUid + " permission to " + perm.uri); - persistChanged |= perm.clearModes(modeFlags, true); + int N = mGrantedUriPermissions.size(); + for (int i = 0; i < N; i++) { + final int targetUid = mGrantedUriPermissions.keyAt(i); + final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i); + + for (Iterator<UriPermission> it = perms.values().iterator(); it.hasNext();) { + final UriPermission perm = it.next(); + if (perm.uri.uri.isPathPrefixMatch(uri)) { + if (DEBUG_URI_PERMISSION) + Slog.v(TAG, + "Revoking " + perm.targetUid + " permission to " + perm.uri); + persistChanged |= perm.revokeModes( + modeFlags | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); if (perm.modeFlags == 0) { it.remove(); } } - if (perms.size() == 0) { - mGrantedUriPermissions.remove( - mGrantedUriPermissions.keyAt(i)); - N--; - i--; - } + } + + if (perms.isEmpty()) { + mGrantedUriPermissions.remove(targetUid); + N--; + i--; } } @@ -6368,7 +6399,7 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public void revokeUriPermission(IApplicationThread caller, Uri uri, - int modeFlags) { + final int modeFlags) { enforceNotIsolatedCaller("revokeUriPermission"); synchronized(this) { final ProcessRecord r = getRecordForAppLocked(caller); @@ -6382,9 +6413,7 @@ public final class ActivityManagerService extends ActivityManagerNative return; } - modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION - | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - if (modeFlags == 0) { + if (!Intent.isAccessUriMode(modeFlags)) { return; } @@ -6419,20 +6448,22 @@ public final class ActivityManagerService extends ActivityManagerNative boolean persistChanged = false; - final int size = mGrantedUriPermissions.size(); - for (int i = 0; i < size; i++) { + int N = mGrantedUriPermissions.size(); + for (int i = 0; i < N; i++) { + final int targetUid = mGrantedUriPermissions.keyAt(i); + final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i); + // Only inspect grants matching user if (userHandle == UserHandle.USER_ALL - || userHandle == UserHandle.getUserId(mGrantedUriPermissions.keyAt(i))) { - final Iterator<UriPermission> it = mGrantedUriPermissions.valueAt(i) - .values().iterator(); - while (it.hasNext()) { + || userHandle == UserHandle.getUserId(targetUid)) { + for (Iterator<UriPermission> it = perms.values().iterator(); it.hasNext();) { final UriPermission perm = it.next(); // Only inspect grants matching package if (packageName == null || perm.sourcePkg.equals(packageName) || perm.targetPkg.equals(packageName)) { - persistChanged |= perm.clearModes(~0, persistable); + persistChanged |= perm.revokeModes( + persistable ? ~0 : ~Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); // Only remove when no modes remain; any persisted grants // will keep this alive. @@ -6441,6 +6472,12 @@ public final class ActivityManagerService extends ActivityManagerNative } } } + + if (perms.isEmpty()) { + mGrantedUriPermissions.remove(targetUid); + N--; + i--; + } } } @@ -6460,7 +6497,7 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg, - Uri uri, int modeFlags) { + Uri uri, final int modeFlags) { synchronized(this) { UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token); if (owner == null) { @@ -6514,8 +6551,9 @@ public final class ActivityManagerService extends ActivityManagerNative ArrayList<UriPermission.Snapshot> persist = Lists.newArrayList(); synchronized (this) { final int size = mGrantedUriPermissions.size(); - for (int i = 0 ; i < size; i++) { - for (UriPermission perm : mGrantedUriPermissions.valueAt(i).values()) { + for (int i = 0; i < size; i++) { + final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i); + for (UriPermission perm : perms.values()) { if (perm.persistedModeFlags != 0) { persist.add(perm.snapshot()); } @@ -6536,7 +6574,8 @@ public final class ActivityManagerService extends ActivityManagerNative writeIntAttribute(out, ATTR_USER_HANDLE, perm.userHandle); out.attribute(null, ATTR_SOURCE_PKG, perm.sourcePkg); out.attribute(null, ATTR_TARGET_PKG, perm.targetPkg); - out.attribute(null, ATTR_URI, String.valueOf(perm.uri)); + out.attribute(null, ATTR_URI, String.valueOf(perm.uri.uri)); + writeBooleanAttribute(out, ATTR_PREFIX, perm.uri.prefix); writeIntAttribute(out, ATTR_MODE_FLAGS, perm.persistedModeFlags); writeLongAttribute(out, ATTR_CREATED_TIME, perm.persistedCreateTime); out.endTag(null, TAG_URI_GRANT); @@ -6572,6 +6611,7 @@ public final class ActivityManagerService extends ActivityManagerNative final String sourcePkg = in.getAttributeValue(null, ATTR_SOURCE_PKG); final String targetPkg = in.getAttributeValue(null, ATTR_TARGET_PKG); final Uri uri = Uri.parse(in.getAttributeValue(null, ATTR_URI)); + final boolean prefix = readBooleanAttribute(in, ATTR_PREFIX); final int modeFlags = readIntAttribute(in, ATTR_MODE_FLAGS); final long createdTime = readLongAttribute(in, ATTR_CREATED_TIME, now); @@ -6587,7 +6627,7 @@ public final class ActivityManagerService extends ActivityManagerNative } if (targetUid != -1) { final UriPermission perm = findOrCreateUriPermissionLocked( - sourcePkg, targetPkg, targetUid, uri); + sourcePkg, targetPkg, targetUid, new GrantUri(uri, prefix)); perm.initPersistedModes(modeFlags, createdTime); } } else { @@ -6609,7 +6649,7 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override - public void takePersistableUriPermission(Uri uri, int modeFlags) { + public void takePersistableUriPermission(Uri uri, final int modeFlags) { enforceNotIsolatedCaller("takePersistableUriPermission"); Preconditions.checkFlagsArgument(modeFlags, @@ -6617,13 +6657,28 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { final int callingUid = Binder.getCallingUid(); - final UriPermission perm = findUriPermissionLocked(callingUid, uri); - if (perm == null) { - throw new SecurityException("No permission grant found for UID " + callingUid - + " and Uri " + uri.toSafeString()); + boolean persistChanged = false; + + UriPermission exactPerm = findUriPermissionLocked(callingUid, new GrantUri(uri, false)); + UriPermission prefixPerm = findUriPermissionLocked(callingUid, new GrantUri(uri, true)); + + final boolean exactValid = (exactPerm != null) + && ((modeFlags & exactPerm.persistableModeFlags) == modeFlags); + final boolean prefixValid = (prefixPerm != null) + && ((modeFlags & prefixPerm.persistableModeFlags) == modeFlags); + + if (!(exactValid || prefixValid)) { + throw new SecurityException("No persistable permission grants found for UID " + + callingUid + " and Uri " + uri.toSafeString()); + } + + if (exactValid) { + persistChanged |= exactPerm.takePersistableModes(modeFlags); + } + if (prefixValid) { + persistChanged |= prefixPerm.takePersistableModes(modeFlags); } - boolean persistChanged = perm.takePersistableModes(modeFlags); persistChanged |= maybePrunePersistedUriGrantsLocked(callingUid); if (persistChanged) { @@ -6633,7 +6688,7 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override - public void releasePersistableUriPermission(Uri uri, int modeFlags) { + public void releasePersistableUriPermission(Uri uri, final int modeFlags) { enforceNotIsolatedCaller("releasePersistableUriPermission"); Preconditions.checkFlagsArgument(modeFlags, @@ -6641,16 +6696,24 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { final int callingUid = Binder.getCallingUid(); + boolean persistChanged = false; - final UriPermission perm = findUriPermissionLocked(callingUid, uri); - if (perm == null) { - Slog.w(TAG, "No permission grant found for UID " + callingUid + " and Uri " - + uri.toSafeString()); - return; + UriPermission exactPerm = findUriPermissionLocked(callingUid, new GrantUri(uri, false)); + UriPermission prefixPerm = findUriPermissionLocked(callingUid, new GrantUri(uri, true)); + if (exactPerm == null && prefixPerm == null) { + throw new SecurityException("No permission grants found for UID " + callingUid + + " and Uri " + uri.toSafeString()); + } + + if (exactPerm != null) { + persistChanged |= exactPerm.releasePersistableModes(modeFlags); + removeUriPermissionIfNeededLocked(exactPerm); + } + if (prefixPerm != null) { + persistChanged |= prefixPerm.releasePersistableModes(modeFlags); + removeUriPermissionIfNeededLocked(prefixPerm); } - final boolean persistChanged = perm.releasePersistableModes(modeFlags); - removeUriPermissionIfNeededLocked(perm); if (persistChanged) { schedulePersistUriGrants(); } @@ -6664,7 +6727,7 @@ public final class ActivityManagerService extends ActivityManagerNative * @return if any mutations occured that require persisting. */ private boolean maybePrunePersistedUriGrantsLocked(int uid) { - final ArrayMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(uid); + final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(uid); if (perms == null) return false; if (perms.size() < MAX_PERSISTED_URI_GRANTS) return false; @@ -6714,13 +6777,12 @@ public final class ActivityManagerService extends ActivityManagerNative final ArrayList<android.content.UriPermission> result = Lists.newArrayList(); synchronized (this) { if (incoming) { - final ArrayMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(callingUid); + final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get( + callingUid); if (perms == null) { Slog.w(TAG, "No permission grants found for " + packageName); } else { - final int size = perms.size(); - for (int i = 0; i < size; i++) { - final UriPermission perm = perms.valueAt(i); + for (UriPermission perm : perms.values()) { if (packageName.equals(perm.targetPkg) && perm.persistedModeFlags != 0) { result.add(perm.buildPersistedPublicApiObject()); } @@ -6729,10 +6791,9 @@ public final class ActivityManagerService extends ActivityManagerNative } else { final int size = mGrantedUriPermissions.size(); for (int i = 0; i < size; i++) { - final ArrayMap<Uri, UriPermission> perms = mGrantedUriPermissions.valueAt(i); - final int permsSize = perms.size(); - for (int j = 0; j < permsSize; j++) { - final UriPermission perm = perms.valueAt(j); + final ArrayMap<GrantUri, UriPermission> perms = + mGrantedUriPermissions.valueAt(i); + for (UriPermission perm : perms.values()) { if (packageName.equals(perm.sourcePkg) && perm.persistedModeFlags != 0) { result.add(perm.buildPersistedPublicApiObject()); } @@ -7602,11 +7663,11 @@ public final class ActivityManagerService extends ActivityManagerNative } } } - - ArrayMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(callingUid); + + final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(callingUid); if (perms != null) { - for (Map.Entry<Uri, UriPermission> uri : perms.entrySet()) { - if (uri.getKey().getAuthority().equals(cpi.authority)) { + for (GrantUri uri : perms.keySet()) { + if (uri.uri.getAuthority().equals(cpi.authority)) { return null; } } @@ -11573,8 +11634,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (dumpUid >= -1 && UserHandle.getAppId(uid) != dumpUid) { continue; } - ArrayMap<Uri, UriPermission> perms - = mGrantedUriPermissions.valueAt(i); + final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i); if (!printed) { if (needSep) pw.println(); needSep = true; @@ -11582,8 +11642,7 @@ public final class ActivityManagerService extends ActivityManagerNative printed = true; printedAnything = true; } - pw.print(" * UID "); pw.print(uid); - pw.println(" holds:"); + pw.print(" * UID "); pw.print(uid); pw.println(" holds:"); for (UriPermission perm : perms.values()) { pw.print(" "); pw.println(perm); if (dumpAll) { diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 60adfb0..3e59def 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -215,14 +215,7 @@ final class ActivityRecord { pw.print(prefix); pw.print("pendingOptions="); pw.println(pendingOptions); } if (uriPermissions != null) { - if (uriPermissions.readUriPermissions != null) { - pw.print(prefix); pw.print("readUriPermissions="); - pw.println(uriPermissions.readUriPermissions); - } - if (uriPermissions.writeUriPermissions != null) { - pw.print(prefix); pw.print("writeUriPermissions="); - pw.println(uriPermissions.writeUriPermissions); - } + uriPermissions.dump(pw, prefix); } pw.print(prefix); pw.print("launchFailed="); pw.print(launchFailed); pw.print(" launchCount="); pw.print(launchCount); diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 363a9b7..e54c95e 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -193,14 +193,7 @@ final class ServiceRecord extends Binder { pw.println(si.neededGrants); } if (si.uriPermissions != null) { - if (si.uriPermissions.readUriPermissions != null) { - pw.print(prefix); pw.print(" readUriPermissions="); - pw.println(si.uriPermissions.readUriPermissions); - } - if (si.uriPermissions.writeUriPermissions != null) { - pw.print(prefix); pw.print(" writeUriPermissions="); - pw.println(si.uriPermissions.writeUriPermissions); - } + si.uriPermissions.dump(pw, prefix); } } } diff --git a/services/core/java/com/android/server/am/UriPermission.java b/services/core/java/com/android/server/am/UriPermission.java index 1f12b74..4970b8d 100644 --- a/services/core/java/com/android/server/am/UriPermission.java +++ b/services/core/java/com/android/server/am/UriPermission.java @@ -17,15 +17,16 @@ package com.android.server.am; import android.content.Intent; -import android.net.Uri; import android.os.UserHandle; +import android.util.ArraySet; import android.util.Log; +import android.util.Slog; +import com.android.server.am.ActivityManagerService.GrantUri; import com.google.android.collect.Sets; import java.io.PrintWriter; import java.util.Comparator; -import java.util.HashSet; /** * Description of a permission granted to an app to access a particular URI. @@ -50,7 +51,7 @@ final class UriPermission { /** Cached UID of {@link #targetPkg}; should not be persisted */ final int targetUid; - final Uri uri; + final GrantUri uri; /** * Allowed modes. All permission enforcement should use this field. Must @@ -61,12 +62,13 @@ final class UriPermission { */ int modeFlags = 0; - /** Allowed modes with explicit owner. */ + /** Allowed modes with active owner. */ int ownedModeFlags = 0; /** Allowed modes without explicit owner. */ int globalModeFlags = 0; /** Allowed modes that have been offered for possible persisting. */ int persistableModeFlags = 0; + /** Allowed modes that should be persisted across device boots. */ int persistedModeFlags = 0; @@ -78,12 +80,12 @@ final class UriPermission { private static final long INVALID_TIME = Long.MIN_VALUE; - private HashSet<UriPermissionOwner> mReadOwners; - private HashSet<UriPermissionOwner> mWriteOwners; + private ArraySet<UriPermissionOwner> mReadOwners; + private ArraySet<UriPermissionOwner> mWriteOwners; private String stringName; - UriPermission(String sourcePkg, String targetPkg, int targetUid, Uri uri) { + UriPermission(String sourcePkg, String targetPkg, int targetUid, GrantUri uri) { this.userHandle = UserHandle.getUserId(targetUid); this.sourcePkg = sourcePkg; this.targetPkg = targetPkg; @@ -100,6 +102,9 @@ final class UriPermission { * global or owner grants. */ void initPersistedModes(int modeFlags, long createdTime) { + modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + persistableModeFlags = modeFlags; persistedModeFlags = modeFlags; persistedCreateTime = createdTime; @@ -107,7 +112,11 @@ final class UriPermission { updateModeFlags(); } - void grantModes(int modeFlags, boolean persistable, UriPermissionOwner owner) { + void grantModes(int modeFlags, UriPermissionOwner owner) { + final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0; + modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + if (persistable) { persistableModeFlags |= modeFlags; } @@ -130,10 +139,14 @@ final class UriPermission { * @return if mode changes should trigger persisting. */ boolean takePersistableModes(int modeFlags) { + modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + if ((modeFlags & persistableModeFlags) != modeFlags) { - throw new SecurityException("Requested flags 0x" + Slog.w(TAG, "Requested flags 0x" + Integer.toHexString(modeFlags) + ", but only 0x" + Integer.toHexString(persistableModeFlags) + " are allowed"); + return false; } final int before = persistedModeFlags; @@ -148,6 +161,9 @@ final class UriPermission { } boolean releasePersistableModes(int modeFlags) { + modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + final int before = persistedModeFlags; persistableModeFlags &= ~modeFlags; @@ -164,7 +180,11 @@ final class UriPermission { /** * @return if mode changes should trigger persisting. */ - boolean clearModes(int modeFlags, boolean persistable) { + boolean revokeModes(int modeFlags) { + final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0; + modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + final int before = persistedModeFlags; if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) { @@ -208,6 +228,8 @@ final class UriPermission { * Return strength of this permission grant for the given flags. */ public int getStrength(int modeFlags) { + modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); if ((persistableModeFlags & modeFlags) == modeFlags) { return STRENGTH_PERSISTABLE; } else if ((globalModeFlags & modeFlags) == modeFlags) { @@ -221,7 +243,7 @@ final class UriPermission { private void addReadOwner(UriPermissionOwner owner) { if (mReadOwners == null) { - mReadOwners = Sets.newHashSet(); + mReadOwners = Sets.newArraySet(); ownedModeFlags |= Intent.FLAG_GRANT_READ_URI_PERMISSION; updateModeFlags(); } @@ -246,7 +268,7 @@ final class UriPermission { private void addWriteOwner(UriPermissionOwner owner) { if (mWriteOwners == null) { - mWriteOwners = Sets.newHashSet(); + mWriteOwners = Sets.newArraySet(); ownedModeFlags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION; updateModeFlags(); } @@ -333,7 +355,7 @@ final class UriPermission { final int userHandle; final String sourcePkg; final String targetPkg; - final Uri uri; + final GrantUri uri; final int persistedModeFlags; final long persistedCreateTime; @@ -352,6 +374,6 @@ final class UriPermission { } public android.content.UriPermission buildPersistedPublicApiObject() { - return new android.content.UriPermission(uri, persistedModeFlags, persistedCreateTime); + return new android.content.UriPermission(uri.uri, persistedModeFlags, persistedCreateTime); } } diff --git a/services/core/java/com/android/server/am/UriPermissionOwner.java b/services/core/java/com/android/server/am/UriPermissionOwner.java index 7bbd3bc..65d7047 100644 --- a/services/core/java/com/android/server/am/UriPermissionOwner.java +++ b/services/core/java/com/android/server/am/UriPermissionOwner.java @@ -20,8 +20,11 @@ import android.content.Intent; import android.net.Uri; import android.os.Binder; import android.os.IBinder; +import android.util.ArraySet; -import java.util.HashSet; +import com.google.android.collect.Sets; + +import java.io.PrintWriter; import java.util.Iterator; final class UriPermissionOwner { @@ -30,8 +33,8 @@ final class UriPermissionOwner { Binder externalToken; - HashSet<UriPermission> readUriPermissions; // special access to reading uris. - HashSet<UriPermission> writeUriPermissions; // special access to writing uris. + private ArraySet<UriPermission> mReadPerms; + private ArraySet<UriPermission> mWritePerms; class ExternalToken extends Binder { UriPermissionOwner getOwner() { @@ -39,9 +42,9 @@ final class UriPermissionOwner { } } - UriPermissionOwner(ActivityManagerService _service, Object _owner) { - service = _service; - owner = _owner; + UriPermissionOwner(ActivityManagerService service, Object owner) { + this.service = service; + this.owner = owner; } Binder getExternalTokenLocked() { @@ -64,82 +67,76 @@ final class UriPermissionOwner { } void removeUriPermissionsLocked(int mode) { - if ((mode&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0 - && readUriPermissions != null) { - for (UriPermission perm : readUriPermissions) { - perm.removeReadOwner(this); - service.removeUriPermissionIfNeededLocked(perm); - } - readUriPermissions = null; - } - if ((mode&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0 - && writeUriPermissions != null) { - for (UriPermission perm : writeUriPermissions) { - perm.removeWriteOwner(this); - service.removeUriPermissionIfNeededLocked(perm); - } - writeUriPermissions = null; - } + removeUriPermissionLocked(null, mode); } void removeUriPermissionLocked(Uri uri, int mode) { - if ((mode&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0 - && readUriPermissions != null) { - Iterator<UriPermission> it = readUriPermissions.iterator(); + if ((mode & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0 + && mReadPerms != null) { + Iterator<UriPermission> it = mReadPerms.iterator(); while (it.hasNext()) { UriPermission perm = it.next(); - if (uri.equals(perm.uri)) { + if (uri == null || uri.equals(perm.uri)) { perm.removeReadOwner(this); service.removeUriPermissionIfNeededLocked(perm); it.remove(); } } - if (readUriPermissions.size() == 0) { - readUriPermissions = null; + if (mReadPerms.isEmpty()) { + mReadPerms = null; } } - if ((mode&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0 - && writeUriPermissions != null) { - Iterator<UriPermission> it = writeUriPermissions.iterator(); + if ((mode & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0 + && mWritePerms != null) { + Iterator<UriPermission> it = mWritePerms.iterator(); while (it.hasNext()) { UriPermission perm = it.next(); - if (uri.equals(perm.uri)) { + if (uri == null || uri.equals(perm.uri)) { perm.removeWriteOwner(this); service.removeUriPermissionIfNeededLocked(perm); it.remove(); } } - if (writeUriPermissions.size() == 0) { - writeUriPermissions = null; + if (mWritePerms.isEmpty()) { + mWritePerms = null; } } } public void addReadPermission(UriPermission perm) { - if (readUriPermissions == null) { - readUriPermissions = new HashSet<UriPermission>(); + if (mReadPerms == null) { + mReadPerms = Sets.newArraySet(); } - readUriPermissions.add(perm); + mReadPerms.add(perm); } public void addWritePermission(UriPermission perm) { - if (writeUriPermissions == null) { - writeUriPermissions = new HashSet<UriPermission>(); + if (mWritePerms == null) { + mWritePerms = Sets.newArraySet(); } - writeUriPermissions.add(perm); + mWritePerms.add(perm); } public void removeReadPermission(UriPermission perm) { - readUriPermissions.remove(perm); - if (readUriPermissions.size() == 0) { - readUriPermissions = null; + mReadPerms.remove(perm); + if (mReadPerms.isEmpty()) { + mReadPerms = null; } } public void removeWritePermission(UriPermission perm) { - writeUriPermissions.remove(perm); - if (writeUriPermissions.size() == 0) { - writeUriPermissions = null; + mWritePerms.remove(perm); + if (mWritePerms.isEmpty()) { + mWritePerms = null; + } + } + + public void dump(PrintWriter pw, String prefix) { + if (mReadPerms != null) { + pw.print(prefix); pw.print("readUriPermissions="); pw.println(mReadPerms); + } + if (mWritePerms != null) { + pw.print(prefix); pw.print("writeUriPermissions="); pw.println(mWritePerms); } } |