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.impl; 18 19 import static androidx.work.impl.utils.PackageManagerHelper.setComponentEnabled; 20 21 import android.content.Context; 22 import android.os.Build; 23 import android.support.annotation.NonNull; 24 import android.support.annotation.RestrictTo; 25 import android.support.annotation.VisibleForTesting; 26 import android.util.Log; 27 28 import androidx.work.Configuration; 29 import androidx.work.impl.background.systemalarm.SystemAlarmScheduler; 30 import androidx.work.impl.background.systemalarm.SystemAlarmService; 31 import androidx.work.impl.background.systemjob.SystemJobScheduler; 32 import androidx.work.impl.background.systemjob.SystemJobService; 33 import androidx.work.impl.model.WorkSpec; 34 import androidx.work.impl.model.WorkSpecDao; 35 36 import java.lang.reflect.InvocationTargetException; 37 import java.util.List; 38 39 /** 40 * Helper methods for {@link Scheduler}s. 41 * 42 * Helps schedule {@link androidx.work.impl.model.WorkSpec}s while enforcing 43 * {@link Scheduler#MAX_SCHEDULER_LIMIT}s. 44 * 45 * @hide 46 */ 47 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 48 public class Schedulers { 49 50 private static final String TAG = "Schedulers"; 51 private static final String FIREBASE_JOB_SCHEDULER_CLASSNAME = 52 "androidx.work.impl.background.firebase.FirebaseJobScheduler"; 53 54 @VisibleForTesting 55 static final String FIREBASE_JOB_SERVICE_CLASSNAME = 56 "androidx.work.impl.background.firebase.FirebaseJobService"; 57 58 /** 59 * Schedules {@link WorkSpec}s while honoring the {@link Scheduler#MAX_SCHEDULER_LIMIT}. 60 * 61 * @param workDatabase The {@link WorkDatabase}. 62 * @param schedulers The {@link List} of {@link Scheduler}s to delegate to. 63 */ schedule( @onNull Configuration configuration, @NonNull WorkDatabase workDatabase, List<Scheduler> schedulers)64 public static void schedule( 65 @NonNull Configuration configuration, 66 @NonNull WorkDatabase workDatabase, 67 List<Scheduler> schedulers) { 68 69 WorkSpecDao workSpecDao = workDatabase.workSpecDao(); 70 List<WorkSpec> eligibleWorkSpecs = 71 workSpecDao.getEligibleWorkForScheduling( 72 configuration.getMaxSchedulerLimit()); 73 scheduleInternal(workDatabase, schedulers, eligibleWorkSpecs); 74 } 75 scheduleInternal( @onNull WorkDatabase workDatabase, List<Scheduler> schedulers, List<WorkSpec> workSpecs)76 private static void scheduleInternal( 77 @NonNull WorkDatabase workDatabase, 78 List<Scheduler> schedulers, 79 List<WorkSpec> workSpecs) { 80 81 if (workSpecs == null || schedulers == null) { 82 return; 83 } 84 85 long now = System.currentTimeMillis(); 86 WorkSpecDao workSpecDao = workDatabase.workSpecDao(); 87 // Mark all the WorkSpecs as scheduled. 88 // Calls to Scheduler#schedule() could potentially result in more schedules 89 // on a separate thread. Therefore, this needs to be done first. 90 workDatabase.beginTransaction(); 91 try { 92 for (WorkSpec workSpec : workSpecs) { 93 workSpecDao.markWorkSpecScheduled(workSpec.id, now); 94 } 95 workDatabase.setTransactionSuccessful(); 96 } finally { 97 workDatabase.endTransaction(); 98 } 99 WorkSpec[] eligibleWorkSpecsArray = workSpecs.toArray(new WorkSpec[0]); 100 // Delegate to the underlying scheduler. 101 for (Scheduler scheduler : schedulers) { 102 scheduler.schedule(eligibleWorkSpecsArray); 103 } 104 } 105 createBestAvailableBackgroundScheduler( @onNull Context context, @NonNull WorkManagerImpl workManager)106 static @NonNull Scheduler createBestAvailableBackgroundScheduler( 107 @NonNull Context context, 108 @NonNull WorkManagerImpl workManager) { 109 110 Scheduler scheduler; 111 boolean enableFirebaseJobService = false; 112 boolean enableSystemAlarmService = false; 113 114 if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) { 115 scheduler = new SystemJobScheduler(context, workManager); 116 setComponentEnabled(context, SystemJobService.class, true); 117 Log.d(TAG, "Created SystemJobScheduler and enabled SystemJobService"); 118 } else { 119 try { 120 scheduler = tryCreateFirebaseJobScheduler(context); 121 enableFirebaseJobService = true; 122 Log.d(TAG, "Created FirebaseJobScheduler"); 123 } catch (Exception e) { 124 // Also catches the exception thrown if Play Services was not found on the device. 125 scheduler = new SystemAlarmScheduler(context); 126 enableSystemAlarmService = true; 127 Log.d(TAG, "Created SystemAlarmScheduler"); 128 } 129 } 130 131 try { 132 Class firebaseJobServiceClass = Class.forName(FIREBASE_JOB_SERVICE_CLASSNAME); 133 setComponentEnabled(context, firebaseJobServiceClass, enableFirebaseJobService); 134 } catch (ClassNotFoundException e) { 135 // Do nothing. 136 } 137 138 setComponentEnabled(context, SystemAlarmService.class, enableSystemAlarmService); 139 140 return scheduler; 141 } 142 143 @NonNull tryCreateFirebaseJobScheduler(@onNull Context context)144 private static Scheduler tryCreateFirebaseJobScheduler(@NonNull Context context) 145 throws ClassNotFoundException, IllegalAccessException, InstantiationException, 146 InvocationTargetException, NoSuchMethodException { 147 Class<?> firebaseJobSchedulerClass = Class.forName(FIREBASE_JOB_SCHEDULER_CLASSNAME); 148 return (Scheduler) firebaseJobSchedulerClass 149 .getConstructor(Context.class) 150 .newInstance(context); 151 } 152 Schedulers()153 private Schedulers() { 154 } 155 } 156