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