• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.voicemail.impl.scheduling;
18 
19 import android.content.Context;
20 import android.content.Intent;
21 import android.os.Bundle;
22 import android.os.SystemClock;
23 import android.support.annotation.CallSuper;
24 import android.support.annotation.MainThread;
25 import android.support.annotation.NonNull;
26 import android.support.annotation.VisibleForTesting;
27 import android.support.annotation.WorkerThread;
28 import android.telecom.PhoneAccountHandle;
29 import com.android.dialer.proguard.UsedByReflection;
30 import com.android.voicemail.impl.Assert;
31 import com.android.voicemail.impl.NeededForTesting;
32 import java.util.ArrayList;
33 import java.util.List;
34 
35 /**
36  * Provides common utilities for task implementations, such as execution time and managing {@link
37  * Policy}
38  */
39 @UsedByReflection(value = "Tasks.java")
40 public abstract class BaseTask implements Task {
41 
42   @VisibleForTesting
43   public static final String EXTRA_PHONE_ACCOUNT_HANDLE = "extra_phone_account_handle";
44 
45   private static final String EXTRA_EXECUTION_TIME = "extra_execution_time";
46 
47   private Bundle extras;
48 
49   private Context context;
50 
51   private int id;
52   private PhoneAccountHandle phoneAccountHandle;
53 
54   private boolean hasStarted;
55   private volatile boolean hasFailed;
56 
57   @NonNull private final List<Policy> policies = new ArrayList<>();
58 
59   private long executionTime;
60 
61   private static Clock clock = new Clock();
62 
BaseTask(int id)63   protected BaseTask(int id) {
64     this.id = id;
65     executionTime = getTimeMillis();
66   }
67 
68   /**
69    * Modify the task ID to prevent arbitrary task from executing. Can only be called before {@link
70    * #onCreate(Context, Bundle)} returns.
71    */
72   @MainThread
setId(int id)73   public void setId(int id) {
74     Assert.isMainThread();
75     this.id = id;
76   }
77 
78   @MainThread
hasStarted()79   public boolean hasStarted() {
80     Assert.isMainThread();
81     return hasStarted;
82   }
83 
84   @MainThread
hasFailed()85   public boolean hasFailed() {
86     Assert.isMainThread();
87     return hasFailed;
88   }
89 
getContext()90   public Context getContext() {
91     return context;
92   }
93 
getPhoneAccountHandle()94   public PhoneAccountHandle getPhoneAccountHandle() {
95     return phoneAccountHandle;
96   }
97   /**
98    * Should be call in the constructor or {@link Policy#onCreate(BaseTask, Bundle)} will be missed.
99    */
100   @MainThread
addPolicy(Policy policy)101   public BaseTask addPolicy(Policy policy) {
102     Assert.isMainThread();
103     policies.add(policy);
104     return this;
105   }
106 
107   /**
108    * Indicate the task has failed. {@link Policy#onFail()} will be triggered once the execution
109    * ends. This mechanism is used by policies for actions such as determining whether to schedule a
110    * retry. Must be call inside {@link #onExecuteInBackgroundThread()}
111    */
112   @WorkerThread
fail()113   public void fail() {
114     Assert.isNotMainThread();
115     hasFailed = true;
116   }
117 
118   /** @param timeMillis the time since epoch, in milliseconds. */
119   @MainThread
setExecutionTime(long timeMillis)120   public void setExecutionTime(long timeMillis) {
121     Assert.isMainThread();
122     executionTime = timeMillis;
123   }
124 
getTimeMillis()125   public long getTimeMillis() {
126     return clock.getTimeMillis();
127   }
128 
129   /**
130    * Creates an intent that can be used to restart the current task. Derived class should build
131    * their intent upon this.
132    */
createRestartIntent()133   public Intent createRestartIntent() {
134     return createIntent(getContext(), this.getClass(), phoneAccountHandle);
135   }
136 
137   /**
138    * Creates an intent that can be used to be broadcast to the {@link TaskReceiver}. Derived class
139    * should build their intent upon this.
140    */
createIntent( Context context, Class<? extends BaseTask> task, PhoneAccountHandle phoneAccountHandle)141   public static Intent createIntent(
142       Context context, Class<? extends BaseTask> task, PhoneAccountHandle phoneAccountHandle) {
143     Intent intent = Tasks.createIntent(context, task);
144     intent.putExtra(EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
145     return intent;
146   }
147 
148   @Override
getId()149   public TaskId getId() {
150     return new TaskId(id, phoneAccountHandle);
151   }
152 
153   @Override
toBundle()154   public Bundle toBundle() {
155     extras.putLong(EXTRA_EXECUTION_TIME, executionTime);
156     return extras;
157   }
158 
159   @Override
160   @CallSuper
onCreate(Context context, Bundle extras)161   public void onCreate(Context context, Bundle extras) {
162     this.context = context;
163     this.extras = extras;
164     phoneAccountHandle = extras.getParcelable(EXTRA_PHONE_ACCOUNT_HANDLE);
165     for (Policy policy : policies) {
166       policy.onCreate(this, extras);
167     }
168   }
169 
170   @Override
171   @CallSuper
onRestore(Bundle extras)172   public void onRestore(Bundle extras) {
173     if (this.extras.containsKey(EXTRA_EXECUTION_TIME)) {
174       executionTime = extras.getLong(EXTRA_EXECUTION_TIME);
175     }
176   }
177 
178   @Override
getReadyInMilliSeconds()179   public long getReadyInMilliSeconds() {
180     return executionTime - getTimeMillis();
181   }
182 
183   @Override
184   @CallSuper
onBeforeExecute()185   public void onBeforeExecute() {
186     for (Policy policy : policies) {
187       policy.onBeforeExecute();
188     }
189     hasStarted = true;
190   }
191 
192   @Override
193   @CallSuper
onCompleted()194   public void onCompleted() {
195     if (hasFailed) {
196       for (Policy policy : policies) {
197         policy.onFail();
198       }
199     }
200 
201     for (Policy policy : policies) {
202       policy.onCompleted();
203     }
204   }
205 
206   @Override
onDuplicatedTaskAdded(Task task)207   public void onDuplicatedTaskAdded(Task task) {
208     for (Policy policy : policies) {
209       policy.onDuplicatedTaskAdded();
210     }
211   }
212 
213   @NeededForTesting
214   static class Clock {
215 
getTimeMillis()216     public long getTimeMillis() {
217       return SystemClock.elapsedRealtime();
218     }
219   }
220 
221   /** Used to replace the clock with an deterministic clock */
222   @NeededForTesting
setClockForTesting(Clock clock)223   static void setClockForTesting(Clock clock) {
224     BaseTask.clock = clock;
225   }
226 }
227