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 android.annotation.CallSuper;
19 import android.annotation.TargetApi;
20 import android.app.Instrumentation;
21 import android.app.job.JobScheduler;
22 import android.content.ClipData;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.pm.PackageManager;
27 import android.jobscheduler.MockJobService;
28 import android.jobscheduler.TriggerContentJobService;
29 import android.net.Uri;
30 import android.os.Bundle;
31 import android.os.Process;
32 import android.os.SystemClock;
33 import android.os.UserHandle;
34 import android.provider.DeviceConfig;
35 import android.test.InstrumentationTestCase;
36 import android.util.Log;
37 
38 import com.android.compatibility.common.util.BatteryUtils;
39 import com.android.compatibility.common.util.DeviceConfigStateHelper;
40 import com.android.compatibility.common.util.SystemUtil;
41 
42 import java.io.IOException;
43 
44 /**
45  * Common functionality from which the other test case classes derive.
46  */
47 @TargetApi(21)
48 public abstract class BaseJobSchedulerTest extends InstrumentationTestCase {
49     /** Environment that notifies of JobScheduler callbacks. */
50     static MockJobService.TestEnvironment kTestEnvironment =
51             MockJobService.TestEnvironment.getTestEnvironment();
52     static TriggerContentJobService.TestEnvironment kTriggerTestEnvironment =
53             TriggerContentJobService.TestEnvironment.getTestEnvironment();
54     /** Handle for the service which receives the execution callbacks from the JobScheduler. */
55     static ComponentName kJobServiceComponent;
56     static ComponentName kTriggerContentServiceComponent;
57     JobScheduler mJobScheduler;
58 
59     Context mContext;
60     DeviceConfigStateHelper mDeviceConfigStateHelper;
61 
62     static final String MY_PACKAGE = "android.jobscheduler.cts";
63 
64     static final String JOBPERM_PACKAGE = "android.jobscheduler.cts.jobperm";
65     static final String JOBPERM_AUTHORITY = "android.jobscheduler.cts.jobperm.provider";
66     static final String JOBPERM_PERM = "android.jobscheduler.cts.jobperm.perm";
67 
68     Uri mFirstUri;
69     Bundle mFirstUriBundle;
70     Uri mSecondUri;
71     Bundle mSecondUriBundle;
72     ClipData mFirstClipData;
73     ClipData mSecondClipData;
74 
75     boolean mStorageStateChanged;
76 
77     @Override
injectInstrumentation(Instrumentation instrumentation)78     public void injectInstrumentation(Instrumentation instrumentation) {
79         super.injectInstrumentation(instrumentation);
80         mContext = instrumentation.getContext();
81         kJobServiceComponent = new ComponentName(getContext(), MockJobService.class);
82         kTriggerContentServiceComponent = new ComponentName(getContext(),
83                 TriggerContentJobService.class);
84         mJobScheduler = (JobScheduler) getContext().getSystemService(Context.JOB_SCHEDULER_SERVICE);
85         mFirstUri = Uri.parse("content://" + JOBPERM_AUTHORITY + "/protected/foo");
86         mFirstUriBundle = new Bundle();
87         mFirstUriBundle.putParcelable("uri", mFirstUri);
88         mSecondUri = Uri.parse("content://" + JOBPERM_AUTHORITY + "/protected/bar");
89         mSecondUriBundle = new Bundle();
90         mSecondUriBundle.putParcelable("uri", mSecondUri);
91         mFirstClipData = new ClipData("JobPerm1", new String[] { "application/*" },
92                 new ClipData.Item(mFirstUri));
93         mSecondClipData = new ClipData("JobPerm2", new String[] { "application/*" },
94                 new ClipData.Item(mSecondUri));
95         try {
96             SystemUtil.runShellCommand(getInstrumentation(), "cmd activity set-inactive "
97                     + mContext.getPackageName() + " false");
98         } catch (IOException e) {
99             Log.w("ConstraintTest", "Failed setting inactive false", e);
100         }
101     }
102 
getContext()103     public Context getContext() {
104         return mContext;
105     }
106 
107     @CallSuper
108     @Override
setUp()109     public void setUp() throws Exception {
110         super.setUp();
111         mDeviceConfigStateHelper =
112                 new DeviceConfigStateHelper(DeviceConfig.NAMESPACE_JOB_SCHEDULER);
113         kTestEnvironment.setUp();
114         kTriggerTestEnvironment.setUp();
115         mJobScheduler.cancelAll();
116     }
117 
118     @CallSuper
119     @Override
tearDown()120     public void tearDown() throws Exception {
121         SystemUtil.runShellCommand(getInstrumentation(), "cmd battery reset");
122         if (mStorageStateChanged) {
123             // Put storage service back in to normal operation.
124             SystemUtil.runShellCommand(getInstrumentation(), "cmd devicestoragemonitor reset");
125             mStorageStateChanged = false;
126         }
127         SystemUtil.runShellCommand(getInstrumentation(),
128                 "cmd jobscheduler reset-execution-quota -u current "
129                         + kJobServiceComponent.getPackageName());
130         mDeviceConfigStateHelper.restoreOriginalValues();
131 
132         // The super method should be called at the end.
133         super.tearDown();
134     }
135 
assertHasUriPermission(Uri uri, int grantFlags)136     public void assertHasUriPermission(Uri uri, int grantFlags) {
137         if ((grantFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
138             assertEquals(PackageManager.PERMISSION_GRANTED,
139                     getContext().checkUriPermission(uri, Process.myPid(),
140                             Process.myUid(), Intent.FLAG_GRANT_READ_URI_PERMISSION));
141         }
142         if ((grantFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
143             assertEquals(PackageManager.PERMISSION_GRANTED,
144                     getContext().checkUriPermission(uri, Process.myPid(),
145                             Process.myUid(), Intent.FLAG_GRANT_WRITE_URI_PERMISSION));
146         }
147     }
148 
waitPermissionRevoke(Uri uri, int access, long timeout)149     void waitPermissionRevoke(Uri uri, int access, long timeout) {
150         long startTime = SystemClock.elapsedRealtime();
151         while (getContext().checkUriPermission(uri, Process.myPid(), Process.myUid(), access)
152                 != PackageManager.PERMISSION_DENIED) {
153             try {
154                 Thread.sleep(50);
155             } catch (InterruptedException e) {
156             }
157             if ((SystemClock.elapsedRealtime()-startTime) >= timeout) {
158                 fail("Timed out waiting for permission revoke");
159             }
160         }
161     }
162 
163     // Note we are just using storage state as a way to control when the job gets executed.
setStorageStateLow(boolean low)164     void setStorageStateLow(boolean low) throws Exception {
165         mStorageStateChanged = true;
166         String res;
167         if (low) {
168             res = SystemUtil.runShellCommand(getInstrumentation(),
169                     "cmd devicestoragemonitor force-low -f");
170         } else {
171             res = SystemUtil.runShellCommand(getInstrumentation(),
172                     "cmd devicestoragemonitor force-not-low -f");
173         }
174         int seq = Integer.parseInt(res.trim());
175         long startTime = SystemClock.elapsedRealtime();
176 
177         // Wait for the storage update to be processed by job scheduler before proceeding.
178         int curSeq;
179         do {
180             curSeq = Integer.parseInt(SystemUtil.runShellCommand(getInstrumentation(),
181                     "cmd jobscheduler get-storage-seq").trim());
182             if (curSeq == seq) {
183                 return;
184             }
185         } while ((SystemClock.elapsedRealtime()-startTime) < 1000);
186 
187         fail("Timed out waiting for job scheduler: expected seq=" + seq + ", cur=" + curSeq);
188     }
189 
getJobState(int jobId)190     String getJobState(int jobId) throws Exception {
191         return SystemUtil.runShellCommand(getInstrumentation(),
192                 "cmd jobscheduler get-job-state --user cur "
193                         + kJobServiceComponent.getPackageName() + " " + jobId).trim();
194     }
195 
assertJobReady(int jobId)196     void assertJobReady(int jobId) throws Exception {
197         String state = getJobState(jobId);
198         assertTrue("Job unexpectedly not ready, in state: " + state, state.contains("ready"));
199     }
200 
assertJobWaiting(int jobId)201     void assertJobWaiting(int jobId) throws Exception {
202         String state = getJobState(jobId);
203         assertTrue("Job unexpectedly not waiting, in state: " + state, state.contains("waiting"));
204     }
205 
assertJobNotReady(int jobId)206     void assertJobNotReady(int jobId) throws Exception {
207         String state = getJobState(jobId);
208         assertTrue("Job unexpectedly ready, in state: " + state, !state.contains("ready"));
209     }
210 
211     /**
212      * Set the screen state.
213      */
toggleScreenOn(final boolean screenon)214     static void toggleScreenOn(final boolean screenon) throws Exception {
215         BatteryUtils.turnOnScreen(screenon);
216         // Wait a little bit for the broadcasts to be processed.
217         Thread.sleep(2_000);
218     }
219 
220     /** Asks (not forces) JobScheduler to run the job if constraints are met. */
runSatisfiedJob(int jobId)221     void runSatisfiedJob(int jobId) throws Exception {
222         SystemUtil.runShellCommand(getInstrumentation(),
223                 "cmd jobscheduler run -s"
224                 + " -u " + UserHandle.myUserId()
225                 + " " + kJobServiceComponent.getPackageName()
226                 + " " + jobId);
227     }
228 }
229