blob: 70d22745b3dc07cc99a11c244dd7d0d443001468 [file] [log] [blame]
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -07001/*
2 * Copyright (C) 2017 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
17package com.android.server.job;
18
19import static com.android.servicestests.apps.jobtestapp.TestJobService.ACTION_JOB_STARTED;
20import static com.android.servicestests.apps.jobtestapp.TestJobService.ACTION_JOB_STOPPED;
21import static com.android.servicestests.apps.jobtestapp.TestJobService.JOB_PARAMS_EXTRA_KEY;
22
23import static org.junit.Assert.assertFalse;
24import static org.junit.Assert.assertTrue;
25
26import android.app.ActivityManager;
27import android.app.AppOpsManager;
28import android.app.IActivityManager;
29import android.app.job.JobParameters;
30import android.content.BroadcastReceiver;
31import android.content.ComponentName;
32import android.content.Context;
33import android.content.Intent;
34import android.content.IntentFilter;
35import android.content.pm.PackageManager;
36import android.os.IDeviceIdleController;
37import android.os.RemoteException;
38import android.os.ServiceManager;
39import android.os.SystemClock;
40import android.os.UserHandle;
41import android.provider.Settings;
42import android.support.test.InstrumentationRegistry;
43import android.support.test.filters.LargeTest;
44import android.support.test.runner.AndroidJUnit4;
45import android.util.Log;
46
47import com.android.servicestests.apps.jobtestapp.TestJobActivity;
48
49import org.junit.After;
50import org.junit.Before;
51import org.junit.Test;
52import org.junit.runner.RunWith;
53
54/**
55 * TODO: Also add a test for temp power whitelist
56 * Tests that background restrictions on jobs work as expected.
57 * This test requires test-apps/JobTestApp to be installed on the device.
58 * To run this test from root of checkout:
59 * <pre>
60 * mmm -j32 frameworks/base/services/tests/servicestests/
61 * adb install out/target/product/marlin/data/app/JobTestApp/JobTestApp.apk
62 * adb install out/target/product/marlin/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
63 * adb shell am instrument -e class 'com.android.server.job.BackgroundRestrictionsTest' -w \
64 * 'com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner'
65 * </pre>
66 */
67@RunWith(AndroidJUnit4.class)
68@LargeTest
69public class BackgroundRestrictionsTest {
70 private static final String TAG = BackgroundRestrictionsTest.class.getSimpleName();
71 private static final String TEST_APP_PACKAGE = "com.android.servicestests.apps.jobtestapp";
72 private static final String TEST_APP_ACTIVITY = TEST_APP_PACKAGE + ".TestJobActivity";
73 private static final long POLL_INTERVAL = 2000;
74 private static final long DEFAULT_WAIT_TIMEOUT = 5000;
75
76 private Context mContext;
77 private AppOpsManager mAppOpsManager;
78 private IDeviceIdleController mDeviceIdleController;
79 private IActivityManager mIActivityManager;
80 private int mTestJobId;
81 private int mTestPackageUid;
82 /* accesses must be synchronized on itself */
83 private final TestJobStatus mTestJobStatus = new TestJobStatus();
84 private final BroadcastReceiver mJobStateChangeReceiver = new BroadcastReceiver() {
85 @Override
86 public void onReceive(Context context, Intent intent) {
87 final JobParameters params = intent.getParcelableExtra(JOB_PARAMS_EXTRA_KEY);
88 Log.d(TAG, "Received action " + intent.getAction());
89 synchronized (mTestJobStatus) {
90 switch (intent.getAction()) {
91 case ACTION_JOB_STARTED:
92 mTestJobStatus.running = true;
93 mTestJobStatus.jobId = params.getJobId();
94 mTestJobStatus.stopReason = JobParameters.REASON_CANCELED;
95 break;
96 case ACTION_JOB_STOPPED:
97 mTestJobStatus.running = false;
98 mTestJobStatus.jobId = params.getJobId();
99 mTestJobStatus.stopReason = params.getStopReason();
100 break;
101 }
102 }
103 }
104 };
105
106 @Before
107 public void setUp() throws Exception {
108 mContext = InstrumentationRegistry.getTargetContext();
109 mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
110 mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
111 ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
112 mIActivityManager = ActivityManager.getService();
113 mTestPackageUid = mContext.getPackageManager().getPackageUid(TEST_APP_PACKAGE, 0);
114 mTestJobId = (int) (SystemClock.uptimeMillis() / 1000);
115 mTestJobStatus.reset();
116 final IntentFilter intentFilter = new IntentFilter();
117 intentFilter.addAction(ACTION_JOB_STARTED);
118 intentFilter.addAction(ACTION_JOB_STOPPED);
119 mContext.registerReceiver(mJobStateChangeReceiver, intentFilter);
120 setGlobalSwitch(true);
121 setAppOpsModeAllowed(true);
122 setPowerWhiteListed(false);
123 }
124
125 private void scheduleAndAssertJobStarted() throws Exception {
126 final Intent scheduleJobIntent = new Intent(TestJobActivity.ACTION_START_JOB);
127 scheduleJobIntent.putExtra(TestJobActivity.EXTRA_JOB_ID_KEY, mTestJobId);
128 scheduleJobIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
129 scheduleJobIntent.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_ACTIVITY));
130 mContext.startActivity(scheduleJobIntent);
131 Thread.sleep(TestJobActivity.JOB_MINIMUM_LATENCY);
132 assertTrue("Job did not start after scheduling", awaitJobStart(DEFAULT_WAIT_TIMEOUT));
133 }
134
135 @Test
136 public void testGlobalSwitch() throws Exception {
137 setGlobalSwitch(false); // Job should not stop now.
138 scheduleAndAssertJobStarted();
139 setAppOpsModeAllowed(false);
140 mIActivityManager.makePackageIdle(TEST_APP_PACKAGE, UserHandle.USER_CURRENT);
141 assertFalse("Job stopped even when feature switch is off",
142 awaitJobStop(DEFAULT_WAIT_TIMEOUT));
143 }
144
145 @Test
146 public void testPowerWhiteList() throws Exception {
147 scheduleAndAssertJobStarted();
148 setAppOpsModeAllowed(false);
149 mIActivityManager.makePackageIdle(TEST_APP_PACKAGE, UserHandle.USER_CURRENT);
150 assertTrue("Job did not stop after making idle", awaitJobStop(DEFAULT_WAIT_TIMEOUT));
151 setPowerWhiteListed(true);
152 Thread.sleep(TestJobActivity.JOB_INITIAL_BACKOFF);
153 assertTrue("Job did not start after adding to power whitelist",
154 awaitJobStart(DEFAULT_WAIT_TIMEOUT));
155 setPowerWhiteListed(false);
156 assertTrue("Job did not stop after removing from power whitelist",
157 awaitJobStop(DEFAULT_WAIT_TIMEOUT));
158 }
159
160 @After
161 public void tearDown() throws Exception {
162 Intent cancelJobsIntent = new Intent(TestJobActivity.ACTION_CANCEL_JOBS);
163 cancelJobsIntent.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_ACTIVITY));
164 cancelJobsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
165 mContext.startActivity(cancelJobsIntent);
166 mContext.unregisterReceiver(mJobStateChangeReceiver);
167 setGlobalSwitch(false);
168 setAppOpsModeAllowed(true);
169 setPowerWhiteListed(false);
170 }
171
172 private void setGlobalSwitch(boolean enabled) {
173 Settings.Global.putString(mContext.getContentResolver(),
174 Settings.Global.JOB_SCHEDULER_CONSTANTS, "bg_jobs_restricted=" + enabled);
175 }
176
177 private void setPowerWhiteListed(boolean whitelist) throws RemoteException {
178 if (whitelist) {
179 mDeviceIdleController.addPowerSaveWhitelistApp(TEST_APP_PACKAGE);
180 } else {
181 mDeviceIdleController.removePowerSaveWhitelistApp(TEST_APP_PACKAGE);
182 }
183 }
184
185 private void setAppOpsModeAllowed(boolean allow) throws PackageManager.NameNotFoundException {
186 mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mTestPackageUid,
187 TEST_APP_PACKAGE, allow ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
188 }
189
190 private boolean awaitJobStart(long timeout) throws InterruptedException {
191 return waitUntilTrue(timeout, () -> {
192 synchronized (mTestJobStatus) {
193 return (mTestJobStatus.jobId == mTestJobId) && mTestJobStatus.running;
194 }
195 });
196 }
197
198 private boolean awaitJobStop(long timeout) throws InterruptedException {
199 return waitUntilTrue(timeout, () -> {
200 synchronized (mTestJobStatus) {
201 return (mTestJobStatus.jobId == mTestJobId) && !mTestJobStatus.running &&
202 mTestJobStatus.stopReason == JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED;
203 }
204 });
205 }
206
207 private boolean waitUntilTrue(long timeout, Condition condition) throws InterruptedException {
208 final long deadLine = SystemClock.uptimeMillis() + timeout;
209 do {
210 Thread.sleep(POLL_INTERVAL);
211 } while (!condition.isTrue() && SystemClock.uptimeMillis() < deadLine);
212 return condition.isTrue();
213 }
214
215 private static final class TestJobStatus {
216 int jobId;
217 int stopReason;
218 boolean running;
219 private void reset() {
220 running = false;
221 stopReason = jobId = 0;
222 }
223 }
224
225 private interface Condition {
226 boolean isTrue();
227 }
228}