1 /*
2  * Copyright (C) 2016 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.dialer.shortcuts;
18 
19 import android.annotation.TargetApi;
20 import android.app.job.JobInfo;
21 import android.app.job.JobParameters;
22 import android.app.job.JobScheduler;
23 import android.app.job.JobService;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.os.Build.VERSION;
27 import android.os.Build.VERSION_CODES;
28 import android.support.annotation.MainThread;
29 import android.support.annotation.NonNull;
30 import android.support.v4.os.UserManagerCompat;
31 import com.android.dialer.common.Assert;
32 import com.android.dialer.common.LogUtil;
33 import com.android.dialer.constants.ScheduledJobIds;
34 import java.util.concurrent.TimeUnit;
35 
36 /**
37  * {@link JobService} which starts the periodic job to refresh dynamic and pinned shortcuts.
38  *
39  * <p>Only {@link #schedulePeriodicJob(Context)} should be used by callers.
40  */
41 @TargetApi(VERSION_CODES.N_MR1) // Shortcuts introduced in N MR1
42 public final class PeriodicJobService extends JobService {
43 
44   private static final long REFRESH_PERIOD_MILLIS = TimeUnit.HOURS.toMillis(24);
45 
46   private RefreshShortcutsTask refreshShortcutsTask;
47 
48   /**
49    * Schedules the periodic job to refresh shortcuts. If called repeatedly, the job will just be
50    * rescheduled.
51    *
52    * <p>The job will not be scheduled if the build version is not at least N MR1 or if the user is
53    * locked.
54    */
55   @MainThread
schedulePeriodicJob(@onNull Context context)56   public static void schedulePeriodicJob(@NonNull Context context) {
57     Assert.isMainThread();
58     LogUtil.enterBlock("PeriodicJobService.schedulePeriodicJob");
59 
60     if (VERSION.SDK_INT >= VERSION_CODES.N_MR1 && UserManagerCompat.isUserUnlocked(context)) {
61       JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
62       if (jobScheduler.getPendingJob(ScheduledJobIds.SHORTCUT_PERIODIC_JOB) != null) {
63         LogUtil.i("PeriodicJobService.schedulePeriodicJob", "job already scheduled.");
64         return;
65       }
66       JobInfo jobInfo =
67           new JobInfo.Builder(
68                   ScheduledJobIds.SHORTCUT_PERIODIC_JOB,
69                   new ComponentName(context, PeriodicJobService.class))
70               .setPeriodic(REFRESH_PERIOD_MILLIS)
71               .setPersisted(true)
72               .setRequiresCharging(true)
73               .setRequiresDeviceIdle(true)
74               .build();
75       jobScheduler.schedule(jobInfo);
76     }
77   }
78 
79   /** Cancels the periodic job. */
80   @MainThread
cancelJob(@onNull Context context)81   public static void cancelJob(@NonNull Context context) {
82     Assert.isMainThread();
83     LogUtil.enterBlock("PeriodicJobService.cancelJob");
84 
85     context.getSystemService(JobScheduler.class).cancel(ScheduledJobIds.SHORTCUT_PERIODIC_JOB);
86   }
87 
88   @Override
89   @MainThread
onStartJob(@onNull JobParameters params)90   public boolean onStartJob(@NonNull JobParameters params) {
91     Assert.isMainThread();
92     LogUtil.enterBlock("PeriodicJobService.onStartJob");
93 
94     if (VERSION.SDK_INT >= VERSION_CODES.N_MR1) {
95       (refreshShortcutsTask = new RefreshShortcutsTask(this)).execute(params);
96     } else {
97       // It is possible for the job to have been scheduled on NMR1+ and then the system was
98       // downgraded to < NMR1. In this case, shortcuts are no longer supported so we cancel the job
99       // which creates them.
100       LogUtil.i("PeriodicJobService.onStartJob", "not running on NMR1, cancelling job");
101       cancelJob(this);
102       return false;
103     }
104     return true;
105   }
106 
107   @Override
108   @MainThread
onStopJob(@onNull JobParameters params)109   public boolean onStopJob(@NonNull JobParameters params) {
110     Assert.isMainThread();
111     LogUtil.enterBlock("PeriodicJobService.onStopJob");
112 
113     if (refreshShortcutsTask != null) {
114       refreshShortcutsTask.cancel(false /* mayInterruptIfRunning */);
115     }
116     return false;
117   }
118 }
119