blob: 3dd5a2b2aabdf4f2a90a02b58fedbcf1e6710eb3 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.jobscheduler.cts;
import static com.android.compatibility.common.util.TestUtils.waitUntil;
import android.annotation.CallSuper;
import android.annotation.TargetApi;
import android.app.Instrumentation;
import android.app.job.JobScheduler;
import android.content.ClipData;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.jobscheduler.MockJobService;
import android.jobscheduler.TriggerContentJobService;
import android.net.Uri;
import android.os.Bundle;
import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.test.InstrumentationTestCase;
import android.util.Log;
import com.android.compatibility.common.util.BatteryUtils;
import com.android.compatibility.common.util.DeviceConfigStateHelper;
import com.android.compatibility.common.util.SystemUtil;
import java.io.IOException;
/**
* Common functionality from which the other test case classes derive.
*/
@TargetApi(21)
public abstract class BaseJobSchedulerTest extends InstrumentationTestCase {
/** Environment that notifies of JobScheduler callbacks. */
static MockJobService.TestEnvironment kTestEnvironment =
MockJobService.TestEnvironment.getTestEnvironment();
static TriggerContentJobService.TestEnvironment kTriggerTestEnvironment =
TriggerContentJobService.TestEnvironment.getTestEnvironment();
/** Handle for the service which receives the execution callbacks from the JobScheduler. */
static ComponentName kJobServiceComponent;
static ComponentName kTriggerContentServiceComponent;
JobScheduler mJobScheduler;
Context mContext;
DeviceConfigStateHelper mDeviceConfigStateHelper;
static final String MY_PACKAGE = "android.jobscheduler.cts";
static final String JOBPERM_PACKAGE = "android.jobscheduler.cts.jobperm";
static final String JOBPERM_AUTHORITY = "android.jobscheduler.cts.jobperm.provider";
static final String JOBPERM_PERM = "android.jobscheduler.cts.jobperm.perm";
Uri mFirstUri;
Bundle mFirstUriBundle;
Uri mSecondUri;
Bundle mSecondUriBundle;
ClipData mFirstClipData;
ClipData mSecondClipData;
boolean mStorageStateChanged;
private String mInitialBatteryStatsConstants;
@Override
public void injectInstrumentation(Instrumentation instrumentation) {
super.injectInstrumentation(instrumentation);
mContext = instrumentation.getContext();
kJobServiceComponent = new ComponentName(getContext(), MockJobService.class);
kTriggerContentServiceComponent = new ComponentName(getContext(),
TriggerContentJobService.class);
mJobScheduler = (JobScheduler) getContext().getSystemService(Context.JOB_SCHEDULER_SERVICE);
mFirstUri = Uri.parse("content://" + JOBPERM_AUTHORITY + "/protected/foo");
mFirstUriBundle = new Bundle();
mFirstUriBundle.putParcelable("uri", mFirstUri);
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");
} catch (IOException e) {
Log.w("ConstraintTest", "Failed setting inactive false", e);
}
}
public Context getContext() {
return mContext;
}
@CallSuper
@Override
public void setUp() throws Exception {
super.setUp();
mDeviceConfigStateHelper =
new DeviceConfigStateHelper(DeviceConfig.NAMESPACE_JOB_SCHEDULER);
kTestEnvironment.setUp();
kTriggerTestEnvironment.setUp();
mJobScheduler.cancelAll();
mInitialBatteryStatsConstants = Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.BATTERY_STATS_CONSTANTS);
// Make sure ACTION_CHARGING is sent immediately.
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.BATTERY_STATS_CONSTANTS, "battery_charged_delay_ms=0");
}
@CallSuper
@Override
public void tearDown() throws Exception {
SystemUtil.runShellCommand(getInstrumentation(), "cmd battery reset");
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.BATTERY_STATS_CONSTANTS, mInitialBatteryStatsConstants);
if (mStorageStateChanged) {
// Put storage service back in to normal operation.
SystemUtil.runShellCommand(getInstrumentation(), "cmd devicestoragemonitor reset");
mStorageStateChanged = false;
}
SystemUtil.runShellCommand(getInstrumentation(),
"cmd jobscheduler reset-execution-quota -u current "
+ kJobServiceComponent.getPackageName());
mDeviceConfigStateHelper.restoreOriginalValues();
// The super method should be called at the end.
super.tearDown();
}
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_DENIED) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
if ((SystemClock.elapsedRealtime()-startTime) >= timeout) {
fail("Timed out waiting for permission revoke");
}
}
}
// Note we are just using storage state as a way to control when the job gets executed.
void setStorageStateLow(boolean low) throws Exception {
mStorageStateChanged = true;
String res;
if (low) {
res = SystemUtil.runShellCommand(getInstrumentation(),
"cmd devicestoragemonitor force-low -f");
} else {
res = SystemUtil.runShellCommand(getInstrumentation(),
"cmd devicestoragemonitor force-not-low -f");
}
int seq = Integer.parseInt(res.trim());
long startTime = SystemClock.elapsedRealtime();
// Wait for the storage update to be processed by job scheduler before proceeding.
int curSeq;
do {
curSeq = Integer.parseInt(SystemUtil.runShellCommand(getInstrumentation(),
"cmd jobscheduler get-storage-seq").trim());
if (curSeq == seq) {
return;
}
} while ((SystemClock.elapsedRealtime()-startTime) < 1000);
fail("Timed out waiting for job scheduler: expected seq=" + seq + ", cur=" + curSeq);
}
String getJobState(int jobId) throws Exception {
return SystemUtil.runShellCommand(getInstrumentation(),
"cmd jobscheduler get-job-state --user cur "
+ kJobServiceComponent.getPackageName() + " " + jobId).trim();
}
void assertJobReady(int jobId) throws Exception {
String state = getJobState(jobId);
assertTrue("Job unexpectedly not ready, in state: " + state, state.contains("ready"));
}
void assertJobWaiting(int jobId) throws Exception {
String state = getJobState(jobId);
assertTrue("Job unexpectedly not waiting, in state: " + state, state.contains("waiting"));
}
void assertJobNotReady(int jobId) throws Exception {
String state = getJobState(jobId);
assertTrue("Job unexpectedly ready, in state: " + state, !state.contains("ready"));
}
/**
* Set the screen state.
*/
static void toggleScreenOn(final boolean screenon) throws Exception {
BatteryUtils.turnOnScreen(screenon);
// Wait a little bit for the broadcasts to be processed.
Thread.sleep(2_000);
}
void setBatteryState(boolean plugged, int level) throws Exception {
if (plugged) {
SystemUtil.runShellCommand(getInstrumentation(), "cmd battery set ac 1");
final int curLevel = Integer.parseInt(SystemUtil.runShellCommand(getInstrumentation(),
"dumpsys battery get level").trim());
if (curLevel >= level) {
// Lower the level so when we set it to the desired level, JobScheduler thinks
// the device is charging.
SystemUtil.runShellCommand(getInstrumentation(),
"cmd battery set level " + Math.max(1, level - 1));
}
} else {
SystemUtil.runShellCommand(getInstrumentation(), "cmd battery unplug");
}
int seq = Integer.parseInt(SystemUtil.runShellCommand(getInstrumentation(),
"cmd battery set -f level " + level).trim());
// Wait for the battery update to be processed by job scheduler before proceeding.
waitUntil("JobScheduler didn't update charging status to " + plugged, 15 /* seconds */,
() -> {
int curSeq;
boolean curCharging;
curSeq = Integer.parseInt(SystemUtil.runShellCommand(getInstrumentation(),
"cmd jobscheduler get-battery-seq").trim());
curCharging = Boolean.parseBoolean(
SystemUtil.runShellCommand(getInstrumentation(),
"cmd jobscheduler get-battery-charging").trim());
return curSeq >= seq && curCharging == plugged;
});
}
/** Asks (not forces) JobScheduler to run the job if constraints are met. */
void runSatisfiedJob(int jobId) throws Exception {
SystemUtil.runShellCommand(getInstrumentation(),
"cmd jobscheduler run -s"
+ " -u " + UserHandle.myUserId()
+ " " + kJobServiceComponent.getPackageName()
+ " " + jobId);
}
}