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.DeviceAdminReceiver.ACTION_PROFILE_PROVISIONING_COMPLETE;
20 import static android.app.admin.DevicePolicyManager.ACTION_PROVISIONING_SUCCESSFUL;
21 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
22 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE;
23 import static com.android.internal.util.Preconditions.checkNotNull;
24 
25 import android.annotation.NonNull;
26 import android.app.Activity;
27 import android.content.BroadcastReceiver;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.os.UserHandle;
31 
32 import com.android.internal.annotations.VisibleForTesting;
33 import com.android.managedprovisioning.common.IllegalProvisioningArgumentException;
34 import com.android.managedprovisioning.common.ProvisionLogger;
35 import com.android.managedprovisioning.common.SettingsFacade;
36 import com.android.managedprovisioning.common.Utils;
37 import com.android.managedprovisioning.model.ProvisioningParams;
38 
39 import java.io.File;
40 
41 /**
42  * Controller for the finalization of managed provisioning.
43  *
44  * <p>This controller is invoked when the active provisioning is completed via
45  * {@link #provisioningInitiallyDone(ProvisioningParams)}. In the case of provisioning during SUW,
46  * it is invoked again when provisioning is finalized via {@link #provisioningFinalized()}.</p>
47  */
48 public class FinalizationController {
49     private static final String PROVISIONING_PARAMS_FILE_NAME =
50             "finalization_activity_provisioning_params.xml";
51 
52     private final Context mContext;
53     private final Utils mUtils;
54     private final SettingsFacade mSettingsFacade;
55     private final UserProvisioningStateHelper mHelper;
56 
FinalizationController(Context context)57     public FinalizationController(Context context) {
58         this(
59                 context,
60                 new Utils(),
61                 new SettingsFacade(),
62                 new UserProvisioningStateHelper(context));
63     }
64 
65     @VisibleForTesting
FinalizationController(Context context, Utils utils, SettingsFacade settingsFacade, UserProvisioningStateHelper helper)66     FinalizationController(Context context,
67             Utils utils,
68             SettingsFacade settingsFacade,
69             UserProvisioningStateHelper helper) {
70         mContext = checkNotNull(context);
71         mUtils = checkNotNull(utils);
72         mSettingsFacade = checkNotNull(settingsFacade);
73         mHelper = checkNotNull(helper);
74     }
75 
76     /**
77      * This method is invoked when the provisioning process is done.
78      *
79      * <p>If provisioning happens as part of SUW, we rely on {@link #provisioningFinalized()} to be
80      * called at the end of SUW. Otherwise, this method will finalize provisioning. If called after
81      * SUW, this method notifies the DPC about the completed provisioning; otherwise, it stores the
82      * provisioning params for later digestion.</p>
83      *
84      * @param params the provisioning params
85      */
provisioningInitiallyDone(ProvisioningParams params)86     public void provisioningInitiallyDone(ProvisioningParams params) {
87         if (!mHelper.isStateUnmanagedOrFinalized()) {
88             // In any other state than STATE_USER_UNMANAGED and STATE_USER_SETUP_FINALIZED, we've
89             // already run this method, so don't do anything.
90             // STATE_USER_SETUP_FINALIZED can occur here if a managed profile is provisioned on a
91             // device owner device.
92             ProvisionLogger.logw("provisioningInitiallyDone called, but state is not finalized or "
93                     + "unmanaged");
94             return;
95         }
96 
97         mHelper.markUserProvisioningStateInitiallyDone(params);
98         if (ACTION_PROVISION_MANAGED_PROFILE.equals(params.provisioningAction)
99                 && mSettingsFacade.isUserSetupCompleted(mContext)) {
100             // If a managed profile was provisioned after SUW, notify the DPC straight away
101             notifyDpcManagedProfile(params);
102         } else {
103             // Otherwise store the information and wait for provisioningFinalized to be called
104             storeProvisioningParams(params);
105         }
106     }
107 
108     /**
109      * This method is invoked when provisioning is finalized.
110      *
111      * <p>This method has to be invoked after {@link #provisioningInitiallyDone(ProvisioningParams)}
112      * was called. It is commonly invoked at the end of SUW if provisioning occurs during SUW. It
113      * loads the provisioning params from the storage, notifies the DPC about the completed
114      * provisioning and sets the right user provisioning states.</p>
115      */
provisioningFinalized()116     void provisioningFinalized() {
117         if (mHelper.isStateUnmanagedOrFinalized()) {
118             ProvisionLogger.logw("provisioningInitiallyDone called, but state is finalized or "
119                     + "unmanaged");
120             return;
121         }
122 
123         final ProvisioningParams params = loadProvisioningParamsAndClearFile();
124         if (params == null) {
125             ProvisionLogger.logw("FinalizationController invoked, but no stored params");
126             return;
127         }
128 
129         if (params.provisioningAction.equals(ACTION_PROVISION_MANAGED_PROFILE)) {
130             notifyDpcManagedProfile(params);
131         } else {
132             // For managed user and device owner, we send the provisioning complete intent and maybe
133             // launch the DPC.
134             Intent provisioningCompleteIntent = createProvisioningCompleteIntent(params);
135             if (provisioningCompleteIntent == null) {
136                 return;
137             }
138             mContext.sendBroadcast(provisioningCompleteIntent);
139 
140             maybeLaunchDpc(params, UserHandle.myUserId());
141         }
142 
143         mHelper.markUserProvisioningStateFinalized(params);
144     }
145 
146     /**
147      * Notify the DPC on the managed profile that provisioning has completed. When the DPC has
148      * received the intent, send notify the primary instance that the profile is ready.
149      */
notifyDpcManagedProfile(ProvisioningParams params)150     private void notifyDpcManagedProfile(ProvisioningParams params) {
151         UserHandle managedUserHandle = mUtils.getManagedProfile(mContext);
152 
153         // Use an ordered broadcast, so that we only finish when the DPC has received it.
154         // Avoids a lag in the transition between provisioning and the DPC.
155         BroadcastReceiver dpcReceivedSuccessReceiver =
156                 new DpcReceivedSuccessReceiver(params.accountToMigrate,
157                         params.keepAccountMigrated, managedUserHandle,
158                         params.deviceAdminComponentName.getPackageName());
159         Intent completeIntent = createProvisioningCompleteIntent(params);
160 
161         mContext.sendOrderedBroadcastAsUser(completeIntent, managedUserHandle, null,
162                 dpcReceivedSuccessReceiver, null, Activity.RESULT_OK, null, null);
163         ProvisionLogger.logd("Provisioning complete broadcast has been sent to user "
164                 + managedUserHandle.getIdentifier());
165 
166         maybeLaunchDpc(params, managedUserHandle.getIdentifier());
167     }
168 
maybeLaunchDpc(ProvisioningParams params, int userId)169     private void maybeLaunchDpc(ProvisioningParams params, int userId) {
170         final Intent dpcLaunchIntent = createDpcLaunchIntent(params);
171         if (mUtils.canResolveIntentAsUser(mContext, dpcLaunchIntent, userId)) {
172             mContext.startActivityAsUser(dpcLaunchIntent, UserHandle.of(userId));
173             ProvisionLogger.logd("Dpc was launched for user: " + userId);
174         }
175     }
176 
createProvisioningCompleteIntent(@onNull ProvisioningParams params)177     private Intent createProvisioningCompleteIntent(@NonNull ProvisioningParams params) {
178         Intent intent = new Intent(ACTION_PROFILE_PROVISIONING_COMPLETE);
179         try {
180             intent.setComponent(mUtils.findDeviceAdmin(
181                     params.deviceAdminPackageName,
182                     params.deviceAdminComponentName, mContext));
183         } catch (IllegalProvisioningArgumentException e) {
184             ProvisionLogger.loge("Failed to infer the device admin component name", e);
185             return null;
186         }
187         intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES | Intent.FLAG_RECEIVER_FOREGROUND);
188         addExtrasToIntent(intent, params);
189         return intent;
190     }
191 
createDpcLaunchIntent(@onNull ProvisioningParams params)192     private Intent createDpcLaunchIntent(@NonNull ProvisioningParams params) {
193         Intent intent = new Intent(ACTION_PROVISIONING_SUCCESSFUL);
194         final String packageName = params.inferDeviceAdminPackageName();
195         if (packageName == null) {
196             ProvisionLogger.loge("Device admin package name is null");
197             return null;
198         }
199         intent.setPackage(packageName);
200         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
201         addExtrasToIntent(intent, params);
202         return intent;
203     }
204 
addExtrasToIntent(Intent intent, ProvisioningParams params)205     private void addExtrasToIntent(Intent intent, ProvisioningParams params) {
206         intent.putExtra(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE, params.adminExtrasBundle);
207     }
208 
storeProvisioningParams(ProvisioningParams params)209     private void storeProvisioningParams(ProvisioningParams params) {
210         params.save(getProvisioningParamsFile());
211     }
212 
getProvisioningParamsFile()213     private File getProvisioningParamsFile() {
214         return new File(mContext.getFilesDir(), PROVISIONING_PARAMS_FILE_NAME);
215     }
216 
217     @VisibleForTesting
loadProvisioningParamsAndClearFile()218     ProvisioningParams loadProvisioningParamsAndClearFile() {
219         File file = getProvisioningParamsFile();
220         ProvisioningParams result = ProvisioningParams.load(file);
221         file.delete();
222         return result;
223     }
224 }
225