1 /* 2 * Copyright (C) 2019 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.app.ActivityManager.getCapabilitiesSummary; 19 import static android.app.ActivityManager.procStateToString; 20 import static android.jobscheduler.cts.jobtestapp.TestJobSchedulerReceiver.EXTRA_REQUEST_JOB_UID_STATE; 21 import static android.jobscheduler.cts.jobtestapp.TestJobService.ACTION_JOB_STARTED; 22 import static android.jobscheduler.cts.jobtestapp.TestJobService.ACTION_JOB_STOPPED; 23 import static android.jobscheduler.cts.jobtestapp.TestJobService.INVALID_ADJ; 24 import static android.jobscheduler.cts.jobtestapp.TestJobService.JOB_CAPABILITIES_KEY; 25 import static android.jobscheduler.cts.jobtestapp.TestJobService.JOB_OOM_SCORE_ADJ_KEY; 26 import static android.jobscheduler.cts.jobtestapp.TestJobService.JOB_PARAMS_EXTRA_KEY; 27 import static android.jobscheduler.cts.jobtestapp.TestJobService.JOB_PROC_STATE_KEY; 28 import static android.server.wm.WindowManagerState.STATE_RESUMED; 29 30 import static org.junit.Assert.assertEquals; 31 32 import android.app.ActivityManager; 33 import android.app.job.JobParameters; 34 import android.content.BroadcastReceiver; 35 import android.content.ComponentName; 36 import android.content.Context; 37 import android.content.Intent; 38 import android.content.IntentFilter; 39 import android.jobscheduler.cts.jobtestapp.TestActivity; 40 import android.jobscheduler.cts.jobtestapp.TestJobSchedulerReceiver; 41 import android.os.SystemClock; 42 import android.os.UserHandle; 43 import android.server.wm.WindowManagerStateHelper; 44 import android.util.Log; 45 46 import com.android.compatibility.common.util.CallbackAsserter; 47 import com.android.compatibility.common.util.SystemUtil; 48 49 import java.util.Map; 50 51 /** 52 * Common functions to interact with the test app. 53 */ 54 class TestAppInterface { 55 private static final String TAG = TestAppInterface.class.getSimpleName(); 56 57 static final String TEST_APP_PACKAGE = "android.jobscheduler.cts.jobtestapp"; 58 private static final String TEST_APP_ACTIVITY = TEST_APP_PACKAGE + ".TestActivity"; 59 static final String TEST_APP_RECEIVER = TEST_APP_PACKAGE + ".TestJobSchedulerReceiver"; 60 61 private final Context mContext; 62 private final int mJobId; 63 64 /* accesses must be synchronized on itself */ 65 private final TestJobState mTestJobState = new TestJobState(); 66 TestAppInterface(Context ctx, int jobId)67 TestAppInterface(Context ctx, int jobId) { 68 mContext = ctx; 69 mJobId = jobId; 70 71 final IntentFilter intentFilter = new IntentFilter(); 72 intentFilter.addAction(ACTION_JOB_STARTED); 73 intentFilter.addAction(ACTION_JOB_STOPPED); 74 mContext.registerReceiver(mReceiver, intentFilter); 75 } 76 cleanup()77 void cleanup() { 78 final Intent cancelJobsIntent = new Intent(TestJobSchedulerReceiver.ACTION_CANCEL_JOBS); 79 cancelJobsIntent.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_RECEIVER)); 80 cancelJobsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 81 mContext.sendBroadcast(cancelJobsIntent); 82 closeActivity(); 83 mContext.unregisterReceiver(mReceiver); 84 mTestJobState.reset(); 85 } 86 scheduleJob(boolean allowWhileIdle, int requiredNetworkType, boolean asExpeditedJob)87 void scheduleJob(boolean allowWhileIdle, int requiredNetworkType, boolean asExpeditedJob) 88 throws Exception { 89 scheduleJob( 90 Map.of( 91 TestJobSchedulerReceiver.EXTRA_ALLOW_IN_IDLE, allowWhileIdle, 92 TestJobSchedulerReceiver.EXTRA_AS_EXPEDITED, asExpeditedJob 93 ), 94 Map.of( 95 TestJobSchedulerReceiver.EXTRA_REQUIRED_NETWORK_TYPE, requiredNetworkType 96 )); 97 } 98 scheduleJob(Map<String, Boolean> booleanExtras, Map<String, Integer> intExtras)99 void scheduleJob(Map<String, Boolean> booleanExtras, Map<String, Integer> intExtras) 100 throws Exception { 101 final Intent scheduleJobIntent = new Intent(TestJobSchedulerReceiver.ACTION_SCHEDULE_JOB); 102 scheduleJobIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 103 scheduleJobIntent.putExtra(TestJobSchedulerReceiver.EXTRA_JOB_ID_KEY, mJobId); 104 booleanExtras.forEach(scheduleJobIntent::putExtra); 105 intExtras.forEach(scheduleJobIntent::putExtra); 106 scheduleJobIntent.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_RECEIVER)); 107 108 final CallbackAsserter resultBroadcastAsserter = CallbackAsserter.forBroadcast( 109 new IntentFilter(TestJobSchedulerReceiver.ACTION_JOB_SCHEDULE_RESULT)); 110 mContext.sendBroadcast(scheduleJobIntent); 111 resultBroadcastAsserter.assertCalled("Didn't get schedule job result broadcast", 112 15 /* 15 seconds */); 113 } 114 115 /** Asks (not forces) JobScheduler to run the job if constraints are met. */ runSatisfiedJob()116 void runSatisfiedJob() throws Exception { 117 SystemUtil.runShellCommand("cmd jobscheduler run -s" 118 + " -u " + UserHandle.myUserId() + " " + TEST_APP_PACKAGE + " " + mJobId); 119 } 120 startAndKeepTestActivity()121 void startAndKeepTestActivity() { 122 startAndKeepTestActivity(false); 123 } 124 startAndKeepTestActivity(boolean waitForResume)125 void startAndKeepTestActivity(boolean waitForResume) { 126 final Intent testActivity = new Intent(); 127 testActivity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 128 ComponentName testComponentName = new ComponentName(TEST_APP_PACKAGE, TEST_APP_ACTIVITY); 129 testActivity.setComponent(testComponentName); 130 mContext.startActivity(testActivity); 131 if (waitForResume) { 132 new WindowManagerStateHelper().waitForActivityState(testComponentName, STATE_RESUMED); 133 } 134 } 135 closeActivity()136 void closeActivity() { 137 mContext.sendBroadcast(new Intent(TestActivity.ACTION_FINISH_ACTIVITY)); 138 } 139 140 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 141 @Override 142 public void onReceive(Context context, Intent intent) { 143 Log.d(TAG, "Received action " + intent.getAction()); 144 switch (intent.getAction()) { 145 case ACTION_JOB_STARTED: 146 case ACTION_JOB_STOPPED: 147 final JobParameters params = intent.getParcelableExtra(JOB_PARAMS_EXTRA_KEY); 148 Log.d(TAG, "JobId: " + params.getJobId()); 149 synchronized (mTestJobState) { 150 mTestJobState.running = ACTION_JOB_STARTED.equals(intent.getAction()); 151 mTestJobState.jobId = params.getJobId(); 152 mTestJobState.params = params; 153 if (intent.getBooleanExtra(EXTRA_REQUEST_JOB_UID_STATE, false)) { 154 mTestJobState.procState = intent.getIntExtra(JOB_PROC_STATE_KEY, 155 ActivityManager.PROCESS_STATE_NONEXISTENT); 156 mTestJobState.capabilities = intent.getIntExtra(JOB_CAPABILITIES_KEY, 157 ActivityManager.PROCESS_CAPABILITY_NONE); 158 mTestJobState.oomScoreAdj = intent.getIntExtra(JOB_OOM_SCORE_ADJ_KEY, 159 INVALID_ADJ); 160 } 161 } 162 break; 163 } 164 } 165 }; 166 awaitJobStart(long maxWait)167 boolean awaitJobStart(long maxWait) throws Exception { 168 return waitUntilTrue(maxWait, () -> { 169 synchronized (mTestJobState) { 170 return (mTestJobState.jobId == mJobId) && mTestJobState.running; 171 } 172 }); 173 } 174 175 boolean awaitJobStop(long maxWait) throws Exception { 176 return waitUntilTrue(maxWait, () -> { 177 synchronized (mTestJobState) { 178 return (mTestJobState.jobId == mJobId) && !mTestJobState.running; 179 } 180 }); 181 } 182 183 void assertJobUidState(int procState, int capabilities, int oomScoreAdj) { 184 synchronized (mTestJobState) { 185 assertEquals("procState expected=" + procStateToString(procState) 186 + ",actual=" + procStateToString(mTestJobState.procState), 187 procState, mTestJobState.procState); 188 assertEquals("capabilities expected=" + getCapabilitiesSummary(capabilities) 189 + ",actual=" + getCapabilitiesSummary(mTestJobState.capabilities), 190 capabilities, mTestJobState.capabilities); 191 assertEquals("Unexpected oomScoreAdj", oomScoreAdj, mTestJobState.oomScoreAdj); 192 } 193 } 194 195 private boolean waitUntilTrue(long maxWait, Condition condition) throws Exception { 196 final long deadLine = SystemClock.uptimeMillis() + maxWait; 197 do { 198 Thread.sleep(500); 199 } while (!condition.isTrue() && SystemClock.uptimeMillis() < deadLine); 200 return condition.isTrue(); 201 } 202 203 JobParameters getLastParams() { 204 synchronized (mTestJobState) { 205 return mTestJobState.params; 206 } 207 } 208 209 private static final class TestJobState { 210 int jobId; 211 boolean running; 212 int procState; 213 int capabilities; 214 int oomScoreAdj; 215 JobParameters params; 216 217 TestJobState() { 218 initState(); 219 } 220 221 private void reset() { 222 initState(); 223 } 224 225 private void initState() { 226 running = false; 227 procState = ActivityManager.PROCESS_STATE_NONEXISTENT; 228 capabilities = ActivityManager.PROCESS_CAPABILITY_NONE; 229 oomScoreAdj = INVALID_ADJ; 230 } 231 } 232 233 private interface Condition { 234 boolean isTrue() throws Exception; 235 } 236 } 237