summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt19
-rw-r--r--core/java/android/app/ActivityManagerNative.java100
-rw-r--r--core/java/android/app/IActivityManager.java17
-rw-r--r--core/java/android/content/ContentResolver.java78
-rw-r--r--core/java/android/content/Intent.java18
-rw-r--r--core/java/android/content/UriPermission.java117
-rw-r--r--core/java/android/provider/DocumentsContract.java36
-rw-r--r--core/java/android/provider/DocumentsProvider.java4
-rw-r--r--core/java/com/android/internal/util/XmlUtils.java9
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java2
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/TestActivity.java4
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java225
-rw-r--r--services/java/com/android/server/am/UriPermission.java190
13 files changed, 581 insertions, 238 deletions
diff --git a/api/current.txt b/api/current.txt
index a603b773b933..5362fdf4d8f4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5744,11 +5744,10 @@ package android.content {
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 @@ package android.content {
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 @@ package android.content {
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 @@ package android.content {
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 @@ package android.content {
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 @@ package android.provider {
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 7d4d57cf5c77..3b88973940ca 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -23,9 +23,11 @@ import android.content.IIntentSender;
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 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
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 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
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 @@ class ActivityManagerProxy implements IActivityManager
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 @@ class ActivityManagerProxy implements IActivityManager
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 25c02df80840..9a7737748d11 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -27,9 +27,11 @@ import android.content.IIntentSender;
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 @@ public interface IActivityManager extends IInterface {
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 interface IActivityManager extends IInterface {
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 @@ public interface IActivityManager extends IInterface {
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 2750d6866d7b..95fb6858545e 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -266,12 +266,18 @@ public abstract class ContentResolver {
/** @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 @@ public abstract class ContentResolver {
}
/**
- * 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:
- *
- * <pre class="prettyprint">
- * getIncomingUriPermissionGrants(Intent.FLAG_GRANT_READ_URI_PERMISSION,
- * Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
- * </pre>
+ * 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()}.
*
- * @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>
+ * @see #getPersistedUriPermissions()
+ */
+ public void releasePersistableUriPermission(Uri uri, int modeFlags) {
+ try {
+ ActivityManagerNative.getDefault().releasePersistableUriPermission(uri, modeFlags);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * 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.
*
- * @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 #takePersistableUriPermission(Uri, int)
+ * @see #releasePersistableUriPermission(Uri, int)
*/
- public Uri[] getOutgoingUriPermissionGrants(int modeFlags, int modeMask) {
+ public List<UriPermission> getPersistedUriPermissions() {
try {
- return ActivityManagerNative.getDefault()
- .getGrantedUriPermissions(getPackageName(), null, modeFlags, modeMask);
+ return ActivityManagerNative.getDefault().getPersistedUriPermissions().getList();
} catch (RemoteException e) {
- return new Uri[0];
+ 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 dcc63288afd9..047f175c360d 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3339,12 +3339,18 @@ public class Intent implements Parcelable, Cloneable {
/**
* 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 @@ public class Intent implements Parcelable, Cloneable {
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 000000000000..df9200d7084c
--- /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 3f33e8062016..8f22312a3714 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -21,10 +21,7 @@ import static libcore.io.OsConstants.SEEK_SET;
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.ParcelFileDescriptor.OnCloseListener;
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 @@ public final class DocumentsContract {
}
/**
- * 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 bc4e28bdf481..337b7359335e 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -387,7 +387,7 @@ public abstract class DocumentsProvider extends ContentProvider {
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 @@ public abstract class DocumentsProvider extends ContentProvider {
// 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 f40f48c54326..0b74cf323891 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -931,6 +931,15 @@ public class XmlUtils {
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 ca9bea43aab1..72fdc57f9cab 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -1001,7 +1001,7 @@ public class DocumentsActivity extends Activity {
} 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 1cc35a7d97de..9861399436f9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
@@ -209,6 +209,8 @@ public class TestActivity extends Activity {
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 @@ public class TestActivity extends Activity {
} 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 4f735889ee86..387641df6770 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -18,7 +18,9 @@ package com.android.server.am;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.internal.util.XmlUtils.readIntAttribute;
+import static com.android.internal.util.XmlUtils.readLongAttribute;
import static com.android.internal.util.XmlUtils.writeIntAttribute;
+import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -36,7 +38,6 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.TransferPipe;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.MemInfoReader;
@@ -113,6 +114,7 @@ import android.content.pm.IPackageManager;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PathPermission;
@@ -153,6 +155,7 @@ import android.os.SystemProperties;
import android.os.UpdateLock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.text.format.DateUtils;
import android.text.format.Time;
import android.util.AtomicFile;
import android.util.EventLog;
@@ -310,6 +313,9 @@ public final class ActivityManagerService extends ActivityManagerNative
// to respond with the result.
static final int PENDING_ASSIST_EXTRAS_TIMEOUT = 500;
+ // Maximum number of persisted Uri grants a package is allowed
+ static final int MAX_PERSISTED_URI_GRANTS = 128;
+
static final int MY_PID = Process.myPid();
static final String[] EMPTY_STRING_ARRAY = new String[0];
@@ -685,6 +691,7 @@ public final class ActivityManagerService extends ActivityManagerNative
private static final String ATTR_TARGET_PKG = "targetPkg";
private static final String ATTR_URI = "uri";
private static final String ATTR_MODE_FLAGS = "modeFlags";
+ private static final String ATTR_CREATED_TIME = "createdTime";
/**
* Global set of specific {@link Uri} permissions that have been granted.
@@ -5669,6 +5676,15 @@ public final class ActivityManagerService extends ActivityManagerNative
return pi;
}
+ private UriPermission findUriPermissionLocked(int targetUid, Uri uri) {
+ ArrayMap<Uri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
+ if (targetUris != null) {
+ return targetUris.get(uri);
+ } else {
+ return null;
+ }
+ }
+
private UriPermission findOrCreateUriPermissionLocked(
String sourcePkg, String targetPkg, int targetUid, Uri uri) {
ArrayMap<Uri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
@@ -5686,8 +5702,8 @@ public final class ActivityManagerService extends ActivityManagerNative
return perm;
}
- private final boolean checkUriPermissionLocked(Uri uri, int uid,
- int modeFlags) {
+ private final boolean checkUriPermissionLocked(
+ Uri uri, int uid, int modeFlags, int minStrength) {
// Root gets to do everything.
if (uid == 0) {
return true;
@@ -5696,7 +5712,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (perms == null) return false;
UriPermission perm = perms.get(uri);
if (perm == null) return false;
- return (modeFlags&perm.modeFlags) == modeFlags;
+ return perm.getStrength(modeFlags) >= minStrength;
}
@Override
@@ -5716,7 +5732,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return PackageManager.PERMISSION_GRANTED;
}
synchronized(this) {
- return checkUriPermissionLocked(uri, uid, modeFlags)
+ return checkUriPermissionLocked(uri, uid, modeFlags, UriPermission.STRENGTH_OWNED)
? PackageManager.PERMISSION_GRANTED
: PackageManager.PERMISSION_DENIED;
}
@@ -5733,7 +5749,7 @@ public final class ActivityManagerService extends ActivityManagerNative
*/
int checkGrantUriPermissionLocked(int callingUid, String targetPkg,
Uri uri, int modeFlags, int lastTargetUid) {
- final boolean persist = (modeFlags & Intent.FLAG_PERSIST_GRANT_URI_PERMISSION) != 0;
+ final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if (modeFlags == 0) {
@@ -5777,7 +5793,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (targetUid >= 0) {
// First... does the target actually need this permission?
- if (checkHoldingPermissionsLocked(pm, pi, uri, targetUid, modeFlags) && !persist) {
+ if (checkHoldingPermissionsLocked(pm, pi, uri, targetUid, modeFlags)) {
// No need to grant the target this permission.
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
"Target " + targetPkg + " already has full permission to " + uri);
@@ -5796,7 +5812,7 @@ public final class ActivityManagerService extends ActivityManagerNative
allowed = false;
}
}
- if (allowed && !persist) {
+ if (allowed) {
return -1;
}
}
@@ -5830,7 +5846,10 @@ public final class ActivityManagerService extends ActivityManagerNative
// this uri?
if (callingUid != Process.myUid()) {
if (!checkHoldingPermissionsLocked(pm, pi, uri, callingUid, modeFlags)) {
- if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) {
+ // Require they hold a strong enough Uri permission
+ final int minStrength = persistable ? UriPermission.STRENGTH_PERSISTABLE
+ : UriPermission.STRENGTH_OWNED;
+ if (!checkUriPermissionLocked(uri, callingUid, modeFlags, minStrength)) {
throw new SecurityException("Uid " + callingUid
+ " does not have permission to uri " + uri);
}
@@ -5851,7 +5870,7 @@ public final class ActivityManagerService extends ActivityManagerNative
void grantUriPermissionUncheckedLocked(
int targetUid, String targetPkg, Uri uri, int modeFlags, UriPermissionOwner owner) {
- final boolean persist = (modeFlags & Intent.FLAG_PERSIST_GRANT_URI_PERMISSION) != 0;
+ final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if (modeFlags == 0) {
@@ -5874,11 +5893,7 @@ public final class ActivityManagerService extends ActivityManagerNative
final UriPermission perm = findOrCreateUriPermissionLocked(
pi.packageName, targetPkg, targetUid, uri);
- final boolean persistChanged = perm.grantModes(modeFlags, persist, owner);
- if (persistChanged) {
- mHandler.removeMessages(PERSIST_URI_GRANTS_MSG);
- mHandler.obtainMessage(PERSIST_URI_GRANTS_MSG).sendToTarget();
- }
+ perm.grantModes(modeFlags, persistable, owner);
}
void grantUriPermissionLocked(int callingUid, String targetPkg, Uri uri,
@@ -5930,6 +5945,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (data == null && clip == null) {
return null;
}
+
if (data != null) {
int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, data,
mode, needed != null ? needed.targetUid : -1);
@@ -6011,6 +6027,14 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new IllegalArgumentException("null uri");
}
+ // Persistable only supported through Intents
+ modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ if (modeFlags == 0) {
+ throw new IllegalArgumentException("Mode flags must be "
+ + "FLAG_GRANT_READ_URI_PERMISSION and/or FLAG_GRANT_WRITE_URI_PERMISSION");
+ }
+
grantUriPermissionLocked(r.uid, targetPkg, uri, modeFlags,
null);
}
@@ -6032,8 +6056,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- private void revokeUriPermissionLocked(
- int callingUid, Uri uri, int modeFlags, boolean persist) {
+ private void revokeUriPermissionLocked(int callingUid, Uri uri, int modeFlags) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Revoking all granted permissions to " + uri);
final IPackageManager pm = AppGlobals.getPackageManager();
@@ -6086,7 +6109,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
"Revoking " + perm.targetUid + " permission to " + perm.uri);
- persistChanged |= perm.clearModes(modeFlags, persist);
+ persistChanged |= perm.clearModes(modeFlags, true);
if (perm.modeFlags == 0) {
it.remove();
}
@@ -6101,8 +6124,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
if (persistChanged) {
- mHandler.removeMessages(PERSIST_URI_GRANTS_MSG);
- mHandler.obtainMessage(PERSIST_URI_GRANTS_MSG).sendToTarget();
+ schedulePersistUriGrants();
}
}
@@ -6122,7 +6144,6 @@ public final class ActivityManagerService extends ActivityManagerNative
return;
}
- final boolean persist = (modeFlags & Intent.FLAG_PERSIST_GRANT_URI_PERMISSION) != 0;
modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if (modeFlags == 0) {
@@ -6138,7 +6159,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return;
}
- revokeUriPermissionLocked(r.uid, uri, modeFlags, persist);
+ revokeUriPermissionLocked(r.uid, uri, modeFlags);
}
}
@@ -6150,10 +6171,10 @@ public final class ActivityManagerService extends ActivityManagerNative
* packages.
* @param userHandle User to match, or {@link UserHandle#USER_ALL} to apply
* to all users.
- * @param persist If persistent grants should be removed.
+ * @param persistable If persistable grants should be removed.
*/
private void removeUriPermissionsForPackageLocked(
- String packageName, int userHandle, boolean persist) {
+ String packageName, int userHandle, boolean persistable) {
if (userHandle == UserHandle.USER_ALL && packageName == null) {
throw new IllegalArgumentException("Must narrow by either package or user");
}
@@ -6173,7 +6194,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// Only inspect grants matching package
if (packageName == null || perm.sourcePkg.equals(packageName)
|| perm.targetPkg.equals(packageName)) {
- persistChanged |= perm.clearModes(~0, persist);
+ persistChanged |= perm.clearModes(~0, persistable);
// Only remove when no modes remain; any persisted grants
// will keep this alive.
@@ -6186,8 +6207,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
if (persistChanged) {
- mHandler.removeMessages(PERSIST_URI_GRANTS_MSG);
- mHandler.obtainMessage(PERSIST_URI_GRANTS_MSG).sendToTarget();
+ schedulePersistUriGrants();
}
}
@@ -6242,6 +6262,13 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ private void schedulePersistUriGrants() {
+ if (!mHandler.hasMessages(PERSIST_URI_GRANTS_MSG)) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(PERSIST_URI_GRANTS_MSG),
+ 10 * DateUtils.SECOND_IN_MILLIS);
+ }
+ }
+
private void writeGrantedUriPermissions() {
if (DEBUG_URI_PERMISSION) Slog.v(TAG, "writeGrantedUriPermissions()");
@@ -6273,6 +6300,7 @@ public final class ActivityManagerService extends ActivityManagerNative
out.attribute(null, ATTR_TARGET_PKG, perm.targetPkg);
out.attribute(null, ATTR_URI, String.valueOf(perm.uri));
writeIntAttribute(out, ATTR_MODE_FLAGS, perm.persistedModeFlags);
+ writeLongAttribute(out, ATTR_CREATED_TIME, perm.persistedCreateTime);
out.endTag(null, TAG_URI_GRANT);
}
out.endTag(null, TAG_URI_GRANTS);
@@ -6289,6 +6317,8 @@ public final class ActivityManagerService extends ActivityManagerNative
private void readGrantedUriPermissionsLocked() {
if (DEBUG_URI_PERMISSION) Slog.v(TAG, "readGrantedUriPermissions()");
+ final long now = System.currentTimeMillis();
+
FileInputStream fis = null;
try {
fis = mGrantFile.openRead();
@@ -6305,6 +6335,7 @@ public final class ActivityManagerService extends ActivityManagerNative
final String targetPkg = in.getAttributeValue(null, ATTR_TARGET_PKG);
final Uri uri = Uri.parse(in.getAttributeValue(null, ATTR_URI));
final int modeFlags = readIntAttribute(in, ATTR_MODE_FLAGS);
+ final long createdTime = readLongAttribute(in, ATTR_CREATED_TIME, now);
// Sanity check that provider still belongs to source package
final ProviderInfo pi = getProviderInfoLocked(
@@ -6319,7 +6350,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (targetUid != -1) {
final UriPermission perm = findOrCreateUriPermissionLocked(
sourcePkg, targetPkg, targetUid, uri);
- perm.grantModes(modeFlags, true, null);
+ perm.initPersistedModes(modeFlags, createdTime);
}
} else {
Slog.w(TAG, "Persisted grant for " + uri + " had source " + sourcePkg
@@ -6340,47 +6371,117 @@ public final class ActivityManagerService extends ActivityManagerNative
}
@Override
- public Uri[] getGrantedUriPermissions(
- String sourcePackage, String targetPackage, int modeFlags, int modeMask) {
- enforceNotIsolatedCaller("getGrantedUriPermissions");
+ public void takePersistableUriPermission(Uri uri, int modeFlags) {
+ enforceNotIsolatedCaller("takePersistableUriPermission");
+
+ modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ if (modeFlags == 0) {
+ return;
+ }
+
synchronized (this) {
- // Verify that caller owns at least one of the requested packages
- final int uid = Binder.getCallingUid();
- final IPackageManager pm = AppGlobals.getPackageManager();
- final String[] callerPackages;
- try {
- callerPackages = pm.getPackagesForUid(uid);
- } catch (RemoteException e) {
- throw new SecurityException("Failed to find packages for UID " + uid);
+ final int callingUid = Binder.getCallingUid();
+ final UriPermission perm = findUriPermissionLocked(callingUid, uri);
+ if (perm == null) {
+ Slog.w(TAG, "No permission grant found for UID " + callingUid + " and Uri "
+ + uri.toSafeString());
+ return;
}
- final boolean callerOwnsSource = sourcePackage != null
- && ArrayUtils.contains(callerPackages, sourcePackage);
- final boolean callerOwnsTarget = targetPackage != null
- && ArrayUtils.contains(callerPackages, targetPackage);
- if (!(callerOwnsSource || callerOwnsTarget)) {
- throw new SecurityException("Caller " + Arrays.toString(callerPackages)
- + " doesn't own " + sourcePackage + " or " + targetPackage);
+
+ boolean persistChanged = perm.takePersistableModes(modeFlags);
+ persistChanged |= maybePrunePersistedUriGrantsLocked(callingUid);
+
+ if (persistChanged) {
+ schedulePersistUriGrants();
}
+ }
+ }
- final ArrayList<Uri> result = Lists.newArrayList();
- final int size = mGrantedUriPermissions.size();
- for (int i = 0; i < size; i++) {
- final ArrayMap<Uri, UriPermission> map = mGrantedUriPermissions.valueAt(i);
- final int mapSize = map.size();
- for (int j = 0; j < mapSize; j++) {
- final UriPermission perm = map.valueAt(j);
- final boolean sourceMatch = sourcePackage == null
- || sourcePackage.equals(perm.sourcePkg);
- final boolean targetMatch = targetPackage == null
- || targetPackage.equals(perm.targetPkg);
- final boolean modeMatch = (perm.modeFlags & modeMask) == modeFlags;
- if (sourceMatch && targetMatch && modeMatch) {
- result.add(perm.uri);
+ @Override
+ public void releasePersistableUriPermission(Uri uri, int modeFlags) {
+ enforceNotIsolatedCaller("releasePersistableUriPermission");
+
+ modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ if (modeFlags == 0) {
+ return;
+ }
+
+ synchronized (this) {
+ final int callingUid = Binder.getCallingUid();
+
+ final UriPermission perm = findUriPermissionLocked(callingUid, uri);
+ if (perm == null) {
+ Slog.w(TAG, "No permission grant found for UID " + callingUid + " and Uri "
+ + uri.toSafeString());
+ return;
+ }
+
+ final boolean persistChanged = perm.releasePersistableModes(modeFlags);
+ removeUriPermissionIfNeededLocked(perm);
+ if (persistChanged) {
+ schedulePersistUriGrants();
+ }
+ }
+ }
+
+ /**
+ * Prune any older {@link UriPermission} for the given UID until outstanding
+ * persisted grants are below {@link #MAX_PERSISTED_URI_GRANTS}.
+ *
+ * @return if any mutations occured that require persisting.
+ */
+ private boolean maybePrunePersistedUriGrantsLocked(int uid) {
+ final ArrayMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(uid);
+ if (perms == null) return false;
+ if (perms.size() < MAX_PERSISTED_URI_GRANTS) return false;
+
+ final ArrayList<UriPermission> persisted = Lists.newArrayList();
+ for (UriPermission perm : perms.values()) {
+ if (perm.persistedModeFlags != 0) {
+ persisted.add(perm);
+ }
+ }
+
+ final int trimCount = persisted.size() - MAX_PERSISTED_URI_GRANTS;
+ if (trimCount <= 0) return false;
+
+ Collections.sort(persisted, new UriPermission.PersistedTimeComparator());
+ for (int i = 0; i < trimCount; i++) {
+ final UriPermission perm = persisted.get(i);
+
+ if (DEBUG_URI_PERMISSION) {
+ Slog.v(TAG, "Trimming grant created at " + perm.persistedCreateTime);
+ }
+
+ perm.releasePersistableModes(~0);
+ removeUriPermissionIfNeededLocked(perm);
+ }
+
+ return true;
+ }
+
+ @Override
+ public ParceledListSlice<android.content.UriPermission> getPersistedUriPermissions() {
+ enforceNotIsolatedCaller("getPersistedUriPermissions");
+
+ synchronized (this) {
+ final int callingUid = Binder.getCallingUid();
+ final ArrayList<android.content.UriPermission> result = Lists.newArrayList();
+ final ArrayMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
+ if (perms == null) {
+ Slog.w(TAG, "No permission grants found for UID " + callingUid);
+ } else {
+ final int size = perms.size();
+ for (int i = 0; i < size; i++) {
+ final UriPermission perm = perms.valueAt(i);
+ if (perm.persistedModeFlags != 0) {
+ result.add(perm.buildPersistedPublicApiObject());
}
}
}
-
- return result.toArray(new Uri[result.size()]);
+ return new ParceledListSlice<android.content.UriPermission>(result);
}
}
diff --git a/services/java/com/android/server/am/UriPermission.java b/services/java/com/android/server/am/UriPermission.java
index 5e2ad00955ed..7057c248258d 100644
--- a/services/java/com/android/server/am/UriPermission.java
+++ b/services/java/com/android/server/am/UriPermission.java
@@ -20,11 +20,12 @@ import android.content.Intent;
import android.net.Uri;
import android.os.UserHandle;
import android.util.Log;
+import android.util.Slog;
-import com.android.internal.util.IndentingPrintWriter;
import com.google.android.collect.Sets;
import java.io.PrintWriter;
+import java.util.Comparator;
import java.util.HashSet;
/**
@@ -38,6 +39,11 @@ import java.util.HashSet;
final class UriPermission {
private static final String TAG = "UriPermission";
+ public static final int STRENGTH_NONE = 0;
+ public static final int STRENGTH_OWNED = 1;
+ public static final int STRENGTH_GLOBAL = 2;
+ public static final int STRENGTH_PERSISTABLE = 3;
+
final int userHandle;
final String sourcePkg;
final String targetPkg;
@@ -49,26 +55,29 @@ final class UriPermission {
/**
* Allowed modes. All permission enforcement should use this field. Must
- * always be a superset of {@link #globalModeFlags},
- * {@link #persistedModeFlags}, {@link #mReadOwners}, and
- * {@link #mWriteOwners}. Mutations should only be performed by the owning
- * class.
+ * always be a combination of {@link #ownedModeFlags},
+ * {@link #globalModeFlags}, {@link #persistableModeFlags}, and
+ * {@link #persistedModeFlags}. Mutations <em>must</em> only be performed by
+ * the owning class.
*/
int modeFlags = 0;
- /**
- * Allowed modes without explicit owner. Must always be a superset of
- * {@link #persistedModeFlags}. Mutations should only be performed by the
- * owning class.
- */
+ /** Allowed modes with explicit owner. */
+ int ownedModeFlags = 0;
+ /** Allowed modes without explicit owner. */
int globalModeFlags = 0;
+ /** Allowed modes that have been offered for possible persisting. */
+ int persistableModeFlags = 0;
+ /** Allowed modes that should be persisted across device boots. */
+ int persistedModeFlags = 0;
/**
- * Allowed modes that should be persisted across device boots. These modes
- * have no explicit owners. Mutations should only be performed by the owning
- * class.
+ * Timestamp when {@link #persistedModeFlags} was first defined in
+ * {@link System#currentTimeMillis()} time base.
*/
- int persistedModeFlags = 0;
+ long persistedCreateTime = INVALID_TIME;
+
+ private static final long INVALID_TIME = Long.MIN_VALUE;
private HashSet<UriPermissionOwner> mReadOwners;
private HashSet<UriPermissionOwner> mWriteOwners;
@@ -83,21 +92,25 @@ final class UriPermission {
this.uri = uri;
}
+ private void updateModeFlags() {
+ modeFlags = ownedModeFlags | globalModeFlags | persistableModeFlags | persistedModeFlags;
+ }
+
/**
- * @return If mode changes should trigger persisting.
+ * Initialize persisted modes as read from file. This doesn't issue any
+ * global or owner grants.
*/
- boolean grantModes(int modeFlagsToGrant, boolean persist, UriPermissionOwner owner) {
- boolean persistChanged = false;
-
- modeFlags |= modeFlagsToGrant;
+ void initPersistedModes(int modeFlags, long createdTime) {
+ persistableModeFlags = modeFlags;
+ persistedModeFlags = modeFlags;
+ persistedCreateTime = createdTime;
- if (persist) {
- final int before = persistedModeFlags;
- persistedModeFlags |= modeFlagsToGrant;
- persistChanged = persistedModeFlags != before;
+ updateModeFlags();
+ }
- // Treat persisted grants as global (ownerless)
- owner = null;
+ void grantModes(int modeFlags, boolean persistable, UriPermissionOwner owner) {
+ if (persistable) {
+ persistableModeFlags |= modeFlags;
}
if (owner == null) {
@@ -105,43 +118,77 @@ final class UriPermission {
} else {
if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
addReadOwner(owner);
- owner.addReadPermission(this);
}
if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
addWriteOwner(owner);
- owner.addWritePermission(this);
}
}
- return persistChanged;
+ updateModeFlags();
}
-
+
/**
- * @return If mode changes should trigger persisting.
+ * @return if mode changes should trigger persisting.
*/
- boolean clearModes(int modeFlagsToClear, boolean persist) {
+ boolean takePersistableModes(int modeFlags) {
+ if ((~persistableModeFlags & modeFlags) != 0) {
+ Slog.w(TAG, "Trying to take 0x" + Integer.toHexString(modeFlags) + " but only 0x"
+ + Integer.toHexString(persistableModeFlags) + " are available");
+ }
+
+ final int before = persistedModeFlags;
+ persistedModeFlags |= (persistableModeFlags & modeFlags);
+
+ if (persistedModeFlags != 0) {
+ persistedCreateTime = System.currentTimeMillis();
+ }
+
+ updateModeFlags();
+ return persistedModeFlags != before;
+ }
+
+ boolean releasePersistableModes(int modeFlags) {
final int before = persistedModeFlags;
- if ((modeFlagsToClear & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
- if (persist) {
+ persistableModeFlags &= ~modeFlags;
+ persistedModeFlags &= ~modeFlags;
+
+ if (persistedModeFlags == 0) {
+ persistedCreateTime = INVALID_TIME;
+ }
+
+ updateModeFlags();
+ return persistedModeFlags != before;
+ }
+
+ /**
+ * @return if mode changes should trigger persisting.
+ */
+ boolean clearModes(int modeFlags, boolean persistable) {
+ final int before = persistedModeFlags;
+
+ if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+ if (persistable) {
+ persistableModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
persistedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
}
globalModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
- modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
if (mReadOwners != null) {
+ ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
for (UriPermissionOwner r : mReadOwners) {
r.removeReadPermission(this);
}
mReadOwners = null;
}
}
- if ((modeFlagsToClear & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
- if (persist) {
+ if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+ if (persistable) {
+ persistableModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
persistedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
}
globalModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
- modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
if (mWriteOwners != null) {
+ ownedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
for (UriPermissionOwner r : mWriteOwners) {
r.removeWritePermission(this);
}
@@ -149,18 +196,38 @@ final class UriPermission {
}
}
- // Mode flags always bubble up
- globalModeFlags |= persistedModeFlags;
- modeFlags |= globalModeFlags;
+ if (persistedModeFlags == 0) {
+ persistedCreateTime = INVALID_TIME;
+ }
+ updateModeFlags();
return persistedModeFlags != before;
}
+ /**
+ * Return strength of this permission grant for the given flags.
+ */
+ public int getStrength(int modeFlags) {
+ if ((persistableModeFlags & modeFlags) == modeFlags) {
+ return STRENGTH_PERSISTABLE;
+ } else if ((globalModeFlags & modeFlags) == modeFlags) {
+ return STRENGTH_GLOBAL;
+ } else if ((ownedModeFlags & modeFlags) == modeFlags) {
+ return STRENGTH_OWNED;
+ } else {
+ return STRENGTH_NONE;
+ }
+ }
+
private void addReadOwner(UriPermissionOwner owner) {
if (mReadOwners == null) {
mReadOwners = Sets.newHashSet();
+ ownedModeFlags |= Intent.FLAG_GRANT_READ_URI_PERMISSION;
+ updateModeFlags();
+ }
+ if (mReadOwners.add(owner)) {
+ owner.addReadPermission(this);
}
- mReadOwners.add(owner);
}
/**
@@ -172,17 +239,20 @@ final class UriPermission {
}
if (mReadOwners.size() == 0) {
mReadOwners = null;
- if ((globalModeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
- modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
- }
+ ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+ updateModeFlags();
}
}
private void addWriteOwner(UriPermissionOwner owner) {
if (mWriteOwners == null) {
mWriteOwners = Sets.newHashSet();
+ ownedModeFlags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+ updateModeFlags();
+ }
+ if (mWriteOwners.add(owner)) {
+ owner.addWritePermission(this);
}
- mWriteOwners.add(owner);
}
/**
@@ -194,9 +264,8 @@ final class UriPermission {
}
if (mWriteOwners.size() == 0) {
mWriteOwners = null;
- if ((globalModeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
- modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
- }
+ ownedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+ updateModeFlags();
}
}
@@ -221,9 +290,15 @@ final class UriPermission {
pw.println(" targetPkg=" + targetPkg);
pw.print(prefix);
- pw.print("modeFlags=0x" + Integer.toHexString(modeFlags));
- pw.print(" globalModeFlags=0x" + Integer.toHexString(globalModeFlags));
- pw.println(" persistedModeFlags=0x" + Integer.toHexString(persistedModeFlags));
+ pw.print("mode=0x" + Integer.toHexString(modeFlags));
+ pw.print(" owned=0x" + Integer.toHexString(ownedModeFlags));
+ pw.print(" global=0x" + Integer.toHexString(globalModeFlags));
+ pw.print(" persistable=0x" + Integer.toHexString(persistableModeFlags));
+ pw.print(" persisted=0x" + Integer.toHexString(persistedModeFlags));
+ if (persistedCreateTime != INVALID_TIME) {
+ pw.print(" persistedCreate=" + persistedCreateTime);
+ }
+ pw.println();
if (mReadOwners != null) {
pw.print(prefix);
@@ -243,6 +318,13 @@ final class UriPermission {
}
}
+ public static class PersistedTimeComparator implements Comparator<UriPermission> {
+ @Override
+ public int compare(UriPermission lhs, UriPermission rhs) {
+ return Long.compare(lhs.persistedCreateTime, rhs.persistedCreateTime);
+ }
+ }
+
/**
* Snapshot of {@link UriPermission} with frozen
* {@link UriPermission#persistedModeFlags} state.
@@ -253,6 +335,7 @@ final class UriPermission {
final String targetPkg;
final Uri uri;
final int persistedModeFlags;
+ final long persistedCreateTime;
private Snapshot(UriPermission perm) {
this.userHandle = perm.userHandle;
@@ -260,10 +343,15 @@ final class UriPermission {
this.targetPkg = perm.targetPkg;
this.uri = perm.uri;
this.persistedModeFlags = perm.persistedModeFlags;
+ this.persistedCreateTime = perm.persistedCreateTime;
}
}
public Snapshot snapshot() {
return new Snapshot(this);
}
+
+ public android.content.UriPermission buildPersistedPublicApiObject() {
+ return new android.content.UriPermission(uri, persistedModeFlags, persistedCreateTime);
+ }
}