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.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_TOTAL_TASK_TIME_MS;
     20 import static com.android.internal.util.Preconditions.checkNotNull;
     21 import static com.android.managedprovisioning.analytics.ProvisioningAnalyticsTracker.CANCELLED_DURING_PROVISIONING;
     22 
     23 import android.content.ComponentName;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.os.Handler;
     27 import android.os.HandlerThread;
     28 import android.os.Looper;
     29 import android.util.Pair;
     30 
     31 import com.android.internal.annotations.GuardedBy;
     32 import com.android.internal.annotations.VisibleForTesting;
     33 import com.android.managedprovisioning.analytics.ProvisioningAnalyticsTracker;
     34 import com.android.managedprovisioning.analytics.TimeLogger;
     35 import com.android.managedprovisioning.common.Globals;
     36 import com.android.managedprovisioning.common.ProvisionLogger;
     37 import com.android.managedprovisioning.model.ProvisioningParams;
     38 
     39 import java.util.ArrayList;
     40 import java.util.List;
     41 
     42 /**
     43  * Singleton instance that provides communications between the ongoing provisioning process and the
     44  * UI layer.
     45  */
     46 public class ProvisioningManager implements ProvisioningControllerCallback {
     47     private static ProvisioningManager sInstance;
     48 
     49     private static final Intent SERVICE_INTENT = new Intent().setComponent(new ComponentName(
     50             Globals.MANAGED_PROVISIONING_PACKAGE_NAME,
     51             ProvisioningService.class.getName()));
     52 
     53     private static final int CALLBACK_NONE = 0;
     54     private static final int CALLBACK_ERROR = 1;
     55     private static final int CALLBACK_PROGRESS = 2;
     56     private static final int CALLBACK_PRE_FINALIZED = 3;
     57 
     58     private final Context mContext;
     59     private final ProvisioningControllerFactory mFactory;
     60     private final Handler mUiHandler;
     61 
     62     @GuardedBy("this")
     63     private AbstractProvisioningController mController;
     64     @GuardedBy("this")
     65     private List<ProvisioningManagerCallback> mCallbacks = new ArrayList<>();
     66 
     67     private final ProvisioningAnalyticsTracker mProvisioningAnalyticsTracker;
     68     private final TimeLogger mTimeLogger;
     69     private int mLastCallback = CALLBACK_NONE;
     70     private Pair<Pair<Integer, Integer>, Boolean> mLastError; // TODO: refactor
     71     private int mLastProgressMsgId;
     72     private HandlerThread mHandlerThread;
     73 
     74     public static ProvisioningManager getInstance(Context context) {
     75         if (sInstance == null) {
     76             sInstance = new ProvisioningManager(context.getApplicationContext());
     77         }
     78         return sInstance;
     79     }
     80 
     81     private ProvisioningManager(Context context) {
     82         this(
     83                 context,
     84                 new Handler(Looper.getMainLooper()),
     85                 new ProvisioningControllerFactory(),
     86                 ProvisioningAnalyticsTracker.getInstance(),
     87                 new TimeLogger(context, PROVISIONING_TOTAL_TASK_TIME_MS));
     88     }
     89 
     90     @VisibleForTesting
     91     ProvisioningManager(
     92             Context context,
     93             Handler uiHandler,
     94             ProvisioningControllerFactory factory,
     95             ProvisioningAnalyticsTracker analyticsTracker,
     96             TimeLogger timeLogger) {
     97         mContext = checkNotNull(context);
     98         mUiHandler = checkNotNull(uiHandler);
     99         mFactory = checkNotNull(factory);
    100         mProvisioningAnalyticsTracker = checkNotNull(analyticsTracker);
    101         mTimeLogger = checkNotNull(timeLogger);
    102     }
    103 
    104     /**
    105      * Initiate a new provisioning process, unless one is already ongoing.
    106      *
    107      * @param params {@link ProvisioningParams} associated with the new provisioning process.
    108      */
    109     public void maybeStartProvisioning(final ProvisioningParams params) {
    110         synchronized (this) {
    111             if (mController == null) {
    112                 mTimeLogger.start();
    113                 startNewProvisioningLocked(params);
    114                 mProvisioningAnalyticsTracker.logProvisioningStarted(mContext, params);
    115             } else {
    116                 ProvisionLogger.loge("Trying to start provisioning, but it's already running");
    117             }
    118         }
    119    }
    120 
    121     private void startNewProvisioningLocked(final ProvisioningParams params) {
    122         ProvisionLogger.logd("Initializing provisioning process");
    123         if (mHandlerThread == null) {
    124             mHandlerThread = new HandlerThread("Provisioning Worker");
    125             mHandlerThread.start();
    126             mContext.startService(SERVICE_INTENT);
    127         }
    128         mLastCallback = CALLBACK_NONE;
    129         mLastError = null;
    130         mLastProgressMsgId = 0;
    131 
    132         mController = mFactory.createProvisioningController(mContext, params, this);
    133         mController.start(mHandlerThread.getLooper());
    134     }
    135 
    136     /**
    137      * Cancel the provisioning progress.
    138      */
    139     public void cancelProvisioning() {
    140         synchronized (this) {
    141             if (mController != null) {
    142                 mProvisioningAnalyticsTracker.logProvisioningCancelled(mContext,
    143                         CANCELLED_DURING_PROVISIONING);
    144                 mController.cancel();
    145             } else {
    146                 ProvisionLogger.loge("Trying to cancel provisioning, but controller is null");
    147             }
    148         }
    149     }
    150 
    151     /**
    152      * Register a listener for updates of the provisioning progress.
    153      *
    154      * <p>Registering a listener will immediately result in the last callback being sent to the
    155      * listener. All callbacks will occur on the UI thread.</p>
    156      *
    157      * @param callback listener to be registered.
    158      */
    159     public void registerListener(ProvisioningManagerCallback callback) {
    160         synchronized (this) {
    161             mCallbacks.add(callback);
    162             callLastCallbackLocked(callback);
    163         }
    164     }
    165 
    166     /**
    167      * Unregister a listener from updates of the provisioning progress.
    168      *
    169      * @param callback listener to be unregistered.
    170      */
    171     public void unregisterListener(ProvisioningManagerCallback callback) {
    172         synchronized (this) {
    173             mCallbacks.remove(callback);
    174         }
    175     }
    176 
    177     @Override
    178     public void cleanUpCompleted() {
    179         synchronized (this) {
    180             clearControllerLocked();
    181         }
    182     }
    183 
    184     @Override
    185     public void error(int titleId, int messageId, boolean factoryResetRequired) {
    186         synchronized (this) {
    187             for (ProvisioningManagerCallback callback : mCallbacks) {
    188                 mUiHandler.post(() -> callback.error(titleId, messageId, factoryResetRequired));
    189             }
    190             mLastCallback = CALLBACK_ERROR;
    191             mLastError = Pair.create(Pair.create(titleId, messageId), factoryResetRequired);
    192         }
    193     }
    194 
    195     @Override
    196     public void progressUpdate(int progressMsgId) {
    197         synchronized (this) {
    198             for (ProvisioningManagerCallback callback : mCallbacks) {
    199                 mUiHandler.post(() -> callback.progressUpdate(progressMsgId));
    200             }
    201             mLastCallback = CALLBACK_PROGRESS;
    202             mLastProgressMsgId = progressMsgId;
    203         }
    204     }
    205 
    206     @Override
    207     public void provisioningTasksCompleted() {
    208         synchronized (this) {
    209             mTimeLogger.stop();
    210             if (mController != null) {
    211                 mUiHandler.post(mController::preFinalize);
    212             } else {
    213                 ProvisionLogger.loge("Trying to pre-finalize provisioning, but controller is null");
    214             }
    215         }
    216     }
    217 
    218     @Override
    219     public void preFinalizationCompleted() {
    220         synchronized (this) {
    221             for (ProvisioningManagerCallback callback : mCallbacks) {
    222                 mUiHandler.post(callback::preFinalizationCompleted);
    223             }
    224             mLastCallback = CALLBACK_PRE_FINALIZED;
    225             mProvisioningAnalyticsTracker.logProvisioningSessionCompleted(mContext);
    226             clearControllerLocked();
    227             ProvisionLogger.logi("ProvisioningManager pre-finalization completed");
    228         }
    229     }
    230 
    231     private void callLastCallbackLocked(ProvisioningManagerCallback callback) {
    232         switch (mLastCallback) {
    233             case CALLBACK_ERROR:
    234                 final Pair<Pair<Integer, Integer>, Boolean> error = mLastError;
    235                 mUiHandler.post(
    236                         () -> callback.error(error.first.first, error.first.second, error.second));
    237                 break;
    238             case CALLBACK_PROGRESS:
    239                 final int progressMsg = mLastProgressMsgId;
    240                 mUiHandler.post(() -> callback.progressUpdate(progressMsg));
    241                 break;
    242             case CALLBACK_PRE_FINALIZED:
    243                 mUiHandler.post(callback::preFinalizationCompleted);
    244                 break;
    245             default:
    246                 ProvisionLogger.logd("No previous callback");
    247         }
    248     }
    249 
    250     private void clearControllerLocked() {
    251         mController = null;
    252 
    253         if (mHandlerThread != null) {
    254             mHandlerThread.quitSafely();
    255             mHandlerThread = null;
    256             mContext.stopService(SERVICE_INTENT);
    257         }
    258     }
    259 }
    260