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