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
diff --git a/api/current.txt b/api/current.txt
index a603b773..5362fdf 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5744,11 +5744,10 @@
method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
method public static deprecated android.content.SyncInfo getCurrentSync();
method public static java.util.List<android.content.SyncInfo> getCurrentSyncs();
- method public android.net.Uri[] getIncomingUriPermissionGrants(int, int);
method public static int getIsSyncable(android.accounts.Account, java.lang.String);
method public static boolean getMasterSyncAutomatically();
- method public android.net.Uri[] getOutgoingUriPermissionGrants(int, int);
method public static java.util.List<android.content.PeriodicSync> getPeriodicSyncs(android.accounts.Account, java.lang.String);
+ method public java.util.List<android.content.UriPermission> getPersistedUriPermissions();
method public java.lang.String[] getStreamTypes(android.net.Uri, java.lang.String);
method public static android.content.SyncAdapterType[] getSyncAdapterTypes();
method public static boolean getSyncAutomatically(android.accounts.Account, java.lang.String);
@@ -5770,6 +5769,7 @@
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
method public final void registerContentObserver(android.net.Uri, boolean, android.database.ContentObserver);
+ method public void releasePersistableUriPermission(android.net.Uri, int);
method public static void removePeriodicSync(android.accounts.Account, java.lang.String, android.os.Bundle);
method public static void removeStatusChangeListener(java.lang.Object);
method public static void requestSync(android.accounts.Account, java.lang.String, android.os.Bundle);
@@ -5778,6 +5778,7 @@
method public static void setMasterSyncAutomatically(boolean);
method public static void setSyncAutomatically(android.accounts.Account, java.lang.String, boolean);
method public deprecated void startSync(android.net.Uri, android.os.Bundle);
+ method public void takePersistableUriPermission(android.net.Uri, int);
method public final android.net.Uri uncanonicalize(android.net.Uri);
method public final void unregisterContentObserver(android.database.ContentObserver);
method public final int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
@@ -6515,10 +6516,10 @@
field public static final int FLAG_DEBUG_LOG_RESOLUTION = 8; // 0x8
field public static final int FLAG_EXCLUDE_STOPPED_PACKAGES = 16; // 0x10
field public static final int FLAG_FROM_BACKGROUND = 4; // 0x4
+ field public static final int FLAG_GRANT_PERSISTABLE_URI_PERMISSION = 64; // 0x40
field public static final int FLAG_GRANT_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_NO_ABORT = 134217728; // 0x8000000
field public static final int FLAG_RECEIVER_REGISTERED_ONLY = 1073741824; // 0x40000000
@@ -6911,6 +6912,17 @@
field public static final int NO_MATCH = -1; // 0xffffffff
}
+ public final class UriPermission implements android.os.Parcelable {
+ method public int describeContents();
+ method public long getPersistedTime();
+ method public android.net.Uri getUri();
+ method public boolean isReadPermission();
+ method public boolean isWritePermission();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final long INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
+ }
+
}
package android.content.pm {
@@ -20954,7 +20966,6 @@
method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri);
method public static java.lang.String getDocumentId(android.net.Uri);
method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal);
- method public static android.net.Uri[] getOpenDocuments(android.content.Context);
method public static java.lang.String getRootId(android.net.Uri);
method public static java.lang.String getSearchDocumentsQuery(android.net.Uri);
field public static final java.lang.String EXTRA_ERROR = "error";
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 7d4d57c..3b88973 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -23,9 +23,11 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
+import android.content.UriPermission;
import android.content.pm.ApplicationInfo;
import android.content.pm.ConfigurationInfo;
import android.content.pm.IPackageDataObserver;
+import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
@@ -1130,6 +1132,32 @@
return true;
}
+ case TAKE_PERSISTABLE_URI_PERMISSION_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ Uri uri = Uri.CREATOR.createFromParcel(data);
+ int mode = data.readInt();
+ takePersistableUriPermission(uri, mode);
+ reply.writeNoException();
+ return true;
+ }
+
+ case RELEASE_PERSISTABLE_URI_PERMISSION_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ Uri uri = Uri.CREATOR.createFromParcel(data);
+ int mode = data.readInt();
+ releasePersistableUriPermission(uri, mode);
+ reply.writeNoException();
+ return true;
+ }
+
+ case GET_PERSISTED_URI_PERMISSIONS_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ final ParceledListSlice<UriPermission> perms = getPersistedUriPermissions();
+ reply.writeNoException();
+ perms.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ return true;
+ }
+
case SHOW_WAITING_FOR_DEBUGGER_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
@@ -1983,19 +2011,6 @@
return true;
}
- case GET_GRANTED_URI_PERMISSIONS_TRANSACTION: {
- data.enforceInterface(IActivityManager.descriptor);
- final String sourcePackage = data.readString();
- final String targetPackage = data.readString();
- final int modeFlags = data.readInt();
- final int modeMask = data.readInt();
- final Uri[] uris = getGrantedUriPermissions(
- sourcePackage, targetPackage, modeFlags, modeMask);
- reply.writeNoException();
- reply.writeParcelableArray(uris, 0);
- return true;
- }
-
case PERFORM_IDLE_MAINTENANCE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
performIdleMaintenance();
@@ -3436,6 +3451,47 @@
data.recycle();
reply.recycle();
}
+
+ @Override
+ public void takePersistableUriPermission(Uri uri, int mode) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ uri.writeToParcel(data, 0);
+ data.writeInt(mode);
+ mRemote.transact(TAKE_PERSISTABLE_URI_PERMISSION_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ @Override
+ public void releasePersistableUriPermission(Uri uri, int mode) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ uri.writeToParcel(data, 0);
+ data.writeInt(mode);
+ mRemote.transact(RELEASE_PERSISTABLE_URI_PERMISSION_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ @Override
+ public ParceledListSlice<UriPermission> getPersistedUriPermissions() throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(GET_PERSISTED_URI_PERMISSIONS_TRANSACTION, data, reply, 0);
+ reply.readException();
+ final ParceledListSlice<UriPermission> perms = ParceledListSlice.CREATOR.createFromParcel(
+ reply);
+ data.recycle();
+ reply.recycle();
+ return perms;
+ }
+
public void showWaitingForDebugger(IApplicationThread who, boolean waiting)
throws RemoteException {
Parcel data = Parcel.obtain();
@@ -4567,24 +4623,6 @@
reply.recycle();
}
- public Uri[] getGrantedUriPermissions(
- String sourcePackage, String targetPackage, int modeFlags, int modeMask)
- throws RemoteException {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken(IActivityManager.descriptor);
- data.writeString(sourcePackage);
- data.writeString(targetPackage);
- data.writeInt(modeFlags);
- data.writeInt(modeMask);
- mRemote.transact(GET_GRANTED_URI_PERMISSIONS_TRANSACTION, data, reply, 0);
- reply.readException();
- final Uri[] uris = (Uri[]) reply.readParcelableArray(null);
- data.recycle();
- reply.recycle();
- return uris;
- }
-
public void performIdleMaintenance() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 25c02df..9a77377 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -27,9 +27,11 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
+import android.content.UriPermission;
import android.content.pm.ApplicationInfo;
import android.content.pm.ConfigurationInfo;
import android.content.pm.IPackageDataObserver;
+import android.content.pm.ParceledListSlice;
import android.content.pm.ProviderInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
@@ -210,7 +212,10 @@
Uri uri, int mode) throws RemoteException;
public void revokeUriPermission(IApplicationThread caller, Uri uri,
int mode) throws RemoteException;
-
+ public void takePersistableUriPermission(Uri uri, int modeFlags) throws RemoteException;
+ public void releasePersistableUriPermission(Uri uri, int modeFlags) throws RemoteException;
+ public ParceledListSlice<UriPermission> getPersistedUriPermissions() throws RemoteException;
+
public void showWaitingForDebugger(IApplicationThread who, boolean waiting)
throws RemoteException;
@@ -399,10 +404,6 @@
public void restart() throws RemoteException;
- public Uri[] getGrantedUriPermissions(
- String sourcePackage, String targetPackage, int modeFlags, int modeMask)
- throws RemoteException;
-
public void performIdleMaintenance() throws RemoteException;
/*
@@ -686,6 +687,8 @@
int NOTIFY_ACTIVITY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+175;
int REPORT_ACTIVITY_FULLY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+176;
int RESTART_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+177;
- int GET_GRANTED_URI_PERMISSIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+178;
- int PERFORM_IDLE_MAINTENANCE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+179;
+ int PERFORM_IDLE_MAINTENANCE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+178;
+ int TAKE_PERSISTABLE_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+179;
+ int RELEASE_PERSISTABLE_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+180;
+ int GET_PERSISTED_URI_PERMISSIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+181;
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 2750d68..95fb6858 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -266,12 +266,18 @@
/** @hide */
protected abstract IContentProvider acquireProvider(Context c, String name);
- /** Providing a default implementation of this, to avoid having to change
- * a lot of other things, but implementations of ContentResolver should
- * implement it. @hide */
+
+ /**
+ * Providing a default implementation of this, to avoid having to change a
+ * lot of other things, but implementations of ContentResolver should
+ * implement it.
+ *
+ * @hide
+ */
protected IContentProvider acquireExistingProvider(Context c, String name) {
return acquireProvider(c, name);
}
+
/** @hide */
public abstract boolean releaseProvider(IContentProvider icp);
/** @hide */
@@ -1616,54 +1622,50 @@
}
/**
- * Return list of all Uri permissions that have been granted <em>to</em> the
- * calling package, and which exactly match the requested flags. For
- * example, to return all Uris that the calling application has
- * <em>non-persistent</em> read access to:
+ * Take a persistable Uri permission grant that has been offered. Once
+ * taken, the permission grant will be remembered across device reboots.
+ * Only Uri permissions granted with
+ * {@link Intent#FLAG_GRANT_PERSISTABLE_URI_PERMISSION} can be persisted. If
+ * the grant has already been persisted, taking it again will touch
+ * {@link UriPermission#getPersistedTime()}.
*
- * <pre class="prettyprint">
- * getIncomingUriPermissionGrants(Intent.FLAG_GRANT_READ_URI_PERMISSION,
- * Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
- * </pre>
- *
- * @param modeFlags any combination of
- * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION},
- * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, or
- * {@link Intent#FLAG_PERSIST_GRANT_URI_PERMISSION}.
- * @param modeMask mask indicating which flags must match.
+ * @see #getPersistedUriPermissions()
*/
- public Uri[] getIncomingUriPermissionGrants(int modeFlags, int modeMask) {
+ public void takePersistableUriPermission(Uri uri, int modeFlags) {
try {
- return ActivityManagerNative.getDefault()
- .getGrantedUriPermissions(null, getPackageName(), modeFlags, modeMask);
+ ActivityManagerNative.getDefault().takePersistableUriPermission(uri, modeFlags);
} catch (RemoteException e) {
- return new Uri[0];
}
}
/**
- * Return list of all Uri permissions that have been granted <em>from</em> the
- * calling package, and which exactly match the requested flags. For
- * example, to return all Uris that the calling application has granted
- * <em>non-persistent</em> read access to:
+ * Relinquish a persisted Uri permission grant. The Uri must have been
+ * previously made persistent with
+ * {@link #takePersistableUriPermission(Uri, int)}. Any non-persistent
+ * grants to the calling package will remain intact.
*
- * <pre class="prettyprint">
- * getOutgoingUriPermissionGrants(Intent.FLAG_GRANT_READ_URI_PERMISSION,
- * Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
- * </pre>
- *
- * @param modeFlags any combination of
- * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION},
- * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, or
- * {@link Intent#FLAG_PERSIST_GRANT_URI_PERMISSION}.
- * @param modeMask mask indicating which flags must match.
+ * @see #getPersistedUriPermissions()
*/
- public Uri[] getOutgoingUriPermissionGrants(int modeFlags, int modeMask) {
+ public void releasePersistableUriPermission(Uri uri, int modeFlags) {
try {
- return ActivityManagerNative.getDefault()
- .getGrantedUriPermissions(getPackageName(), null, modeFlags, modeMask);
+ ActivityManagerNative.getDefault().releasePersistableUriPermission(uri, modeFlags);
} catch (RemoteException e) {
- return new Uri[0];
+ }
+ }
+
+ /**
+ * Return list of all Uri permission grants that have been persisted for the
+ * calling app. Only persistable grants taken with
+ * {@link #takePersistableUriPermission(Uri, int)} are returned.
+ *
+ * @see #takePersistableUriPermission(Uri, int)
+ * @see #releasePersistableUriPermission(Uri, int)
+ */
+ public List<UriPermission> getPersistedUriPermissions() {
+ try {
+ return ActivityManagerNative.getDefault().getPersistedUriPermissions().getList();
+ } catch (RemoteException e) {
+ throw new RuntimeException("Activity manager has died", e);
}
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index dcc6328..047f175 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3339,12 +3339,18 @@
/**
* 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.
+ * {@link #FLAG_GRANT_WRITE_URI_PERMISSION}, the Uri permission grant can be
+ * persisted across device reboots until explicitly revoked with
+ * {@link Context#revokeUriPermission(Uri, int)}. This flag only offers the
+ * grant for possible persisting; the receiving application must call
+ * {@link ContentResolver#takePersistableUriPermission(Uri, int)} to
+ * actually persist.
+ *
+ * @see ContentResolver#takePersistableUriPermission(Uri, int)
+ * @see ContentResolver#releasePersistableUriPermission(Uri, int)
+ * @see ContentResolver#getPersistedUriPermissions()
*/
- public static final int FLAG_PERSIST_GRANT_URI_PERMISSION = 0x00000040;
+ public static final int FLAG_GRANT_PERSISTABLE_URI_PERMISSION = 0x00000040;
/**
* If set, the new activity is not kept in the history stack. As soon as
@@ -7173,7 +7179,7 @@
setClipData(target.getClipData());
addFlags(target.getFlags()
& (FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION
- | FLAG_PERSIST_GRANT_URI_PERMISSION));
+ | FLAG_GRANT_PERSISTABLE_URI_PERMISSION));
return true;
} else {
return false;
diff --git a/core/java/android/content/UriPermission.java b/core/java/android/content/UriPermission.java
new file mode 100644
index 0000000..df9200d
--- /dev/null
+++ b/core/java/android/content/UriPermission.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Description of a single Uri permission grant. This grants may have been
+ * created via {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}, etc when sending
+ * an {@link Intent}, or explicitly through
+ * {@link Context#grantUriPermission(String, android.net.Uri, int)}.
+ *
+ * @see ContentResolver#getPersistedUriPermissions()
+ */
+public final class UriPermission implements Parcelable {
+ private final Uri mUri;
+ private final int mModeFlags;
+ private final long mPersistedTime;
+
+ /**
+ * Value returned when a permission has not been persisted.
+ */
+ public static final long INVALID_TIME = Long.MIN_VALUE;
+
+ /** {@hide} */
+ public UriPermission(Uri uri, int modeFlags, long persistedTime) {
+ mUri = uri;
+ mModeFlags = modeFlags;
+ mPersistedTime = persistedTime;
+ }
+
+ /** {@hide} */
+ public UriPermission(Parcel in) {
+ mUri = in.readParcelable(null);
+ mModeFlags = in.readInt();
+ mPersistedTime = in.readLong();
+ }
+
+ /**
+ * Return the Uri this permission pertains to.
+ */
+ public Uri getUri() {
+ return mUri;
+ }
+
+ /**
+ * Returns if this permission offers read access.
+ */
+ public boolean isReadPermission() {
+ return (mModeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0;
+ }
+
+ /**
+ * Returns if this permission offers write access.
+ */
+ public boolean isWritePermission() {
+ return (mModeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0;
+ }
+
+ /**
+ * Return the time when this permission was first persisted, in milliseconds
+ * since January 1, 1970 00:00:00.0 UTC. Returns {@link #INVALID_TIME} if
+ * not persisted.
+ *
+ * @see ContentResolver#takePersistableUriPermission(Uri, int)
+ * @see System#currentTimeMillis()
+ */
+ public long getPersistedTime() {
+ return mPersistedTime;
+ }
+
+ @Override
+ public String toString() {
+ return "UriPermission {uri=" + mUri + ", modeFlags=" + mModeFlags + ", persistedTime="
+ + mPersistedTime + "}";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mUri, flags);
+ dest.writeInt(mModeFlags);
+ dest.writeLong(mPersistedTime);
+ }
+
+ public static final Creator<UriPermission> CREATOR = new Creator<UriPermission>() {
+ @Override
+ public UriPermission createFromParcel(Parcel source) {
+ return new UriPermission(source);
+ }
+
+ @Override
+ public UriPermission[] newArray(int size) {
+ return new UriPermission[size];
+ }
+ };
+}
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 3f33e80..8f22312 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -21,10 +21,7 @@
import android.content.ContentProviderClient;
import android.content.ContentResolver;
-import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.graphics.Bitmap;
@@ -38,8 +35,6 @@
import android.os.RemoteException;
import android.util.Log;
-import com.google.android.collect.Lists;
-
import libcore.io.ErrnoException;
import libcore.io.IoUtils;
import libcore.io.Libcore;
@@ -624,37 +619,6 @@
}
/**
- * Return list of all documents that the calling package has "open." These
- * are Uris matching {@link DocumentsContract} to which persistent
- * read/write access has been granted, usually through
- * {@link Intent#ACTION_OPEN_DOCUMENT} or
- * {@link Intent#ACTION_CREATE_DOCUMENT}.
- *
- * @see Context#grantUriPermission(String, Uri, int)
- * @see Context#revokeUriPermission(Uri, int)
- * @see ContentResolver#getIncomingUriPermissionGrants(int, int)
- */
- public static Uri[] getOpenDocuments(Context context) {
- final int openedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION;
- final Uri[] uris = context.getContentResolver()
- .getIncomingUriPermissionGrants(openedFlags, openedFlags);
-
- // Filter to only include document providers
- final PackageManager pm = context.getPackageManager();
- final List<Uri> result = Lists.newArrayList();
- for (Uri uri : uris) {
- final ProviderInfo info = pm.resolveContentProvider(
- uri.getAuthority(), PackageManager.GET_META_DATA);
- if (info.metaData.containsKey(META_DATA_DOCUMENT_PROVIDER)) {
- result.add(uri);
- }
- }
-
- return result.toArray(new Uri[result.size()]);
- }
-
- /**
* Return thumbnail representing the document at the given Uri. Callers are
* responsible for their own in-memory caching.
*
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index bc4e28b..337b735 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -387,7 +387,7 @@
context.grantUriPermission(getCallingPackage(), newDocumentUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
}
} else if (METHOD_DELETE_DOCUMENT.equals(method)) {
@@ -396,7 +396,7 @@
// Document no longer exists, clean up any grants
context.revokeUriPermission(documentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
} else {
throw new UnsupportedOperationException("Method not supported " + method);
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index f40f48c..0b74cf3 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -931,6 +931,15 @@
out.attribute(null, name, Integer.toString(value));
}
+ public static long readLongAttribute(XmlPullParser in, String name, long defaultValue) {
+ final String value = in.getAttributeValue(null, name);
+ try {
+ return Long.parseLong(value);
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
+
public static long readLongAttribute(XmlPullParser in, String name) throws IOException {
final String value = in.getAttributeValue(null, name);
try {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index ca9bea4..72fdc57 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -1001,7 +1001,7 @@
} else {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
}
setResult(Activity.RESULT_OK, intent);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java b/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
index 1cc35a7..9861399 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
@@ -209,6 +209,8 @@
if (requestCode == CODE_READ) {
final Uri uri = data != null ? data.getData() : null;
if (uri != null) {
+ getContentResolver()
+ .takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
InputStream is = null;
try {
is = getContentResolver().openInputStream(uri);
@@ -226,6 +228,8 @@
} else if (requestCode == CODE_WRITE) {
final Uri uri = data != null ? data.getData() : null;
if (uri != null) {
+ getContentResolver()
+ .takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
OutputStream os = null;
try {
os = getContentResolver().openOutputStream(uri);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 4f73588..387641df 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -18,7 +18,9 @@
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.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.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.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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
*/
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 @@
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 @@
allowed = false;
}
}
- if (allowed && !persist) {
+ if (allowed) {
return -1;
}
}
@@ -5830,7 +5846,10 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
}
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 @@
}
if (persistChanged) {
- mHandler.removeMessages(PERSIST_URI_GRANTS_MSG);
- mHandler.obtainMessage(PERSIST_URI_GRANTS_MSG).sendToTarget();
+ schedulePersistUriGrants();
}
}
@@ -6122,7 +6144,6 @@
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 @@
return;
}
- revokeUriPermissionLocked(r.uid, uri, modeFlags, persist);
+ revokeUriPermissionLocked(r.uid, uri, modeFlags);
}
}
@@ -6150,10 +6171,10 @@
* 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 @@
// 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 @@
}
if (persistChanged) {
- mHandler.removeMessages(PERSIST_URI_GRANTS_MSG);
- mHandler.obtainMessage(PERSIST_URI_GRANTS_MSG).sendToTarget();
+ schedulePersistUriGrants();
}
}
@@ -6242,6 +6262,13 @@
}
}
+ 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 @@
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 @@
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 @@
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 @@
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 @@
}
@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 boolean callerOwnsSource = sourcePackage != null
- && ArrayUtils.contains(callerPackages, sourcePackage);
- final boolean callerOwnsTarget = targetPackage != null
- && ArrayUtils.contains(callerPackages, targetPackage);
- if (!(callerOwnsSource || callerOwnsTarget)) {
- throw new SecurityException("Caller " + Arrays.toString(callerPackages)
- + " doesn't own " + sourcePackage + " or " + targetPackage);
+ final 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 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);
+ boolean persistChanged = perm.takePersistableModes(modeFlags);
+ persistChanged |= maybePrunePersistedUriGrantsLocked(callingUid);
+
+ if (persistChanged) {
+ schedulePersistUriGrants();
+ }
+ }
+ }
+
+ @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 5e2ad009..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.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 @@
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 @@
/**
* 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 @@
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;
+ void initPersistedModes(int modeFlags, long createdTime) {
+ persistableModeFlags = modeFlags;
+ persistedModeFlags = modeFlags;
+ persistedCreateTime = createdTime;
- modeFlags |= modeFlagsToGrant;
+ updateModeFlags();
+ }
- if (persist) {
- final int before = persistedModeFlags;
- persistedModeFlags |= modeFlagsToGrant;
- persistChanged = persistedModeFlags != before;
-
- // 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 @@
} 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 @@
}
}
- // 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();
}
- mReadOwners.add(owner);
+ if (mReadOwners.add(owner)) {
+ owner.addReadPermission(this);
+ }
}
/**
@@ -172,17 +239,20 @@
}
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();
}
- mWriteOwners.add(owner);
+ if (mWriteOwners.add(owner)) {
+ owner.addWritePermission(this);
+ }
}
/**
@@ -194,9 +264,8 @@
}
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 @@
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 @@
}
}
+ 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 String targetPkg;
final Uri uri;
final int persistedModeFlags;
+ final long persistedCreateTime;
private Snapshot(UriPermission perm) {
this.userHandle = perm.userHandle;
@@ -260,10 +343,15 @@
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);
+ }
}