1 /* 2 * Copyright 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 17 package androidx.work.impl.background.systemjob; 18 19 import android.annotation.TargetApi; 20 import android.app.job.JobParameters; 21 import android.app.job.JobService; 22 import android.os.Build; 23 import android.os.PersistableBundle; 24 import android.support.annotation.NonNull; 25 import android.support.annotation.RestrictTo; 26 import android.text.TextUtils; 27 import android.util.Log; 28 29 import androidx.work.impl.ExecutionListener; 30 import androidx.work.impl.Extras; 31 import androidx.work.impl.WorkManagerImpl; 32 33 import java.util.HashMap; 34 import java.util.Map; 35 36 /** 37 * Service invoked by {@link android.app.job.JobScheduler} to run work tasks. 38 * 39 * @hide 40 */ 41 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 42 @TargetApi(WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) 43 public class SystemJobService extends JobService implements ExecutionListener { 44 private static final String TAG = "SystemJobService"; 45 private WorkManagerImpl mWorkManagerImpl; 46 private final Map<String, JobParameters> mJobParameters = new HashMap<>(); 47 48 @Override onCreate()49 public void onCreate() { 50 super.onCreate(); 51 mWorkManagerImpl = WorkManagerImpl.getInstance(); 52 mWorkManagerImpl.getProcessor().addExecutionListener(this); 53 } 54 55 @Override onDestroy()56 public void onDestroy() { 57 super.onDestroy(); 58 mWorkManagerImpl.getProcessor().removeExecutionListener(this); 59 } 60 61 @Override onStartJob(JobParameters params)62 public boolean onStartJob(JobParameters params) { 63 PersistableBundle extras = params.getExtras(); 64 String workSpecId = extras.getString(SystemJobInfoConverter.EXTRA_WORK_SPEC_ID); 65 if (TextUtils.isEmpty(workSpecId)) { 66 Log.e(TAG, "WorkSpec id not found!"); 67 return false; 68 } 69 70 synchronized (mJobParameters) { 71 if (mJobParameters.containsKey(workSpecId)) { 72 // This condition may happen due to our workaround for an undesired behavior in API 73 // 23. See the documentation in {@link SystemJobScheduler#schedule}. 74 Log.d(TAG, String.format( 75 "Job is already being executed by SystemJobService: %s", workSpecId)); 76 return false; 77 } 78 79 boolean isPeriodic = extras.getBoolean(SystemJobInfoConverter.EXTRA_IS_PERIODIC, false); 80 if (isPeriodic && params.isOverrideDeadlineExpired()) { 81 Log.d(TAG, String.format( 82 "Override deadline expired for id %s. Retry requested", workSpecId)); 83 jobFinished(params, true); 84 return false; 85 } 86 87 Log.d(TAG, String.format("onStartJob for %s", workSpecId)); 88 mJobParameters.put(workSpecId, params); 89 } 90 91 Extras.RuntimeExtras runtimeExtras = null; 92 if (Build.VERSION.SDK_INT >= 24) { 93 runtimeExtras = new Extras.RuntimeExtras(); 94 if (params.getTriggeredContentUris() != null 95 || params.getTriggeredContentAuthorities() != null) { 96 runtimeExtras.triggeredContentUris = params.getTriggeredContentUris(); 97 runtimeExtras.triggeredContentAuthorities = 98 params.getTriggeredContentAuthorities(); 99 } 100 101 if (Build.VERSION.SDK_INT >= 28) { 102 runtimeExtras.network = params.getNetwork(); 103 } 104 } 105 106 mWorkManagerImpl.startWork(workSpecId, runtimeExtras); 107 return true; 108 } 109 110 @Override onStopJob(JobParameters params)111 public boolean onStopJob(JobParameters params) { 112 String workSpecId = params.getExtras().getString(SystemJobInfoConverter.EXTRA_WORK_SPEC_ID); 113 if (TextUtils.isEmpty(workSpecId)) { 114 Log.e(TAG, "WorkSpec id not found!"); 115 return false; 116 } 117 118 Log.d(TAG, String.format("onStopJob for %s", workSpecId)); 119 120 synchronized (mJobParameters) { 121 mJobParameters.remove(workSpecId); 122 } 123 mWorkManagerImpl.stopWork(workSpecId); 124 return !mWorkManagerImpl.getProcessor().isCancelled(workSpecId); 125 } 126 127 @Override onExecuted( @onNull String workSpecId, boolean isSuccessful, boolean needsReschedule)128 public void onExecuted( 129 @NonNull String workSpecId, 130 boolean isSuccessful, 131 boolean needsReschedule) { 132 Log.d(TAG, String.format("%s executed on JobScheduler", workSpecId)); 133 JobParameters parameters; 134 synchronized (mJobParameters) { 135 parameters = mJobParameters.get(workSpecId); 136 } 137 if (parameters != null) { 138 jobFinished(parameters, needsReschedule); 139 } 140 } 141 } 142