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