1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.jobscheduler.cts; 17 18 import static com.android.compatibility.common.util.TestUtils.waitUntil; 19 20 import android.annotation.CallSuper; 21 import android.annotation.TargetApi; 22 import android.app.Instrumentation; 23 import android.app.job.JobScheduler; 24 import android.content.ClipData; 25 import android.content.ComponentName; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.pm.PackageManager; 29 import android.jobscheduler.MockJobService; 30 import android.jobscheduler.TriggerContentJobService; 31 import android.net.Uri; 32 import android.os.Bundle; 33 import android.os.Process; 34 import android.os.SystemClock; 35 import android.os.UserHandle; 36 import android.provider.DeviceConfig; 37 import android.provider.Settings; 38 import android.test.InstrumentationTestCase; 39 import android.util.Log; 40 41 import com.android.compatibility.common.util.BatteryUtils; 42 import com.android.compatibility.common.util.DeviceConfigStateHelper; 43 import com.android.compatibility.common.util.SystemUtil; 44 45 import java.io.IOException; 46 47 /** 48 * Common functionality from which the other test case classes derive. 49 */ 50 @TargetApi(21) 51 public abstract class BaseJobSchedulerTest extends InstrumentationTestCase { 52 /** Environment that notifies of JobScheduler callbacks. */ 53 static MockJobService.TestEnvironment kTestEnvironment = 54 MockJobService.TestEnvironment.getTestEnvironment(); 55 static TriggerContentJobService.TestEnvironment kTriggerTestEnvironment = 56 TriggerContentJobService.TestEnvironment.getTestEnvironment(); 57 /** Handle for the service which receives the execution callbacks from the JobScheduler. */ 58 static ComponentName kJobServiceComponent; 59 static ComponentName kTriggerContentServiceComponent; 60 JobScheduler mJobScheduler; 61 62 Context mContext; 63 DeviceConfigStateHelper mDeviceConfigStateHelper; 64 65 static final String MY_PACKAGE = "android.jobscheduler.cts"; 66 67 static final String JOBPERM_PACKAGE = "android.jobscheduler.cts.jobperm"; 68 static final String JOBPERM_AUTHORITY = "android.jobscheduler.cts.jobperm.provider"; 69 static final String JOBPERM_PERM = "android.jobscheduler.cts.jobperm.perm"; 70 71 Uri mFirstUri; 72 Bundle mFirstUriBundle; 73 Uri mSecondUri; 74 Bundle mSecondUriBundle; 75 ClipData mFirstClipData; 76 ClipData mSecondClipData; 77 78 boolean mStorageStateChanged; 79 80 private String mInitialBatteryStatsConstants; 81 82 @Override injectInstrumentation(Instrumentation instrumentation)83 public void injectInstrumentation(Instrumentation instrumentation) { 84 super.injectInstrumentation(instrumentation); 85 mContext = instrumentation.getContext(); 86 kJobServiceComponent = new ComponentName(getContext(), MockJobService.class); 87 kTriggerContentServiceComponent = new ComponentName(getContext(), 88 TriggerContentJobService.class); 89 mJobScheduler = (JobScheduler) getContext().getSystemService(Context.JOB_SCHEDULER_SERVICE); 90 mFirstUri = Uri.parse("content://" + JOBPERM_AUTHORITY + "/protected/foo"); 91 mFirstUriBundle = new Bundle(); 92 mFirstUriBundle.putParcelable("uri", mFirstUri); 93 mSecondUri = Uri.parse("content://" + JOBPERM_AUTHORITY + "/protected/bar"); 94 mSecondUriBundle = new Bundle(); 95 mSecondUriBundle.putParcelable("uri", mSecondUri); 96 mFirstClipData = new ClipData("JobPerm1", new String[] { "application/*" }, 97 new ClipData.Item(mFirstUri)); 98 mSecondClipData = new ClipData("JobPerm2", new String[] { "application/*" }, 99 new ClipData.Item(mSecondUri)); 100 try { 101 SystemUtil.runShellCommand(getInstrumentation(), "cmd activity set-inactive " 102 + mContext.getPackageName() + " false"); 103 } catch (IOException e) { 104 Log.w("ConstraintTest", "Failed setting inactive false", e); 105 } 106 } 107 getContext()108 public Context getContext() { 109 return mContext; 110 } 111 112 @CallSuper 113 @Override setUp()114 public void setUp() throws Exception { 115 super.setUp(); 116 mDeviceConfigStateHelper = 117 new DeviceConfigStateHelper(DeviceConfig.NAMESPACE_JOB_SCHEDULER); 118 kTestEnvironment.setUp(); 119 kTriggerTestEnvironment.setUp(); 120 mJobScheduler.cancelAll(); 121 122 mInitialBatteryStatsConstants = Settings.Global.getString(mContext.getContentResolver(), 123 Settings.Global.BATTERY_STATS_CONSTANTS); 124 // Make sure ACTION_CHARGING is sent immediately. 125 Settings.Global.putString(mContext.getContentResolver(), 126 Settings.Global.BATTERY_STATS_CONSTANTS, "battery_charged_delay_ms=0"); 127 } 128 129 @CallSuper 130 @Override tearDown()131 public void tearDown() throws Exception { 132 SystemUtil.runShellCommand(getInstrumentation(), "cmd battery reset"); 133 Settings.Global.putString(mContext.getContentResolver(), 134 Settings.Global.BATTERY_STATS_CONSTANTS, mInitialBatteryStatsConstants); 135 if (mStorageStateChanged) { 136 // Put storage service back in to normal operation. 137 SystemUtil.runShellCommand(getInstrumentation(), "cmd devicestoragemonitor reset"); 138 mStorageStateChanged = false; 139 } 140 SystemUtil.runShellCommand(getInstrumentation(), 141 "cmd jobscheduler reset-execution-quota -u current " 142 + kJobServiceComponent.getPackageName()); 143 mDeviceConfigStateHelper.restoreOriginalValues(); 144 145 // The super method should be called at the end. 146 super.tearDown(); 147 } 148 assertHasUriPermission(Uri uri, int grantFlags)149 public void assertHasUriPermission(Uri uri, int grantFlags) { 150 if ((grantFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) { 151 assertEquals(PackageManager.PERMISSION_GRANTED, 152 getContext().checkUriPermission(uri, Process.myPid(), 153 Process.myUid(), Intent.FLAG_GRANT_READ_URI_PERMISSION)); 154 } 155 if ((grantFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) { 156 assertEquals(PackageManager.PERMISSION_GRANTED, 157 getContext().checkUriPermission(uri, Process.myPid(), 158 Process.myUid(), Intent.FLAG_GRANT_WRITE_URI_PERMISSION)); 159 } 160 } 161 waitPermissionRevoke(Uri uri, int access, long timeout)162 void waitPermissionRevoke(Uri uri, int access, long timeout) { 163 long startTime = SystemClock.elapsedRealtime(); 164 while (getContext().checkUriPermission(uri, Process.myPid(), Process.myUid(), access) 165 != PackageManager.PERMISSION_DENIED) { 166 try { 167 Thread.sleep(50); 168 } catch (InterruptedException e) { 169 } 170 if ((SystemClock.elapsedRealtime()-startTime) >= timeout) { 171 fail("Timed out waiting for permission revoke"); 172 } 173 } 174 } 175 176 // Note we are just using storage state as a way to control when the job gets executed. setStorageStateLow(boolean low)177 void setStorageStateLow(boolean low) throws Exception { 178 mStorageStateChanged = true; 179 String res; 180 if (low) { 181 res = SystemUtil.runShellCommand(getInstrumentation(), 182 "cmd devicestoragemonitor force-low -f"); 183 } else { 184 res = SystemUtil.runShellCommand(getInstrumentation(), 185 "cmd devicestoragemonitor force-not-low -f"); 186 } 187 int seq = Integer.parseInt(res.trim()); 188 long startTime = SystemClock.elapsedRealtime(); 189 190 // Wait for the storage update to be processed by job scheduler before proceeding. 191 int curSeq; 192 do { 193 curSeq = Integer.parseInt(SystemUtil.runShellCommand(getInstrumentation(), 194 "cmd jobscheduler get-storage-seq").trim()); 195 if (curSeq == seq) { 196 return; 197 } 198 } while ((SystemClock.elapsedRealtime()-startTime) < 1000); 199 200 fail("Timed out waiting for job scheduler: expected seq=" + seq + ", cur=" + curSeq); 201 } 202 getJobState(int jobId)203 String getJobState(int jobId) throws Exception { 204 return SystemUtil.runShellCommand(getInstrumentation(), 205 "cmd jobscheduler get-job-state --user cur " 206 + kJobServiceComponent.getPackageName() + " " + jobId).trim(); 207 } 208 assertJobReady(int jobId)209 void assertJobReady(int jobId) throws Exception { 210 String state = getJobState(jobId); 211 assertTrue("Job unexpectedly not ready, in state: " + state, state.contains("ready")); 212 } 213 assertJobWaiting(int jobId)214 void assertJobWaiting(int jobId) throws Exception { 215 String state = getJobState(jobId); 216 assertTrue("Job unexpectedly not waiting, in state: " + state, state.contains("waiting")); 217 } 218 assertJobNotReady(int jobId)219 void assertJobNotReady(int jobId) throws Exception { 220 String state = getJobState(jobId); 221 assertTrue("Job unexpectedly ready, in state: " + state, !state.contains("ready")); 222 } 223 224 /** 225 * Set the screen state. 226 */ toggleScreenOn(final boolean screenon)227 static void toggleScreenOn(final boolean screenon) throws Exception { 228 BatteryUtils.turnOnScreen(screenon); 229 // Wait a little bit for the broadcasts to be processed. 230 Thread.sleep(2_000); 231 } 232 setBatteryState(boolean plugged, int level)233 void setBatteryState(boolean plugged, int level) throws Exception { 234 if (plugged) { 235 SystemUtil.runShellCommand(getInstrumentation(), "cmd battery set ac 1"); 236 final int curLevel = Integer.parseInt(SystemUtil.runShellCommand(getInstrumentation(), 237 "dumpsys battery get level").trim()); 238 if (curLevel >= level) { 239 // Lower the level so when we set it to the desired level, JobScheduler thinks 240 // the device is charging. 241 SystemUtil.runShellCommand(getInstrumentation(), 242 "cmd battery set level " + Math.max(1, level - 1)); 243 } 244 } else { 245 SystemUtil.runShellCommand(getInstrumentation(), "cmd battery unplug"); 246 } 247 int seq = Integer.parseInt(SystemUtil.runShellCommand(getInstrumentation(), 248 "cmd battery set -f level " + level).trim()); 249 250 // Wait for the battery update to be processed by job scheduler before proceeding. 251 waitUntil("JobScheduler didn't update charging status to " + plugged, 15 /* seconds */, 252 () -> { 253 int curSeq; 254 boolean curCharging; 255 curSeq = Integer.parseInt(SystemUtil.runShellCommand(getInstrumentation(), 256 "cmd jobscheduler get-battery-seq").trim()); 257 curCharging = Boolean.parseBoolean( 258 SystemUtil.runShellCommand(getInstrumentation(), 259 "cmd jobscheduler get-battery-charging").trim()); 260 return curSeq >= seq && curCharging == plugged; 261 }); 262 } 263 264 /** Asks (not forces) JobScheduler to run the job if constraints are met. */ runSatisfiedJob(int jobId)265 void runSatisfiedJob(int jobId) throws Exception { 266 SystemUtil.runShellCommand(getInstrumentation(), 267 "cmd jobscheduler run -s" 268 + " -u " + UserHandle.myUserId() 269 + " " + kJobServiceComponent.getPackageName() 270 + " " + jobId); 271 } 272 } 273