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.jobscheduler.cts.jobtestapp.TestJobService.ACTION_JOB_STARTED;
19 import static android.jobscheduler.cts.jobtestapp.TestJobService.ACTION_JOB_STOPPED;
20 import static android.jobscheduler.cts.jobtestapp.TestJobService.JOB_PARAMS_EXTRA_KEY;
21 
22 import android.app.job.JobParameters;
23 import android.content.BroadcastReceiver;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.jobscheduler.cts.jobtestapp.TestActivity;
29 import android.jobscheduler.cts.jobtestapp.TestJobSchedulerReceiver;
30 import android.os.SystemClock;
31 import android.util.Log;
32 
33 /**
34  * Common functions to interact with the test app.
35  */
36 class TestAppInterface {
37     private static final String TAG = TestAppInterface.class.getSimpleName();
38 
39     static final String TEST_APP_PACKAGE = "android.jobscheduler.cts.jobtestapp";
40     private static final String TEST_APP_ACTIVITY = TEST_APP_PACKAGE + ".TestActivity";
41     static final String TEST_APP_RECEIVER = TEST_APP_PACKAGE + ".TestJobSchedulerReceiver";
42 
43     private final Context mContext;
44     private final int mJobId;
45 
46     /* accesses must be synchronized on itself */
47     private final TestJobStatus mTestJobStatus = new TestJobStatus();
48 
TestAppInterface(Context ctx, int jobId)49     TestAppInterface(Context ctx, int jobId) {
50         mContext = ctx;
51         mJobId = jobId;
52 
53         final IntentFilter intentFilter = new IntentFilter();
54         intentFilter.addAction(ACTION_JOB_STARTED);
55         intentFilter.addAction(ACTION_JOB_STOPPED);
56         mContext.registerReceiver(mReceiver, intentFilter);
57     }
58 
cleanup()59     void cleanup() {
60         final Intent cancelJobsIntent = new Intent(TestJobSchedulerReceiver.ACTION_CANCEL_JOBS);
61         cancelJobsIntent.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_RECEIVER));
62         cancelJobsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
63         mContext.sendBroadcast(cancelJobsIntent);
64         mContext.sendBroadcast(new Intent(TestActivity.ACTION_FINISH_ACTIVITY));
65         mContext.unregisterReceiver(mReceiver);
66         mTestJobStatus.reset();
67     }
68 
scheduleJob(boolean allowWhileIdle, boolean needNetwork)69     void scheduleJob(boolean allowWhileIdle, boolean needNetwork) {
70         final Intent scheduleJobIntent = new Intent(TestJobSchedulerReceiver.ACTION_SCHEDULE_JOB);
71         scheduleJobIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
72         scheduleJobIntent.putExtra(TestJobSchedulerReceiver.EXTRA_JOB_ID_KEY, mJobId);
73         scheduleJobIntent.putExtra(TestJobSchedulerReceiver.EXTRA_ALLOW_IN_IDLE, allowWhileIdle);
74         scheduleJobIntent.putExtra(TestJobSchedulerReceiver.EXTRA_REQUIRE_NETWORK_ANY, needNetwork);
75         scheduleJobIntent.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_RECEIVER));
76         mContext.sendBroadcast(scheduleJobIntent);
77     }
78 
startAndKeepTestActivity()79     void startAndKeepTestActivity() {
80         final Intent testActivity = new Intent();
81         testActivity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
82         testActivity.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_ACTIVITY));
83         mContext.startActivity(testActivity);
84     }
85 
86     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
87         @Override
88         public void onReceive(Context context, Intent intent) {
89             Log.d(TAG, "Received action " + intent.getAction());
90             switch (intent.getAction()) {
91                 case ACTION_JOB_STARTED:
92                 case ACTION_JOB_STOPPED:
93                     final JobParameters params = intent.getParcelableExtra(JOB_PARAMS_EXTRA_KEY);
94                     Log.d(TAG, "JobId: " + params.getJobId());
95                     synchronized (mTestJobStatus) {
96                         mTestJobStatus.running = ACTION_JOB_STARTED.equals(intent.getAction());
97                         mTestJobStatus.jobId = params.getJobId();
98                     }
99                     break;
100             }
101         }
102     };
103 
awaitJobStart(long maxWait)104     boolean awaitJobStart(long maxWait) throws Exception {
105         return waitUntilTrue(maxWait, () -> {
106             synchronized (mTestJobStatus) {
107                 return (mTestJobStatus.jobId == mJobId) && mTestJobStatus.running;
108             }
109         });
110     }
111 
112     boolean awaitJobStop(long maxWait) throws Exception {
113         return waitUntilTrue(maxWait, () -> {
114             synchronized (mTestJobStatus) {
115                 return (mTestJobStatus.jobId == mJobId) && !mTestJobStatus.running;
116             }
117         });
118     }
119 
120     private boolean waitUntilTrue(long maxWait, Condition condition) throws Exception {
121         final long deadLine = SystemClock.uptimeMillis() + maxWait;
122         do {
123             Thread.sleep(500);
124         } while (!condition.isTrue() && SystemClock.uptimeMillis() < deadLine);
125         return condition.isTrue();
126     }
127 
128     private static final class TestJobStatus {
129         int jobId;
130         boolean running;
131 
132         private void reset() {
133             running = false;
134         }
135     }
136 
137     private interface Condition {
138         boolean isTrue() throws Exception;
139     }
140 }
141