diff options
19 files changed, 490 insertions, 76 deletions
diff --git a/api/current.txt b/api/current.txt index a15e07dedeb8..d890cdb59bb2 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6740,6 +6740,8 @@ package android.app.job { public class JobInfo implements android.os.Parcelable { method public int describeContents(); method public int getBackoffPolicy(); + method public android.content.ClipData getClipData(); + method public int getClipGrantFlags(); method public android.os.PersistableBundle getExtras(); method public long getFlexMillis(); method public int getId(); @@ -6778,6 +6780,7 @@ package android.app.job { method public android.app.job.JobInfo.Builder addTriggerContentUri(android.app.job.JobInfo.TriggerContentUri); method public android.app.job.JobInfo build(); method public android.app.job.JobInfo.Builder setBackoffCriteria(long, int); + method public android.app.job.JobInfo.Builder setClipData(android.content.ClipData, int); method public android.app.job.JobInfo.Builder setExtras(android.os.PersistableBundle); method public android.app.job.JobInfo.Builder setMinimumLatency(long); method public android.app.job.JobInfo.Builder setOverrideDeadline(long); @@ -6806,6 +6809,8 @@ package android.app.job { public class JobParameters implements android.os.Parcelable { method public int describeContents(); + method public android.content.ClipData getClipData(); + method public int getClipGrantFlags(); method public android.os.PersistableBundle getExtras(); method public int getJobId(); method public android.os.Bundle getTransientExtras(); @@ -8841,6 +8846,7 @@ package android.content { method public abstract deprecated void removeStickyBroadcast(android.content.Intent); method public abstract deprecated void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle); method public abstract void revokeUriPermission(android.net.Uri, int); + method public abstract void revokeUriPermission(java.lang.String, android.net.Uri, int); method public abstract void sendBroadcast(android.content.Intent); method public abstract void sendBroadcast(android.content.Intent, java.lang.String); method public abstract void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle); @@ -9033,6 +9039,7 @@ package android.content { method public deprecated void removeStickyBroadcast(android.content.Intent); method public deprecated void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle); method public void revokeUriPermission(android.net.Uri, int); + method public void revokeUriPermission(java.lang.String, android.net.Uri, int); method public void sendBroadcast(android.content.Intent); method public void sendBroadcast(android.content.Intent, java.lang.String); method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle); @@ -40676,6 +40683,7 @@ package android.test.mock { method public void removeStickyBroadcast(android.content.Intent); method public void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle); method public void revokeUriPermission(android.net.Uri, int); + method public void revokeUriPermission(java.lang.String, android.net.Uri, int); method public void sendBroadcast(android.content.Intent); method public void sendBroadcast(android.content.Intent, java.lang.String); method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle); diff --git a/api/system-current.txt b/api/system-current.txt index 63a0ad678d85..3e739d9f13a2 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -7167,6 +7167,8 @@ package android.app.job { public class JobInfo implements android.os.Parcelable { method public int describeContents(); method public int getBackoffPolicy(); + method public android.content.ClipData getClipData(); + method public int getClipGrantFlags(); method public android.os.PersistableBundle getExtras(); method public long getFlexMillis(); method public int getId(); @@ -7205,6 +7207,7 @@ package android.app.job { method public android.app.job.JobInfo.Builder addTriggerContentUri(android.app.job.JobInfo.TriggerContentUri); method public android.app.job.JobInfo build(); method public android.app.job.JobInfo.Builder setBackoffCriteria(long, int); + method public android.app.job.JobInfo.Builder setClipData(android.content.ClipData, int); method public android.app.job.JobInfo.Builder setExtras(android.os.PersistableBundle); method public android.app.job.JobInfo.Builder setMinimumLatency(long); method public android.app.job.JobInfo.Builder setOverrideDeadline(long); @@ -7233,6 +7236,8 @@ package android.app.job { public class JobParameters implements android.os.Parcelable { method public int describeContents(); + method public android.content.ClipData getClipData(); + method public int getClipGrantFlags(); method public android.os.PersistableBundle getExtras(); method public int getJobId(); method public android.os.Bundle getTransientExtras(); @@ -9333,6 +9338,7 @@ package android.content { method public abstract deprecated void removeStickyBroadcast(android.content.Intent); method public abstract deprecated void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle); method public abstract void revokeUriPermission(android.net.Uri, int); + method public abstract void revokeUriPermission(java.lang.String, android.net.Uri, int); method public abstract void sendBroadcast(android.content.Intent); method public abstract void sendBroadcast(android.content.Intent, java.lang.String); method public abstract void sendBroadcast(android.content.Intent, java.lang.String, android.os.Bundle); @@ -9539,6 +9545,7 @@ package android.content { method public deprecated void removeStickyBroadcast(android.content.Intent); method public deprecated void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle); method public void revokeUriPermission(android.net.Uri, int); + method public void revokeUriPermission(java.lang.String, android.net.Uri, int); method public void sendBroadcast(android.content.Intent); method public void sendBroadcast(android.content.Intent, java.lang.String); method public void sendBroadcast(android.content.Intent, java.lang.String, android.os.Bundle); @@ -44112,6 +44119,7 @@ package android.test.mock { method public void removeStickyBroadcast(android.content.Intent); method public void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle); method public void revokeUriPermission(android.net.Uri, int); + method public void revokeUriPermission(java.lang.String, android.net.Uri, int); method public void sendBroadcast(android.content.Intent); method public void sendBroadcast(android.content.Intent, java.lang.String); method public void sendBroadcast(android.content.Intent, java.lang.String, android.os.Bundle); diff --git a/api/test-current.txt b/api/test-current.txt index efeb5a893252..6fba51a06fc2 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -6769,6 +6769,8 @@ package android.app.job { public class JobInfo implements android.os.Parcelable { method public int describeContents(); method public int getBackoffPolicy(); + method public android.content.ClipData getClipData(); + method public int getClipGrantFlags(); method public android.os.PersistableBundle getExtras(); method public long getFlexMillis(); method public int getId(); @@ -6807,6 +6809,7 @@ package android.app.job { method public android.app.job.JobInfo.Builder addTriggerContentUri(android.app.job.JobInfo.TriggerContentUri); method public android.app.job.JobInfo build(); method public android.app.job.JobInfo.Builder setBackoffCriteria(long, int); + method public android.app.job.JobInfo.Builder setClipData(android.content.ClipData, int); method public android.app.job.JobInfo.Builder setExtras(android.os.PersistableBundle); method public android.app.job.JobInfo.Builder setMinimumLatency(long); method public android.app.job.JobInfo.Builder setOverrideDeadline(long); @@ -6835,6 +6838,8 @@ package android.app.job { public class JobParameters implements android.os.Parcelable { method public int describeContents(); + method public android.content.ClipData getClipData(); + method public int getClipGrantFlags(); method public android.os.PersistableBundle getExtras(); method public int getJobId(); method public android.os.Bundle getTransientExtras(); @@ -8873,6 +8878,7 @@ package android.content { method public abstract deprecated void removeStickyBroadcast(android.content.Intent); method public abstract deprecated void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle); method public abstract void revokeUriPermission(android.net.Uri, int); + method public abstract void revokeUriPermission(java.lang.String, android.net.Uri, int); method public abstract void sendBroadcast(android.content.Intent); method public abstract void sendBroadcast(android.content.Intent, java.lang.String); method public abstract void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle); @@ -9066,6 +9072,7 @@ package android.content { method public deprecated void removeStickyBroadcast(android.content.Intent); method public deprecated void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle); method public void revokeUriPermission(android.net.Uri, int); + method public void revokeUriPermission(java.lang.String, android.net.Uri, int); method public void sendBroadcast(android.content.Intent); method public void sendBroadcast(android.content.Intent, java.lang.String); method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle); @@ -40880,6 +40887,7 @@ package android.test.mock { method public void removeStickyBroadcast(android.content.Intent); method public void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle); method public void revokeUriPermission(android.net.Uri, int); + method public void revokeUriPermission(java.lang.String, android.net.Uri, int); method public void sendBroadcast(android.content.Intent); method public void sendBroadcast(android.content.Intent, java.lang.String); method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 467ba996ad4f..5a7246a4ed38 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1790,7 +1790,18 @@ class ContextImpl extends Context { public void revokeUriPermission(Uri uri, int modeFlags) { try { ActivityManager.getService().revokeUriPermission( - mMainThread.getApplicationThread(), + mMainThread.getApplicationThread(), null, + ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri)); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void revokeUriPermission(String targetPackage, Uri uri, int modeFlags) { + try { + ActivityManager.getService().revokeUriPermission( + mMainThread.getApplicationThread(), targetPackage, ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 0a5e4bef8dca..f4d26fd7d9d8 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -172,7 +172,8 @@ interface IActivityManager { in IBinder callerToken); void grantUriPermission(in IApplicationThread caller, in String targetPkg, in Uri uri, int mode, int userId); - void revokeUriPermission(in IApplicationThread caller, in Uri uri, int mode, int userId); + void revokeUriPermission(in IApplicationThread caller, in String targetPkg, in Uri uri, + int mode, int userId); void setActivityController(in IActivityController watcher, boolean imAMonkey); void showWaitingForDebugger(in IApplicationThread who, boolean waiting); /* diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java index f9094c0d6eb3..8220ff94361f 100644 --- a/core/java/android/app/job/JobInfo.java +++ b/core/java/android/app/job/JobInfo.java @@ -20,6 +20,7 @@ import static android.util.TimeUtils.formatDuration; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.ClipData; import android.content.ComponentName; import android.net.Uri; import android.os.Bundle; @@ -208,6 +209,8 @@ public class JobInfo implements Parcelable { private final int jobId; private final PersistableBundle extras; private final Bundle transientExtras; + private final ClipData clipData; + private final int clipGrantFlags; private final ComponentName service; private final int constraintFlags; private final TriggerContentUri[] triggerContentUris; @@ -251,6 +254,21 @@ public class JobInfo implements Parcelable { } /** + * ClipData of information that is returned to your application at execution time, + * but not persisted by the system. + */ + public ClipData getClipData() { + return clipData; + } + + /** + * Permission grants that go along with {@link #getClipData}. + */ + public int getClipGrantFlags() { + return clipGrantFlags; + } + + /** * Name of the service endpoint that will be called back into by the JobScheduler. */ public ComponentName getService() { @@ -428,6 +446,13 @@ public class JobInfo implements Parcelable { jobId = in.readInt(); extras = in.readPersistableBundle(); transientExtras = in.readBundle(); + if (in.readInt() != 0) { + clipData = ClipData.CREATOR.createFromParcel(in); + clipGrantFlags = in.readInt(); + } else { + clipData = null; + clipGrantFlags = 0; + } service = in.readParcelable(null); constraintFlags = in.readInt(); triggerContentUris = in.createTypedArray(TriggerContentUri.CREATOR); @@ -452,6 +477,8 @@ public class JobInfo implements Parcelable { jobId = b.mJobId; extras = b.mExtras.deepCopy(); transientExtras = b.mTransientExtras.deepCopy(); + clipData = b.mClipData; + clipGrantFlags = b.mClipGrantFlags; service = b.mJobService; constraintFlags = b.mConstraintFlags; triggerContentUris = b.mTriggerContentUris != null @@ -484,6 +511,13 @@ public class JobInfo implements Parcelable { out.writeInt(jobId); out.writePersistableBundle(extras); out.writeBundle(transientExtras); + if (clipData != null) { + out.writeInt(1); + clipData.writeToParcel(out, flags); + out.writeInt(clipGrantFlags); + } else { + out.writeInt(0); + } out.writeParcelable(service, flags); out.writeInt(constraintFlags); out.writeTypedArray(triggerContentUris, flags); @@ -610,6 +644,8 @@ public class JobInfo implements Parcelable { private final ComponentName mJobService; private PersistableBundle mExtras = PersistableBundle.EMPTY; private Bundle mTransientExtras = Bundle.EMPTY; + private ClipData mClipData; + private int mClipGrantFlags; private int mPriority = PRIORITY_DEFAULT; private int mFlags; // Requirements. @@ -682,6 +718,34 @@ public class JobInfo implements Parcelable { } /** + * Set a {@link ClipData} associated with this Job. + * + * <p>The main purpose of providing a ClipData is to allow granting of + * URI permissions for data associated with the clip. The exact kind + * of permission grant to perform is specified through <var>grantFlags</var>. + * + * <p>If the ClipData contains items that are Intents, any + * grant flags in those Intents will be ignored. Only flags provided as an argument + * to this method are respected, and will be applied to all Uri or + * Intent items in the clip (or sub-items of the clip). + * + * <p>Because setting this property is not compatible with persisted + * jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when + * {@link android.app.job.JobInfo.Builder#build()} is called.</p> + * + * @param clip The new clip to set. May be null to clear the current clip. + * @param grantFlags The desired permissions to grant for any URIs. This should be + * a combination of {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION}, + * {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, and + * {@link android.content.Intent#FLAG_GRANT_PREFIX_URI_PERMISSION}. + */ + public Builder setClipData(ClipData clip, int grantFlags) { + mClipData = clip; + mClipGrantFlags = grantFlags; + return this; + } + + /** * Set some description of the kind of network type your job needs to have. * Not calling this function means the network is not necessary, as the default is * {@link #NETWORK_TYPE_NONE}. @@ -905,25 +969,33 @@ public class JobInfo implements Parcelable { "constraints, this is not allowed."); } // Check that a deadline was not set on a periodic job. - if (mIsPeriodic && (mMaxExecutionDelayMillis != 0L)) { - throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " + - "periodic job."); - } - if (mIsPeriodic && (mMinLatencyMillis != 0L)) { - throw new IllegalArgumentException("Can't call setMinimumLatency() on a " + - "periodic job"); - } - if (mIsPeriodic && (mTriggerContentUris != null)) { - throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " + - "periodic job"); - } - if (mIsPersisted && (mTriggerContentUris != null)) { - throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " + - "persisted job"); + if (mIsPeriodic) { + if (mMaxExecutionDelayMillis != 0L) { + throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " + + "periodic job."); + } + if (mMinLatencyMillis != 0L) { + throw new IllegalArgumentException("Can't call setMinimumLatency() on a " + + "periodic job"); + } + if (mTriggerContentUris != null) { + throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " + + "periodic job"); + } } - if (mIsPersisted && !mTransientExtras.isEmpty()) { - throw new IllegalArgumentException("Can't call setTransientExtras() on a " + - "persisted job"); + if (mIsPersisted) { + if (mTriggerContentUris != null) { + throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " + + "persisted job"); + } + if (!mTransientExtras.isEmpty()) { + throw new IllegalArgumentException("Can't call setTransientExtras() on a " + + "persisted job"); + } + if (mClipData != null) { + throw new IllegalArgumentException("Can't call setClipData() on a " + + "persisted job"); + } } if (mBackoffPolicySet && (mConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) { throw new IllegalArgumentException("An idle mode job will not respect any" + diff --git a/core/java/android/app/job/JobParameters.java b/core/java/android/app/job/JobParameters.java index ba168b7a924f..8d52d3b96941 100644 --- a/core/java/android/app/job/JobParameters.java +++ b/core/java/android/app/job/JobParameters.java @@ -17,6 +17,7 @@ package android.app.job; import android.app.job.IJobCallback; +import android.content.ClipData; import android.net.Uri; import android.os.Bundle; import android.os.IBinder; @@ -44,6 +45,8 @@ public class JobParameters implements Parcelable { private final int jobId; private final PersistableBundle extras; private final Bundle transientExtras; + private final ClipData clipData; + private final int clipGrantFlags; private final IBinder callback; private final boolean overrideDeadlineExpired; private final Uri[] mTriggeredContentUris; @@ -53,11 +56,14 @@ public class JobParameters implements Parcelable { /** @hide */ public JobParameters(IBinder callback, int jobId, PersistableBundle extras, - Bundle transientExtras, boolean overrideDeadlineExpired, Uri[] triggeredContentUris, + Bundle transientExtras, ClipData clipData, int clipGrantFlags, + boolean overrideDeadlineExpired, Uri[] triggeredContentUris, String[] triggeredContentAuthorities) { this.jobId = jobId; this.extras = extras; this.transientExtras = transientExtras; + this.clipData = clipData; + this.clipGrantFlags = clipGrantFlags; this.callback = callback; this.overrideDeadlineExpired = overrideDeadlineExpired; this.mTriggeredContentUris = triggeredContentUris; @@ -98,6 +104,24 @@ public class JobParameters implements Parcelable { } /** + * @return The clip you passed in when constructing this job with + * {@link android.app.job.JobInfo.Builder#setClipData(ClipData, int)}. Will be null + * if it was not set. + */ + public ClipData getClipData() { + return clipData; + } + + /** + * @return The clip grant flags you passed in when constructing this job with + * {@link android.app.job.JobInfo.Builder#setClipData(ClipData, int)}. Will be 0 + * if it was not set. + */ + public int getClipGrantFlags() { + return clipGrantFlags; + } + + /** * For jobs with {@link android.app.job.JobInfo.Builder#setOverrideDeadline(long)} set, this * provides an easy way to tell whether the job is being executed due to the deadline * expiring. Note: If the job is running because its deadline expired, it implies that its @@ -140,6 +164,13 @@ public class JobParameters implements Parcelable { jobId = in.readInt(); extras = in.readPersistableBundle(); transientExtras = in.readBundle(); + if (in.readInt() != 0) { + clipData = ClipData.CREATOR.createFromParcel(in); + clipGrantFlags = in.readInt(); + } else { + clipData = null; + clipGrantFlags = 0; + } callback = in.readStrongBinder(); overrideDeadlineExpired = in.readInt() == 1; mTriggeredContentUris = in.createTypedArray(Uri.CREATOR); @@ -162,6 +193,13 @@ public class JobParameters implements Parcelable { dest.writeInt(jobId); dest.writePersistableBundle(extras); dest.writeBundle(transientExtras); + if (clipData != null) { + dest.writeInt(1); + clipData.writeToParcel(dest, flags); + dest.writeInt(clipGrantFlags); + } else { + dest.writeInt(0); + } dest.writeStrongBinder(callback); dest.writeInt(overrideDeadlineExpired ? 1 : 0); dest.writeTypedArray(mTriggeredContentUris, flags); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 1803bbe24610..dbbfe308868c 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4078,8 +4078,8 @@ public abstract class Context { /** * Remove all permissions to access a particular content provider Uri - * that were previously added with {@link #grantUriPermission}. The given - * Uri will match all previously granted Uris that are the same or a + * that were previously added with {@link #grantUriPermission} or <em>any other</em> mechanism. + * The given Uri will match all previously granted Uris that are the same or a * sub-path of the given Uri. That is, revoking "content://foo/target" will * revoke both "content://foo/target" and "content://foo/target/sub", but not * "content://foo". It will not remove any prefix grants that exist at a @@ -4089,10 +4089,16 @@ public abstract class Context { * regular permission access to a Uri, but had received access to it through * a specific Uri permission grant, you could not revoke that grant with this * function and a {@link SecurityException} would be thrown. As of - * {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this function will not throw a security exception, - * but will remove whatever permission grants to the Uri had been given to the app + * {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this function will not throw a security + * exception, but will remove whatever permission grants to the Uri had been given to the app * (or none).</p> * + * <p>Unlike {@link #revokeUriPermission(String, Uri, int)}, this method impacts all permission + * grants matching the given Uri, for any package they had been granted to, through any + * mechanism this had happened (such as indirectly through the clipboard, activity launch, + * service start, etc). That means this can be potentially dangerous to use, as it can + * revoke grants that another app could be strongly expecting to stick around.</p> + * * @param uri The Uri you would like to revoke access to. * @param modeFlags The desired access modes. Any combination of * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION @@ -4105,6 +4111,34 @@ public abstract class Context { public abstract void revokeUriPermission(Uri uri, @Intent.AccessUriMode int modeFlags); /** + * Remove permissions to access a particular content provider Uri + * that were previously added with {@link #grantUriPermission} for a specific target + * package. The given Uri will match all previously granted Uris that are the same or a + * sub-path of the given Uri. That is, revoking "content://foo/target" will + * revoke both "content://foo/target" and "content://foo/target/sub", but not + * "content://foo". It will not remove any prefix grants that exist at a + * higher level. + * + * <p>Unlike {@link #revokeUriPermission(Uri, int)}, this method will <em>only</em> + * revoke permissions that had been explicitly granted through {@link #grantUriPermission} + * and only for the package specified. Any matching grants that have happened through + * other mechanisms (clipboard, activity launching, service starting, etc) will not be + * removed.</p> + * + * @param toPackage The package you had previously granted access to. + * @param uri The Uri you would like to revoke access to. + * @param modeFlags The desired access modes. Any combination of + * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION + * Intent.FLAG_GRANT_READ_URI_PERMISSION} or + * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION + * Intent.FLAG_GRANT_WRITE_URI_PERMISSION}. + * + * @see #grantUriPermission + */ + public abstract void revokeUriPermission(String toPackage, Uri uri, + @Intent.AccessUriMode int modeFlags); + + /** * Determine whether a particular process and user ID has been granted * permission to access a specific URI. This only checks for permissions * that have been explicitly granted -- if the given process/uid has diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 75784a69c74c..53b021cbdf9f 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -781,6 +781,11 @@ public class ContextWrapper extends Context { } @Override + public void revokeUriPermission(String targetPackage, Uri uri, int modeFlags) { + mBase.revokeUriPermission(targetPackage, uri, modeFlags); + } + + @Override public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) { return mBase.checkUriPermission(uri, pid, uid, modeFlags); } diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java index e82fe0391430..6f388e27fe6e 100644 --- a/core/java/android/os/BaseBundle.java +++ b/core/java/android/os/BaseBundle.java @@ -311,6 +311,20 @@ public class BaseBundle { } /** + * @hide this should probably be the implementation of isEmpty(). To do that we + * need to ensure we always use the special empty parcel form when the bundle is + * empty. (This may already be the case, but to be safe we'll do this later when + * we aren't trying to stabilize.) + */ + public boolean maybeIsEmpty() { + if (isParcelled()) { + return isEmptyParcel(); + } else { + return isEmpty(); + } + } + + /** * Removes all elements from the mapping of this Bundle. */ public void clear() { diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java index c1292e7f894b..9b5ff29d592e 100644 --- a/core/java/android/os/Bundle.java +++ b/core/java/android/os/Bundle.java @@ -1209,4 +1209,18 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { } return "Bundle[" + mMap.toString() + "]"; } + + /** + * @hide + */ + public synchronized String toShortString() { + if (mParcelledData != null) { + if (isEmptyParcel()) { + return "EMPTY_PARCEL"; + } else { + return "mParcelledData.dataSize=" + mParcelledData.dataSize(); + } + } + return mMap.toString(); + } } diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java index 75f9c11873f0..3ed5b1745245 100644 --- a/core/java/android/os/PersistableBundle.java +++ b/core/java/android/os/PersistableBundle.java @@ -309,4 +309,16 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa } return "PersistableBundle[" + mMap.toString() + "]"; } + + /** @hide */ + synchronized public String toShortString() { + if (mParcelledData != null) { + if (isEmptyParcel()) { + return "EMPTY_PARCEL"; + } else { + return "mParcelledData.dataSize=" + mParcelledData.dataSize(); + } + } + return mMap.toString(); + } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index c64204f57448..0cf0b3f554c9 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2388,7 +2388,7 @@ public class ActivityManagerService extends IActivityManager.Stub } break; case DELETE_DUMPHEAP_MSG: { revokeUriPermission(ActivityThread.currentActivityThread().getApplicationThread(), - DumpHeapActivity.JAVA_URI, + null, DumpHeapActivity.JAVA_URI, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION, UserHandle.myUserId()); @@ -8947,7 +8947,8 @@ public class ActivityManagerService extends IActivityManager.Stub } } - private void revokeUriPermissionLocked(int callingUid, GrantUri grantUri, final int modeFlags) { + private void revokeUriPermissionLocked(String targetPackage, int callingUid, GrantUri grantUri, + final int modeFlags) { if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, "Revoking all granted permissions to " + grantUri); @@ -8968,8 +8969,11 @@ public class ActivityManagerService extends IActivityManager.Stub final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(callingUid); if (perms != null) { boolean persistChanged = false; - for (Iterator<UriPermission> it = perms.values().iterator(); it.hasNext();) { - final UriPermission perm = it.next(); + for (int i = perms.size()-1; i >= 0; i--) { + final UriPermission perm = perms.valueAt(i); + if (targetPackage != null && !targetPackage.equals(perm.targetPkg)) { + continue; + } if (perm.uri.sourceUserId == grantUri.sourceUserId && perm.uri.uri.isPathPrefixMatch(grantUri.uri)) { if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, @@ -8978,7 +8982,7 @@ public class ActivityManagerService extends IActivityManager.Stub persistChanged |= perm.revokeModes( modeFlags | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false); if (perm.modeFlags == 0) { - it.remove(); + perms.removeAt(i); } } } @@ -8995,29 +8999,30 @@ public class ActivityManagerService extends IActivityManager.Stub boolean persistChanged = false; // Go through all of the permissions and remove any that match. - int N = mGrantedUriPermissions.size(); - for (int i = 0; i < N; i++) { + for (int i = mGrantedUriPermissions.size()-1; i >= 0; i--) { final int targetUid = mGrantedUriPermissions.keyAt(i); final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i); - for (Iterator<UriPermission> it = perms.values().iterator(); it.hasNext();) { - final UriPermission perm = it.next(); + for (int j = perms.size()-1; j >= 0; j--) { + final UriPermission perm = perms.valueAt(j); + if (targetPackage != null && !targetPackage.equals(perm.targetPkg)) { + continue; + } if (perm.uri.sourceUserId == grantUri.sourceUserId && perm.uri.uri.isPathPrefixMatch(grantUri.uri)) { if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, "Revoking " + perm.targetUid + " permission to " + perm.uri); persistChanged |= perm.revokeModes( - modeFlags | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, true); + modeFlags | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, + targetPackage == null); if (perm.modeFlags == 0) { - it.remove(); + perms.removeAt(j); } } } if (perms.isEmpty()) { - mGrantedUriPermissions.remove(targetUid); - N--; - i--; + mGrantedUriPermissions.removeAt(i); } } @@ -9031,8 +9036,8 @@ public class ActivityManagerService extends IActivityManager.Stub * @param userId The userId in which the uri is to be resolved. */ @Override - public void revokeUriPermission(IApplicationThread caller, Uri uri, final int modeFlags, - int userId) { + public void revokeUriPermission(IApplicationThread caller, String targetPackage, Uri uri, + final int modeFlags, int userId) { enforceNotIsolatedCaller("revokeUriPermission"); synchronized(this) { final ProcessRecord r = getRecordForAppLocked(caller); @@ -9059,7 +9064,8 @@ public class ActivityManagerService extends IActivityManager.Stub return; } - revokeUriPermissionLocked(r.uid, new GrantUri(userId, uri, false), modeFlags); + revokeUriPermissionLocked(targetPackage, r.uid, new GrantUri(userId, uri, false), + modeFlags); } } @@ -21189,7 +21195,7 @@ public class ActivityManagerService extends IActivityManager.Stub public void run() { revokeUriPermission(ActivityThread.currentActivityThread() .getApplicationThread(), - DumpHeapActivity.JAVA_URI, + null, DumpHeapActivity.JAVA_URI, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION, UserHandle.myUserId()); diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index 46d7bfc06d84..2de9aaedf7c8 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -616,6 +616,9 @@ public final class JobSchedulerService extends com.android.server.SystemService } } + // This may throw a SecurityException. + jobStatus.prepare(ActivityManager.getService()); + toCancel = mJobs.getJobByUidAndJobId(uId, job.getId()); if (toCancel != null) { cancelJobImpl(toCancel, jobStatus); @@ -712,6 +715,7 @@ public final class JobSchedulerService extends com.android.server.SystemService private void cancelJobImpl(JobStatus cancelled, JobStatus incomingJob) { if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString()); + cancelled.unprepare(ActivityManager.getService()); stopTrackingJob(cancelled, incomingJob, true /* writeBack */); synchronized (mLock) { // Remove from pending queue. @@ -892,6 +896,9 @@ public final class JobSchedulerService extends com.android.server.SystemService */ private void startTrackingJob(JobStatus jobStatus, JobStatus lastJob) { synchronized (mLock) { + if (!jobStatus.isPrepared()) { + Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus); + } final boolean update = mJobs.add(jobStatus); if (mReadyToRock) { for (int i = 0; i < mControllers.size(); i++) { @@ -1078,11 +1085,22 @@ public final class JobSchedulerService extends com.android.server.SystemService // that may cause ordering problems if the app removes jobStatus while in here. if (needsReschedule) { JobStatus rescheduled = getRescheduleJobForFailure(jobStatus); + try { + rescheduled.prepare(ActivityManager.getService()); + } catch (SecurityException e) { + Slog.w(TAG, "Unable to regrant job permissions for " + rescheduled); + } startTrackingJob(rescheduled, jobStatus); } else if (jobStatus.getJob().isPeriodic()) { JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus); + try { + rescheduledPeriodic.prepare(ActivityManager.getService()); + } catch (SecurityException e) { + Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic); + } startTrackingJob(rescheduledPeriodic, jobStatus); } + jobStatus.unprepare(ActivityManager.getService()); reportActive(); mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget(); } diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java index 618feb113c8d..728ed72d2151 100644 --- a/services/core/java/com/android/server/job/JobServiceContext.java +++ b/services/core/java/com/android/server/job/JobServiceContext.java @@ -17,6 +17,7 @@ package com.android.server.job; import android.app.ActivityManager; +import android.app.job.JobInfo; import android.app.job.JobParameters; import android.app.job.IJobCallback; import android.app.job.IJobService; @@ -187,9 +188,10 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne triggeredAuthorities = new String[job.changedAuthorities.size()]; job.changedAuthorities.toArray(triggeredAuthorities); } - mParams = new JobParameters(this, job.getJobId(), job.getExtras(), - job.getTransientExtras(), isDeadlineExpired, - triggeredUris, triggeredAuthorities); + final JobInfo ji = job.getJob(); + mParams = new JobParameters(this, job.getJobId(), ji.getExtras(), + ji.getTransientExtras(), ji.getClipData(), ji.getClipGrantFlags(), + isDeadlineExpired, triggeredUris, triggeredAuthorities); mExecutionStartTimeElapsed = SystemClock.elapsedRealtime(); mVerb = VERB_BINDING; diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java index ccfc287f6057..c0264df9c66c 100644 --- a/services/core/java/com/android/server/job/JobStore.java +++ b/services/core/java/com/android/server/job/JobStore.java @@ -16,6 +16,8 @@ package com.android.server.job; +import android.app.ActivityManager; +import android.app.IActivityManager; import android.content.ComponentName; import android.app.job.JobInfo; import android.content.Context; @@ -294,7 +296,7 @@ public class JobStore { addAttributesToJobTag(out, jobStatus); writeConstraintsToXml(out, jobStatus); writeExecutionCriteriaToXml(out, jobStatus); - writeBundleToXml(jobStatus.getExtras(), out); + writeBundleToXml(jobStatus.getJob().getExtras(), out); out.endTag(null, "job"); } out.endTag(null, "job-info"); @@ -449,8 +451,11 @@ public class JobStore { synchronized (mLock) { jobs = readJobMapImpl(fis); if (jobs != null) { + IActivityManager am = ActivityManager.getService(); for (int i=0; i<jobs.size(); i++) { - this.jobSet.add(jobs.get(i)); + JobStatus js = jobs.get(i); + js.prepare(am); + this.jobSet.add(js); } } } diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java index ebb53a1cb01b..47630d0d734b 100644 --- a/services/core/java/com/android/server/job/controllers/JobStatus.java +++ b/services/core/java/com/android/server/job/controllers/JobStatus.java @@ -17,19 +17,25 @@ package com.android.server.job.controllers; import android.app.AppGlobals; +import android.app.IActivityManager; import android.app.job.JobInfo; +import android.content.ClipData; import android.content.ComponentName; +import android.content.ContentProvider; +import android.content.Intent; import android.net.Uri; -import android.os.Bundle; -import android.os.PersistableBundle; +import android.os.Binder; +import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.text.format.DateUtils; import android.util.ArraySet; +import android.util.Slog; import android.util.TimeUtils; import java.io.PrintWriter; +import java.util.Arrays; /** * Uniquely identifies a job internally. @@ -43,6 +49,8 @@ import java.io.PrintWriter; * @hide */ public final class JobStatus { + static final String TAG = "JobSchedulerService"; + public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE; public static final long NO_EARLIEST_RUNTIME = 0L; @@ -88,6 +96,9 @@ public final class JobStatus { final String tag; + private IBinder permissionOwner; + private boolean prepared; + /** * Earliest point in the future at which this job will be eligible to run. A value of 0 * indicates there is no delay constraint. See {@link #hasTimingDelayConstraint()}. @@ -241,6 +252,93 @@ public final class JobStatus { earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis); } + public void prepare(IActivityManager am) { + if (prepared) { + Slog.wtf(TAG, "Already prepared: " + this); + return; + } + prepared = true; + final ClipData clip = job.getClipData(); + if (clip != null) { + final int N = clip.getItemCount(); + for (int i = 0; i < N; i++) { + grantItemLocked(am, clip.getItemAt(i), sourceUid, sourcePackageName, sourceUserId); + } + } + } + + public void unprepare(IActivityManager am) { + if (!prepared) { + Slog.wtf(TAG, "Hasn't been prepared: " + this); + return; + } + prepared = false; + if (permissionOwner != null) { + final ClipData clip = job.getClipData(); + if (clip != null) { + final int N = clip.getItemCount(); + for (int i = 0; i < N; i++) { + revokeItemLocked(am, clip.getItemAt(i)); + } + } + } + } + + public boolean isPrepared() { + return prepared; + } + + private final void grantUriLocked(IActivityManager am, Uri uri, int sourceUid, + String targetPackage, int targetUserId) { + try { + int sourceUserId = ContentProvider.getUserIdFromUri(uri, + UserHandle.getUserId(sourceUid)); + uri = ContentProvider.getUriWithoutUserId(uri); + if (permissionOwner == null) { + permissionOwner = am.newUriPermissionOwner("job: " + toShortString()); + } + am.grantUriPermissionFromOwner(permissionOwner, sourceUid, targetPackage, + uri, job.getClipGrantFlags(), sourceUserId, targetUserId); + } catch (RemoteException e) { + Slog.e("JobScheduler", "AM dead"); + } + } + + private final void grantItemLocked(IActivityManager am, ClipData.Item item, int sourceUid, + String targetPackage, int targetUserId) { + if (item.getUri() != null) { + grantUriLocked(am, item.getUri(), sourceUid, targetPackage, targetUserId); + } + Intent intent = item.getIntent(); + if (intent != null && intent.getData() != null) { + grantUriLocked(am, intent.getData(), sourceUid, targetPackage, targetUserId); + } + } + + private final void revokeUriLocked(IActivityManager am, Uri uri) { + int userId = ContentProvider.getUserIdFromUri(uri, + UserHandle.getUserId(Binder.getCallingUid())); + long ident = Binder.clearCallingIdentity(); + try { + uri = ContentProvider.getUriWithoutUserId(uri); + am.revokeUriPermissionFromOwner(permissionOwner, uri, + job.getClipGrantFlags(), userId); + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private final void revokeItemLocked(IActivityManager am, ClipData.Item item) { + if (item.getUri() != null) { + revokeUriLocked(am, item.getUri()); + } + Intent intent = item.getIntent(); + if (intent != null && intent.getData() != null) { + revokeUriLocked(am, intent.getData()); + } + } + public JobInfo getJob() { return job; } @@ -295,14 +393,6 @@ public final class JobStatus { return tag; } - public PersistableBundle getExtras() { - return job.getExtras(); - } - - public Bundle getTransientExtras() { - return job.getTransientExtras(); - } - public int getPriority() { return job.getPriority(); } @@ -505,22 +595,64 @@ public final class JobStatus { @Override public String toString() { - return String.valueOf(hashCode()).substring(0, 3) + ".." - + ":[" + job.getService() - + ",jId=" + job.getId() - + ",u" + getUserId() - + ",suid=" + getSourceUid() - + ",R=(" + formatRunTime(earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME) - + "," + formatRunTime(latestRunTimeElapsedMillis, NO_LATEST_RUNTIME) + ")" - + ",N=" + job.getNetworkType() + ",C=" + job.isRequireCharging() - + ",BL=" + job.isRequireBatteryNotLow() - + ",I=" + job.isRequireDeviceIdle() - + ",U=" + (job.getTriggerContentUris() != null) - + ",F=" + numFailures + ",P=" + job.isPersisted() - + ",ANI=" + ((satisfiedConstraints&CONSTRAINT_APP_NOT_IDLE) != 0) - + ",DND=" + ((satisfiedConstraints&CONSTRAINT_DEVICE_NOT_DOZING) != 0) - + (isReady() ? "(READY)" : "") - + "]"; + StringBuilder sb = new StringBuilder(128); + sb.append("JobStatus{"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(" #"); + UserHandle.formatUid(sb, callingUid); + sb.append("/"); + sb.append(job.getId()); + sb.append(' '); + sb.append(batteryName); + sb.append(" u="); + sb.append(getUserId()); + sb.append(" s="); + sb.append(getSourceUid()); + if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME + || latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) { + sb.append(" TIME="); + sb.append(formatRunTime(earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME)); + sb.append("-"); + sb.append(formatRunTime(latestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME)); + } + if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) { + sb.append(" NET="); + sb.append(job.getNetworkType()); + } + if (job.isRequireCharging()) { + sb.append(" CHARGING"); + } + if (job.isRequireBatteryNotLow()) { + sb.append(" BATNOTLOW"); + } + if (job.isRequireStorageNotLow()) { + sb.append(" STORENOTLOW"); + } + if (job.isRequireDeviceIdle()) { + sb.append(" IDLE"); + } + if (job.isPersisted()) { + sb.append(" PERSISTED"); + } + if ((satisfiedConstraints&CONSTRAINT_APP_NOT_IDLE) == 0) { + sb.append(" WAIT:APP_NOT_IDLE"); + } + if ((satisfiedConstraints&CONSTRAINT_DEVICE_NOT_DOZING) == 0) { + sb.append(" WAIT:DEV_NOT_DOZING"); + } + if (job.getTriggerContentUris() != null) { + sb.append(" URIS="); + sb.append(Arrays.toString(job.getTriggerContentUris())); + } + if (numFailures != 0) { + sb.append(" failures="); + sb.append(numFailures); + } + if (isReady()) { + sb.append(" READY"); + } + sb.append("}"); + return sb.toString(); } private String formatRunTime(long runtime, long defaultValue) { @@ -613,8 +745,9 @@ public final class JobStatus { pw.print(" user="); pw.print(getSourceUserId()); pw.print(" pkg="); pw.println(getSourcePackageName()); if (full) { - pw.print(prefix); pw.println("JobInfo:"); pw.print(prefix); - pw.print(" Service: "); pw.println(job.getService().flattenToShortString()); + pw.print(prefix); pw.println("JobInfo:"); + pw.print(prefix); pw.print(" Service: "); + pw.println(job.getService().flattenToShortString()); if (job.isPeriodic()) { pw.print(prefix); pw.print(" PERIODIC: interval="); TimeUtils.formatDuration(job.getIntervalMillis(), pw); @@ -654,6 +787,20 @@ public final class JobStatus { pw.println(); } } + if (job.getExtras() != null && !job.getExtras().maybeIsEmpty()) { + pw.print(prefix); pw.print(" Extras: "); + pw.println(job.getExtras().toShortString()); + } + if (job.getTransientExtras() != null && !job.getTransientExtras().maybeIsEmpty()) { + pw.print(prefix); pw.print(" Transient extras: "); + pw.println(job.getTransientExtras().toShortString()); + } + if (job.getClipData() != null) { + pw.print(prefix); pw.print(" Clip data: "); + StringBuilder b = new StringBuilder(128); + job.getClipData().toShortString(b); + pw.println(b); + } if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) { pw.print(prefix); pw.print(" Network type: "); pw.println(job.getNetworkType()); } diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java index dca74ffdcc8c..a8eb9862202d 100644 --- a/test-runner/src/android/test/mock/MockContext.java +++ b/test-runner/src/android/test/mock/MockContext.java @@ -653,6 +653,11 @@ public class MockContext extends Context { } @Override + public void revokeUriPermission(String targetPackage, Uri uri, int modeFlags) { + throw new UnsupportedOperationException(); + } + + @Override public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) { throw new UnsupportedOperationException(); } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index 06272c80f99e..cc5a0c334116 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -1659,6 +1659,12 @@ public class BridgeContext extends Context { } @Override + public void revokeUriPermission(String arg0, Uri arg1, int arg2) { + // pass + + } + + @Override public void sendBroadcast(Intent arg0) { // pass |