diff options
author | Jeff Sharkey <jsharkey@android.com> | 2013-09-20 14:30:59 -0700 |
---|---|---|
committer | Jeff Sharkey <jsharkey@android.com> | 2013-09-25 15:16:41 -0700 |
commit | e66c1778f80f4b18e29e018eca3a338f125f23b9 (patch) | |
tree | d3dac08442ebac90e41d660a04111195eaa3df35 /services/java | |
parent | d7fdcad22683e6191e92141a7e45e2414fe40a44 (diff) | |
download | frameworks_base-e66c1778f80f4b18e29e018eca3a338f125f23b9.zip frameworks_base-e66c1778f80f4b18e29e018eca3a338f125f23b9.tar.gz frameworks_base-e66c1778f80f4b18e29e018eca3a338f125f23b9.tar.bz2 |
Require that persistable Uri permissions be taken.
Change our Intent flag to indicate that a Uri permission grant is
persistable, but don't actually persist it until explicitly taken by
the receiving app. This prevents apps from spamming each other if
persisted permissions aren't really required.
Remember the last time a persisted grant was taken by an app, and
use this to prune away the oldest grants when the number of grants
grows too large. Allow apps to query persisted grants they are
holding, and allow them to release previously persisted grants. Add
public UriPermission class to return grant details and timestamp.
Track various permission strengths separately, and combine together
after each mutation pass. Persistable grants are currently treated
like global grants, but they could be moved to have owners in the
future. Require that grant holders trying to extend a persistable
permission actually hold a persistable permission themselves.
Bug: 10835779
Change-Id: I95b2f797c04ce7fd2612f9a644685dbd44e03759
Diffstat (limited to 'services/java')
-rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 225 | ||||
-rw-r--r-- | services/java/com/android/server/am/UriPermission.java | 190 |
2 files changed, 302 insertions, 113 deletions
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 4f73588..387641d 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -18,7 +18,9 @@ package com.android.server.am; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static com.android.internal.util.XmlUtils.readIntAttribute; +import static com.android.internal.util.XmlUtils.readLongAttribute; import static com.android.internal.util.XmlUtils.writeIntAttribute; +import static com.android.internal.util.XmlUtils.writeLongAttribute; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.START_TAG; @@ -36,7 +38,6 @@ 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; @@ -113,6 +114,7 @@ import android.content.pm.IPackageManager; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PathPermission; @@ -153,6 +155,7 @@ import android.os.SystemProperties; import android.os.UpdateLock; import android.os.UserHandle; import android.provider.Settings; +import android.text.format.DateUtils; import android.text.format.Time; import android.util.AtomicFile; import android.util.EventLog; @@ -310,6 +313,9 @@ public final class ActivityManagerService extends ActivityManagerNative // to respond with the result. static final int PENDING_ASSIST_EXTRAS_TIMEOUT = 500; + // Maximum number of persisted Uri grants a package is allowed + static final int MAX_PERSISTED_URI_GRANTS = 128; + static final int MY_PID = Process.myPid(); static final String[] EMPTY_STRING_ARRAY = new String[0]; @@ -685,6 +691,7 @@ public final class ActivityManagerService extends ActivityManagerNative private static final String ATTR_TARGET_PKG = "targetPkg"; private static final String ATTR_URI = "uri"; private static final String ATTR_MODE_FLAGS = "modeFlags"; + private static final String ATTR_CREATED_TIME = "createdTime"; /** * Global set of specific {@link Uri} permissions that have been granted. @@ -5669,6 +5676,15 @@ public final class ActivityManagerService extends ActivityManagerNative return pi; } + private UriPermission findUriPermissionLocked(int targetUid, Uri uri) { + ArrayMap<Uri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid); + if (targetUris != null) { + return targetUris.get(uri); + } else { + return null; + } + } + private UriPermission findOrCreateUriPermissionLocked( String sourcePkg, String targetPkg, int targetUid, Uri uri) { ArrayMap<Uri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid); @@ -5686,8 +5702,8 @@ public final class ActivityManagerService extends ActivityManagerNative return perm; } - private final boolean checkUriPermissionLocked(Uri uri, int uid, - int modeFlags) { + private final boolean checkUriPermissionLocked( + Uri uri, int uid, int modeFlags, int minStrength) { // Root gets to do everything. if (uid == 0) { return true; @@ -5696,7 +5712,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (perms == null) return false; UriPermission perm = perms.get(uri); if (perm == null) return false; - return (modeFlags&perm.modeFlags) == modeFlags; + return perm.getStrength(modeFlags) >= minStrength; } @Override @@ -5716,7 +5732,7 @@ public final class ActivityManagerService extends ActivityManagerNative return PackageManager.PERMISSION_GRANTED; } synchronized(this) { - return checkUriPermissionLocked(uri, uid, modeFlags) + return checkUriPermissionLocked(uri, uid, modeFlags, UriPermission.STRENGTH_OWNED) ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED; } @@ -5733,7 +5749,7 @@ public final class ActivityManagerService extends ActivityManagerNative */ int checkGrantUriPermissionLocked(int callingUid, String targetPkg, Uri uri, int modeFlags, int lastTargetUid) { - final boolean persist = (modeFlags & Intent.FLAG_PERSIST_GRANT_URI_PERMISSION) != 0; + 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) { @@ -5777,7 +5793,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (targetUid >= 0) { // First... does the target actually need this permission? - if (checkHoldingPermissionsLocked(pm, pi, uri, targetUid, modeFlags) && !persist) { + if (checkHoldingPermissionsLocked(pm, pi, uri, targetUid, modeFlags)) { // No need to grant the target this permission. if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Target " + targetPkg + " already has full permission to " + uri); @@ -5796,7 +5812,7 @@ public final class ActivityManagerService extends ActivityManagerNative allowed = false; } } - if (allowed && !persist) { + if (allowed) { return -1; } } @@ -5830,7 +5846,10 @@ public final class ActivityManagerService extends ActivityManagerNative // this uri? if (callingUid != Process.myUid()) { if (!checkHoldingPermissionsLocked(pm, pi, uri, callingUid, modeFlags)) { - if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) { + // Require they hold a strong enough Uri permission + final int minStrength = persistable ? UriPermission.STRENGTH_PERSISTABLE + : UriPermission.STRENGTH_OWNED; + if (!checkUriPermissionLocked(uri, callingUid, modeFlags, minStrength)) { throw new SecurityException("Uid " + callingUid + " does not have permission to uri " + uri); } @@ -5851,7 +5870,7 @@ public final class ActivityManagerService extends ActivityManagerNative void grantUriPermissionUncheckedLocked( int targetUid, String targetPkg, Uri uri, int modeFlags, UriPermissionOwner owner) { - final boolean persist = (modeFlags & Intent.FLAG_PERSIST_GRANT_URI_PERMISSION) != 0; + 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) { @@ -5874,11 +5893,7 @@ public final class ActivityManagerService extends ActivityManagerNative final UriPermission perm = findOrCreateUriPermissionLocked( pi.packageName, targetPkg, targetUid, uri); - final boolean persistChanged = perm.grantModes(modeFlags, persist, owner); - if (persistChanged) { - mHandler.removeMessages(PERSIST_URI_GRANTS_MSG); - mHandler.obtainMessage(PERSIST_URI_GRANTS_MSG).sendToTarget(); - } + perm.grantModes(modeFlags, persistable, owner); } void grantUriPermissionLocked(int callingUid, String targetPkg, Uri uri, @@ -5930,6 +5945,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (data == null && clip == null) { return null; } + if (data != null) { int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, data, mode, needed != null ? needed.targetUid : -1); @@ -6011,6 +6027,14 @@ public final class ActivityManagerService extends ActivityManagerNative throw new IllegalArgumentException("null uri"); } + // Persistable only supported through Intents + modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + if (modeFlags == 0) { + throw new IllegalArgumentException("Mode flags must be " + + "FLAG_GRANT_READ_URI_PERMISSION and/or FLAG_GRANT_WRITE_URI_PERMISSION"); + } + grantUriPermissionLocked(r.uid, targetPkg, uri, modeFlags, null); } @@ -6032,8 +6056,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - private void revokeUriPermissionLocked( - int callingUid, Uri uri, int modeFlags, boolean persist) { + private void revokeUriPermissionLocked(int callingUid, Uri uri, int modeFlags) { if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Revoking all granted permissions to " + uri); final IPackageManager pm = AppGlobals.getPackageManager(); @@ -6086,7 +6109,7 @@ public final class ActivityManagerService extends ActivityManagerNative } if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Revoking " + perm.targetUid + " permission to " + perm.uri); - persistChanged |= perm.clearModes(modeFlags, persist); + persistChanged |= perm.clearModes(modeFlags, true); if (perm.modeFlags == 0) { it.remove(); } @@ -6101,8 +6124,7 @@ public final class ActivityManagerService extends ActivityManagerNative } if (persistChanged) { - mHandler.removeMessages(PERSIST_URI_GRANTS_MSG); - mHandler.obtainMessage(PERSIST_URI_GRANTS_MSG).sendToTarget(); + schedulePersistUriGrants(); } } @@ -6122,7 +6144,6 @@ public final class ActivityManagerService extends ActivityManagerNative return; } - final boolean persist = (modeFlags & Intent.FLAG_PERSIST_GRANT_URI_PERMISSION) != 0; modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); if (modeFlags == 0) { @@ -6138,7 +6159,7 @@ public final class ActivityManagerService extends ActivityManagerNative return; } - revokeUriPermissionLocked(r.uid, uri, modeFlags, persist); + revokeUriPermissionLocked(r.uid, uri, modeFlags); } } @@ -6150,10 +6171,10 @@ public final class ActivityManagerService extends ActivityManagerNative * packages. * @param userHandle User to match, or {@link UserHandle#USER_ALL} to apply * to all users. - * @param persist If persistent grants should be removed. + * @param persistable If persistable grants should be removed. */ private void removeUriPermissionsForPackageLocked( - String packageName, int userHandle, boolean persist) { + String packageName, int userHandle, boolean persistable) { if (userHandle == UserHandle.USER_ALL && packageName == null) { throw new IllegalArgumentException("Must narrow by either package or user"); } @@ -6173,7 +6194,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Only inspect grants matching package if (packageName == null || perm.sourcePkg.equals(packageName) || perm.targetPkg.equals(packageName)) { - persistChanged |= perm.clearModes(~0, persist); + persistChanged |= perm.clearModes(~0, persistable); // Only remove when no modes remain; any persisted grants // will keep this alive. @@ -6186,8 +6207,7 @@ public final class ActivityManagerService extends ActivityManagerNative } if (persistChanged) { - mHandler.removeMessages(PERSIST_URI_GRANTS_MSG); - mHandler.obtainMessage(PERSIST_URI_GRANTS_MSG).sendToTarget(); + schedulePersistUriGrants(); } } @@ -6242,6 +6262,13 @@ public final class ActivityManagerService extends ActivityManagerNative } } + private void schedulePersistUriGrants() { + if (!mHandler.hasMessages(PERSIST_URI_GRANTS_MSG)) { + mHandler.sendMessageDelayed(mHandler.obtainMessage(PERSIST_URI_GRANTS_MSG), + 10 * DateUtils.SECOND_IN_MILLIS); + } + } + private void writeGrantedUriPermissions() { if (DEBUG_URI_PERMISSION) Slog.v(TAG, "writeGrantedUriPermissions()"); @@ -6273,6 +6300,7 @@ public final class ActivityManagerService extends ActivityManagerNative out.attribute(null, ATTR_TARGET_PKG, perm.targetPkg); out.attribute(null, ATTR_URI, String.valueOf(perm.uri)); writeIntAttribute(out, ATTR_MODE_FLAGS, perm.persistedModeFlags); + writeLongAttribute(out, ATTR_CREATED_TIME, perm.persistedCreateTime); out.endTag(null, TAG_URI_GRANT); } out.endTag(null, TAG_URI_GRANTS); @@ -6289,6 +6317,8 @@ public final class ActivityManagerService extends ActivityManagerNative private void readGrantedUriPermissionsLocked() { if (DEBUG_URI_PERMISSION) Slog.v(TAG, "readGrantedUriPermissions()"); + final long now = System.currentTimeMillis(); + FileInputStream fis = null; try { fis = mGrantFile.openRead(); @@ -6305,6 +6335,7 @@ public final class ActivityManagerService extends ActivityManagerNative final String targetPkg = in.getAttributeValue(null, ATTR_TARGET_PKG); final Uri uri = Uri.parse(in.getAttributeValue(null, ATTR_URI)); final int modeFlags = readIntAttribute(in, ATTR_MODE_FLAGS); + final long createdTime = readLongAttribute(in, ATTR_CREATED_TIME, now); // Sanity check that provider still belongs to source package final ProviderInfo pi = getProviderInfoLocked( @@ -6319,7 +6350,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (targetUid != -1) { final UriPermission perm = findOrCreateUriPermissionLocked( sourcePkg, targetPkg, targetUid, uri); - perm.grantModes(modeFlags, true, null); + perm.initPersistedModes(modeFlags, createdTime); } } else { Slog.w(TAG, "Persisted grant for " + uri + " had source " + sourcePkg @@ -6340,47 +6371,117 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override - public Uri[] getGrantedUriPermissions( - String sourcePackage, String targetPackage, int modeFlags, int modeMask) { - enforceNotIsolatedCaller("getGrantedUriPermissions"); + public void takePersistableUriPermission(Uri uri, int modeFlags) { + enforceNotIsolatedCaller("takePersistableUriPermission"); + + modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + if (modeFlags == 0) { + return; + } + 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 int callingUid = Binder.getCallingUid(); + final UriPermission perm = findUriPermissionLocked(callingUid, uri); + if (perm == null) { + Slog.w(TAG, "No permission grant found for UID " + callingUid + " and Uri " + + uri.toSafeString()); + return; } - 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); + + boolean persistChanged = perm.takePersistableModes(modeFlags); + persistChanged |= maybePrunePersistedUriGrantsLocked(callingUid); + + if (persistChanged) { + schedulePersistUriGrants(); } + } + } - 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); + @Override + public void releasePersistableUriPermission(Uri uri, int modeFlags) { + enforceNotIsolatedCaller("releasePersistableUriPermission"); + + modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + if (modeFlags == 0) { + return; + } + + synchronized (this) { + final int callingUid = Binder.getCallingUid(); + + final UriPermission perm = findUriPermissionLocked(callingUid, uri); + if (perm == null) { + Slog.w(TAG, "No permission grant found for UID " + callingUid + " and Uri " + + uri.toSafeString()); + return; + } + + final boolean persistChanged = perm.releasePersistableModes(modeFlags); + removeUriPermissionIfNeededLocked(perm); + if (persistChanged) { + schedulePersistUriGrants(); + } + } + } + + /** + * Prune any older {@link UriPermission} for the given UID until outstanding + * persisted grants are below {@link #MAX_PERSISTED_URI_GRANTS}. + * + * @return if any mutations occured that require persisting. + */ + private boolean maybePrunePersistedUriGrantsLocked(int uid) { + final ArrayMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(uid); + if (perms == null) return false; + if (perms.size() < MAX_PERSISTED_URI_GRANTS) return false; + + final ArrayList<UriPermission> persisted = Lists.newArrayList(); + for (UriPermission perm : perms.values()) { + if (perm.persistedModeFlags != 0) { + persisted.add(perm); + } + } + + final int trimCount = persisted.size() - MAX_PERSISTED_URI_GRANTS; + if (trimCount <= 0) return false; + + Collections.sort(persisted, new UriPermission.PersistedTimeComparator()); + for (int i = 0; i < trimCount; i++) { + final UriPermission perm = persisted.get(i); + + if (DEBUG_URI_PERMISSION) { + Slog.v(TAG, "Trimming grant created at " + perm.persistedCreateTime); + } + + perm.releasePersistableModes(~0); + removeUriPermissionIfNeededLocked(perm); + } + + return true; + } + + @Override + public ParceledListSlice<android.content.UriPermission> getPersistedUriPermissions() { + enforceNotIsolatedCaller("getPersistedUriPermissions"); + + synchronized (this) { + final int callingUid = Binder.getCallingUid(); + final ArrayList<android.content.UriPermission> result = Lists.newArrayList(); + final ArrayMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(callingUid); + if (perms == null) { + Slog.w(TAG, "No permission grants found for UID " + callingUid); + } else { + final int size = perms.size(); + for (int i = 0; i < size; i++) { + final UriPermission perm = perms.valueAt(i); + if (perm.persistedModeFlags != 0) { + result.add(perm.buildPersistedPublicApiObject()); } } } - - return result.toArray(new Uri[result.size()]); + return new ParceledListSlice<android.content.UriPermission>(result); } } diff --git a/services/java/com/android/server/am/UriPermission.java b/services/java/com/android/server/am/UriPermission.java index 5e2ad00..7057c24 100644 --- a/services/java/com/android/server/am/UriPermission.java +++ b/services/java/com/android/server/am/UriPermission.java @@ -20,11 +20,12 @@ import android.content.Intent; import android.net.Uri; import android.os.UserHandle; import android.util.Log; +import android.util.Slog; -import com.android.internal.util.IndentingPrintWriter; import com.google.android.collect.Sets; import java.io.PrintWriter; +import java.util.Comparator; import java.util.HashSet; /** @@ -38,6 +39,11 @@ import java.util.HashSet; final class UriPermission { private static final String TAG = "UriPermission"; + public static final int STRENGTH_NONE = 0; + public static final int STRENGTH_OWNED = 1; + public static final int STRENGTH_GLOBAL = 2; + public static final int STRENGTH_PERSISTABLE = 3; + final int userHandle; final String sourcePkg; final String targetPkg; @@ -49,26 +55,29 @@ final class UriPermission { /** * Allowed modes. All permission enforcement should use this field. Must - * always be a superset of {@link #globalModeFlags}, - * {@link #persistedModeFlags}, {@link #mReadOwners}, and - * {@link #mWriteOwners}. Mutations should only be performed by the owning - * class. + * always be a combination of {@link #ownedModeFlags}, + * {@link #globalModeFlags}, {@link #persistableModeFlags}, and + * {@link #persistedModeFlags}. Mutations <em>must</em> only be performed by + * the owning class. */ int modeFlags = 0; - /** - * Allowed modes without explicit owner. Must always be a superset of - * {@link #persistedModeFlags}. Mutations should only be performed by the - * owning class. - */ + /** Allowed modes with explicit 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; /** - * Allowed modes that should be persisted across device boots. These modes - * have no explicit owners. Mutations should only be performed by the owning - * class. + * Timestamp when {@link #persistedModeFlags} was first defined in + * {@link System#currentTimeMillis()} time base. */ - int persistedModeFlags = 0; + long persistedCreateTime = INVALID_TIME; + + private static final long INVALID_TIME = Long.MIN_VALUE; private HashSet<UriPermissionOwner> mReadOwners; private HashSet<UriPermissionOwner> mWriteOwners; @@ -83,21 +92,25 @@ final class UriPermission { this.uri = uri; } + private void updateModeFlags() { + modeFlags = ownedModeFlags | globalModeFlags | persistableModeFlags | persistedModeFlags; + } + /** - * @return If mode changes should trigger persisting. + * Initialize persisted modes as read from file. This doesn't issue any + * global or owner grants. */ - boolean grantModes(int modeFlagsToGrant, boolean persist, UriPermissionOwner owner) { - boolean persistChanged = false; - - modeFlags |= modeFlagsToGrant; + void initPersistedModes(int modeFlags, long createdTime) { + persistableModeFlags = modeFlags; + persistedModeFlags = modeFlags; + persistedCreateTime = createdTime; - if (persist) { - final int before = persistedModeFlags; - persistedModeFlags |= modeFlagsToGrant; - persistChanged = persistedModeFlags != before; + updateModeFlags(); + } - // Treat persisted grants as global (ownerless) - owner = null; + void grantModes(int modeFlags, boolean persistable, UriPermissionOwner owner) { + if (persistable) { + persistableModeFlags |= modeFlags; } if (owner == null) { @@ -105,43 +118,77 @@ final class UriPermission { } else { if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) { addReadOwner(owner); - owner.addReadPermission(this); } if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) { addWriteOwner(owner); - owner.addWritePermission(this); } } - return persistChanged; + updateModeFlags(); } - + /** - * @return If mode changes should trigger persisting. + * @return if mode changes should trigger persisting. */ - boolean clearModes(int modeFlagsToClear, boolean persist) { + boolean takePersistableModes(int modeFlags) { + if ((~persistableModeFlags & modeFlags) != 0) { + Slog.w(TAG, "Trying to take 0x" + Integer.toHexString(modeFlags) + " but only 0x" + + Integer.toHexString(persistableModeFlags) + " are available"); + } + + final int before = persistedModeFlags; + persistedModeFlags |= (persistableModeFlags & modeFlags); + + if (persistedModeFlags != 0) { + persistedCreateTime = System.currentTimeMillis(); + } + + updateModeFlags(); + return persistedModeFlags != before; + } + + boolean releasePersistableModes(int modeFlags) { final int before = persistedModeFlags; - if ((modeFlagsToClear & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) { - if (persist) { + persistableModeFlags &= ~modeFlags; + persistedModeFlags &= ~modeFlags; + + if (persistedModeFlags == 0) { + persistedCreateTime = INVALID_TIME; + } + + updateModeFlags(); + return persistedModeFlags != before; + } + + /** + * @return if mode changes should trigger persisting. + */ + boolean clearModes(int modeFlags, boolean persistable) { + final int before = persistedModeFlags; + + if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) { + if (persistable) { + persistableModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION; persistedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION; } globalModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION; - modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION; if (mReadOwners != null) { + ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION; for (UriPermissionOwner r : mReadOwners) { r.removeReadPermission(this); } mReadOwners = null; } } - if ((modeFlagsToClear & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) { - if (persist) { + if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) { + if (persistable) { + persistableModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION; persistedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION; } globalModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION; - modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION; if (mWriteOwners != null) { + ownedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION; for (UriPermissionOwner r : mWriteOwners) { r.removeWritePermission(this); } @@ -149,18 +196,38 @@ final class UriPermission { } } - // Mode flags always bubble up - globalModeFlags |= persistedModeFlags; - modeFlags |= globalModeFlags; + if (persistedModeFlags == 0) { + persistedCreateTime = INVALID_TIME; + } + updateModeFlags(); return persistedModeFlags != before; } + /** + * Return strength of this permission grant for the given flags. + */ + public int getStrength(int modeFlags) { + if ((persistableModeFlags & modeFlags) == modeFlags) { + return STRENGTH_PERSISTABLE; + } else if ((globalModeFlags & modeFlags) == modeFlags) { + return STRENGTH_GLOBAL; + } else if ((ownedModeFlags & modeFlags) == modeFlags) { + return STRENGTH_OWNED; + } else { + return STRENGTH_NONE; + } + } + private void addReadOwner(UriPermissionOwner owner) { if (mReadOwners == null) { mReadOwners = Sets.newHashSet(); + ownedModeFlags |= Intent.FLAG_GRANT_READ_URI_PERMISSION; + updateModeFlags(); + } + if (mReadOwners.add(owner)) { + owner.addReadPermission(this); } - mReadOwners.add(owner); } /** @@ -172,17 +239,20 @@ final class UriPermission { } if (mReadOwners.size() == 0) { mReadOwners = null; - if ((globalModeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) { - modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION; - } + ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION; + updateModeFlags(); } } private void addWriteOwner(UriPermissionOwner owner) { if (mWriteOwners == null) { mWriteOwners = Sets.newHashSet(); + ownedModeFlags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION; + updateModeFlags(); + } + if (mWriteOwners.add(owner)) { + owner.addWritePermission(this); } - mWriteOwners.add(owner); } /** @@ -194,9 +264,8 @@ final class UriPermission { } if (mWriteOwners.size() == 0) { mWriteOwners = null; - if ((globalModeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) { - modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION; - } + ownedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION; + updateModeFlags(); } } @@ -221,9 +290,15 @@ final class UriPermission { pw.println(" targetPkg=" + targetPkg); pw.print(prefix); - pw.print("modeFlags=0x" + Integer.toHexString(modeFlags)); - pw.print(" globalModeFlags=0x" + Integer.toHexString(globalModeFlags)); - pw.println(" persistedModeFlags=0x" + Integer.toHexString(persistedModeFlags)); + pw.print("mode=0x" + Integer.toHexString(modeFlags)); + pw.print(" owned=0x" + Integer.toHexString(ownedModeFlags)); + pw.print(" global=0x" + Integer.toHexString(globalModeFlags)); + pw.print(" persistable=0x" + Integer.toHexString(persistableModeFlags)); + pw.print(" persisted=0x" + Integer.toHexString(persistedModeFlags)); + if (persistedCreateTime != INVALID_TIME) { + pw.print(" persistedCreate=" + persistedCreateTime); + } + pw.println(); if (mReadOwners != null) { pw.print(prefix); @@ -243,6 +318,13 @@ final class UriPermission { } } + public static class PersistedTimeComparator implements Comparator<UriPermission> { + @Override + public int compare(UriPermission lhs, UriPermission rhs) { + return Long.compare(lhs.persistedCreateTime, rhs.persistedCreateTime); + } + } + /** * Snapshot of {@link UriPermission} with frozen * {@link UriPermission#persistedModeFlags} state. @@ -253,6 +335,7 @@ final class UriPermission { final String targetPkg; final Uri uri; final int persistedModeFlags; + final long persistedCreateTime; private Snapshot(UriPermission perm) { this.userHandle = perm.userHandle; @@ -260,10 +343,15 @@ final class UriPermission { this.targetPkg = perm.targetPkg; this.uri = perm.uri; this.persistedModeFlags = perm.persistedModeFlags; + this.persistedCreateTime = perm.persistedCreateTime; } } public Snapshot snapshot() { return new Snapshot(this); } + + public android.content.UriPermission buildPersistedPublicApiObject() { + return new android.content.UriPermission(uri, persistedModeFlags, persistedCreateTime); + } } |