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 57 public FinalizationController(Context context) { 58 this( 59 context, 60 new Utils(), 61 new SettingsFacade(), 62 new UserProvisioningStateHelper(context)); 63 } 64 65 @VisibleForTesting 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 */ 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 */ 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 */ 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 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 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 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 205 private void addExtrasToIntent(Intent intent, ProvisioningParams params) { 206 intent.putExtra(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE, params.adminExtrasBundle); 207 } 208 209 private void storeProvisioningParams(ProvisioningParams params) { 210 params.save(getProvisioningParamsFile()); 211 } 212 213 private File getProvisioningParamsFile() { 214 return new File(mContext.getFilesDir(), PROVISIONING_PARAMS_FILE_NAME); 215 } 216 217 @VisibleForTesting 218 ProvisioningParams loadProvisioningParamsAndClearFile() { 219 File file = getProvisioningParamsFile(); 220 ProvisioningParams result = ProvisioningParams.load(file); 221 file.delete(); 222 return result; 223 } 224 } 225