From a47223f99b6b7ade4ae909c458d975eb487062b3 Mon Sep 17 00:00:00 2001
From: Dianne Hackborn
Date: Thu, 30 Mar 2017 13:49:13 -0700
Subject: [PATCH] Implement issue #36590595: Add ability to associated a
ClipData with JobInfo
Yum!
Also needed to have a Context.revokeUriPermission() variant that is sane,
so reasonable CTS tests can be written.
Test: new ClipDataJobTest added.
Change-Id: Ia3135ea788a6e32c971bae7dab3a844d0ef4139c
---
api/current.txt | 8 +
api/system-current.txt | 8 +
api/test-current.txt | 8 +
core/java/android/app/ContextImpl.java | 13 +-
core/java/android/app/IActivityManager.aidl | 3 +-
core/java/android/app/job/JobInfo.java | 108 ++++++++--
core/java/android/app/job/JobParameters.java | 40 +++-
core/java/android/content/Context.java | 42 +++-
core/java/android/content/ContextWrapper.java | 5 +
core/java/android/os/BaseBundle.java | 14 ++
core/java/android/os/Bundle.java | 14 ++
core/java/android/os/PersistableBundle.java | 12 ++
.../server/am/ActivityManagerService.java | 42 ++--
.../server/job/JobSchedulerService.java | 18 ++
.../android/server/job/JobServiceContext.java | 8 +-
.../java/com/android/server/job/JobStore.java | 9 +-
.../server/job/controllers/JobStatus.java | 203 +++++++++++++++---
.../src/android/test/mock/MockContext.java | 5 +
.../bridge/android/BridgeContext.java | 6 +
19 files changed, 490 insertions(+), 76 deletions(-)
diff --git a/api/current.txt b/api/current.txt
index ec2a0e6479f0c..e2f8e3a4e9581 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);
@@ -40664,6 +40671,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 2890041f13a1e..1f37f9bfcef78 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);
@@ -44099,6 +44106,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 69ea62018e997..c2fb7e1754bc4 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);
@@ -40868,6 +40875,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 467ba996ad4f9..5a7246a4ed383 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 0a5e4bef8dca0..f4d26fd7d9d85 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 78e4c0d00d6f8..96eb0ea1a7b49 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;
@@ -197,6 +198,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;
@@ -239,6 +242,21 @@ public class JobInfo implements Parcelable {
return transientExtras;
}
+ /**
+ * 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.
*/
@@ -415,6 +433,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);
@@ -439,6 +464,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
@@ -471,6 +498,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);
@@ -597,6 +631,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.
@@ -668,6 +704,34 @@ public class JobInfo implements Parcelable {
return this;
}
+ /**
+ * Set a {@link ClipData} associated with this Job.
+ *
+ * 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 grantFlags.
+ *
+ *
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).
+ *
+ *
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.
+ *
+ * @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
@@ -892,25 +956,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) {
+ 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 (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 (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 ba168b7a924f7..8d52d3b96941d 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;
@@ -97,6 +103,24 @@ public class JobParameters implements Parcelable {
return transientExtras;
}
+ /**
+ * @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
@@ -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 1803bbe246104..dbbfe308868c8 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 any other 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).
*
+ * 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.
+ *
* @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
@@ -4104,6 +4110,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.
+ *
+ * Unlike {@link #revokeUriPermission(Uri, int)}, this method will only
+ * 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.
+ *
+ * @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
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 75784a69c74c6..53b021cbdf9fb 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -780,6 +780,11 @@ public class ContextWrapper extends Context {
mBase.revokeUriPermission(uri, modeFlags);
}
+ @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 e82fe03914304..6f388e27fe6e9 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -310,6 +310,20 @@ public class BaseBundle {
return mMap.isEmpty();
}
+ /**
+ * @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.
*/
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index c1292e7f894b1..9b5ff29d592e5 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 75f9c11873f0e..3ed5b17452456 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 b4da152a18ae1..71ad6744c70c4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2386,7 +2386,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());
@@ -8944,7 +8944,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);
@@ -8965,8 +8966,11 @@ public class ActivityManagerService extends IActivityManager.Stub
final ArrayMap perms = mGrantedUriPermissions.get(callingUid);
if (perms != null) {
boolean persistChanged = false;
- for (Iterator 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,
@@ -8975,7 +8979,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);
}
}
}
@@ -8992,29 +8996,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 perms = mGrantedUriPermissions.valueAt(i);
- for (Iterator 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);
}
}
@@ -9028,8 +9033,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);
@@ -9056,7 +9061,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);
}
}
@@ -21164,7 +21170,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 46d7bfc06d84a..2de9aaedf7c80 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 13370461521ba..19b988806d6f0 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 ccfc287f60572..c0264df9c66c9 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