1 /*
2  * Copyright (C) 2021 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 
17 package com.android.server.job;
18 
19 import android.annotation.TargetApi;
20 import android.app.job.JobInfo;
21 import android.app.job.JobScheduler;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.os.DeviceIdleManager;
25 import android.test.AndroidTestCase;
26 
27 import com.android.server.job.MockBiasJobService.TestEnvironment;
28 import com.android.server.job.MockBiasJobService.TestEnvironment.Event;
29 
30 import java.util.ArrayList;
31 
32 @TargetApi(24)
33 public class BiasSchedulingTest extends AndroidTestCase {
34     /** Environment that notifies of JobScheduler callbacks. */
35     private static final TestEnvironment sTestEnvironment = TestEnvironment.getTestEnvironment();
36     /** Handle for the service which receives the execution callbacks from the JobScheduler. */
37     private static ComponentName sJobServiceComponent;
38     private JobScheduler mJobScheduler;
39 
40     // The system overrides the test app bias to be a minimum of FOREGROUND_SERVICE. We can
41     // bypass that override by using a bias of at least bound foreground service.
42     private static final int HIGH_BIAS = JobInfo.BIAS_BOUND_FOREGROUND_SERVICE + 1;
43     private static final int LOW_BIAS = JobInfo.BIAS_BOUND_FOREGROUND_SERVICE;
44 
45     @Override
setUp()46     public void setUp() throws Exception {
47         super.setUp();
48         sTestEnvironment.setUp();
49         sJobServiceComponent = new ComponentName(getContext(), MockBiasJobService.class);
50         mJobScheduler = (JobScheduler) getContext().getSystemService(Context.JOB_SCHEDULER_SERVICE);
51         mJobScheduler.cancelAll();
52         getContext().getSystemService(DeviceIdleManager.class).endIdle("BiasSchedulingTest");
53     }
54 
55     @Override
tearDown()56     public void tearDown() throws Exception {
57         mJobScheduler.cancelAll();
58         super.tearDown();
59     }
60 
testLowerBiasJobPreempted()61     public void testLowerBiasJobPreempted() throws Exception {
62         for (int i = 0; i < JobConcurrencyManager.MAX_CONCURRENCY_LIMIT; ++i) {
63             JobInfo job = new JobInfo.Builder(100 + i, sJobServiceComponent)
64                     .setBias(LOW_BIAS)
65                     .setOverrideDeadline(0)
66                     .build();
67             mJobScheduler.schedule(job);
68         }
69         final int higherBiasJobId = 100 + JobConcurrencyManager.MAX_CONCURRENCY_LIMIT;
70         JobInfo jobHigher = new JobInfo.Builder(higherBiasJobId, sJobServiceComponent)
71                 .setBias(HIGH_BIAS)
72                 .setMinimumLatency(2000)
73                 .setOverrideDeadline(4000)
74                 .build();
75         mJobScheduler.schedule(jobHigher);
76         Thread.sleep(10000);  // Wait for jobHigher to preempt one of the lower bias jobs
77 
78         Event jobHigherExecution = new Event(TestEnvironment.EVENT_START_JOB, higherBiasJobId);
79         ArrayList<Event> executedEvents = sTestEnvironment.getExecutedEvents();
80         boolean wasJobHigherExecuted = executedEvents.contains(jobHigherExecution);
81         boolean wasSomeJobPreempted = false;
82         for (Event event: executedEvents) {
83             if (event.event == TestEnvironment.EVENT_PREEMPT_JOB) {
84                 wasSomeJobPreempted = true;
85                 break;
86             }
87         }
88         assertTrue("No job was preempted.", wasSomeJobPreempted);
89         assertTrue("Lower bias jobs were not preempted.", wasJobHigherExecuted);
90     }
91 
testHigherBiasJobNotPreempted()92     public void testHigherBiasJobNotPreempted() throws Exception {
93         for (int i = 0; i < JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT; ++i) {
94             JobInfo job = new JobInfo.Builder(100 + i, sJobServiceComponent)
95                     .setBias(HIGH_BIAS)
96                     .setOverrideDeadline(0)
97                     .build();
98             mJobScheduler.schedule(job);
99         }
100         final int lowerBiasJobId = 100 + JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT;
101         JobInfo jobLower = new JobInfo.Builder(lowerBiasJobId, sJobServiceComponent)
102                 .setBias(LOW_BIAS)
103                 .setMinimumLatency(2000)
104                 .setOverrideDeadline(3000)
105                 .build();
106         mJobScheduler.schedule(jobLower);
107         Thread.sleep(10000);
108 
109         Event jobLowerExecution = new Event(TestEnvironment.EVENT_START_JOB, lowerBiasJobId);
110         boolean wasLowerExecuted = sTestEnvironment.getExecutedEvents().contains(jobLowerExecution);
111         assertFalse("Higher bias job was preempted.", wasLowerExecuted);
112     }
113 }
114