blob: c124a4fd15bd3773c65bc7aa0c7d44a6678204d9 [file] [log] [blame]
/*
* Copyright 2016, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.managedprovisioning.provisioning;
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.MainThread;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import com.android.internal.annotations.VisibleForTesting;
import com.android.managedprovisioning.analytics.ProvisioningAnalyticsTracker;
import com.android.managedprovisioning.common.ProvisionLogger;
import com.android.managedprovisioning.finalization.FinalizationController;
import com.android.managedprovisioning.model.ProvisioningParams;
import com.android.managedprovisioning.task.AbstractProvisioningTask;
import java.util.ArrayList;
import java.util.List;
/**
* Controller that manages the provisioning process. It controls the order of provisioning tasks,
* reacts to errors and user cancellation.
*/
public abstract class AbstractProvisioningController implements AbstractProvisioningTask.Callback {
@VisibleForTesting
static final int MSG_RUN_TASK = 1;
protected final Context mContext;
protected final ProvisioningParams mParams;
protected int mUserId;
private final ProvisioningAnalyticsTracker mProvisioningAnalyticsTracker;
private final ProvisioningControllerCallback mCallback;
private final FinalizationController mFinalizationController;
private Handler mWorkerHandler;
// Provisioning hasn't started yet
private static final int STATUS_NOT_STARTED = 0;
// Provisioning tasks are being run
private static final int STATUS_RUNNING = 1;
// Provisioning tasks have completed
private static final int STATUS_TASKS_COMPLETED = 2;
// Prefinalization has completed
private static final int STATUS_DONE = 3;
// An error occurred during provisioning
private static final int STATUS_ERROR = 4;
// Provisioning is being cancelled
private static final int STATUS_CANCELLING = 5;
// Cleanup has completed. This happens after STATUS_ERROR or STATUS_CANCELLING
private static final int STATUS_CLEANED_UP = 6;
private int mStatus = STATUS_NOT_STARTED;
private List<AbstractProvisioningTask> mTasks = new ArrayList<>();
protected int mCurrentTaskIndex;
AbstractProvisioningController(
Context context,
ProvisioningParams params,
int userId,
ProvisioningControllerCallback callback,
FinalizationController finalizationController) {
mContext = checkNotNull(context);
mParams = checkNotNull(params);
mUserId = userId;
mCallback = checkNotNull(callback);
mFinalizationController = checkNotNull(finalizationController);
mProvisioningAnalyticsTracker = ProvisioningAnalyticsTracker.getInstance();
setUpTasks();
}
@MainThread
protected synchronized void addTasks(AbstractProvisioningTask... tasks) {
for (AbstractProvisioningTask task : tasks) {
mTasks.add(task);
}
}
protected abstract void setUpTasks();
protected abstract void performCleanup();
protected abstract int getErrorTitle();
protected abstract int getErrorMsgId(AbstractProvisioningTask task, int errorCode);
protected abstract boolean getRequireFactoryReset(AbstractProvisioningTask task, int errorCode);
/**
* Start the provisioning process. The tasks loaded in {@link #setUpTasks()} ()} will be
* processed one by one and the respective callbacks will be given to the UI.
*/
@MainThread
public synchronized void start(Looper looper) {
start(new ProvisioningTaskHandler(looper));
}
@VisibleForTesting
void start(Handler handler) {
if (mStatus != STATUS_NOT_STARTED) {
return;
}
mWorkerHandler = checkNotNull(handler);
mStatus = STATUS_RUNNING;
runTask(0);
}
/**
* Cancel the provisioning progress. When the cancellation is complete, the
* {@link ProvisioningControllerCallback#cleanUpCompleted()} callback will be given.
*/
@MainThread
public synchronized void cancel() {
if (mStatus != STATUS_RUNNING
&& mStatus != STATUS_TASKS_COMPLETED
&& mStatus != STATUS_CANCELLING
&& mStatus != STATUS_CLEANED_UP
&& mStatus != STATUS_ERROR) {
ProvisionLogger.logd("Cancel called, but status is " + mStatus);
return;
}
ProvisionLogger.logd("ProvisioningController: cancelled");
mStatus = STATUS_CANCELLING;
cleanup(STATUS_CLEANED_UP);
}
@MainThread
public synchronized void preFinalize() {
if (mStatus != STATUS_TASKS_COMPLETED) {
return;
}
mStatus = STATUS_DONE;
mFinalizationController.provisioningInitiallyDone(mParams);
mCallback.preFinalizationCompleted();
}
private void runTask(int index) {
AbstractProvisioningTask nextTask = mTasks.get(index);
Message msg = mWorkerHandler.obtainMessage(MSG_RUN_TASK, mUserId, 0 /* arg2 not used */,
nextTask);
mWorkerHandler.sendMessage(msg);
mCallback.progressUpdate(nextTask.getStatusMsgId());
}
private void tasksCompleted() {
mStatus = STATUS_TASKS_COMPLETED;
mCurrentTaskIndex = -1;
mCallback.provisioningTasksCompleted();
}
@Override
// Note that this callback might come on the main thread
public synchronized void onSuccess(AbstractProvisioningTask task) {
if (mStatus != STATUS_RUNNING) {
return;
}
mCurrentTaskIndex++;
if (mCurrentTaskIndex == mTasks.size()) {
tasksCompleted();
} else {
runTask(mCurrentTaskIndex);
}
}
@Override
// Note that this callback might come on the main thread
public synchronized void onError(AbstractProvisioningTask task, int errorCode) {
mStatus = STATUS_ERROR;
cleanup(STATUS_ERROR);
mProvisioningAnalyticsTracker.logProvisioningError(mContext, task, errorCode);
mCallback.error(getErrorTitle(), getErrorMsgId(task, errorCode),
getRequireFactoryReset(task, errorCode));
}
private void cleanup(final int newStatus) {
mWorkerHandler.post(() -> {
performCleanup();
mStatus = newStatus;
mCallback.cleanUpCompleted();
});
}
/**
* Handler that runs the provisioning tasks.
*
* <p>We're using a {@link HandlerThread} for all the provisioning tasks in order to not
* block the UI thread.</p>
*/
protected static class ProvisioningTaskHandler extends Handler {
public ProvisioningTaskHandler(Looper looper) {
super(looper);
}
public void handleMessage(Message msg) {
if (msg.what == MSG_RUN_TASK) {
AbstractProvisioningTask task = (AbstractProvisioningTask) msg.obj;
ProvisionLogger.logd("Running task: " + task.getClass().getSimpleName());
task.run(msg.arg1);
} else {
ProvisionLogger.loge("Unknown message: " + msg.what);
}
}
}
}