1 /*
2  * Copyright 2018 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 androidx.work;
18 
19 import static androidx.work.impl.Scheduler.MAX_SCHEDULER_LIMIT;
20 
21 import android.os.Build;
22 import android.support.annotation.NonNull;
23 import android.support.annotation.RestrictTo;
24 
25 import androidx.work.impl.utils.IdGenerator;
26 
27 import java.util.concurrent.Executor;
28 import java.util.concurrent.Executors;
29 
30 /**
31  * Configuration for {@link WorkManager}.
32  */
33 public final class Configuration {
34 
35     /**
36      * The minimum number of system requests which can be enqueued by {@link WorkManager}
37      * when using {@link android.app.job.JobScheduler} or {@link android.app.AlarmManager}.
38      */
39     public static final int MIN_SCHEDULER_LIMIT = 20;
40 
41     private final Executor mExecutor;
42     private final int mMinJobSchedulerId;
43     private final int mMaxJobSchedulerId;
44     private final int mMaxSchedulerLimit;
45 
Configuration(@onNull Configuration.Builder builder)46     private Configuration(@NonNull Configuration.Builder builder) {
47         if (builder.mExecutor == null) {
48             mExecutor = createDefaultExecutor();
49         } else {
50             mExecutor = builder.mExecutor;
51         }
52         mMinJobSchedulerId = builder.mMinJobSchedulerId;
53         mMaxJobSchedulerId = builder.mMaxJobSchedulerId;
54         mMaxSchedulerLimit = builder.mMaxSchedulerLimit;
55     }
56 
57     /**
58      * @return The {@link Executor} used by {@link WorkManager} to execute {@link Worker}s.
59      */
getExecutor()60     public @NonNull Executor getExecutor() {
61         return mExecutor;
62     }
63 
64     /**
65      * @return The first valid id (inclusive) used by {@link WorkManager} when
66      * creating new instances of {@link android.app.job.JobInfo}s.
67      *
68      * If the current {@code jobId} goes beyond the bounds of the defined range of
69      * ({@link Configuration.Builder#getMinJobSchedulerID()},
70      *  {@link Configuration.Builder#getMaxJobSchedulerID()}), it is reset to
71      *  ({@link Configuration.Builder#getMinJobSchedulerID()}).
72      */
getMinJobSchedulerID()73     public int getMinJobSchedulerID() {
74         return mMinJobSchedulerId;
75     }
76 
77     /**
78      * @return The last valid id (inclusive) used by {@link WorkManager} when
79      * creating new instances of {@link android.app.job.JobInfo}s.
80      *
81      * If the current {@code jobId} goes beyond the bounds of the defined range of
82      * ({@link Configuration.Builder#getMinJobSchedulerID()},
83      *  {@link Configuration.Builder#getMaxJobSchedulerID()}), it is reset to
84      *  ({@link Configuration.Builder#getMinJobSchedulerID()}).
85      */
getMaxJobSchedulerID()86     public int getMaxJobSchedulerID() {
87         return mMaxJobSchedulerId;
88     }
89 
90     /**
91      * @return The maximum number of system requests which can be enqueued by {@link WorkManager}
92      * when using {@link android.app.job.JobScheduler} or {@link android.app.AlarmManager}.
93      *
94      * @hide
95      */
96     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
getMaxSchedulerLimit()97     public int getMaxSchedulerLimit() {
98         // We double schedule jobs in SDK 23. So use half the number of max slots specified.
99         if (Build.VERSION.SDK_INT == 23) {
100             return mMaxSchedulerLimit / 2;
101         } else {
102             return mMaxSchedulerLimit;
103         }
104     }
105 
createDefaultExecutor()106     private Executor createDefaultExecutor() {
107         return Executors.newFixedThreadPool(
108                 // This value is the same as the core pool size for AsyncTask#THREAD_POOL_EXECUTOR.
109                 Math.max(2, Math.min(Runtime.getRuntime().availableProcessors() - 1, 4)));
110     }
111 
112     /**
113      * A Builder for {@link Configuration}.
114      */
115     public static final class Builder {
116 
117         int mMinJobSchedulerId = IdGenerator.INITIAL_ID;
118         int mMaxJobSchedulerId = Integer.MAX_VALUE;
119         int mMaxSchedulerLimit = MIN_SCHEDULER_LIMIT;
120         Executor mExecutor;
121 
122         /**
123          * Specifies a custom {@link Executor} for WorkManager.
124          *
125          * @param executor An {@link Executor} for processing work
126          * @return This {@link Builder} instance
127          */
setExecutor(@onNull Executor executor)128         public Builder setExecutor(@NonNull Executor executor) {
129             mExecutor = executor;
130             return this;
131         }
132 
133         /**
134          * Specifies the range of {@link android.app.job.JobInfo} IDs that can be used by
135          * {@link WorkManager}. {@link WorkManager} needs a range of at least {@code 1000} IDs.
136          *
137          * @param minJobSchedulerId The first valid {@link android.app.job.JobInfo} ID inclusive.
138          * @param maxJobSchedulerId The last valid {@link android.app.job.JobInfo} ID inclusive.
139          * @return This {@link Builder} instance
140          * @throws IllegalArgumentException when the size of the range is < 1000
141          */
setJobSchedulerJobIdRange(int minJobSchedulerId, int maxJobSchedulerId)142         public Builder setJobSchedulerJobIdRange(int minJobSchedulerId, int maxJobSchedulerId) {
143             if ((maxJobSchedulerId - minJobSchedulerId) < 1000) {
144                 throw new IllegalArgumentException(
145                         "WorkManager needs a range of at least 1000 job ids.");
146             }
147 
148             mMinJobSchedulerId = minJobSchedulerId;
149             mMaxJobSchedulerId = maxJobSchedulerId;
150             return this;
151         }
152 
153         /**
154          * Specifies the maximum number of system requests made by {@link WorkManager}
155          * when using {@link android.app.job.JobScheduler} or {@link android.app.AlarmManager}.
156          * When the application exceeds this limit {@link WorkManager} maintains an internal queue
157          * of {@link WorkRequest}s, and enqueues when slots become free.
158          *
159          * {@link WorkManager} requires a minimum of {@link Configuration#MIN_SCHEDULER_LIMIT}
160          * slots. The total number of slots also cannot exceed {@code 100} which is
161          * the {@link android.app.job.JobScheduler} limit.
162          *
163          * @param maxSchedulerLimit The total number of jobs which can be enqueued by
164          *                                {@link WorkManager} when using
165          *                                {@link android.app.job.JobScheduler}.
166          * @return This {@link Builder} instance
167          * @throws IllegalArgumentException when the number of jobs <
168          *                                  {@link Configuration#MIN_SCHEDULER_LIMIT}
169          */
setMaxSchedulerLimit(int maxSchedulerLimit)170         public Builder setMaxSchedulerLimit(int maxSchedulerLimit) {
171             if (maxSchedulerLimit < MIN_SCHEDULER_LIMIT) {
172                 throw new IllegalArgumentException(
173                         "WorkManager needs to be able to schedule at least 20 jobs in "
174                                 + "JobScheduler.");
175             }
176             mMaxSchedulerLimit = Math.min(maxSchedulerLimit, MAX_SCHEDULER_LIMIT);
177             return this;
178         }
179 
180         /**
181          * Specifies a custom {@link Executor} for WorkManager.
182          *
183          * @param executor An {@link Executor} for processing work
184          * @return This {@link Builder} instance
185          * @deprecated Use the {@link Configuration.Builder#setExecutor(Executor)} method instead
186          */
187         @Deprecated
withExecutor(@onNull Executor executor)188         public Builder withExecutor(@NonNull Executor executor) {
189             mExecutor = executor;
190             return this;
191         }
192 
193         /**
194          * Builds a {@link Configuration} object.
195          *
196          * @return A {@link Configuration} object with this {@link Builder}'s parameters.
197          */
build()198         public Configuration build() {
199             return new Configuration(this);
200         }
201     }
202 }
203