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