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.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 import static com.android.managedprovisioning.finalization.SendDpcBroadcastService.EXTRA_PROVISIONING_PARAMS;
24 
25 import android.annotation.IntDef;
26 import android.app.NotificationManager;
27 import android.app.admin.DevicePolicyManager;
28 import android.content.ComponentName;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.pm.PackageManager.NameNotFoundException;
32 import android.os.UserHandle;
33 
34 import com.android.internal.annotations.VisibleForTesting;
35 import com.android.managedprovisioning.analytics.DeferredMetricsReader;
36 import com.android.managedprovisioning.common.NotificationHelper;
37 import com.android.managedprovisioning.common.ProvisionLogger;
38 import com.android.managedprovisioning.common.SettingsFacade;
39 import com.android.managedprovisioning.common.Utils;
40 import com.android.managedprovisioning.model.ProvisioningParams;
41 import com.android.managedprovisioning.provisioning.Constants;
42 
43 import java.io.File;
44 
45 /**
46  * Controller for the finalization of managed provisioning.
47  *
48  * <p>This controller is invoked when the active provisioning is completed via
49  * {@link #provisioningInitiallyDone(ProvisioningParams)}. In the case of provisioning during SUW,
50  * it is invoked again when provisioning is finalized via {@link #provisioningFinalized()}.</p>
51  */
52 public class FinalizationController {
53     private static final String PROVISIONING_PARAMS_FILE_NAME =
54             "finalization_activity_provisioning_params.xml";
55     static final int PROVISIONING_FINALIZED_RESULT_DEFAULT = 1;
56     static final int PROVISIONING_FINALIZED_RESULT_ADMIN_WILL_LAUNCH = 2;
57     static final int PROVISIONING_FINALIZED_RESULT_EARLY_EXIT = 3;
58     @IntDef({
59             PROVISIONING_FINALIZED_RESULT_DEFAULT,
60             PROVISIONING_FINALIZED_RESULT_ADMIN_WILL_LAUNCH,
61             PROVISIONING_FINALIZED_RESULT_EARLY_EXIT})
62     @interface ProvisioningFinalizedResult {}
63 
64     private final Context mContext;
65     private final Utils mUtils;
66     private final SettingsFacade mSettingsFacade;
67     private final UserProvisioningStateHelper mUserProvisioningStateHelper;
68     private final ProvisioningIntentProvider mProvisioningIntentProvider;
69     private final NotificationHelper mNotificationHelper;
70     private final DeferredMetricsReader mDeferredMetricsReader;
71     private @ProvisioningFinalizedResult int mProvisioningFinalizedResult;
72 
FinalizationController(Context context, UserProvisioningStateHelper userProvisioningStateHelper)73     public FinalizationController(Context context,
74           UserProvisioningStateHelper userProvisioningStateHelper) {
75         this(
76                 context,
77                 new Utils(),
78                 new SettingsFacade(),
79                 userProvisioningStateHelper,
80                 new NotificationHelper(context),
81                 new DeferredMetricsReader(
82                         Constants.getDeferredMetricsFile(context)));
83     }
84 
FinalizationController(Context context)85     public FinalizationController(Context context) {
86         this(
87                 context,
88                 new Utils(),
89                 new SettingsFacade(),
90                 new UserProvisioningStateHelper(context),
91                 new NotificationHelper(context),
92                 new DeferredMetricsReader(
93                         Constants.getDeferredMetricsFile(context)));
94     }
95 
96     @VisibleForTesting
FinalizationController(Context context, Utils utils, SettingsFacade settingsFacade, UserProvisioningStateHelper helper, NotificationHelper notificationHelper, DeferredMetricsReader deferredMetricsReader)97     FinalizationController(Context context,
98             Utils utils,
99             SettingsFacade settingsFacade,
100             UserProvisioningStateHelper helper,
101             NotificationHelper notificationHelper,
102             DeferredMetricsReader deferredMetricsReader) {
103         mContext = checkNotNull(context);
104         mUtils = checkNotNull(utils);
105         mSettingsFacade = checkNotNull(settingsFacade);
106         mUserProvisioningStateHelper = checkNotNull(helper);
107         mProvisioningIntentProvider = new ProvisioningIntentProvider();
108         mNotificationHelper = checkNotNull(notificationHelper);
109         mDeferredMetricsReader = checkNotNull(deferredMetricsReader);
110     }
111 
112     /**
113      * This method is invoked when the provisioning process is done.
114      *
115      * <p>If provisioning happens as part of SUW, we rely on {@link #provisioningFinalized()} to be
116      * called at the end of SUW. Otherwise, this method will finalize provisioning. If called after
117      * SUW, this method notifies the DPC about the completed provisioning; otherwise, it stores the
118      * provisioning params for later digestion.</p>
119      *
120      * <p>Note that fully managed device provisioning is only possible during SUW.
121      *
122      * @param params the provisioning params
123      */
provisioningInitiallyDone(ProvisioningParams params)124     public void provisioningInitiallyDone(ProvisioningParams params) {
125         if (!mUserProvisioningStateHelper.isStateUnmanagedOrFinalized()) {
126             // In any other state than STATE_USER_UNMANAGED and STATE_USER_SETUP_FINALIZED, we've
127             // already run this method, so don't do anything.
128             // STATE_USER_SETUP_FINALIZED can occur here if a managed profile is provisioned on a
129             // device owner device.
130             ProvisionLogger.logw("provisioningInitiallyDone called, but state is not finalized or "
131                     + "unmanaged");
132             return;
133         }
134 
135         mUserProvisioningStateHelper.markUserProvisioningStateInitiallyDone(params);
136         if (ACTION_PROVISION_MANAGED_PROFILE.equals(params.provisioningAction)) {
137             if (params.isOrganizationOwnedProvisioning) {
138                 setProfileOwnerCanAccessDeviceIds();
139             }
140             if (!mSettingsFacade.isDuringSetupWizard(mContext)) {
141                 // If a managed profile was provisioned after SUW, notify the DPC straight away.
142                 notifyDpcManagedProfile(params);
143             }
144         }
145         if (mSettingsFacade.isDuringSetupWizard(mContext)) {
146             // Store the information and wait for provisioningFinalized to be called
147             storeProvisioningParams(params);
148         }
149     }
150 
setProfileOwnerCanAccessDeviceIds()151     private void setProfileOwnerCanAccessDeviceIds() {
152         final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
153         final int managedProfileUserId = mUtils.getManagedProfile(mContext).getIdentifier();
154         final ComponentName admin = dpm.getProfileOwnerAsUser(managedProfileUserId);
155         if (admin != null) {
156             try {
157                 final Context profileContext = mContext.createPackageContextAsUser(
158                         mContext.getPackageName(), 0 /* flags */,
159                         UserHandle.of(managedProfileUserId));
160                 final DevicePolicyManager profileDpm =
161                         profileContext.getSystemService(DevicePolicyManager.class);
162                 profileDpm.setProfileOwnerCanAccessDeviceIds(admin);
163             } catch (NameNotFoundException e) {
164                 ProvisionLogger.logw("Error setting access to Device IDs: " + e.getMessage());
165             }
166         }
167     }
168 
169     @VisibleForTesting
getPrimaryProfileFinalizationHelper( ProvisioningParams params)170     PrimaryProfileFinalizationHelper getPrimaryProfileFinalizationHelper(
171             ProvisioningParams params) {
172         return new PrimaryProfileFinalizationHelper(params.accountToMigrate,
173                 params.keepAccountMigrated, mUtils.getManagedProfile(mContext),
174                 params.inferDeviceAdminPackageName(), mUtils,
175                 mUtils.isAdminIntegratedFlow(params));
176     }
177 
178     /**
179      * This method is invoked when provisioning is finalized.
180      *
181      * <p>This method has to be invoked after {@link #provisioningInitiallyDone(ProvisioningParams)}
182      * was called. It is commonly invoked at the end of SUW if provisioning occurs during SUW. It
183      * loads the provisioning params from the storage, notifies the DPC about the completed
184      * provisioning and sets the right user provisioning states.
185      *
186      * <p>To retrieve the resulting state of this method, use
187      * {@link #getProvisioningFinalizedResult()}
188      */
provisioningFinalized()189     void provisioningFinalized() {
190         mProvisioningFinalizedResult = PROVISIONING_FINALIZED_RESULT_EARLY_EXIT;
191         mDeferredMetricsReader.scheduleDumpMetrics(mContext);
192 
193         if (mUserProvisioningStateHelper.isStateUnmanagedOrFinalized()) {
194             ProvisionLogger.logw("provisioningInitiallyDone called, but state is finalized or "
195                     + "unmanaged");
196             return;
197         }
198 
199         final ProvisioningParams params = loadProvisioningParamsAndClearFile();
200         if (params == null) {
201             ProvisionLogger.logw("FinalizationController invoked, but no stored params");
202             return;
203         }
204 
205         mProvisioningFinalizedResult = PROVISIONING_FINALIZED_RESULT_DEFAULT;
206         if (mUtils.isAdminIntegratedFlow(params)) {
207             // Don't send ACTION_PROFILE_PROVISIONING_COMPLETE broadcast to DPC or launch DPC by
208             // ACTION_PROVISIONING_SUCCESSFUL intent if it's admin integrated flow.
209             if (params.provisioningAction.equals(ACTION_PROVISION_MANAGED_PROFILE)) {
210                 getPrimaryProfileFinalizationHelper(params)
211                         .finalizeProvisioningInPrimaryProfile(mContext, null);
212             } else if (ACTION_PROVISION_MANAGED_DEVICE.equals(params.provisioningAction)) {
213                 mNotificationHelper.showPrivacyReminderNotification(
214                         mContext, NotificationManager.IMPORTANCE_DEFAULT);
215             }
216             mProvisioningIntentProvider.launchFinalizationScreen(mContext, params);
217         } else {
218             if (params.provisioningAction.equals(ACTION_PROVISION_MANAGED_PROFILE)) {
219                 notifyDpcManagedProfile(params);
220                 final UserHandle managedProfileUserHandle = mUtils.getManagedProfile(mContext);
221                 final int userId = managedProfileUserHandle.getIdentifier();
222                 mProvisioningFinalizedResult =
223                         mProvisioningIntentProvider.canLaunchDpc(params, userId, mUtils, mContext)
224                         ? PROVISIONING_FINALIZED_RESULT_ADMIN_WILL_LAUNCH
225                         : PROVISIONING_FINALIZED_RESULT_DEFAULT;
226             } else {
227                 // For managed user and device owner, we send the provisioning complete intent and
228                 // maybe launch the DPC.
229                 final int userId = UserHandle.myUserId();
230                 final Intent provisioningCompleteIntent = mProvisioningIntentProvider
231                         .createProvisioningCompleteIntent(params, userId, mUtils, mContext);
232                 if (provisioningCompleteIntent == null) {
233                     return;
234                 }
235                 mContext.sendBroadcast(provisioningCompleteIntent);
236 
237                 mProvisioningIntentProvider.maybeLaunchDpc(params, userId, mUtils, mContext);
238 
239                 if (ACTION_PROVISION_MANAGED_DEVICE.equals(params.provisioningAction)) {
240                     mNotificationHelper.showPrivacyReminderNotification(
241                             mContext, NotificationManager.IMPORTANCE_DEFAULT);
242                 }
243             }
244         }
245 
246         mUserProvisioningStateHelper.markUserProvisioningStateFinalized(params);
247     }
248 
249     /**
250      * @throws IllegalStateException if {@link #provisioningFinalized()} was not called before.
251      */
getProvisioningFinalizedResult()252     @ProvisioningFinalizedResult int getProvisioningFinalizedResult() {
253         if (mProvisioningFinalizedResult == 0) {
254             throw new IllegalStateException("provisioningFinalized() has not been called.");
255         }
256         return mProvisioningFinalizedResult;
257     }
258 
259     /**
260      * Start a service which notifies the DPC on the managed profile that provisioning has
261      * completed. When the DPC has received the intent, send notify the primary instance that the
262      * profile is ready. The service is needed to prevent the managed provisioning process from
263      * getting killed while the user is on the DPC screen.
264      */
notifyDpcManagedProfile(ProvisioningParams params)265     private void notifyDpcManagedProfile(ProvisioningParams params) {
266         mContext.startService(
267                 new Intent(mContext, SendDpcBroadcastService.class)
268                         .putExtra(EXTRA_PROVISIONING_PARAMS, params));
269     }
270 
storeProvisioningParams(ProvisioningParams params)271     private void storeProvisioningParams(ProvisioningParams params) {
272         params.save(getProvisioningParamsFile());
273     }
274 
getProvisioningParamsFile()275     private File getProvisioningParamsFile() {
276         return new File(mContext.getFilesDir(), PROVISIONING_PARAMS_FILE_NAME);
277     }
278 
279     @VisibleForTesting
loadProvisioningParamsAndClearFile()280     ProvisioningParams loadProvisioningParamsAndClearFile() {
281         File file = getProvisioningParamsFile();
282         ProvisioningParams result = ProvisioningParams.load(file);
283         file.delete();
284         return result;
285     }
286 }
287