19f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar/*
29f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar * Copyright 2018 The Android Open Source Project
39f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar *
49f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar * Licensed under the Apache License, Version 2.0 (the "License");
59f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar * you may not use this file except in compliance with the License.
69f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar * You may obtain a copy of the License at
79f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar *
89f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar *      http://www.apache.org/licenses/LICENSE-2.0
99f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar *
109f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar * Unless required by applicable law or agreed to in writing, software
119f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar * distributed under the License is distributed on an "AS IS" BASIS,
129f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar * See the License for the specific language governing permissions and
149f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar * limitations under the License.
159f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar */
169f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar
179f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumarpackage androidx.work.impl;
189f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar
197f44b37e7640e4b91656e024d1754fa7a062a833Sumir Katariaimport static androidx.work.impl.utils.PackageManagerHelper.setComponentEnabled;
207f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria
217f44b37e7640e4b91656e024d1754fa7a062a833Sumir Katariaimport android.content.Context;
227f44b37e7640e4b91656e024d1754fa7a062a833Sumir Katariaimport android.os.Build;
239f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumarimport android.support.annotation.NonNull;
249f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumarimport android.support.annotation.RestrictTo;
257f44b37e7640e4b91656e024d1754fa7a062a833Sumir Katariaimport android.support.annotation.VisibleForTesting;
26697d6a4a3797bc71d0dd8685937a318e9934066bRahul Ravikumarimport android.util.Log;
279f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar
28e326592ec414dfe4c002e2840d9fc4aef0ee8747Rahul Ravikumarimport androidx.work.Configuration;
297f44b37e7640e4b91656e024d1754fa7a062a833Sumir Katariaimport androidx.work.impl.background.systemalarm.SystemAlarmScheduler;
307f44b37e7640e4b91656e024d1754fa7a062a833Sumir Katariaimport androidx.work.impl.background.systemalarm.SystemAlarmService;
317f44b37e7640e4b91656e024d1754fa7a062a833Sumir Katariaimport androidx.work.impl.background.systemjob.SystemJobScheduler;
327f44b37e7640e4b91656e024d1754fa7a062a833Sumir Katariaimport androidx.work.impl.background.systemjob.SystemJobService;
339f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumarimport androidx.work.impl.model.WorkSpec;
349f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumarimport androidx.work.impl.model.WorkSpecDao;
359f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar
367f44b37e7640e4b91656e024d1754fa7a062a833Sumir Katariaimport java.lang.reflect.InvocationTargetException;
379f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumarimport java.util.List;
389f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar
399f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar/**
407f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria * Helper methods for {@link Scheduler}s.
417f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria *
429f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar * Helps schedule {@link androidx.work.impl.model.WorkSpec}s while enforcing
439f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar * {@link Scheduler#MAX_SCHEDULER_LIMIT}s.
449f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar *
459f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar * @hide
469f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar */
479f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
489f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumarpublic class Schedulers {
499f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar
507f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria    private static final String TAG = "Schedulers";
517f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria    private static final String FIREBASE_JOB_SCHEDULER_CLASSNAME =
527f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria            "androidx.work.impl.background.firebase.FirebaseJobScheduler";
537f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria
547f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria    @VisibleForTesting
557f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria    static final String FIREBASE_JOB_SERVICE_CLASSNAME =
567f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria            "androidx.work.impl.background.firebase.FirebaseJobService";
577f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria
589f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar    /**
599f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar     * Schedules {@link WorkSpec}s while honoring the {@link Scheduler#MAX_SCHEDULER_LIMIT}.
609f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar     *
619f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar     * @param workDatabase The {@link WorkDatabase}.
629f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar     * @param schedulers   The {@link List} of {@link Scheduler}s to delegate to.
639f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar     */
649f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar    public static void schedule(
65e326592ec414dfe4c002e2840d9fc4aef0ee8747Rahul Ravikumar            @NonNull Configuration configuration,
669f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar            @NonNull WorkDatabase workDatabase,
679f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar            List<Scheduler> schedulers) {
689f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar
699f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar        WorkSpecDao workSpecDao = workDatabase.workSpecDao();
70e326592ec414dfe4c002e2840d9fc4aef0ee8747Rahul Ravikumar        List<WorkSpec> eligibleWorkSpecs =
71e326592ec414dfe4c002e2840d9fc4aef0ee8747Rahul Ravikumar                workSpecDao.getEligibleWorkForScheduling(
72e326592ec414dfe4c002e2840d9fc4aef0ee8747Rahul Ravikumar                        configuration.getMaxSchedulerLimit());
739f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar        scheduleInternal(workDatabase, schedulers, eligibleWorkSpecs);
749f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar    }
759f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar
769f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar    private static void scheduleInternal(
779f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar            @NonNull WorkDatabase workDatabase,
789f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar            List<Scheduler> schedulers,
799f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar            List<WorkSpec> workSpecs) {
809f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar
819f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar        if (workSpecs == null || schedulers == null) {
829f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar            return;
839f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar        }
849f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar
859f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar        long now = System.currentTimeMillis();
869f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar        WorkSpecDao workSpecDao = workDatabase.workSpecDao();
879f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar        // Mark all the WorkSpecs as scheduled.
889f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar        // Calls to Scheduler#schedule() could potentially result in more schedules
899f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar        // on a separate thread. Therefore, this needs to be done first.
909f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar        workDatabase.beginTransaction();
919f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar        try {
929f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar            for (WorkSpec workSpec : workSpecs) {
939f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar                workSpecDao.markWorkSpecScheduled(workSpec.id, now);
949f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar            }
959f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar            workDatabase.setTransactionSuccessful();
969f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar        } finally {
979f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar            workDatabase.endTransaction();
989f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar        }
999f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar        WorkSpec[] eligibleWorkSpecsArray = workSpecs.toArray(new WorkSpec[0]);
1009f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar        // Delegate to the underlying scheduler.
1019f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar        for (Scheduler scheduler : schedulers) {
1029f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar            scheduler.schedule(eligibleWorkSpecsArray);
1039f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar        }
1049f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar    }
1057f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria
10622a8129c2b3313100f851460f7da9e56ca98bd8fRahul Ravikumar    static @NonNull Scheduler createBestAvailableBackgroundScheduler(
10722a8129c2b3313100f851460f7da9e56ca98bd8fRahul Ravikumar            @NonNull Context context,
108494b77cd228de249649440c6210bbae1ddf33d76Rahul Ravikumar            @NonNull WorkManagerImpl workManager) {
10922a8129c2b3313100f851460f7da9e56ca98bd8fRahul Ravikumar
1107f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria        Scheduler scheduler;
1117f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria        boolean enableFirebaseJobService = false;
1127f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria        boolean enableSystemAlarmService = false;
1137f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria
1147f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria        if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) {
115494b77cd228de249649440c6210bbae1ddf33d76Rahul Ravikumar            scheduler = new SystemJobScheduler(context, workManager);
1167f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria            setComponentEnabled(context, SystemJobService.class, true);
117697d6a4a3797bc71d0dd8685937a318e9934066bRahul Ravikumar            Log.d(TAG, "Created SystemJobScheduler and enabled SystemJobService");
1187f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria        } else {
1197f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria            try {
1207f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria                scheduler = tryCreateFirebaseJobScheduler(context);
1217f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria                enableFirebaseJobService = true;
122697d6a4a3797bc71d0dd8685937a318e9934066bRahul Ravikumar                Log.d(TAG, "Created FirebaseJobScheduler");
1237f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria            } catch (Exception e) {
1247f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria                // Also catches the exception thrown if Play Services was not found on the device.
1257f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria                scheduler = new SystemAlarmScheduler(context);
1267f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria                enableSystemAlarmService = true;
127697d6a4a3797bc71d0dd8685937a318e9934066bRahul Ravikumar                Log.d(TAG, "Created SystemAlarmScheduler");
1287f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria            }
1297f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria        }
1307f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria
1316aa349fecb357634a176b8ca3343231eefcee249Sumir Kataria        try {
1326aa349fecb357634a176b8ca3343231eefcee249Sumir Kataria            Class firebaseJobServiceClass = Class.forName(FIREBASE_JOB_SERVICE_CLASSNAME);
1336aa349fecb357634a176b8ca3343231eefcee249Sumir Kataria            setComponentEnabled(context, firebaseJobServiceClass, enableFirebaseJobService);
1346aa349fecb357634a176b8ca3343231eefcee249Sumir Kataria        } catch (ClassNotFoundException e) {
1356aa349fecb357634a176b8ca3343231eefcee249Sumir Kataria            // Do nothing.
1366aa349fecb357634a176b8ca3343231eefcee249Sumir Kataria        }
1376aa349fecb357634a176b8ca3343231eefcee249Sumir Kataria
1387f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria        setComponentEnabled(context, SystemAlarmService.class, enableSystemAlarmService);
1397f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria
1407f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria        return scheduler;
1417f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria    }
1427f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria
1437f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria    @NonNull
1447f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria    private static Scheduler tryCreateFirebaseJobScheduler(@NonNull Context context)
1457f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria            throws ClassNotFoundException, IllegalAccessException, InstantiationException,
1467f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria            InvocationTargetException, NoSuchMethodException {
1477f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria        Class<?> firebaseJobSchedulerClass = Class.forName(FIREBASE_JOB_SCHEDULER_CLASSNAME);
1487f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria        return (Scheduler) firebaseJobSchedulerClass
1497f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria                .getConstructor(Context.class)
1507f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria                .newInstance(context);
1517f44b37e7640e4b91656e024d1754fa7a062a833Sumir Kataria    }
152f97ddbc502678a00306afcd73e7a6bb3bcc4c189Sumir Kataria
153f97ddbc502678a00306afcd73e7a6bb3bcc4c189Sumir Kataria    private Schedulers() {
154f97ddbc502678a00306afcd73e7a6bb3bcc4c189Sumir Kataria    }
1559f91ee8c71606f36a51177cd0b5c3005834be1ffRahul Ravikumar}
156