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