• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.server.wm.WindowManagerState.STATE_RESUMED;
19 
20 import static com.android.compatibility.common.util.TestUtils.waitUntil;
21 
22 import android.annotation.CallSuper;
23 import android.annotation.TargetApi;
24 import android.app.Instrumentation;
25 import android.app.job.JobScheduler;
26 import android.content.ClipData;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.pm.PackageManager;
31 import android.jobscheduler.MockJobService;
32 import android.jobscheduler.TestActivity;
33 import android.jobscheduler.TriggerContentJobService;
34 import android.net.Uri;
35 import android.os.Bundle;
36 import android.os.PowerManager;
37 import android.os.Process;
38 import android.os.SystemClock;
39 import android.os.UserHandle;
40 import android.provider.DeviceConfig;
41 import android.provider.Settings;
42 import android.server.wm.WindowManagerStateHelper;
43 import android.test.InstrumentationTestCase;
44 import android.util.Log;
45 
46 import com.android.compatibility.common.util.BatteryUtils;
47 import com.android.compatibility.common.util.DeviceConfigStateHelper;
48 import com.android.compatibility.common.util.SystemUtil;
49 
50 import java.io.IOException;
51 
52 /**
53  * Common functionality from which the other test case classes derive.
54  */
55 @TargetApi(21)
56 public abstract class BaseJobSchedulerTest extends InstrumentationTestCase {
57     /** Environment that notifies of JobScheduler callbacks. */
58     static MockJobService.TestEnvironment kTestEnvironment =
59             MockJobService.TestEnvironment.getTestEnvironment();
60     static TriggerContentJobService.TestEnvironment kTriggerTestEnvironment =
61             TriggerContentJobService.TestEnvironment.getTestEnvironment();
62     /** Handle for the service which receives the execution callbacks from the JobScheduler. */
63     static ComponentName kJobServiceComponent;
64     static ComponentName kTriggerContentServiceComponent;
65     JobScheduler mJobScheduler;
66 
67     Context mContext;
68     DeviceConfigStateHelper mDeviceConfigStateHelper;
69 
70     static final String MY_PACKAGE = "android.jobscheduler.cts";
71 
72     static final String JOBPERM_PACKAGE = "android.jobscheduler.cts.jobperm";
73     static final String JOBPERM_AUTHORITY = "android.jobscheduler.cts.jobperm.provider";
74     static final String JOBPERM_PERM = "android.jobscheduler.cts.jobperm.perm";
75 
76     Uri mFirstUri;
77     Bundle mFirstUriBundle;
78     Uri mSecondUri;
79     Bundle mSecondUriBundle;
80     ClipData mFirstClipData;
81     ClipData mSecondClipData;
82 
83     boolean mStorageStateChanged;
84     boolean mActivityStarted;
85 
86     private boolean mDeviceIdleEnabled;
87     private boolean mDeviceLightIdleEnabled;
88 
89     private String mInitialBatteryStatsConstants;
90 
91     @Override
injectInstrumentation(Instrumentation instrumentation)92     public void injectInstrumentation(Instrumentation instrumentation) {
93         super.injectInstrumentation(instrumentation);
94         mContext = instrumentation.getContext();
95         kJobServiceComponent = new ComponentName(getContext(), MockJobService.class);
96         kTriggerContentServiceComponent = new ComponentName(getContext(),
97                 TriggerContentJobService.class);
98         mJobScheduler = (JobScheduler) getContext().getSystemService(Context.JOB_SCHEDULER_SERVICE);
99         mFirstUri = Uri.parse("content://" + JOBPERM_AUTHORITY + "/protected/foo");
100         mFirstUriBundle = new Bundle();
101         mFirstUriBundle.putParcelable("uri", mFirstUri);
102         mSecondUri = Uri.parse("content://" + JOBPERM_AUTHORITY + "/protected/bar");
103         mSecondUriBundle = new Bundle();
104         mSecondUriBundle.putParcelable("uri", mSecondUri);
105         mFirstClipData = new ClipData("JobPerm1", new String[] { "application/*" },
106                 new ClipData.Item(mFirstUri));
107         mSecondClipData = new ClipData("JobPerm2", new String[] { "application/*" },
108                 new ClipData.Item(mSecondUri));
109         try {
110             SystemUtil.runShellCommand(getInstrumentation(), "cmd activity set-inactive "
111                     + mContext.getPackageName() + " false");
112         } catch (IOException e) {
113             Log.w("ConstraintTest", "Failed setting inactive false", e);
114         }
115     }
116 
getContext()117     public Context getContext() {
118         return mContext;
119     }
120 
121     @CallSuper
122     @Override
setUp()123     public void setUp() throws Exception {
124         super.setUp();
125         mDeviceConfigStateHelper =
126                 new DeviceConfigStateHelper(DeviceConfig.NAMESPACE_JOB_SCHEDULER);
127         mDeviceConfigStateHelper.set("fc_enable_flexibility", "false");
128         kTestEnvironment.setUp();
129         kTriggerTestEnvironment.setUp();
130         mJobScheduler.cancelAll();
131 
132         mDeviceIdleEnabled = isDeviceIdleEnabled();
133         mDeviceLightIdleEnabled = isDeviceLightIdleEnabled();
134         if (mDeviceIdleEnabled || mDeviceLightIdleEnabled) {
135             // Make sure the device isn't dozing since it will affect execution of regular jobs
136             setDeviceIdleState(false);
137         }
138 
139         mInitialBatteryStatsConstants = Settings.Global.getString(mContext.getContentResolver(),
140                 Settings.Global.BATTERY_STATS_CONSTANTS);
141         // Make sure ACTION_CHARGING is sent immediately.
142         Settings.Global.putString(mContext.getContentResolver(),
143                 Settings.Global.BATTERY_STATS_CONSTANTS, "battery_charged_delay_ms=0");
144     }
145 
146     @CallSuper
147     @Override
tearDown()148     public void tearDown() throws Exception {
149         SystemUtil.runShellCommand(getInstrumentation(), "cmd jobscheduler monitor-battery off");
150         SystemUtil.runShellCommand(getInstrumentation(), "cmd battery reset");
151         Settings.Global.putString(mContext.getContentResolver(),
152                 Settings.Global.BATTERY_STATS_CONSTANTS, mInitialBatteryStatsConstants);
153         if (mStorageStateChanged) {
154             // Put storage service back in to normal operation.
155             SystemUtil.runShellCommand(getInstrumentation(), "cmd devicestoragemonitor reset");
156             mStorageStateChanged = false;
157         }
158         SystemUtil.runShellCommand(getInstrumentation(),
159                 "cmd jobscheduler reset-execution-quota -u current "
160                         + kJobServiceComponent.getPackageName());
161         mDeviceConfigStateHelper.restoreOriginalValues();
162 
163         if (mActivityStarted) {
164             closeActivity();
165         }
166 
167         if (mDeviceIdleEnabled || mDeviceLightIdleEnabled) {
168             resetDeviceIdleState();
169         }
170 
171         // The super method should be called at the end.
172         super.tearDown();
173     }
174 
assertHasUriPermission(Uri uri, int grantFlags)175     public void assertHasUriPermission(Uri uri, int grantFlags) {
176         if ((grantFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
177             assertEquals(PackageManager.PERMISSION_GRANTED,
178                     getContext().checkUriPermission(uri, Process.myPid(),
179                             Process.myUid(), Intent.FLAG_GRANT_READ_URI_PERMISSION));
180         }
181         if ((grantFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
182             assertEquals(PackageManager.PERMISSION_GRANTED,
183                     getContext().checkUriPermission(uri, Process.myPid(),
184                             Process.myUid(), Intent.FLAG_GRANT_WRITE_URI_PERMISSION));
185         }
186     }
187 
waitPermissionRevoke(Uri uri, int access, long timeout)188     void waitPermissionRevoke(Uri uri, int access, long timeout) {
189         long startTime = SystemClock.elapsedRealtime();
190         while (getContext().checkUriPermission(uri, Process.myPid(), Process.myUid(), access)
191                 != PackageManager.PERMISSION_DENIED) {
192             try {
193                 Thread.sleep(50);
194             } catch (InterruptedException e) {
195             }
196             if ((SystemClock.elapsedRealtime()-startTime) >= timeout) {
197                 fail("Timed out waiting for permission revoke");
198             }
199         }
200     }
201 
isDeviceIdleFeatureEnabled()202     boolean isDeviceIdleFeatureEnabled() throws Exception {
203         return mDeviceIdleEnabled || mDeviceLightIdleEnabled;
204     }
205 
isDeviceIdleEnabled()206     static boolean isDeviceIdleEnabled() throws Exception {
207         final String output = SystemUtil.runShellCommand("cmd deviceidle enabled deep").trim();
208         return Integer.parseInt(output) != 0;
209     }
210 
isDeviceLightIdleEnabled()211     static boolean isDeviceLightIdleEnabled() throws Exception {
212         final String output = SystemUtil.runShellCommand("cmd deviceidle enabled light").trim();
213         return Integer.parseInt(output) != 0;
214     }
215 
216     /** Returns the current storage-low state, as believed by JobScheduler. */
isJsStorageStateLow()217     private boolean isJsStorageStateLow() throws Exception {
218         return !Boolean.parseBoolean(
219                 SystemUtil.runShellCommand(getInstrumentation(),
220                         "cmd jobscheduler get-storage-not-low").trim());
221     }
222 
223     // Note we are just using storage state as a way to control when the job gets executed.
setStorageStateLow(boolean low)224     void setStorageStateLow(boolean low) throws Exception {
225         if (isJsStorageStateLow() == low) {
226             // Nothing to do here
227             return;
228         }
229         mStorageStateChanged = true;
230         String res;
231         if (low) {
232             res = SystemUtil.runShellCommand(getInstrumentation(),
233                     "cmd devicestoragemonitor force-low -f");
234         } else {
235             res = SystemUtil.runShellCommand(getInstrumentation(),
236                     "cmd devicestoragemonitor force-not-low -f");
237         }
238         int seq = Integer.parseInt(res.trim());
239         long startTime = SystemClock.elapsedRealtime();
240 
241         // Wait for the storage update to be processed by job scheduler before proceeding.
242         int curSeq;
243         do {
244             curSeq = Integer.parseInt(SystemUtil.runShellCommand(getInstrumentation(),
245                     "cmd jobscheduler get-storage-seq").trim());
246             if (curSeq == seq) {
247                 return;
248             }
249             Thread.sleep(500);
250         } while ((SystemClock.elapsedRealtime() - startTime) < 10_000);
251 
252         fail("Timed out waiting for job scheduler: expected seq=" + seq + ", cur=" + curSeq);
253     }
254 
startAndKeepTestActivity()255     void startAndKeepTestActivity() {
256         final Intent testActivity = new Intent();
257         testActivity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
258         ComponentName testComponentName = new ComponentName(mContext, TestActivity.class);
259         testActivity.setComponent(testComponentName);
260         mContext.startActivity(testActivity);
261         new WindowManagerStateHelper().waitForActivityState(testComponentName, STATE_RESUMED);
262         mActivityStarted = true;
263     }
264 
closeActivity()265     void closeActivity() {
266         mContext.sendBroadcast(new Intent(TestActivity.ACTION_FINISH_ACTIVITY));
267         mActivityStarted = false;
268     }
269 
getJobState(int jobId)270     String getJobState(int jobId) throws Exception {
271         return SystemUtil.runShellCommand(getInstrumentation(),
272                 "cmd jobscheduler get-job-state --user cur "
273                         + kJobServiceComponent.getPackageName() + " " + jobId).trim();
274     }
275 
assertJobReady(int jobId)276     void assertJobReady(int jobId) throws Exception {
277         String state = getJobState(jobId);
278         assertTrue("Job unexpectedly not ready, in state: " + state, state.contains("ready"));
279     }
280 
assertJobWaiting(int jobId)281     void assertJobWaiting(int jobId) throws Exception {
282         String state = getJobState(jobId);
283         assertTrue("Job unexpectedly not waiting, in state: " + state, state.contains("waiting"));
284     }
285 
assertJobNotReady(int jobId)286     void assertJobNotReady(int jobId) throws Exception {
287         String state = getJobState(jobId);
288         assertTrue("Job unexpectedly ready, in state: " + state, !state.contains("ready"));
289     }
290 
291     /**
292      * Set the screen state.
293      */
toggleScreenOn(final boolean screenon)294     static void toggleScreenOn(final boolean screenon) throws Exception {
295         BatteryUtils.turnOnScreen(screenon);
296         // Wait a little bit for the broadcasts to be processed.
297         Thread.sleep(2_000);
298     }
299 
resetDeviceIdleState()300     void resetDeviceIdleState() throws Exception {
301         SystemUtil.runShellCommand("cmd deviceidle unforce");
302     }
303 
setBatteryState(boolean plugged, int level)304     void setBatteryState(boolean plugged, int level) throws Exception {
305         SystemUtil.runShellCommand(getInstrumentation(), "cmd jobscheduler monitor-battery on");
306         if (plugged) {
307             SystemUtil.runShellCommand(getInstrumentation(), "cmd battery set ac 1");
308             final int curLevel = Integer.parseInt(SystemUtil.runShellCommand(getInstrumentation(),
309                     "dumpsys battery get level").trim());
310             if (curLevel >= level) {
311                 // Lower the level so when we set it to the desired level, JobScheduler thinks
312                 // the device is charging.
313                 SystemUtil.runShellCommand(getInstrumentation(),
314                         "cmd battery set level " + Math.max(1, level - 1));
315             }
316         } else {
317             SystemUtil.runShellCommand(getInstrumentation(), "cmd battery unplug");
318         }
319         int seq = Integer.parseInt(SystemUtil.runShellCommand(getInstrumentation(),
320                 "cmd battery set -f level " + level).trim());
321 
322         // Wait for the battery update to be processed by job scheduler before proceeding.
323         waitUntil("JobScheduler didn't update charging status to " + plugged, 15 /* seconds */,
324                 () -> {
325                     int curSeq;
326                     boolean curCharging;
327                     curSeq = Integer.parseInt(SystemUtil.runShellCommand(getInstrumentation(),
328                             "cmd jobscheduler get-battery-seq").trim());
329                     curCharging = Boolean.parseBoolean(
330                             SystemUtil.runShellCommand(getInstrumentation(),
331                                     "cmd jobscheduler get-battery-charging").trim());
332                     return curSeq >= seq && curCharging == plugged;
333                 });
334     }
335 
setDeviceIdleState(final boolean idle)336     void setDeviceIdleState(final boolean idle) throws Exception {
337         final String changeCommand;
338         if (idle) {
339             changeCommand = "force-idle " + (mDeviceIdleEnabled ? "deep" : "light");
340         } else {
341             changeCommand = "force-active";
342         }
343         SystemUtil.runShellCommand("cmd deviceidle " + changeCommand);
344         waitUntil("Could not change device idle state to " + idle, 15 /* seconds */,
345                 () -> {
346                     PowerManager powerManager = getContext().getSystemService(PowerManager.class);
347                     if (idle) {
348                         return mDeviceIdleEnabled
349                                 ? powerManager.isDeviceIdleMode()
350                                 : powerManager.isDeviceLightIdleMode();
351                     } else {
352                         return !powerManager.isDeviceIdleMode()
353                                 && !powerManager.isDeviceLightIdleMode();
354                     }
355                 });
356     }
357 
358     /** Asks (not forces) JobScheduler to run the job if constraints are met. */
runSatisfiedJob(int jobId)359     void runSatisfiedJob(int jobId) throws Exception {
360         runSatisfiedJob(jobId, null);
361     }
362 
runSatisfiedJob(int jobId, String namespace)363     void runSatisfiedJob(int jobId, String namespace) throws Exception {
364         SystemUtil.runShellCommand(getInstrumentation(),
365                 "cmd jobscheduler run -s"
366                 + " -u " + UserHandle.myUserId()
367                 + (namespace == null ? "" : " -n " + namespace)
368                 + " " + kJobServiceComponent.getPackageName()
369                 + " " + jobId);
370     }
371 }
372