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.managedprovisioning.analytics.ProvisioningAnalyticsTracker.CANCELLED_DURING_PROVISIONING;
21 
22 import static java.util.Objects.requireNonNull;
23 
24 import android.content.Context;
25 import android.os.Bundle;
26 
27 import androidx.appcompat.app.AppCompatActivity;
28 import androidx.lifecycle.ViewModelProvider;
29 import androidx.lifecycle.ViewModelStoreOwner;
30 
31 import com.android.internal.annotations.GuardedBy;
32 import com.android.internal.annotations.VisibleForTesting;
33 import com.android.managedprovisioning.analytics.MetricsWriterFactory;
34 import com.android.managedprovisioning.analytics.ProvisioningAnalyticsTracker;
35 import com.android.managedprovisioning.analytics.TimeLogger;
36 import com.android.managedprovisioning.common.ManagedProvisioningSharedPreferences;
37 import com.android.managedprovisioning.common.ProvisionLogger;
38 import com.android.managedprovisioning.common.SettingsFacade;
39 import com.android.managedprovisioning.model.ProvisioningParams;
40 import com.android.managedprovisioning.provisioning.ProvisioningViewModel.ProvisioningViewModelFactory;
41 import com.android.managedprovisioning.provisioning.TransitionAnimationHelper.TransitionAnimationState;
42 
43 import com.google.android.setupcompat.logging.ScreenKey;
44 import com.google.android.setupcompat.logging.SetupMetric;
45 import com.google.android.setupcompat.logging.SetupMetricsLogger;
46 
47 /**
48  * Singleton instance that provides communications between the ongoing provisioning process and the
49  * UI layer.
50  * <p>{@link #setViewModelStoreOwner(ViewModelStoreOwner)} must be called when the view model store
51  * owner has been created (for example, {@link AppCompatActivity#onCreate(Bundle)}). In addition,
52  * to prevent memory leaks when the view model store owner is destroyed, {@link
53  * #clearViewModelStoreOwner()} must be called (for example, in {@link
54  * AppCompatActivity#onDestroy()} when {@link AppCompatActivity#isFinishing()} is {@code true}).
55  */
56 public class ProvisioningManager implements ProvisioningControllerCallback,
57         ProvisioningManagerInterface {
58 
59     private static ProvisioningManager sInstance;
60 
61     private final Context mContext;
62     private final ProvisioningControllerFactory mFactory;
63     private final ProvisioningAnalyticsTracker mProvisioningAnalyticsTracker;
64     private final TimeLogger mTimeLogger;
65     private final ProvisioningManagerHelper mHelper;
66     private ProvisioningViewModel mViewModel;
67     private final ScreenKey mScreenKey;
68     private static String setupMetricScreenName = "ProvisioningScreen";
69     private static String setupMetricProvisioningStartedName = "ProvisioningStarted";
70 
71     @GuardedBy("this")
72     private AbstractProvisioningController mController;
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 ProvisioningControllerFactory(),
85                 new ProvisioningAnalyticsTracker(
86                         MetricsWriterFactory.getMetricsWriter(context, new SettingsFacade()),
87                         new ManagedProvisioningSharedPreferences(context)),
88                 new TimeLogger(context, PROVISIONING_TOTAL_TASK_TIME_MS));
89     }
90 
91     @VisibleForTesting
ProvisioningManager( Context context, ProvisioningControllerFactory factory, ProvisioningAnalyticsTracker analyticsTracker, TimeLogger timeLogger)92     ProvisioningManager(
93             Context context,
94             ProvisioningControllerFactory factory,
95             ProvisioningAnalyticsTracker analyticsTracker,
96             TimeLogger timeLogger) {
97         mContext = requireNonNull(context);
98         mFactory = requireNonNull(factory);
99         mProvisioningAnalyticsTracker = requireNonNull(analyticsTracker);
100         mTimeLogger = requireNonNull(timeLogger);
101         mHelper = new ProvisioningManagerHelper();
102         mScreenKey = ScreenKey.of(setupMetricScreenName, context);
103     }
104 
105     /**
106      * Sets the view model store owner.
107      * <p>Must be called when the view model store owner has been created (for example, {@link
108      * AppCompatActivity#onCreate(Bundle)}).
109      * @see #clearViewModelStoreOwner()
110      */
setViewModelStoreOwner(ViewModelStoreOwner owner)111     void setViewModelStoreOwner(ViewModelStoreOwner owner) {
112         mViewModel = new ViewModelProvider(owner, new ProvisioningViewModelFactory())
113                 .get(ProvisioningViewModel.class);
114     }
115 
116     /**
117      * Clears the view model store owner.
118      * <p>Must be called when the view model store owner is getting destroyed (for example, in
119      * {@link AppCompatActivity#onDestroy()} when {@link AppCompatActivity#isFinishing()} is {@code
120      * true}), in order to clean the reference and prevent memory leaks.
121      */
clearViewModelStoreOwner()122     void clearViewModelStoreOwner() {
123         mViewModel = null;
124     }
125 
126     @Override
maybeStartProvisioning(final ProvisioningParams params)127     public void maybeStartProvisioning(final ProvisioningParams params) {
128         synchronized (this) {
129             if (mController == null) {
130                 SetupMetricsLogger.logMetrics(mContext, mScreenKey,
131                         SetupMetric.ofWaitingStart(setupMetricProvisioningStartedName));
132                 mTimeLogger.start();
133                 mController = getController(params);
134                 mHelper.startNewProvisioningLocked(mController);
135                 mProvisioningAnalyticsTracker.logProvisioningStarted(mContext, params);
136             } else {
137                 ProvisionLogger.loge("Trying to start provisioning, but it's already running");
138             }
139         }
140     }
141 
142     @Override
registerListener(ProvisioningManagerCallback callback)143     public void registerListener(ProvisioningManagerCallback callback) {
144         mHelper.registerListener(callback);
145     }
146 
147     @Override
unregisterListener(ProvisioningManagerCallback callback)148     public void unregisterListener(ProvisioningManagerCallback callback) {
149         mHelper.unregisterListener(callback);
150     }
151 
152     @Override
cancelProvisioning()153     public void cancelProvisioning() {
154         synchronized (this) {
155             final boolean provisioningCanceled = mHelper.cancelProvisioning(mController);
156             if (provisioningCanceled) {
157                 mProvisioningAnalyticsTracker.logProvisioningCancelled(mContext,
158                         CANCELLED_DURING_PROVISIONING);
159             }
160         }
161     }
162 
163     @Override
provisioningTasksCompleted()164     public void provisioningTasksCompleted() {
165         synchronized (this) {
166             mTimeLogger.stop();
167             preFinalizationCompleted();
168         }
169     }
170 
171     @Override
preFinalizationCompleted()172     public void preFinalizationCompleted() {
173         synchronized (this) {
174             mHelper.notifyPreFinalizationCompleted();
175             mProvisioningAnalyticsTracker.logProvisioningSessionCompleted(mContext);
176             clearControllerLocked();
177             ProvisionLogger.logi("ProvisioningManager pre-finalization completed");
178         }
179     }
180 
181     @Override
cleanUpCompleted()182     public void cleanUpCompleted() {
183         synchronized (this) {
184             clearControllerLocked();
185         }
186     }
187 
188     @Override
error(int titleId, int messageId, boolean factoryResetRequired)189     public void error(int titleId, int messageId, boolean factoryResetRequired) {
190         mHelper.error(titleId, messageId, factoryResetRequired);
191     }
192 
193     @Override
error(int titleId, String errorMessage, boolean factoryResetRequired)194     public void error(int titleId, String errorMessage, boolean factoryResetRequired) {
195         mHelper.error(titleId, errorMessage, factoryResetRequired);
196     }
197 
saveTransitionAnimationState(TransitionAnimationState transitionAnimationState)198     void saveTransitionAnimationState(TransitionAnimationState transitionAnimationState) {
199         mViewModel.saveTransitionAnimationState(transitionAnimationState);
200     }
201 
restoreTransitionAnimationState()202     TransitionAnimationState restoreTransitionAnimationState() {
203         return mViewModel.restoreTransitionAnimationState();
204     }
205 
getController(ProvisioningParams params)206     private AbstractProvisioningController getController(ProvisioningParams params) {
207         return mFactory.createProvisioningController(mContext, params, this);
208     }
209 
clearControllerLocked()210     private void clearControllerLocked() {
211         mController = null;
212         mHelper.clearResourcesLocked();
213     }
214 }
215