diff options
-rw-r--r-- | api/current.txt | 1 | ||||
-rw-r--r-- | core/java/android/content/Context.java | 2 | ||||
-rw-r--r-- | core/java/android/content/Intent.java | 12 | ||||
-rw-r--r-- | core/java/com/android/internal/util/IndentingPrintWriter.java | 4 | ||||
-rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 383 | ||||
-rw-r--r-- | services/java/com/android/server/am/UriPermission.java | 230 | ||||
-rw-r--r-- | services/java/com/android/server/am/UriPermissionOwner.java | 32 |
7 files changed, 509 insertions, 155 deletions
diff --git a/api/current.txt b/api/current.txt index cce01c8..dbab390 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6251,6 +6251,7 @@ package android.content { 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 + field public static final int FLAG_PERSIST_GRANT_URI_PERMISSION = 64; // 0x40 field public static final int FLAG_RECEIVER_FOREGROUND = 268435456; // 0x10000000 field public static final int FLAG_RECEIVER_REGISTERED_ONLY = 1073741824; // 0x40000000 field public static final int FLAG_RECEIVER_REPLACE_PENDING = 536870912; // 0x20000000 diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 03e241a..cb5c315 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2443,7 +2443,7 @@ public abstract class Context { * Remove all permissions to access a particular content provider Uri * that were previously added with {@link #grantUriPermission}. The given * Uri will match all previously granted Uris that are the same or a - * sub-path of the given Uri. That is, revoking "content://foo/one" will + * 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". * diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index e36f815..36c69ef 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -3228,6 +3228,15 @@ public class Intent implements Parcelable, Cloneable { public static final int FLAG_INCLUDE_STOPPED_PACKAGES = 0x00000020; /** + * When combined with {@link #FLAG_GRANT_READ_URI_PERMISSION} and/or + * {@link #FLAG_GRANT_WRITE_URI_PERMISSION}, the grant will be remembered + * until explicitly revoked with + * {@link Context#revokeUriPermission(Uri, int)}. These grants persist + * across device reboots. + */ + public static final int FLAG_PERSIST_GRANT_URI_PERMISSION = 0x00000040; + + /** * 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 @@ -7016,7 +7025,8 @@ public class Intent implements Parcelable, Cloneable { // and flags to ourselves to grant. setClipData(target.getClipData()); addFlags(target.getFlags() - & (FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION)); + & (FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION + | FLAG_PERSIST_GRANT_URI_PERMISSION)); return true; } else { return false; diff --git a/core/java/com/android/internal/util/IndentingPrintWriter.java b/core/java/com/android/internal/util/IndentingPrintWriter.java index d01a817..6fddd09 100644 --- a/core/java/com/android/internal/util/IndentingPrintWriter.java +++ b/core/java/com/android/internal/util/IndentingPrintWriter.java @@ -68,6 +68,10 @@ public class IndentingPrintWriter extends PrintWriter { print(key + "=" + String.valueOf(value) + " "); } + public void printHexPair(String key, int value) { + print(key + "=0x" + Integer.toHexString(value) + " "); + } + @Override public void write(char[] buf, int offset, int count) { final int indentLength = mIndentBuilder.length(); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 9db7503..7137a9d 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -17,14 +17,20 @@ 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.writeIntAttribute; +import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; +import static org.xmlpull.v1.XmlPullParser.START_TAG; import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID; import android.app.AppOpsManager; import android.appwidget.AppWidgetManager; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.ProcessStats; +import com.android.internal.util.FastXmlSerializer; import com.android.server.AppOpsService; import com.android.server.AttributeCache; import com.android.server.IntentResolver; @@ -37,9 +43,17 @@ import com.android.server.pm.UserManagerService; import com.android.server.wm.AppTransition; import com.android.server.wm.StackBox; import com.android.server.wm.WindowManagerService; +import com.google.android.collect.Lists; +import com.google.android.collect.Maps; import dalvik.system.Zygote; +import libcore.io.IoUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; @@ -129,6 +143,7 @@ import android.os.UpdateLock; import android.os.UserHandle; import android.provider.Settings; import android.text.format.Time; +import android.util.AtomicFile; import android.util.EventLog; import android.util.Log; import android.util.Pair; @@ -136,6 +151,7 @@ import android.util.PrintWriterPrinter; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; +import android.util.Xml; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; @@ -623,10 +639,27 @@ public final class ActivityManagerService extends ActivityManagerNative = new ArrayList<ContentProviderRecord>(); /** - * Global set of specific Uri permissions that have been granted. + * File storing persisted {@link #mGrantedUriPermissions}. */ - final private SparseArray<HashMap<Uri, UriPermission>> mGrantedUriPermissions - = new SparseArray<HashMap<Uri, UriPermission>>(); + private final AtomicFile mGrantFile; + + /** XML constants used in {@link #mGrantFile} */ + private static final String TAG_URI_GRANTS = "uri-grants"; + private static final String TAG_URI_GRANT = "uri-grant"; + private static final String ATTR_USER_HANDLE = "userHandle"; + private static final String ATTR_SOURCE_PKG = "sourcePkg"; + private static final String ATTR_TARGET_PKG = "targetPkg"; + private static final String ATTR_URI = "uri"; + private static final String ATTR_MODE_FLAGS = "modeFlags"; + + /** + * Global set of specific {@link Uri} permissions that have been granted. + * This optimized lookup structure maps from {@link UriPermission#targetUid} + * to {@link UriPermission#uri} to {@link UriPermission}. + */ + @GuardedBy("this") + private final SparseArray<HashMap<Uri, UriPermission>> + mGrantedUriPermissions = new SparseArray<HashMap<Uri, UriPermission>>(); CoreSettingsObserver mCoreSettingsObserver; @@ -923,6 +956,7 @@ public final class ActivityManagerService extends ActivityManagerNative static final int CONTINUE_USER_SWITCH_MSG = 35; static final int USER_SWITCH_TIMEOUT_MSG = 36; static final int IMMERSIVE_MODE_LOCK_MSG = 37; + static final int PERSIST_URI_GRANTS = 38; static final int FIRST_ACTIVITY_STACK_MSG = 100; static final int FIRST_BROADCAST_QUEUE_MSG = 200; @@ -1406,6 +1440,10 @@ public final class ActivityManagerService extends ActivityManagerNative } break; } + case PERSIST_URI_GRANTS: { + writeGrantedUriPermissions(); + break; + } } } }; @@ -1654,6 +1692,9 @@ public final class ActivityManagerService extends ActivityManagerNative mUsageStatsService = new UsageStatsService(new File( systemDir, "usagestats").toString()); mAppOpsService = new AppOpsService(new File(systemDir, "appops.xml")); + + mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml")); + mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0")); // User 0 is the first and only user that runs at boot. @@ -3552,8 +3593,12 @@ public final class ActivityManagerService extends ActivityManagerNative } try { - //clear application user data + // Clear application user data pm.clearApplicationUserData(packageName, observer, userId); + + // Remove all permissions granted from/to this package + removeUriPermissionsForPackageLocked(packageName, userId, true); + Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED, Uri.fromParts("package", packageName, null)); intent.putExtra(Intent.EXTRA_UID, pkgUid); @@ -4004,6 +4049,9 @@ public final class ActivityManagerService extends ActivityManagerNative removeDyingProviderLocked(null, providers.get(i), true); } + // Remove transient permissions granted from/to this package/user + removeUriPermissionsForPackageLocked(name, userId, false); + if (name == null) { // Remove pending intents. For now we only do this when force // stopping users, because we have some problems when doing this @@ -5128,6 +5176,38 @@ public final class ActivityManagerService extends ActivityManagerNative return readMet && writeMet; } + private ProviderInfo getProviderInfoLocked(String authority, int userHandle) { + ProviderInfo pi = null; + ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userHandle); + if (cpr != null) { + pi = cpr.info; + } else { + try { + pi = AppGlobals.getPackageManager().resolveContentProvider( + authority, PackageManager.GET_URI_PERMISSION_PATTERNS, userHandle); + } catch (RemoteException ex) { + } + } + return pi; + } + + private UriPermission findOrCreateUriPermissionLocked( + String sourcePkg, String targetPkg, int targetUid, Uri uri) { + HashMap<Uri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid); + if (targetUris == null) { + targetUris = Maps.newHashMap(); + mGrantedUriPermissions.put(targetUid, targetUris); + } + + UriPermission perm = targetUris.get(uri); + if (perm == null) { + perm = new UriPermission(sourcePkg, targetPkg, targetUid, uri); + targetUris.put(uri, perm); + } + + return perm; + } + private final boolean checkUriPermissionLocked(Uri uri, int uid, int modeFlags) { // Root gets to do everything. @@ -5174,6 +5254,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; modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); if (modeFlags == 0) { @@ -5194,20 +5275,8 @@ public final class ActivityManagerService extends ActivityManagerNative return -1; } - String name = uri.getAuthority(); - ProviderInfo pi = null; - ContentProviderRecord cpr = mProviderMap.getProviderByName(name, - UserHandle.getUserId(callingUid)); - if (cpr != null) { - pi = cpr.info; - } else { - try { - pi = pm.resolveContentProvider(name, - PackageManager.GET_URI_PERMISSION_PATTERNS, - UserHandle.getUserId(callingUid)); - } catch (RemoteException ex) { - } - } + final String authority = uri.getAuthority(); + final ProviderInfo pi = getProviderInfoLocked(authority, UserHandle.getUserId(callingUid)); if (pi == null) { Slog.w(TAG, "No content provider found for permission check: " + uri.toSafeString()); return -1; @@ -5229,7 +5298,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)) { + if (checkHoldingPermissionsLocked(pm, pi, uri, targetUid, modeFlags) && !persist) { // No need to grant the target this permission. if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Target " + targetPkg + " already has full permission to " + uri); @@ -5248,7 +5317,7 @@ public final class ActivityManagerService extends ActivityManagerNative allowed = false; } } - if (allowed) { + if (allowed && !persist) { return -1; } } @@ -5300,8 +5369,9 @@ public final class ActivityManagerService extends ActivityManagerNative } } - void grantUriPermissionUncheckedLocked(int targetUid, String targetPkg, - Uri uri, int modeFlags, UriPermissionOwner owner) { + void grantUriPermissionUncheckedLocked( + int targetUid, String targetPkg, Uri uri, int modeFlags, UriPermissionOwner owner) { + 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) { @@ -5314,32 +5384,20 @@ public final class ActivityManagerService extends ActivityManagerNative if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Granting " + targetPkg + "/" + targetUid + " permission to " + uri); - - HashMap<Uri, UriPermission> targetUris - = mGrantedUriPermissions.get(targetUid); - if (targetUris == null) { - targetUris = new HashMap<Uri, UriPermission>(); - mGrantedUriPermissions.put(targetUid, targetUris); - } - UriPermission perm = targetUris.get(uri); - if (perm == null) { - perm = new UriPermission(targetUid, uri); - targetUris.put(uri, perm); + final String authority = uri.getAuthority(); + final ProviderInfo pi = getProviderInfoLocked(authority, UserHandle.getUserId(targetUid)); + if (pi == null) { + Slog.w(TAG, "No content provider found for grant: " + uri.toSafeString()); + return; } - perm.modeFlags |= modeFlags; - if (owner == null) { - perm.globalModeFlags |= modeFlags; - } else { - if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) { - perm.readOwners.add(owner); - owner.addReadPermission(perm); - } - if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) { - perm.writeOwners.add(owner); - owner.addWritePermission(perm); - } + final UriPermission perm = findOrCreateUriPermissionLocked( + pi.packageName, targetPkg, targetUid, uri); + final boolean persistChanged = perm.grantModes(modeFlags, persist, owner); + if (persistChanged) { + mHandler.removeMessages(PERSIST_URI_GRANTS); + mHandler.obtainMessage(PERSIST_URI_GRANTS).sendToTarget(); } } @@ -5362,10 +5420,10 @@ public final class ActivityManagerService extends ActivityManagerNative final int targetUid; final int flags; - NeededUriGrants(String _targetPkg, int _targetUid, int _flags) { - targetPkg = _targetPkg; - targetUid = _targetUid; - flags = _flags; + NeededUriGrants(String targetPkg, int targetUid, int flags) { + this.targetPkg = targetPkg; + this.targetUid = targetUid; + this.flags = flags; } } @@ -5393,11 +5451,11 @@ public final class ActivityManagerService extends ActivityManagerNative return null; } if (data != null) { - int target = checkGrantUriPermissionLocked(callingUid, targetPkg, data, + int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, data, mode, needed != null ? needed.targetUid : -1); - if (target > 0) { + if (targetUid > 0) { if (needed == null) { - needed = new NeededUriGrants(targetPkg, target, mode); + needed = new NeededUriGrants(targetPkg, targetUid, mode); } needed.add(data); } @@ -5406,12 +5464,12 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=0; i<clip.getItemCount(); i++) { Uri uri = clip.getItemAt(i).getUri(); if (uri != null) { - int target = -1; - target = checkGrantUriPermissionLocked(callingUid, targetPkg, uri, + int targetUid = -1; + targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, uri, mode, needed != null ? needed.targetUid : -1); - if (target > 0) { + if (targetUid > 0) { if (needed == null) { - needed = new NeededUriGrants(targetPkg, target, mode); + needed = new NeededUriGrants(targetPkg, targetUid, mode); } needed.add(uri); } @@ -5481,44 +5539,25 @@ public final class ActivityManagerService extends ActivityManagerNative if ((perm.modeFlags&(Intent.FLAG_GRANT_READ_URI_PERMISSION |Intent.FLAG_GRANT_WRITE_URI_PERMISSION)) == 0) { HashMap<Uri, UriPermission> perms - = mGrantedUriPermissions.get(perm.uid); + = mGrantedUriPermissions.get(perm.targetUid); if (perms != null) { if (DEBUG_URI_PERMISSION) Slog.v(TAG, - "Removing " + perm.uid + " permission to " + perm.uri); + "Removing " + perm.targetUid + " permission to " + perm.uri); perms.remove(perm.uri); if (perms.size() == 0) { - mGrantedUriPermissions.remove(perm.uid); + mGrantedUriPermissions.remove(perm.targetUid); } } } } - private void revokeUriPermissionLocked(int callingUid, Uri uri, - int modeFlags) { - modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION - | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - if (modeFlags == 0) { - return; - } + private void revokeUriPermissionLocked( + int callingUid, Uri uri, int modeFlags, boolean persist) { + if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Revoking all granted permissions to " + uri); - if (DEBUG_URI_PERMISSION) Slog.v(TAG, - "Revoking all granted permissions to " + uri); - final IPackageManager pm = AppGlobals.getPackageManager(); - final String authority = uri.getAuthority(); - ProviderInfo pi = null; - int userId = UserHandle.getUserId(callingUid); - ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userId); - if (cpr != null) { - pi = cpr.info; - } else { - try { - pi = pm.resolveContentProvider(authority, - PackageManager.GET_URI_PERMISSION_PATTERNS, userId); - } catch (RemoteException ex) { - } - } + final ProviderInfo pi = getProviderInfoLocked(authority, UserHandle.getUserId(callingUid)); if (pi == null) { Slog.w(TAG, "No content provider found for permission revoke: " + uri.toSafeString()); return; @@ -5534,6 +5573,8 @@ 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) { @@ -5563,8 +5604,8 @@ public final class ActivityManagerService extends ActivityManagerNative } } if (DEBUG_URI_PERMISSION) Slog.v(TAG, - "Revoking " + perm.uid + " permission to " + perm.uri); - perm.clearModes(modeFlags); + "Revoking " + perm.targetUid + " permission to " + perm.uri); + persistChanged |= perm.clearModes(modeFlags, persist); if (perm.modeFlags == 0) { it.remove(); } @@ -5577,6 +5618,11 @@ public final class ActivityManagerService extends ActivityManagerNative } } } + + if (persistChanged) { + mHandler.removeMessages(PERSIST_URI_GRANTS); + mHandler.obtainMessage(PERSIST_URI_GRANTS).sendToTarget(); + } } public void revokeUriPermission(IApplicationThread caller, Uri uri, @@ -5594,6 +5640,7 @@ 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) { @@ -5601,26 +5648,64 @@ public final class ActivityManagerService extends ActivityManagerNative } final IPackageManager pm = AppGlobals.getPackageManager(); - final String authority = uri.getAuthority(); - ProviderInfo pi = null; - ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, r.userId); - if (cpr != null) { - pi = cpr.info; - } else { - try { - pi = pm.resolveContentProvider(authority, - PackageManager.GET_URI_PERMISSION_PATTERNS, r.userId); - } catch (RemoteException ex) { - } - } + final ProviderInfo pi = getProviderInfoLocked(authority, r.userId); if (pi == null) { Slog.w(TAG, "No content provider found for permission revoke: " + uri.toSafeString()); return; } - revokeUriPermissionLocked(r.uid, uri, modeFlags); + revokeUriPermissionLocked(r.uid, uri, modeFlags, persist); + } + } + + /** + * Remove any {@link UriPermission} granted <em>from</em> or <em>to</em> the + * given package. + * + * @param packageName Package name to match, or {@code null} to apply to all + * packages. + * @param userHandle User to match, or {@link UserHandle#USER_ALL} to apply + * to all users. + * @param persist If persistent grants should be removed. + */ + private void removeUriPermissionsForPackageLocked( + String packageName, int userHandle, boolean persist) { + if (userHandle == UserHandle.USER_ALL && packageName == null) { + throw new IllegalArgumentException("Must narrow by either package or user"); + } + + boolean persistChanged = false; + + final int size = mGrantedUriPermissions.size(); + for (int i = 0; i < size; 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()) { + 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, persist); + + // Only remove when no modes remain; any persisted grants + // will keep this alive. + if (perm.modeFlags == 0) { + it.remove(); + } + } + } + } + } + + if (persistChanged) { + mHandler.removeMessages(PERSIST_URI_GRANTS); + mHandler.obtainMessage(PERSIST_URI_GRANTS).sendToTarget(); } } @@ -5675,6 +5760,103 @@ public final class ActivityManagerService extends ActivityManagerNative } } + private void writeGrantedUriPermissions() { + if (DEBUG_URI_PERMISSION) Slog.v(TAG, "writeGrantedUriPermissions()"); + + // Snapshot permissions so we can persist without lock + 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()) { + if (perm.persistedModeFlags != 0) { + persist.add(perm.snapshot()); + } + } + } + } + + FileOutputStream fos = null; + try { + fos = mGrantFile.startWrite(); + + XmlSerializer out = new FastXmlSerializer(); + out.setOutput(fos, "utf-8"); + out.startDocument(null, true); + out.startTag(null, TAG_URI_GRANTS); + for (UriPermission.Snapshot perm : persist) { + out.startTag(null, TAG_URI_GRANT); + 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)); + writeIntAttribute(out, ATTR_MODE_FLAGS, perm.persistedModeFlags); + out.endTag(null, TAG_URI_GRANT); + } + out.endTag(null, TAG_URI_GRANTS); + out.endDocument(); + + mGrantFile.finishWrite(fos); + } catch (IOException e) { + if (fos != null) { + mGrantFile.failWrite(fos); + } + } + } + + private void readGrantedUriPermissionsLocked() { + if (DEBUG_URI_PERMISSION) Slog.v(TAG, "readGrantedUriPermissions()"); + + FileInputStream fis = null; + try { + fis = mGrantFile.openRead(); + final XmlPullParser in = Xml.newPullParser(); + in.setInput(fis, null); + + int type; + while ((type = in.next()) != END_DOCUMENT) { + final String tag = in.getName(); + if (type == START_TAG) { + if (TAG_URI_GRANT.equals(tag)) { + final int userHandle = readIntAttribute(in, ATTR_USER_HANDLE); + 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 int modeFlags = readIntAttribute(in, ATTR_MODE_FLAGS); + + // Sanity check that provider still belongs to source package + final ProviderInfo pi = getProviderInfoLocked( + uri.getAuthority(), userHandle); + if (pi != null && sourcePkg.equals(pi.packageName)) { + int targetUid = -1; + try { + targetUid = AppGlobals.getPackageManager() + .getPackageUid(targetPkg, userHandle); + } catch (RemoteException e) { + } + if (targetUid != -1) { + final UriPermission perm = findOrCreateUriPermissionLocked( + sourcePkg, targetPkg, targetUid, uri); + perm.grantModes(modeFlags, true, null); + } + } else { + Slog.w(TAG, "Persisted grant for " + uri + " had source " + sourcePkg + + " but instead found " + pi); + } + } + } + } + } catch (FileNotFoundException e) { + // Missing grants is okay + } catch (IOException e) { + Log.wtf(TAG, "Failed reading Uri grants", e); + } catch (XmlPullParserException e) { + Log.wtf(TAG, "Failed reading Uri grants", e); + } finally { + IoUtils.closeQuietly(fis); + } + } + public void showWaitingForDebugger(IApplicationThread who, boolean waiting) { synchronized (this) { ProcessRecord app = @@ -7960,6 +8142,10 @@ public final class ActivityManagerService extends ActivityManagerNative retrieveSettings(); + synchronized (this) { + readGrantedUriPermissionsLocked(); + } + if (goingCallback != null) goingCallback.run(); synchronized (this) { @@ -11634,6 +11820,9 @@ public final class ActivityManagerService extends ActivityManagerNative if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { mAppOpsService.packageRemoved( intent.getIntExtra(Intent.EXTRA_UID, -1), ssp); + + // Remove all permissions granted from/to this package + removeUriPermissionsForPackageLocked(ssp, userId, true); } } } diff --git a/services/java/com/android/server/am/UriPermission.java b/services/java/com/android/server/am/UriPermission.java index c5b1c7b..e79faf9 100644 --- a/services/java/com/android/server/am/UriPermission.java +++ b/services/java/com/android/server/am/UriPermission.java @@ -18,6 +18,11 @@ package com.android.server.am; import android.content.Intent; import android.net.Uri; +import android.os.UserHandle; +import android.util.Log; + +import com.android.internal.util.IndentingPrintWriter; +import com.google.android.collect.Sets; import java.io.PrintWriter; import java.util.HashSet; @@ -31,43 +36,171 @@ import java.util.HashSet; * src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java */ class UriPermission { - final int uid; + private static final String TAG = "UriPermission"; + + final int userHandle; + final String sourcePkg; + final String targetPkg; + + /** Cached UID of {@link #targetPkg}; should not be persisted */ + final int targetUid; + final Uri uri; + + /** + * 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. + */ 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. + */ int globalModeFlags = 0; - final HashSet<UriPermissionOwner> readOwners = new HashSet<UriPermissionOwner>(); - final HashSet<UriPermissionOwner> writeOwners = new HashSet<UriPermissionOwner>(); - - String stringName; - - UriPermission(int _uid, Uri _uri) { - uid = _uid; - uri = _uri; + + /** + * Allowed modes that should be persisted across device boots. These modes + * have no explicit owners. Mutations should only be performed by the owning + * class. + */ + int persistedModeFlags = 0; + + private HashSet<UriPermissionOwner> mReadOwners; + private HashSet<UriPermissionOwner> mWriteOwners; + + private String stringName; + + UriPermission(String sourcePkg, String targetPkg, int targetUid, Uri uri) { + this.userHandle = UserHandle.getUserId(targetUid); + this.sourcePkg = sourcePkg; + this.targetPkg = targetPkg; + this.targetUid = targetUid; + this.uri = uri; + } + + /** + * @return If mode changes should trigger persisting. + */ + boolean grantModes(int modeFlagsToGrant, boolean persist, UriPermissionOwner owner) { + boolean persistChanged = false; + + modeFlags |= modeFlagsToGrant; + + if (persist) { + final int before = persistedModeFlags; + persistedModeFlags |= modeFlagsToGrant; + persistChanged = persistedModeFlags != before; + + // Treat persisted grants as global (ownerless) + owner = null; + } + + if (owner == null) { + globalModeFlags |= modeFlags; + } 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; } - void clearModes(int modeFlagsToClear) { - if ((modeFlagsToClear&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) { + /** + * @return If mode changes should trigger persisting. + */ + boolean clearModes(int modeFlagsToClear, boolean persist) { + final int before = persistedModeFlags; + + if ((modeFlagsToClear & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) { + if (persist) { + persistedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION; + } globalModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION; modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION; - if (readOwners.size() > 0) { - for (UriPermissionOwner r : readOwners) { + if (mReadOwners != null) { + for (UriPermissionOwner r : mReadOwners) { r.removeReadPermission(this); } - readOwners.clear(); + mReadOwners = null; } } - if ((modeFlagsToClear&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) { + if ((modeFlagsToClear & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) { + if (persist) { + persistedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION; + } globalModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION; modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION; - if (writeOwners.size() > 0) { - for (UriPermissionOwner r : writeOwners) { + if (mWriteOwners != null) { + for (UriPermissionOwner r : mWriteOwners) { r.removeWritePermission(this); } - writeOwners.clear(); + mWriteOwners = null; + } + } + + // Mode flags always bubble up + globalModeFlags |= persistedModeFlags; + modeFlags |= globalModeFlags; + + return persistedModeFlags != before; + } + + private void addReadOwner(UriPermissionOwner owner) { + if (mReadOwners == null) { + mReadOwners = Sets.newHashSet(); + } + mReadOwners.add(owner); + } + + /** + * Remove given read owner, updating {@Link #modeFlags} as needed. + */ + void removeReadOwner(UriPermissionOwner owner) { + if (!mReadOwners.remove(owner)) { + Log.wtf(TAG, "Unknown read owner " + owner + " in " + this); + } + if (mReadOwners.size() == 0) { + mReadOwners = null; + if ((globalModeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) { + modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION; } } } - + + private void addWriteOwner(UriPermissionOwner owner) { + if (mWriteOwners == null) { + mWriteOwners = Sets.newHashSet(); + } + mWriteOwners.add(owner); + } + + /** + * Remove given write owner, updating {@Link #modeFlags} as needed. + */ + void removeWriteOwner(UriPermissionOwner owner) { + if (!mWriteOwners.remove(owner)) { + Log.wtf(TAG, "Unknown write owner " + owner + " in " + this); + } + if (mWriteOwners.size() == 0) { + mWriteOwners = null; + if ((globalModeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) { + modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION; + } + } + } + + @Override public String toString() { if (stringName != null) { return stringName; @@ -82,22 +215,55 @@ class UriPermission { } void dump(PrintWriter pw, String prefix) { - pw.print(prefix); pw.print("modeFlags=0x"); - pw.print(Integer.toHexString(modeFlags)); - pw.print(" uid="); pw.print(uid); - pw.print(" globalModeFlags=0x"); - pw.println(Integer.toHexString(globalModeFlags)); - if (readOwners.size() != 0) { - pw.print(prefix); pw.println("readOwners:"); - for (UriPermissionOwner owner : readOwners) { - pw.print(prefix); pw.print(" * "); pw.println(owner); + pw.print(prefix); + pw.print("userHandle=" + userHandle); + pw.print("sourcePkg=" + sourcePkg); + 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)); + + if (mReadOwners != null) { + pw.print(prefix); + pw.println("readOwners:"); + for (UriPermissionOwner owner : mReadOwners) { + pw.print(prefix); + pw.println(" * " + owner); } } - if (writeOwners.size() != 0) { - pw.print(prefix); pw.println("writeOwners:"); - for (UriPermissionOwner owner : writeOwners) { - pw.print(prefix); pw.print(" * "); pw.println(owner); + if (mWriteOwners != null) { + pw.print(prefix); + pw.println("writeOwners:"); + for (UriPermissionOwner owner : mReadOwners) { + pw.print(prefix); + pw.println(" * " + owner); } } } + + /** + * Snapshot of {@link UriPermission} with frozen + * {@link UriPermission#persistedModeFlags} state. + */ + public static class Snapshot { + final int userHandle; + final String sourcePkg; + final String targetPkg; + final Uri uri; + final int persistedModeFlags; + + private Snapshot(UriPermission perm) { + this.userHandle = perm.userHandle; + this.sourcePkg = perm.sourcePkg; + this.targetPkg = perm.targetPkg; + this.uri = perm.uri; + this.persistedModeFlags = perm.persistedModeFlags; + } + } + + public Snapshot snapshot() { + return new Snapshot(this); + } } diff --git a/services/java/com/android/server/am/UriPermissionOwner.java b/services/java/com/android/server/am/UriPermissionOwner.java index 68a2e0f..90ac88d 100644 --- a/services/java/com/android/server/am/UriPermissionOwner.java +++ b/services/java/com/android/server/am/UriPermissionOwner.java @@ -67,24 +67,16 @@ class UriPermissionOwner { if ((mode&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0 && readUriPermissions != null) { for (UriPermission perm : readUriPermissions) { - perm.readOwners.remove(this); - if (perm.readOwners.size() == 0 && (perm.globalModeFlags - &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) { - perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION; - service.removeUriPermissionIfNeededLocked(perm); - } + perm.removeReadOwner(this); + service.removeUriPermissionIfNeededLocked(perm); } readUriPermissions = null; } if ((mode&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0 && writeUriPermissions != null) { for (UriPermission perm : writeUriPermissions) { - perm.writeOwners.remove(this); - if (perm.writeOwners.size() == 0 && (perm.globalModeFlags - &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) { - perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION; - service.removeUriPermissionIfNeededLocked(perm); - } + perm.removeWriteOwner(this); + service.removeUriPermissionIfNeededLocked(perm); } writeUriPermissions = null; } @@ -97,12 +89,8 @@ class UriPermissionOwner { while (it.hasNext()) { UriPermission perm = it.next(); if (uri.equals(perm.uri)) { - perm.readOwners.remove(this); - if (perm.readOwners.size() == 0 && (perm.globalModeFlags - &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) { - perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION; - service.removeUriPermissionIfNeededLocked(perm); - } + perm.removeReadOwner(this); + service.removeUriPermissionIfNeededLocked(perm); it.remove(); } } @@ -116,12 +104,8 @@ class UriPermissionOwner { while (it.hasNext()) { UriPermission perm = it.next(); if (uri.equals(perm.uri)) { - perm.writeOwners.remove(this); - if (perm.writeOwners.size() == 0 && (perm.globalModeFlags - &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) { - perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION; - service.removeUriPermissionIfNeededLocked(perm); - } + perm.removeWriteOwner(this); + service.removeUriPermissionIfNeededLocked(perm); it.remove(); } } |