1 /*
2  * Copyright 2019, 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.finalization;
18 
19 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE;
20 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
21 
22 import static com.android.internal.util.Preconditions.checkNotNull;
23 
24 import android.annotation.IntDef;
25 import android.app.Activity;
26 import android.app.NotificationManager;
27 import android.os.Bundle;
28 import android.os.UserManager;
29 
30 import com.android.internal.annotations.VisibleForTesting;
31 import com.android.managedprovisioning.analytics.DeferredMetricsReader;
32 import com.android.managedprovisioning.common.NotificationHelper;
33 import com.android.managedprovisioning.common.ProvisionLogger;
34 import com.android.managedprovisioning.common.SettingsFacade;
35 import com.android.managedprovisioning.common.Utils;
36 import com.android.managedprovisioning.model.ProvisioningParams;
37 import com.android.managedprovisioning.provisioning.Constants;
38 
39 import java.io.File;
40 
41 /**
42  * Controller for the finalization of managed provisioning.  This class should be invoked after
43  * {@link PreFinalizationController}.  Provisioning is finalized via calls to
44  * {@link #provisioningFinalized()} and {@link #commitFinalizedState()}.  Different instances of
45  * this class will be tailored to run these two methods at different points in the Setup Wizard user
46  * flows, based on the type of FinalizationControllerLogic they are constructed with.
47  */
48 public final class FinalizationController {
49 
50     static final int PROVISIONING_FINALIZED_RESULT_NO_CHILD_ACTIVITY_LAUNCHED = 1;
51     static final int PROVISIONING_FINALIZED_RESULT_CHILD_ACTIVITY_LAUNCHED = 2;
52     static final int PROVISIONING_FINALIZED_RESULT_SKIPPED = 3;
53     static final int PROVISIONING_FINALIZED_RESULT_WAIT_FOR_WORK_PROFILE_AVAILABLE = 4;
54     @IntDef({
55             PROVISIONING_FINALIZED_RESULT_NO_CHILD_ACTIVITY_LAUNCHED,
56             PROVISIONING_FINALIZED_RESULT_CHILD_ACTIVITY_LAUNCHED,
57             PROVISIONING_FINALIZED_RESULT_SKIPPED,
58             PROVISIONING_FINALIZED_RESULT_WAIT_FOR_WORK_PROFILE_AVAILABLE})
59     @interface ProvisioningFinalizedResult {}
60 
61     private static final int DPC_SETUP_REQUEST_CODE = 1;
62     private static final int FINAL_SCREEN_REQUEST_CODE = 2;
63 
64     private final FinalizationControllerLogic mFinalizationControllerLogic;
65     private final Activity mActivity;
66     private final Utils mUtils;
67     private final SettingsFacade mSettingsFacade;
68     private final UserProvisioningStateHelper mUserProvisioningStateHelper;
69     private final ProvisioningIntentProvider mProvisioningIntentProvider;
70     private final NotificationHelper mNotificationHelper;
71     private final DeferredMetricsReader mDeferredMetricsReader;
72     private @ProvisioningFinalizedResult int mProvisioningFinalizedResult;
73     private ProvisioningParamsUtils mProvisioningParamsUtils;
74 
FinalizationController(Activity activity, FinalizationControllerLogic finalizationControllerLogic, UserProvisioningStateHelper userProvisioningStateHelper)75     public FinalizationController(Activity activity,
76             FinalizationControllerLogic finalizationControllerLogic,
77             UserProvisioningStateHelper userProvisioningStateHelper) {
78         this(
79                 activity,
80                 finalizationControllerLogic,
81                 new Utils(),
82                 new SettingsFacade(),
83                 userProvisioningStateHelper,
84                 new NotificationHelper(activity),
85                 new DeferredMetricsReader(
86                         Constants.getDeferredMetricsFile(activity)),
87                 new ProvisioningParamsUtils());
88     }
89 
FinalizationController(Activity activity, FinalizationControllerLogic finalizationControllerLogic)90     public FinalizationController(Activity activity,
91             FinalizationControllerLogic finalizationControllerLogic) {
92         this(
93                 activity,
94                 finalizationControllerLogic,
95                 new Utils(),
96                 new SettingsFacade(),
97                 new UserProvisioningStateHelper(activity),
98                 new NotificationHelper(activity),
99                 new DeferredMetricsReader(
100                         Constants.getDeferredMetricsFile(activity)),
101                 new ProvisioningParamsUtils());
102     }
103 
104     @VisibleForTesting
FinalizationController(Activity activity, FinalizationControllerLogic finalizationControllerLogic, Utils utils, SettingsFacade settingsFacade, UserProvisioningStateHelper helper, NotificationHelper notificationHelper, DeferredMetricsReader deferredMetricsReader, ProvisioningParamsUtils provisioningParamsUtils)105     FinalizationController(Activity activity,
106             FinalizationControllerLogic finalizationControllerLogic,
107             Utils utils,
108             SettingsFacade settingsFacade,
109             UserProvisioningStateHelper helper,
110             NotificationHelper notificationHelper,
111             DeferredMetricsReader deferredMetricsReader,
112             ProvisioningParamsUtils provisioningParamsUtils) {
113         mActivity = checkNotNull(activity);
114         mFinalizationControllerLogic = checkNotNull(finalizationControllerLogic);
115         mUtils = checkNotNull(utils);
116         mSettingsFacade = checkNotNull(settingsFacade);
117         mUserProvisioningStateHelper = checkNotNull(helper);
118         mProvisioningIntentProvider = new ProvisioningIntentProvider();
119         mNotificationHelper = checkNotNull(notificationHelper);
120         mDeferredMetricsReader = checkNotNull(deferredMetricsReader);
121         mProvisioningParamsUtils = provisioningParamsUtils;
122     }
123 
124     @VisibleForTesting
getPrimaryProfileFinalizationHelper( ProvisioningParams params)125     final PrimaryProfileFinalizationHelper getPrimaryProfileFinalizationHelper(
126             ProvisioningParams params) {
127         return new PrimaryProfileFinalizationHelper(params.accountToMigrate,
128                 mUtils.getManagedProfile(mActivity), params.inferDeviceAdminPackageName());
129     }
130 
131     /**
132      * This method is invoked when provisioning is finalized.
133      *
134      * <p>This method has to be invoked after
135      * {@link PreFinalizationController#deviceManagementEstablished(ProvisioningParams)}
136      * was called. It is commonly invoked at the end of the setup flow, if provisioning occurs
137      * during the setup flow. It loads the provisioning params from the storage, notifies the DPC
138      * about the completed provisioning and sets the right user provisioning states.
139      *
140      * <p>To retrieve the resulting state of this method, use
141      * {@link #getProvisioningFinalizedResult()}
142      *
143      * <p>This method may be called multiple times.  {@link #commitFinalizedState()} ()} must be
144      * called after the final call to this method.  If this method is called again after that, it
145      * will return immediately without taking any action.
146      */
provisioningFinalized()147     final void provisioningFinalized() {
148         mProvisioningFinalizedResult = PROVISIONING_FINALIZED_RESULT_SKIPPED;
149 
150         if (mUserProvisioningStateHelper.isStateUnmanagedOrFinalized()) {
151             ProvisionLogger.logw("provisioningFinalized called, but state is finalized or "
152                     + "unmanaged");
153             return;
154         }
155 
156         final ProvisioningParams params = loadProvisioningParams();
157         if (params == null) {
158             ProvisionLogger.logw("FinalizationController invoked, but no stored params");
159             return;
160         }
161 
162         mProvisioningFinalizedResult = PROVISIONING_FINALIZED_RESULT_NO_CHILD_ACTIVITY_LAUNCHED;
163         if (params.provisioningAction.equals(ACTION_PROVISION_MANAGED_PROFILE)) {
164             UserManager userManager = mActivity.getSystemService(UserManager.class);
165             if (!userManager.isUserUnlocked(mUtils.getManagedProfile(mActivity))) {
166                 mProvisioningFinalizedResult =
167                         PROVISIONING_FINALIZED_RESULT_WAIT_FOR_WORK_PROFILE_AVAILABLE;
168             } else {
169                 mProvisioningFinalizedResult =
170                         mFinalizationControllerLogic.notifyDpcManagedProfile(
171                                 params, DPC_SETUP_REQUEST_CODE);
172             }
173         } else {
174             mProvisioningFinalizedResult =
175                     mFinalizationControllerLogic.notifyDpcManagedDeviceOrUser(
176                             params, DPC_SETUP_REQUEST_CODE);
177         }
178     }
179 
180     /**
181      * @throws IllegalStateException if {@link #provisioningFinalized()} was not called before.
182      */
getProvisioningFinalizedResult()183     final @ProvisioningFinalizedResult int getProvisioningFinalizedResult() {
184         if (mProvisioningFinalizedResult == 0) {
185             throw new IllegalStateException("provisioningFinalized() has not been called.");
186         }
187         return mProvisioningFinalizedResult;
188     }
189 
190     @VisibleForTesting
clearParamsFile()191     final void clearParamsFile() {
192         final File file = mProvisioningParamsUtils.getProvisioningParamsFile(mActivity);
193         if (file != null) {
194             file.delete();
195         }
196     }
197 
loadProvisioningParams()198     private ProvisioningParams loadProvisioningParams() {
199         final File file = mProvisioningParamsUtils.getProvisioningParamsFile(mActivity);
200         final ProvisioningParams params = ProvisioningParams.load(file);
201         return params;
202     }
203 
204     /**
205      * Update the system's provisioning state, and commit any other irreversible changes that
206      * must wait until finalization is 100% completed.
207      */
commitFinalizedState(ProvisioningParams params)208     private void commitFinalizedState(ProvisioningParams params) {
209         if (ACTION_PROVISION_MANAGED_DEVICE.equals(params.provisioningAction)) {
210             mNotificationHelper.showPrivacyReminderNotification(
211                     mActivity, NotificationManager.IMPORTANCE_DEFAULT);
212         } else if (ACTION_PROVISION_MANAGED_PROFILE.equals(params.provisioningAction)
213                 && mFinalizationControllerLogic.shouldFinalizePrimaryProfile(params)) {
214             getPrimaryProfileFinalizationHelper(params)
215                     .finalizeProvisioningInPrimaryProfile(mActivity, null);
216         }
217 
218         mUserProvisioningStateHelper.markUserProvisioningStateFinalized(params);
219 
220         mDeferredMetricsReader.scheduleDumpMetrics(mActivity);
221         clearParamsFile();
222     }
223 
224     /**
225      * This method is called by the parent activity to force the final commit of all state changes.
226      * After this is called, any further calls to {@link #provisioningFinalized()} will return
227      * immediately without taking any action.
228      */
commitFinalizedState()229     final void commitFinalizedState() {
230         final ProvisioningParams params = loadProvisioningParams();
231         if (params == null) {
232             ProvisionLogger.logw(
233                     "Attempt to commitFinalizedState when params have already been deleted");
234         } else {
235             commitFinalizedState(loadProvisioningParams());
236         }
237     }
238 
239     /**
240      * This method is called when onSaveInstanceState() executes on the finalization activity.
241      */
saveInstanceState(Bundle outState)242     final void saveInstanceState(Bundle outState) {
243         mFinalizationControllerLogic.saveInstanceState(outState);
244     }
245 
246     /**
247      * When saved instance state is passed to the finalization activity in its onCreate() method,
248      * that state is passed to the FinalizationControllerLogic object here so it can be restored.
249      */
restoreInstanceState(Bundle savedInstanceState)250     final void restoreInstanceState(Bundle savedInstanceState) {
251         mFinalizationControllerLogic.restoreInstanceState(savedInstanceState,
252                 loadProvisioningParams());
253     }
254 
255     /**
256      * Cleanup that must happen when the finalization activity is destroyed, even if we haven't yet
257      * called {@link #commitFinalizedState()} to finalize the system's provisioning state.
258      */
activityDestroyed(boolean isFinishing)259     final void activityDestroyed(boolean isFinishing) {
260         mFinalizationControllerLogic.activityDestroyed(isFinishing);
261     }
262 }
263