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