1 /*
2  * Copyright (C) 2022 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 com.android.ondevicepersonalization.services.download.mdd;
18 
19 import static com.android.ondevicepersonalization.services.OnDevicePersonalizationConfig.MDD_CELLULAR_CHARGING_PERIODIC_TASK_JOB_ID;
20 import static com.android.ondevicepersonalization.services.OnDevicePersonalizationConfig.MDD_CHARGING_PERIODIC_TASK_JOB_ID;
21 import static com.android.ondevicepersonalization.services.OnDevicePersonalizationConfig.MDD_MAINTENANCE_PERIODIC_TASK_JOB_ID;
22 import static com.android.ondevicepersonalization.services.OnDevicePersonalizationConfig.MDD_WIFI_CHARGING_PERIODIC_TASK_JOB_ID;
23 
24 import android.app.job.JobInfo;
25 import android.app.job.JobScheduler;
26 import android.content.ComponentName;
27 import android.content.Context;
28 import android.content.SharedPreferences;
29 import android.os.PersistableBundle;
30 
31 import com.google.android.libraries.mobiledatadownload.TaskScheduler;
32 
33 /**
34  * MddTaskScheduler that uses JobScheduler to schedule MDD background tasks
35  */
36 public class MddTaskScheduler implements TaskScheduler {
37     static final String MDD_TASK_TAG_KEY = "MDD_TASK_TAG_KEY";
38     private static final String MDD_TASK_SHARED_PREFS = "mdd_worker_task_periods";
39     private final Context mContext;
40 
MddTaskScheduler(Context context)41     public MddTaskScheduler(Context context) {
42         this.mContext = context;
43     }
44 
getMddTaskJobId(String mddTag)45     static int getMddTaskJobId(String mddTag) {
46         switch (mddTag) {
47             case MAINTENANCE_PERIODIC_TASK:
48                 return MDD_MAINTENANCE_PERIODIC_TASK_JOB_ID;
49             case CHARGING_PERIODIC_TASK:
50                 return MDD_CHARGING_PERIODIC_TASK_JOB_ID;
51             case CELLULAR_CHARGING_PERIODIC_TASK:
52                 return MDD_CELLULAR_CHARGING_PERIODIC_TASK_JOB_ID;
53             default:
54                 return MDD_WIFI_CHARGING_PERIODIC_TASK_JOB_ID;
55         }
56     }
57 
58     // Maps from the MDD-supplied NetworkState to the JobInfo equivalent int code.
getNetworkConstraints(NetworkState networkState)59     static int getNetworkConstraints(NetworkState networkState) {
60         switch (networkState) {
61             case NETWORK_STATE_ANY:
62                 // Network not required.
63                 return JobInfo.NETWORK_TYPE_NONE;
64             case NETWORK_STATE_CONNECTED:
65                 // Metered or unmetered network available.
66                 return JobInfo.NETWORK_TYPE_ANY;
67             case NETWORK_STATE_UNMETERED:
68             default:
69                 return JobInfo.NETWORK_TYPE_UNMETERED;
70         }
71     }
72 
73     @Override
schedulePeriodicTask(String mddTaskTag, long periodSeconds, NetworkState networkState)74     public void schedulePeriodicTask(String mddTaskTag, long periodSeconds,
75             NetworkState networkState) {
76         SharedPreferences prefs =
77                 mContext.getSharedPreferences(MDD_TASK_SHARED_PREFS, Context.MODE_PRIVATE);
78 
79         // When the period change, we will need to update the existing works.
80         boolean updateCurrent = false;
81         if (prefs.getLong(mddTaskTag, 0) != periodSeconds) {
82             SharedPreferences.Editor editor = prefs.edit();
83             editor.putLong(mddTaskTag, periodSeconds);
84             editor.apply();
85             updateCurrent = true;
86         }
87 
88         if (updateCurrent) {
89             schedulePeriodicTaskWithUpdate(mddTaskTag, periodSeconds, networkState);
90         }
91     }
92 
schedulePeriodicTaskWithUpdate(String mddTaskTag, long periodSeconds, NetworkState networkState)93     private void schedulePeriodicTaskWithUpdate(String mddTaskTag, long periodSeconds,
94             NetworkState networkState) {
95         final JobScheduler jobScheduler = mContext.getSystemService(JobScheduler.class);
96 
97         // We use Extra to pass the MDD Task Tag. This will be used in the MddJobService.
98         PersistableBundle extras = new PersistableBundle();
99         extras.putString(MDD_TASK_TAG_KEY, mddTaskTag);
100 
101         final JobInfo job =
102                 new JobInfo.Builder(
103                         getMddTaskJobId(mddTaskTag),
104                         new ComponentName(mContext, MddJobService.class))
105                         .setRequiresDeviceIdle(true)
106                         .setRequiresCharging(false)
107                         .setRequiresBatteryNotLow(true)
108                         .setPeriodic(1000 * periodSeconds) // JobScheduler uses Milliseconds.
109                         // persist this job across boots
110                         .setPersisted(true)
111                         .setRequiredNetworkType(getNetworkConstraints(networkState))
112                         .setExtras(extras)
113                         .build();
114         jobScheduler.schedule(job);
115     }
116 }
117