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