Fix issue #37108191: CTS: ClipDataJobTest#testClipDataGrant failure
It was checking the wrong state of the permission, which just
happened to (often) work because it was racing with the job
finishing and the permission being ungranted.
Oh and also add a test for the feature to have URI permission
grants associated with the job work queue.
Test: this is one
Change-Id: I02d6069c3cafd39e6c11b29fa5fc6a86e98306c9
diff --git a/tests/JobScheduler/src/android/jobscheduler/MockJobService.java b/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
index 7d06682..1955483 100644
--- a/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
+++ b/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
@@ -26,6 +26,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.net.Uri;
import android.os.Process;
import android.util.Log;
@@ -99,16 +100,90 @@
while ((work = params.dequeueWork()) != null) {
Log.i(TAG, "Received work #" + index + ": " + work.getIntent());
mReceivedWork.add(work.getIntent());
- params.completeWork(work);
- if (index < expectedWork.length && expectedWork[index].subitems != null) {
- final TestWorkItem[] sub = expectedWork[index].subitems;
- final JobInfo ji = expectedWork[index].jobInfo;
- final JobScheduler js = (JobScheduler) getSystemService(
- Context.JOB_SCHEDULER_SERVICE);
- for (int subi = 0; subi < sub.length; subi++) {
- js.enqueue(ji, new JobWorkItem(sub[subi].intent));
+
+ if (index < expectedWork.length) {
+ TestWorkItem expected = expectedWork[index];
+ int grantFlags = work.getIntent().getFlags();
+ if (expected.requireUrisGranted != null) {
+ for (int ui = 0; ui < expected.requireUrisGranted.length; ui++) {
+ if ((grantFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+ if (checkUriPermission(expected.requireUrisGranted[ui],
+ Process.myPid(), Process.myUid(),
+ Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ != PackageManager.PERMISSION_GRANTED) {
+ TestEnvironment.getTestEnvironment().notifyExecution(params,
+ permCheckRead, permCheckWrite, null,
+ "Expected read permission but not granted: "
+ + expected.requireUrisGranted[ui]
+ + " @ #" + index);
+ return false;
+ }
+ }
+ if ((grantFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+ if (checkUriPermission(expected.requireUrisGranted[ui],
+ Process.myPid(), Process.myUid(),
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
+ != PackageManager.PERMISSION_GRANTED) {
+ TestEnvironment.getTestEnvironment().notifyExecution(params,
+ permCheckRead, permCheckWrite, null,
+ "Expected write permission but not granted: "
+ + expected.requireUrisGranted[ui]
+ + " @ #" + index);
+ return false;
+ }
+ }
+ }
+ }
+ if (expected.requireUrisNotGranted != null) {
+ // XXX note no delay here, current impl will have fully revoked the
+ // permission by the time we return from completing the last work.
+ for (int ui = 0; ui < expected.requireUrisNotGranted.length; ui++) {
+ if ((grantFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+ if (checkUriPermission(expected.requireUrisNotGranted[ui],
+ Process.myPid(), Process.myUid(),
+ Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ != PackageManager.PERMISSION_DENIED) {
+ TestEnvironment.getTestEnvironment().notifyExecution(params,
+ permCheckRead, permCheckWrite, null,
+ "Not expected read permission but granted: "
+ + expected.requireUrisNotGranted[ui]
+ + " @ #" + index);
+ return false;
+ }
+ }
+ if ((grantFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+ if (checkUriPermission(expected.requireUrisNotGranted[ui],
+ Process.myPid(), Process.myUid(),
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
+ != PackageManager.PERMISSION_DENIED) {
+ TestEnvironment.getTestEnvironment().notifyExecution(params,
+ permCheckRead, permCheckWrite, null,
+ "Not expected write permission but granted: "
+ + expected.requireUrisNotGranted[ui]
+ + " @ #" + index);
+ return false;
+ }
+ }
+ }
}
}
+
+ params.completeWork(work);
+
+ if (index < expectedWork.length) {
+ TestWorkItem expected = expectedWork[index];
+ if (expected.subitems != null) {
+ final TestWorkItem[] sub = expected.subitems;
+ final JobInfo ji = expected.jobInfo;
+ final JobScheduler js = (JobScheduler) getSystemService(
+ Context.JOB_SCHEDULER_SERVICE);
+ for (int subi = 0; subi < sub.length; subi++) {
+ js.enqueue(ji, new JobWorkItem(sub[subi].intent));
+ }
+ }
+ }
+
+ index++;
}
Log.i(TAG, "Done with all work at #" + index);
// We don't notifyExecution here because we want to make sure the job properly
@@ -130,17 +205,32 @@
public final Intent intent;
public final JobInfo jobInfo;
public final TestWorkItem[] subitems;
+ public final Uri[] requireUrisGranted;
+ public final Uri[] requireUrisNotGranted;
public TestWorkItem(Intent _intent) {
intent = _intent;
jobInfo = null;
subitems = null;
+ requireUrisGranted = null;
+ requireUrisNotGranted = null;
}
public TestWorkItem(Intent _intent, JobInfo _jobInfo, TestWorkItem[] _subitems) {
intent = _intent;
jobInfo = _jobInfo;
subitems = _subitems;
+ requireUrisGranted = null;
+ requireUrisNotGranted = null;
+ }
+
+ public TestWorkItem(Intent _intent, Uri[] _requireUrisGranted,
+ Uri[] _requireUrisNotGranted) {
+ intent = _intent;
+ jobInfo = null;
+ subitems = null;
+ requireUrisGranted = _requireUrisGranted;
+ requireUrisNotGranted = _requireUrisNotGranted;
}
}
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/ClipDataJobTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/ClipDataJobTest.java
index 47089d4..03d941e 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/ClipDataJobTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/ClipDataJobTest.java
@@ -73,7 +73,7 @@
// Schedule the job, the system should now also be holding a URI grant for us.
kTestEnvironment.setExpectedExecutions(1);
mJobScheduler.schedule(mBuilder.setRequiresStorageNotLow(true)
- .setClipData(mClipData, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ .setClipData(mFirstClipData, Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION).build());
// Remove the explicit grant, we should still have a grant due to the job.
@@ -105,7 +105,7 @@
public void testClipDataGrant_Failed() throws Exception {
try {
mJobScheduler.schedule(mBuilder.setRequiresStorageNotLow(true)
- .setClipData(mClipData, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ .setClipData(mFirstClipData, Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION).build());
} catch (SecurityException e) {
return;
@@ -131,7 +131,7 @@
// Schedule the job, the system should now also be holding a URI grant for us.
kTestEnvironment.setExpectedExecutions(1);
mJobScheduler.schedule(mBuilder.setMinimumLatency(60*60*1000)
- .setClipData(mClipData, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ .setClipData(mFirstClipData, Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION).build());
// Remove the explicit grant, we should still have a grant due to the job.
@@ -145,7 +145,7 @@
// Now reschedule the job to have it happen right now.
mJobScheduler.schedule(mBuilder.setMinimumLatency(0)
- .setClipData(mClipData, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ .setClipData(mFirstClipData, Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION).build());
assertTrue("Job with storage not low constraint did not fire when storage not low.",
kTestEnvironment.awaitExecution());
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/ConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/ConstraintTest.java
index c92e521..bcc1e08 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/ConstraintTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/ConstraintTest.java
@@ -17,7 +17,6 @@
import android.annotation.TargetApi;
import android.app.Instrumentation;
-import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ClipData;
import android.content.ComponentName;
@@ -30,7 +29,6 @@
import android.os.Bundle;
import android.os.Process;
import android.os.SystemClock;
-import android.test.AndroidTestCase;
import android.test.InstrumentationTestCase;
import android.util.Log;
@@ -67,7 +65,10 @@
Uri mFirstUri;
Bundle mFirstUriBundle;
- ClipData mClipData;
+ Uri mSecondUri;
+ Bundle mSecondUriBundle;
+ ClipData mFirstClipData;
+ ClipData mSecondClipData;
boolean mStorageStateChanged;
@@ -82,8 +83,13 @@
mFirstUri = Uri.parse("content://" + JOBPERM_AUTHORITY + "/protected/foo");
mFirstUriBundle = new Bundle();
mFirstUriBundle.putParcelable("uri", mFirstUri);
- mClipData = new ClipData("JobPerm", new String[] { "application/*" },
+ mSecondUri = Uri.parse("content://" + JOBPERM_AUTHORITY + "/protected/bar");
+ mSecondUriBundle = new Bundle();
+ mSecondUriBundle.putParcelable("uri", mSecondUri);
+ mFirstClipData = new ClipData("JobPerm1", new String[] { "application/*" },
new ClipData.Item(mFirstUri));
+ mSecondClipData = new ClipData("JobPerm2", new String[] { "application/*" },
+ new ClipData.Item(mSecondUri));
try {
SystemUtil.runShellCommand(getInstrumentation(), "cmd activity set-inactive "
+ mContext.getPackageName() + " false");
@@ -122,10 +128,23 @@
getContext().sendBroadcast(EXPEDITE_STABLE_CHARGING);
}
+ public void assertHasUriPermission(Uri uri, int grantFlags) {
+ if ((grantFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+ assertEquals(PackageManager.PERMISSION_GRANTED,
+ getContext().checkUriPermission(uri, Process.myPid(),
+ Process.myUid(), Intent.FLAG_GRANT_READ_URI_PERMISSION));
+ }
+ if ((grantFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+ assertEquals(PackageManager.PERMISSION_GRANTED,
+ getContext().checkUriPermission(uri, Process.myPid(),
+ Process.myUid(), Intent.FLAG_GRANT_WRITE_URI_PERMISSION));
+ }
+ }
+
void waitPermissionRevoke(Uri uri, int access, long timeout) {
long startTime = SystemClock.elapsedRealtime();
while (getContext().checkUriPermission(uri, Process.myPid(), Process.myUid(), access)
- != PackageManager.PERMISSION_GRANTED) {
+ != PackageManager.PERMISSION_DENIED) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/EnqueueJobWorkTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/EnqueueJobWorkTest.java
index ad5a20f..402233b 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/EnqueueJobWorkTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/EnqueueJobWorkTest.java
@@ -22,6 +22,7 @@
import android.content.ContentProviderClient;
import android.content.Intent;
import android.jobscheduler.MockJobService.TestWorkItem;
+import android.net.Uri;
import java.util.ArrayList;
@@ -44,13 +45,13 @@
super.setUp();
mBuilder = new JobInfo.Builder(ENQUEUE_WORK_JOB_ID, kJobServiceComponent);
- //mProvider = getContext().getContentResolver().acquireContentProviderClient(mFirstUri);
+ mProvider = getContext().getContentResolver().acquireContentProviderClient(mFirstUri);
}
@Override
public void tearDown() throws Exception {
super.tearDown();
- //mProvider.close();
+ mProvider.close();
mJobScheduler.cancel(ENQUEUE_WORK_JOB_ID);
}
@@ -74,19 +75,19 @@
}
for (int i = 0; i < received.size(); i++) {
Intent work = received.get(i);
- if (i >= expected.length) {
- fail("Received more than " + expected.length + " work items, first extra is "
- + work);
- }
- if (!intentEquals(work, expectedArray.get(i).intent)) {
- fail("Received work #" + i + " " + work + " but expected " + expected[i]);
- }
if (i < expected.length && expected[i].subitems != null) {
TestWorkItem[] sub = expected[i].subitems;
for (int j = 0; j < sub.length; j++) {
expectedArray.add(sub[j]);
}
}
+ if (i >= expectedArray.size()) {
+ fail("Received more than " + expected.length + " work items, first extra is "
+ + work);
+ }
+ if (!intentEquals(work, expectedArray.get(i).intent)) {
+ fail("Received work #" + i + " " + work + " but expected " + expected[i]);
+ }
}
if (received.size() < expected.length) {
fail("Received only " + received.size() + " work items, but expected "
@@ -176,4 +177,61 @@
kTestEnvironment.awaitExecution());
compareWork(work, kTestEnvironment.getLastReceivedWork());
}
+
+ /**
+ * Test basic enqueueing batches of work.
+ */
+ public void testEnqueueMultipleUriGrantWork() throws Exception {
+ // Start out with storage low, so job is enqueued but not executed yet.
+ setStorageState(true);
+
+ // We need to get a permission grant so that we can grant it to ourself.
+ mProvider.call("grant", MY_PACKAGE, mFirstUriBundle);
+ mProvider.call("grant", MY_PACKAGE, mSecondUriBundle);
+ assertHasUriPermission(mFirstUri, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ assertHasUriPermission(mSecondUri, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+ Intent work1 = new Intent("work1");
+ work1.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ work1.setData(mFirstUri);
+ work1.setClipData(mSecondClipData);
+
+ Intent work2 = new Intent("work2");
+ work2.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ work2.setData(mFirstUri);
+
+ TestWorkItem[] work = new TestWorkItem[] {
+ new TestWorkItem(work1, new Uri[] { mFirstUri, mSecondUri}, new Uri[0]),
+ new TestWorkItem(work2, new Uri[] { mFirstUri }, new Uri[] { mSecondUri}) };
+ kTestEnvironment.setExpectedExecutions(1);
+ kTestEnvironment.setExpectedWork(work);
+ JobInfo ji = mBuilder.setOverrideDeadline(0).setRequiresStorageNotLow(true).build();
+ mJobScheduler.enqueue(ji, new JobWorkItem(work1));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work2));
+
+ // Remove the explicit grant, we should still have a grant due to the job.
+ mProvider.call("revoke", MY_PACKAGE, mFirstUriBundle);
+ mProvider.call("revoke", MY_PACKAGE, mSecondUriBundle);
+ assertHasUriPermission(mFirstUri, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ assertHasUriPermission(mSecondUri, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+ kTestEnvironment.readyToWork();
+
+ // Now allow the job to run.
+ setStorageState(false);
+
+ assertTrue("Job with work enqueued did not fire.",
+ kTestEnvironment.awaitExecution());
+ compareWork(work, kTestEnvironment.getLastReceivedWork());
+
+ // And wait for everything to be cleaned up.
+ waitPermissionRevoke(mFirstUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, 5000);
+ waitPermissionRevoke(mSecondUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, 5000);
+ }
}