diff options
| -rw-r--r-- | api/current.txt | 19 | ||||
| -rw-r--r-- | core/java/android/app/ActivityManagerNative.java | 100 | ||||
| -rw-r--r-- | core/java/android/app/IActivityManager.java | 17 | ||||
| -rw-r--r-- | core/java/android/content/ContentResolver.java | 78 | ||||
| -rw-r--r-- | core/java/android/content/Intent.java | 18 | ||||
| -rw-r--r-- | core/java/android/content/UriPermission.java | 117 | ||||
| -rw-r--r-- | core/java/android/provider/DocumentsContract.java | 36 | ||||
| -rw-r--r-- | core/java/android/provider/DocumentsProvider.java | 4 | ||||
| -rw-r--r-- | core/java/com/android/internal/util/XmlUtils.java | 9 | ||||
| -rw-r--r-- | packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java | 2 | ||||
| -rw-r--r-- | packages/DocumentsUI/src/com/android/documentsui/TestActivity.java | 4 | ||||
| -rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 225 | ||||
| -rw-r--r-- | services/java/com/android/server/am/UriPermission.java | 190 |
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); + } } |