1977efd0397b5bfb285c94bc146b70def4913405aJan Clarin/*
21ecbe025abd63da00f6868d6459afb823241a412Jan Clarin * Copyright 2017 The Android Open Source Project
3977efd0397b5bfb285c94bc146b70def4913405aJan Clarin *
4977efd0397b5bfb285c94bc146b70def4913405aJan Clarin * Licensed under the Apache License, Version 2.0 (the "License");
5977efd0397b5bfb285c94bc146b70def4913405aJan Clarin * you may not use this file except in compliance with the License.
6977efd0397b5bfb285c94bc146b70def4913405aJan Clarin * You may obtain a copy of the License at
7977efd0397b5bfb285c94bc146b70def4913405aJan Clarin *
8977efd0397b5bfb285c94bc146b70def4913405aJan Clarin *      http://www.apache.org/licenses/LICENSE-2.0
9977efd0397b5bfb285c94bc146b70def4913405aJan Clarin *
10977efd0397b5bfb285c94bc146b70def4913405aJan Clarin * Unless required by applicable law or agreed to in writing, software
11977efd0397b5bfb285c94bc146b70def4913405aJan Clarin * distributed under the License is distributed on an "AS IS" BASIS,
12977efd0397b5bfb285c94bc146b70def4913405aJan Clarin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13977efd0397b5bfb285c94bc146b70def4913405aJan Clarin * See the License for the specific language governing permissions and
14977efd0397b5bfb285c94bc146b70def4913405aJan Clarin * limitations under the License.
15977efd0397b5bfb285c94bc146b70def4913405aJan Clarin */
16977efd0397b5bfb285c94bc146b70def4913405aJan Clarin
17564e43098c323d1a90be53c190b8fdbdde973505Sumir Katariapackage androidx.work.impl.background.systemjob;
18115eb6a640a0addca15460a2a994e21692a3ffefSumir Kataria
19115eb6a640a0addca15460a2a994e21692a3ffefSumir Katariaimport static android.support.annotation.VisibleForTesting.PACKAGE_PRIVATE;
20977efd0397b5bfb285c94bc146b70def4913405aJan Clarin
21977efd0397b5bfb285c94bc146b70def4913405aJan Clarinimport android.app.job.JobInfo;
22977efd0397b5bfb285c94bc146b70def4913405aJan Clarinimport android.content.ComponentName;
23977efd0397b5bfb285c94bc146b70def4913405aJan Clarinimport android.content.Context;
24977efd0397b5bfb285c94bc146b70def4913405aJan Clarinimport android.os.Build;
25dc31aa85682ec4cb14784d393b4b70ff4da5d43dXyan Bhatnagarimport android.os.PersistableBundle;
26977efd0397b5bfb285c94bc146b70def4913405aJan Clarinimport android.support.annotation.NonNull;
27977efd0397b5bfb285c94bc146b70def4913405aJan Clarinimport android.support.annotation.RequiresApi;
28d554aebb8aa2aa48222b7b003273b06369d55a89Rahul Ravikumarimport android.support.annotation.RestrictTo;
29115eb6a640a0addca15460a2a994e21692a3ffefSumir Katariaimport android.support.annotation.VisibleForTesting;
30697d6a4a3797bc71d0dd8685937a318e9934066bRahul Ravikumarimport android.util.Log;
31977efd0397b5bfb285c94bc146b70def4913405aJan Clarin
32564e43098c323d1a90be53c190b8fdbdde973505Sumir Katariaimport androidx.work.BackoffPolicy;
33564e43098c323d1a90be53c190b8fdbdde973505Sumir Katariaimport androidx.work.Constraints;
34564e43098c323d1a90be53c190b8fdbdde973505Sumir Katariaimport androidx.work.ContentUriTriggers;
35564e43098c323d1a90be53c190b8fdbdde973505Sumir Katariaimport androidx.work.NetworkType;
36564e43098c323d1a90be53c190b8fdbdde973505Sumir Katariaimport androidx.work.impl.WorkManagerImpl;
37564e43098c323d1a90be53c190b8fdbdde973505Sumir Katariaimport androidx.work.impl.model.WorkSpec;
38564e43098c323d1a90be53c190b8fdbdde973505Sumir Kataria
39977efd0397b5bfb285c94bc146b70def4913405aJan Clarin/**
40977efd0397b5bfb285c94bc146b70def4913405aJan Clarin * Converts a {@link WorkSpec} into a JobInfo.
41d554aebb8aa2aa48222b7b003273b06369d55a89Rahul Ravikumar *
42d554aebb8aa2aa48222b7b003273b06369d55a89Rahul Ravikumar * @hide
43977efd0397b5bfb285c94bc146b70def4913405aJan Clarin */
44d554aebb8aa2aa48222b7b003273b06369d55a89Rahul Ravikumar@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
4590c024663706960aa738040824160d6d8f6df449Sumir Kataria@RequiresApi(api = WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL)
468cf53bfbd2891f174f756d8426120178cc1bad18Jan Clarinclass SystemJobInfoConverter {
472681a88070cc82da9b98817e2d150f0b3f09bec5Sumir Kataria    private static final String TAG = "SystemJobInfoConverter";
488cf53bfbd2891f174f756d8426120178cc1bad18Jan Clarin
498cf53bfbd2891f174f756d8426120178cc1bad18Jan Clarin    static final String EXTRA_WORK_SPEC_ID = "EXTRA_WORK_SPEC_ID";
505683c48bd7343c7b4b969c0711814c4e15d7ad6cSumir Kataria    static final String EXTRA_IS_PERIODIC = "EXTRA_IS_PERIODIC";
51977efd0397b5bfb285c94bc146b70def4913405aJan Clarin
52977efd0397b5bfb285c94bc146b70def4913405aJan Clarin    private final ComponentName mWorkServiceComponent;
53d6b29fd71beba98f01ece4b28b1826ed2b636b72Jan Clarin
54d6b29fd71beba98f01ece4b28b1826ed2b636b72Jan Clarin    @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
55494b77cd228de249649440c6210bbae1ddf33d76Rahul Ravikumar    SystemJobInfoConverter(@NonNull Context context) {
56d6b29fd71beba98f01ece4b28b1826ed2b636b72Jan Clarin        Context appContext = context.getApplicationContext();
57d6b29fd71beba98f01ece4b28b1826ed2b636b72Jan Clarin        mWorkServiceComponent = new ComponentName(appContext, SystemJobService.class);
58977efd0397b5bfb285c94bc146b70def4913405aJan Clarin    }
59977efd0397b5bfb285c94bc146b70def4913405aJan Clarin
602681a88070cc82da9b98817e2d150f0b3f09bec5Sumir Kataria    /**
612681a88070cc82da9b98817e2d150f0b3f09bec5Sumir Kataria     * Converts a {@link WorkSpec} into a {@link JobInfo}.
622681a88070cc82da9b98817e2d150f0b3f09bec5Sumir Kataria     *
63ed0c10f0994ea1af6d11d8f04810f2562edc0f9eJan Clarin     * Note: All {@link JobInfo} are set to persist on reboot.
64ed0c10f0994ea1af6d11d8f04810f2562edc0f9eJan Clarin     *
652681a88070cc82da9b98817e2d150f0b3f09bec5Sumir Kataria     * @param workSpec The {@link WorkSpec} to convert
66494b77cd228de249649440c6210bbae1ddf33d76Rahul Ravikumar     * @param jobId The {@code jobId} to use. This is useful when de-duping jobs on reschedule.
672681a88070cc82da9b98817e2d150f0b3f09bec5Sumir Kataria     * @return The {@link JobInfo} representing the same information as the {@link WorkSpec}
682681a88070cc82da9b98817e2d150f0b3f09bec5Sumir Kataria     */
69494b77cd228de249649440c6210bbae1ddf33d76Rahul Ravikumar    JobInfo convert(WorkSpec workSpec, int jobId) {
70b5728f4e1a4b3f4f1fabf033b1363ca6b1cffdefSumir Kataria        Constraints constraints = workSpec.constraints;
718cf53bfbd2891f174f756d8426120178cc1bad18Jan Clarin        // TODO(janclarin): Support newer required network types if unsupported by API version.
728cf53bfbd2891f174f756d8426120178cc1bad18Jan Clarin        int jobInfoNetworkType = convertNetworkType(constraints.getRequiredNetworkType());
73dc31aa85682ec4cb14784d393b4b70ff4da5d43dXyan Bhatnagar        PersistableBundle extras = new PersistableBundle();
74b5728f4e1a4b3f4f1fabf033b1363ca6b1cffdefSumir Kataria        extras.putString(EXTRA_WORK_SPEC_ID, workSpec.id);
755683c48bd7343c7b4b969c0711814c4e15d7ad6cSumir Kataria        extras.putBoolean(EXTRA_IS_PERIODIC, workSpec.isPeriodic());
768cf53bfbd2891f174f756d8426120178cc1bad18Jan Clarin        JobInfo.Builder builder = new JobInfo.Builder(jobId, mWorkServiceComponent)
778cf53bfbd2891f174f756d8426120178cc1bad18Jan Clarin                .setRequiredNetworkType(jobInfoNetworkType)
786567a298a8826c528835e0448aec9459be138e6cXyan Bhatnagar                .setRequiresCharging(constraints.requiresCharging())
796567a298a8826c528835e0448aec9459be138e6cXyan Bhatnagar                .setRequiresDeviceIdle(constraints.requiresDeviceIdle())
808cf53bfbd2891f174f756d8426120178cc1bad18Jan Clarin                .setExtras(extras);
81dc31aa85682ec4cb14784d393b4b70ff4da5d43dXyan Bhatnagar
826dc64bb8277f53d56a78478a9e785eaf4d682c5eXyan Bhatnagar        if (!constraints.requiresDeviceIdle()) {
836dc64bb8277f53d56a78478a9e785eaf4d682c5eXyan Bhatnagar            // Device Idle and Backoff Criteria cannot be set together
84b5728f4e1a4b3f4f1fabf033b1363ca6b1cffdefSumir Kataria            int backoffPolicy = workSpec.backoffPolicy == BackoffPolicy.LINEAR
856dc64bb8277f53d56a78478a9e785eaf4d682c5eXyan Bhatnagar                    ? JobInfo.BACKOFF_POLICY_LINEAR : JobInfo.BACKOFF_POLICY_EXPONENTIAL;
86b5728f4e1a4b3f4f1fabf033b1363ca6b1cffdefSumir Kataria            builder.setBackoffCriteria(workSpec.backoffDelayDuration, backoffPolicy);
876dc64bb8277f53d56a78478a9e785eaf4d682c5eXyan Bhatnagar        }
886dc64bb8277f53d56a78478a9e785eaf4d682c5eXyan Bhatnagar
89ee302e19355ede7dde7d607940d5bd686d164baeJan Clarin        if (workSpec.isPeriodic()) {
90c4de263bc3abaf06ee41a2835c471752a1825e0cSumir Kataria            if (Build.VERSION.SDK_INT >= 24) {
91b5728f4e1a4b3f4f1fabf033b1363ca6b1cffdefSumir Kataria                builder.setPeriodic(workSpec.intervalDuration, workSpec.flexDuration);
92c4de263bc3abaf06ee41a2835c471752a1825e0cSumir Kataria            } else {
93697d6a4a3797bc71d0dd8685937a318e9934066bRahul Ravikumar                Log.d(TAG,
94c4de263bc3abaf06ee41a2835c471752a1825e0cSumir Kataria                        "Flex duration is currently not supported before API 24. Ignoring.");
95b5728f4e1a4b3f4f1fabf033b1363ca6b1cffdefSumir Kataria                builder.setPeriodic(workSpec.intervalDuration);
96c4de263bc3abaf06ee41a2835c471752a1825e0cSumir Kataria            }
97ee302e19355ede7dde7d607940d5bd686d164baeJan Clarin        } else {
987031a0fbe12b8159ab2dc6d9c50be5b3f38477faRahul Ravikumar            // Even if a WorkRequest has no constraints, setMinimumLatency(0) still needs to be
998b3284fa4a62568df91f706b0b2334284794008fSumir Kataria            // called due to an issue in JobInfo.Builder#build and JobInfo with no constraints. See
1008b3284fa4a62568df91f706b0b2334284794008fSumir Kataria            // b/67716867.
101b5728f4e1a4b3f4f1fabf033b1363ca6b1cffdefSumir Kataria            builder.setMinimumLatency(workSpec.initialDelay);
102ee302e19355ede7dde7d607940d5bd686d164baeJan Clarin        }
103ee302e19355ede7dde7d607940d5bd686d164baeJan Clarin
104c4de263bc3abaf06ee41a2835c471752a1825e0cSumir Kataria        if (Build.VERSION.SDK_INT >= 24 && constraints.hasContentUriTriggers()) {
10554f58a68c9655654e21f29f883a8c1a00528b769Xyan Bhatnagar            for (ContentUriTriggers.Trigger trigger : constraints.getContentUriTriggers()) {
10654f58a68c9655654e21f29f883a8c1a00528b769Xyan Bhatnagar                builder.addTriggerContentUri(convertContentUriTrigger(trigger));
10754f58a68c9655654e21f29f883a8c1a00528b769Xyan Bhatnagar            }
10854f58a68c9655654e21f29f883a8c1a00528b769Xyan Bhatnagar        }
10954f58a68c9655654e21f29f883a8c1a00528b769Xyan Bhatnagar
110494b77cd228de249649440c6210bbae1ddf33d76Rahul Ravikumar        // We don't want to persist these jobs because we reschedule these jobs on BOOT_COMPLETED.
111494b77cd228de249649440c6210bbae1ddf33d76Rahul Ravikumar        // That way ForceStopRunnable correctly reschedules Jobs when necessary.
112494b77cd228de249649440c6210bbae1ddf33d76Rahul Ravikumar        builder.setPersisted(false);
113977efd0397b5bfb285c94bc146b70def4913405aJan Clarin        if (Build.VERSION.SDK_INT >= 26) {
114904ba12beeda5f75519b80f1b92d8f73401c1bf4Sumir Kataria            builder.setRequiresBatteryNotLow(constraints.requiresBatteryNotLow());
115904ba12beeda5f75519b80f1b92d8f73401c1bf4Sumir Kataria            builder.setRequiresStorageNotLow(constraints.requiresStorageNotLow());
116977efd0397b5bfb285c94bc146b70def4913405aJan Clarin        }
117977efd0397b5bfb285c94bc146b70def4913405aJan Clarin        return builder.build();
118977efd0397b5bfb285c94bc146b70def4913405aJan Clarin    }
119977efd0397b5bfb285c94bc146b70def4913405aJan Clarin
120c4de263bc3abaf06ee41a2835c471752a1825e0cSumir Kataria    @RequiresApi(24)
12154f58a68c9655654e21f29f883a8c1a00528b769Xyan Bhatnagar    private static JobInfo.TriggerContentUri convertContentUriTrigger(
12254f58a68c9655654e21f29f883a8c1a00528b769Xyan Bhatnagar            ContentUriTriggers.Trigger trigger) {
12354f58a68c9655654e21f29f883a8c1a00528b769Xyan Bhatnagar        int flag = trigger.shouldTriggerForDescendants()
12454f58a68c9655654e21f29f883a8c1a00528b769Xyan Bhatnagar                ? JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS : 0;
12554f58a68c9655654e21f29f883a8c1a00528b769Xyan Bhatnagar        return new JobInfo.TriggerContentUri(trigger.getUri(), flag);
12654f58a68c9655654e21f29f883a8c1a00528b769Xyan Bhatnagar    }
12754f58a68c9655654e21f29f883a8c1a00528b769Xyan Bhatnagar
1282681a88070cc82da9b98817e2d150f0b3f09bec5Sumir Kataria    /**
129b6430f2f232996be5971e9a57819aa2377daf920Sumir Kataria     * Converts {@link NetworkType} into {@link JobInfo}'s network values.
1302681a88070cc82da9b98817e2d150f0b3f09bec5Sumir Kataria     *
131b6430f2f232996be5971e9a57819aa2377daf920Sumir Kataria     * @param networkType The {@link NetworkType} network type
1322681a88070cc82da9b98817e2d150f0b3f09bec5Sumir Kataria     * @return The {@link JobInfo} network type
1332681a88070cc82da9b98817e2d150f0b3f09bec5Sumir Kataria     */
134b6430f2f232996be5971e9a57819aa2377daf920Sumir Kataria    static int convertNetworkType(NetworkType networkType) {
135977efd0397b5bfb285c94bc146b70def4913405aJan Clarin        switch(networkType) {
136b6430f2f232996be5971e9a57819aa2377daf920Sumir Kataria            case NOT_REQUIRED:
137977efd0397b5bfb285c94bc146b70def4913405aJan Clarin                return JobInfo.NETWORK_TYPE_NONE;
138b6430f2f232996be5971e9a57819aa2377daf920Sumir Kataria            case CONNECTED:
139977efd0397b5bfb285c94bc146b70def4913405aJan Clarin                return JobInfo.NETWORK_TYPE_ANY;
140b6430f2f232996be5971e9a57819aa2377daf920Sumir Kataria            case UNMETERED:
141977efd0397b5bfb285c94bc146b70def4913405aJan Clarin                return JobInfo.NETWORK_TYPE_UNMETERED;
142b6430f2f232996be5971e9a57819aa2377daf920Sumir Kataria            case NOT_ROAMING:
143c4de263bc3abaf06ee41a2835c471752a1825e0cSumir Kataria                if (Build.VERSION.SDK_INT >= 24) {
144c4de263bc3abaf06ee41a2835c471752a1825e0cSumir Kataria                    return JobInfo.NETWORK_TYPE_NOT_ROAMING;
145c4de263bc3abaf06ee41a2835c471752a1825e0cSumir Kataria                }
146c4de263bc3abaf06ee41a2835c471752a1825e0cSumir Kataria                break;
147b6430f2f232996be5971e9a57819aa2377daf920Sumir Kataria            case METERED:
148977efd0397b5bfb285c94bc146b70def4913405aJan Clarin                if (Build.VERSION.SDK_INT >= 26) {
149977efd0397b5bfb285c94bc146b70def4913405aJan Clarin                    return JobInfo.NETWORK_TYPE_METERED;
150977efd0397b5bfb285c94bc146b70def4913405aJan Clarin                }
151977efd0397b5bfb285c94bc146b70def4913405aJan Clarin                break;
152977efd0397b5bfb285c94bc146b70def4913405aJan Clarin        }
153697d6a4a3797bc71d0dd8685937a318e9934066bRahul Ravikumar        Log.d(TAG, String.format(
154697d6a4a3797bc71d0dd8685937a318e9934066bRahul Ravikumar                "API version too low. Cannot convert network type value %s", networkType));
1558cf53bfbd2891f174f756d8426120178cc1bad18Jan Clarin        return JobInfo.NETWORK_TYPE_ANY;
156977efd0397b5bfb285c94bc146b70def4913405aJan Clarin    }
157977efd0397b5bfb285c94bc146b70def4913405aJan Clarin}
158