• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 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.managedprovisioning.provisioning;
18 
19 import static com.android.internal.util.Preconditions.checkNotNull;
20 
21 import android.annotation.MainThread;
22 import android.content.Context;
23 import android.os.Handler;
24 import android.os.HandlerThread;
25 import android.os.Looper;
26 import android.os.Message;
27 import android.os.UserHandle;
28 import android.os.UserManager;
29 
30 import com.android.internal.annotations.VisibleForTesting;
31 import com.android.managedprovisioning.analytics.MetricsWriterFactory;
32 import com.android.managedprovisioning.analytics.ProvisioningAnalyticsTracker;
33 import com.android.managedprovisioning.common.ManagedProvisioningSharedPreferences;
34 import com.android.managedprovisioning.common.ProvisionLogger;
35 import com.android.managedprovisioning.common.SettingsFacade;
36 import com.android.managedprovisioning.model.ProvisioningParams;
37 import com.android.managedprovisioning.task.AbstractProvisioningTask;
38 import com.android.managedprovisioning.task.DownloadPackageTask;
39 import com.android.managedprovisioning.task.InstallExistingPackageTask;
40 import com.android.managedprovisioning.task.InstallPackageTask;
41 import com.android.managedprovisioning.task.VerifyPackageTask;
42 
43 import java.util.ArrayList;
44 import java.util.List;
45 
46 /**
47  * Controller that manages the provisioning process. It controls the order of provisioning tasks,
48  * reacts to errors and user cancellation.
49  */
50 public abstract class AbstractProvisioningController implements AbstractProvisioningTask.Callback {
51 
52     @VisibleForTesting
53     static final int MSG_RUN_TASK = 1;
54 
55     protected final Context mContext;
56     protected final ProvisioningParams mParams;
57     protected int mUserId;
58 
59     private final ProvisioningAnalyticsTracker mProvisioningAnalyticsTracker;
60     private final ProvisioningControllerCallback mCallback;
61     private Handler mWorkerHandler;
62 
63     // Provisioning hasn't started yet
64     private static final int STATUS_NOT_STARTED = 0;
65     // Provisioning tasks are being run
66     private static final int STATUS_RUNNING = 1;
67     // Provisioning tasks have completed
68     private static final int STATUS_TASKS_COMPLETED = 2;
69     // An error occurred during provisioning
70     private static final int STATUS_ERROR = 3;
71     // Provisioning is being cancelled
72     private static final int STATUS_CANCELLING = 4;
73     // Cleanup has completed. This happens after STATUS_ERROR or STATUS_CANCELLING
74     private static final int STATUS_CLEANED_UP = 5;
75 
76     private int mStatus = STATUS_NOT_STARTED;
77     private List<AbstractProvisioningTask> mTasks = new ArrayList<>();
78 
79     protected int mCurrentTaskIndex;
80 
AbstractProvisioningController( Context context, ProvisioningParams params, int userId, ProvisioningControllerCallback callback)81     AbstractProvisioningController(
82             Context context,
83             ProvisioningParams params,
84             int userId,
85             ProvisioningControllerCallback callback) {
86         mContext = checkNotNull(context);
87         mParams = checkNotNull(params);
88         mUserId = userId;
89         mCallback = checkNotNull(callback);
90         mProvisioningAnalyticsTracker = new ProvisioningAnalyticsTracker(
91                 MetricsWriterFactory.getMetricsWriter(mContext, new SettingsFacade()),
92                 new ManagedProvisioningSharedPreferences(context));
93     }
94 
95     @MainThread
addTasks(AbstractProvisioningTask... tasks)96     protected synchronized void addTasks(AbstractProvisioningTask... tasks) {
97         for (AbstractProvisioningTask task : tasks) {
98             mTasks.add(task);
99         }
100     }
101 
setUpTasks()102     protected abstract void setUpTasks();
getErrorTitle()103     protected abstract int getErrorTitle();
getErrorMsgId(AbstractProvisioningTask task, int errorCode)104     protected abstract int getErrorMsgId(AbstractProvisioningTask task, int errorCode);
getRequireFactoryReset(AbstractProvisioningTask task, int errorCode)105     protected abstract boolean getRequireFactoryReset(AbstractProvisioningTask task, int errorCode);
106 
107     /**
108      * Start the provisioning process. The tasks loaded in {@link #setUpTasks()} ()} will be
109      * processed one by one and the respective callbacks will be given to the UI.
110      */
111     @MainThread
start(Looper looper)112     public synchronized void start(Looper looper) {
113         start(new ProvisioningTaskHandler(looper));
114     }
115 
116     @VisibleForTesting
start(Handler handler)117     void start(Handler handler) {
118         if (mStatus != STATUS_NOT_STARTED) {
119             return;
120         }
121         mWorkerHandler = checkNotNull(handler);
122 
123         mStatus = STATUS_RUNNING;
124         runTask(0);
125     }
126 
127     /**
128      * Cancel the provisioning progress. When the cancellation is complete, the
129      * {@link ProvisioningControllerCallback#cleanUpCompleted()} callback will be given.
130      */
131     @MainThread
cancel()132     public synchronized void cancel() {
133         ProvisionLogger.logd("Cancel called, current status is " + mStatus);
134         mStatus = STATUS_CANCELLING;
135         cleanup(STATUS_CLEANED_UP);
136     }
137 
runTask(int index)138     private void runTask(int index) {
139         if (mTasks.isEmpty()) {
140             tasksCompleted();
141             return;
142         }
143         AbstractProvisioningTask nextTask = mTasks.get(index);
144         Message msg = mWorkerHandler.obtainMessage(MSG_RUN_TASK, mUserId, 0 /* arg2 not used */,
145                 nextTask);
146         mWorkerHandler.sendMessage(msg);
147     }
148 
tasksCompleted()149     private void tasksCompleted() {
150         mStatus = STATUS_TASKS_COMPLETED;
151         mCurrentTaskIndex = -1;
152         mCallback.provisioningTasksCompleted();
153     }
154 
155     @Override
156     // Note that this callback might come on the main thread
onSuccess(AbstractProvisioningTask task)157     public synchronized void onSuccess(AbstractProvisioningTask task) {
158         if (mStatus != STATUS_RUNNING) {
159             return;
160         }
161 
162         mCurrentTaskIndex++;
163         if (mCurrentTaskIndex == mTasks.size()) {
164             tasksCompleted();
165         } else {
166             runTask(mCurrentTaskIndex);
167         }
168     }
169 
170     @Override
171     // Note that this callback might come on the main thread
onError(AbstractProvisioningTask task, int errorCode)172     public synchronized void onError(AbstractProvisioningTask task, int errorCode) {
173         mStatus = STATUS_ERROR;
174         cleanup(STATUS_ERROR);
175         mProvisioningAnalyticsTracker.logProvisioningError(mContext, task, errorCode);
176         mCallback.error(getErrorTitle(), getErrorMsgId(task, errorCode),
177                 getRequireFactoryReset(task, errorCode));
178     }
179 
cleanup(final int newStatus)180     private void cleanup(final int newStatus) {
181         mWorkerHandler.post(() -> {
182                 mStatus = newStatus;
183                 mCallback.cleanUpCompleted();
184             });
185     }
186 
addDownloadAndInstallDeviceOwnerPackageTasks()187     protected final void addDownloadAndInstallDeviceOwnerPackageTasks() {
188         if (mParams.deviceAdminDownloadInfo == null) return;
189 
190         DownloadPackageTask downloadTask = new DownloadPackageTask(mContext, mParams, this);
191         addTasks(downloadTask,
192                 new VerifyPackageTask(downloadTask, mContext, mParams, this),
193                 new InstallPackageTask(downloadTask, mContext, mParams, this));
194 
195         // TODO(b/170333009): add unit test for headless system user mode
196         if (UserManager.isHeadlessSystemUserMode() && mUserId != UserHandle.USER_SYSTEM) {
197             ProvisionLogger.logd("Adding InstallExistingPackageTask for system user on "
198                       + "headless system user mode");
199             addTasks(new InstallExistingPackageTask(mParams.inferDeviceAdminPackageName(),
200                     mContext, mParams, this, UserHandle.USER_SYSTEM));
201         }
202     }
203 
204     /**
205      * Handler that runs the provisioning tasks.
206      *
207      * <p>We're using a {@link HandlerThread} for all the provisioning tasks in order to not
208      * block the UI thread.</p>
209      */
210     protected static class ProvisioningTaskHandler extends Handler {
ProvisioningTaskHandler(Looper looper)211         public ProvisioningTaskHandler(Looper looper) {
212             super(looper);
213         }
214 
handleMessage(Message msg)215         public void handleMessage(Message msg) {
216             if (msg.what == MSG_RUN_TASK) {
217                 AbstractProvisioningTask task = (AbstractProvisioningTask) msg.obj;
218                 int userId = msg.arg1;
219                 ProvisionLogger.logd("Running task: " + task.getClass().getSimpleName()
220                         + " for user " + userId);
221                 task.run(userId);
222             } else {
223                 ProvisionLogger.loge("Unknown message: " + msg.what);
224             }
225         }
226     }
227 }
228