• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
getInstance(Context context)74     public static ProvisioningManager getInstance(Context context) {
75         if (sInstance == null) {
76             sInstance = new ProvisioningManager(context.getApplicationContext());
77         }
78         return sInstance;
79     }
80 
ProvisioningManager(Context context)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
ProvisioningManager( Context context, Handler uiHandler, ProvisioningControllerFactory factory, ProvisioningAnalyticsTracker analyticsTracker, TimeLogger timeLogger)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      */
maybeStartProvisioning(final ProvisioningParams params)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 
startNewProvisioningLocked(final ProvisioningParams params)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      */
cancelProvisioning()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      */
registerListener(ProvisioningManagerCallback callback)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      */
unregisterListener(ProvisioningManagerCallback callback)171     public void unregisterListener(ProvisioningManagerCallback callback) {
172         synchronized (this) {
173             mCallbacks.remove(callback);
174         }
175     }
176 
177     @Override
cleanUpCompleted()178     public void cleanUpCompleted() {
179         synchronized (this) {
180             clearControllerLocked();
181         }
182     }
183 
184     @Override
error(int titleId, int messageId, boolean factoryResetRequired)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
progressUpdate(int progressMsgId)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
provisioningTasksCompleted()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
preFinalizationCompleted()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 
callLastCallbackLocked(ProvisioningManagerCallback callback)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 
clearControllerLocked()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