Home | History | Annotate | Download | only in impl
      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      */
     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 
     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 
    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
    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 
    153     private Schedulers() {
    154     }
    155 }
    156