summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt1
-rw-r--r--core/java/android/content/Context.java2
-rw-r--r--core/java/android/content/Intent.java12
-rw-r--r--core/java/com/android/internal/util/IndentingPrintWriter.java4
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java383
-rw-r--r--services/java/com/android/server/am/UriPermission.java230
-rw-r--r--services/java/com/android/server/am/UriPermissionOwner.java32
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();
}
}