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