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
This commit is contained in:
Dianne Hackborn
2017-03-30 13:49:13 -07:00
parent 474662da39
commit a47223f99b
19 changed files with 490 additions and 76 deletions

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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();

View File

@@ -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);
/*

View File

@@ -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.
*
* <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
@@ -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" +

View File

@@ -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);

View File

@@ -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
@@ -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.
*
* <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

View File

@@ -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);

View File

@@ -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.
*/

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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<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,
@@ -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<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);
}
}
@@ -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());

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -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());
}

View File

@@ -652,6 +652,11 @@ public class MockContext extends Context {
throw new UnsupportedOperationException();
}
@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();

View File

@@ -1658,6 +1658,12 @@ public class BridgeContext extends Context {
}
@Override
public void revokeUriPermission(String arg0, Uri arg1, int arg2) {
// pass
}
@Override
public void sendBroadcast(Intent arg0) {
// pass