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