| /* |
| * Copyright 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package androidx.work.impl.background.systemjob; |
| |
| import static android.support.annotation.VisibleForTesting.PACKAGE_PRIVATE; |
| |
| import android.app.job.JobInfo; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.os.Build; |
| import android.os.PersistableBundle; |
| import android.support.annotation.NonNull; |
| import android.support.annotation.RequiresApi; |
| import android.support.annotation.VisibleForTesting; |
| |
| import androidx.work.BackoffPolicy; |
| import androidx.work.Constraints; |
| import androidx.work.ContentUriTriggers; |
| import androidx.work.NetworkType; |
| import androidx.work.impl.WorkManagerImpl; |
| import androidx.work.impl.logger.Logger; |
| import androidx.work.impl.model.WorkSpec; |
| import androidx.work.impl.utils.IdGenerator; |
| |
| /** |
| * Converts a {@link WorkSpec} into a JobInfo. |
| */ |
| @RequiresApi(api = WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) |
| class SystemJobInfoConverter { |
| private static final String TAG = "SystemJobInfoConverter"; |
| |
| static final String EXTRA_WORK_SPEC_ID = "EXTRA_WORK_SPEC_ID"; |
| static final String EXTRA_IS_PERIODIC = "EXTRA_IS_PERIODIC"; |
| |
| private final ComponentName mWorkServiceComponent; |
| private final IdGenerator mIdGenerator; |
| |
| /** |
| * Constructs a {@link IdGenerator}. |
| * |
| * @param context A non-null {@link Context}. |
| */ |
| SystemJobInfoConverter(@NonNull Context context) { |
| this(context, new IdGenerator(context)); |
| } |
| |
| @VisibleForTesting(otherwise = PACKAGE_PRIVATE) |
| SystemJobInfoConverter(@NonNull Context context, IdGenerator idGenerator) { |
| Context appContext = context.getApplicationContext(); |
| mWorkServiceComponent = new ComponentName(appContext, SystemJobService.class); |
| mIdGenerator = idGenerator; |
| } |
| |
| /** |
| * Converts a {@link WorkSpec} into a {@link JobInfo}. |
| * |
| * Note: All {@link JobInfo} are set to persist on reboot. |
| * |
| * @param workSpec The {@link WorkSpec} to convert |
| * @return The {@link JobInfo} representing the same information as the {@link WorkSpec} |
| */ |
| JobInfo convert(WorkSpec workSpec) { |
| Constraints constraints = workSpec.getConstraints(); |
| int jobId = mIdGenerator.nextJobSchedulerId(); |
| // TODO(janclarin): Support newer required network types if unsupported by API version. |
| int jobInfoNetworkType = convertNetworkType(constraints.getRequiredNetworkType()); |
| PersistableBundle extras = new PersistableBundle(); |
| extras.putString(EXTRA_WORK_SPEC_ID, workSpec.getId()); |
| extras.putBoolean(EXTRA_IS_PERIODIC, workSpec.isPeriodic()); |
| JobInfo.Builder builder = new JobInfo.Builder(jobId, mWorkServiceComponent) |
| .setRequiredNetworkType(jobInfoNetworkType) |
| .setRequiresCharging(constraints.requiresCharging()) |
| .setRequiresDeviceIdle(constraints.requiresDeviceIdle()) |
| .setExtras(extras); |
| |
| if (!constraints.requiresDeviceIdle()) { |
| // Device Idle and Backoff Criteria cannot be set together |
| int backoffPolicy = workSpec.getBackoffPolicy() == BackoffPolicy.LINEAR |
| ? JobInfo.BACKOFF_POLICY_LINEAR : JobInfo.BACKOFF_POLICY_EXPONENTIAL; |
| builder.setBackoffCriteria(workSpec.getBackoffDelayDuration(), backoffPolicy); |
| } |
| |
| if (workSpec.isPeriodic()) { |
| if (Build.VERSION.SDK_INT >= 24) { |
| builder.setPeriodic(workSpec.getIntervalDuration(), workSpec.getFlexDuration()); |
| } else { |
| Logger.debug(TAG, |
| "Flex duration is currently not supported before API 24. Ignoring."); |
| builder.setPeriodic(workSpec.getIntervalDuration()); |
| } |
| } else { |
| // Even if a Work has no constraints, setMinimumLatency(0) still needs to be called due |
| // to an issue in JobInfo.Builder#build and JobInfo with no constraints. See b/67716867. |
| builder.setMinimumLatency(workSpec.getInitialDelay()); |
| } |
| |
| if (Build.VERSION.SDK_INT >= 24 && constraints.hasContentUriTriggers()) { |
| for (ContentUriTriggers.Trigger trigger : constraints.getContentUriTriggers()) { |
| builder.addTriggerContentUri(convertContentUriTrigger(trigger)); |
| } |
| } else { |
| // Jobs with Content Uri Triggers cannot be persisted |
| builder.setPersisted(true); |
| } |
| |
| // TODO(janclarin): Support requires[Battery|Storage]NotLow for versions older than 26. |
| if (Build.VERSION.SDK_INT >= 26) { |
| builder.setRequiresBatteryNotLow(constraints.requiresBatteryNotLow()); |
| builder.setRequiresStorageNotLow(constraints.requiresStorageNotLow()); |
| } |
| return builder.build(); |
| } |
| |
| @RequiresApi(24) |
| private static JobInfo.TriggerContentUri convertContentUriTrigger( |
| ContentUriTriggers.Trigger trigger) { |
| int flag = trigger.shouldTriggerForDescendants() |
| ? JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS : 0; |
| return new JobInfo.TriggerContentUri(trigger.getUri(), flag); |
| } |
| |
| /** |
| * Converts {@link NetworkType} into {@link JobInfo}'s network values. |
| * |
| * @param networkType The {@link NetworkType} network type |
| * @return The {@link JobInfo} network type |
| */ |
| static int convertNetworkType(NetworkType networkType) { |
| switch(networkType) { |
| case NOT_REQUIRED: |
| return JobInfo.NETWORK_TYPE_NONE; |
| case CONNECTED: |
| return JobInfo.NETWORK_TYPE_ANY; |
| case UNMETERED: |
| return JobInfo.NETWORK_TYPE_UNMETERED; |
| case NOT_ROAMING: |
| if (Build.VERSION.SDK_INT >= 24) { |
| return JobInfo.NETWORK_TYPE_NOT_ROAMING; |
| } |
| break; |
| case METERED: |
| if (Build.VERSION.SDK_INT >= 26) { |
| return JobInfo.NETWORK_TYPE_METERED; |
| } |
| break; |
| } |
| Logger.debug(TAG, "API version too low. Cannot convert network type value %s", networkType); |
| return JobInfo.NETWORK_TYPE_ANY; |
| } |
| } |