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 int userId = UserHandle.myUserId(); 135 Intent provisioningCompleteIntent = createProvisioningCompleteIntent(params, userId); 136 if (provisioningCompleteIntent == null) { 137 return; 138 } 139 mContext.sendBroadcast(provisioningCompleteIntent); 140 141 maybeLaunchDpc(params, userId); 142 } 143 144 mHelper.markUserProvisioningStateFinalized(params); 145 } 146 147 /** 148 * Notify the DPC on the managed profile that provisioning has completed. When the DPC has 149 * received the intent, send notify the primary instance that the profile is ready. 150 */ 151 private void notifyDpcManagedProfile(ProvisioningParams params) { 152 UserHandle managedProfileUserHandle = mUtils.getManagedProfile(mContext); 153 154 // Use an ordered broadcast, so that we only finish when the DPC has received it. 155 // Avoids a lag in the transition between provisioning and the DPC. 156 BroadcastReceiver dpcReceivedSuccessReceiver = 157 new DpcReceivedSuccessReceiver(params.accountToMigrate, 158 params.keepAccountMigrated, managedProfileUserHandle, 159 params.inferDeviceAdminPackageName()); 160 Intent completeIntent = createProvisioningCompleteIntent( 161 params, managedProfileUserHandle.getIdentifier()); 162 163 mContext.sendOrderedBroadcastAsUser(completeIntent, managedProfileUserHandle, null, 164 dpcReceivedSuccessReceiver, null, Activity.RESULT_OK, null, null); 165 ProvisionLogger.logd("Provisioning complete broadcast has been sent to user " 166 + managedProfileUserHandle.getIdentifier()); 167 168 maybeLaunchDpc(params, managedProfileUserHandle.getIdentifier()); 169 } 170 171 private void maybeLaunchDpc(ProvisioningParams params, int userId) { 172 final Intent dpcLaunchIntent = createDpcLaunchIntent(params); 173 if (mUtils.canResolveIntentAsUser(mContext, dpcLaunchIntent, userId)) { 174 mContext.startActivityAsUser(dpcLaunchIntent, UserHandle.of(userId)); 175 ProvisionLogger.logd("Dpc was launched for user: " + userId); 176 } 177 } 178 179 private Intent createProvisioningCompleteIntent( 180 @NonNull ProvisioningParams params, int userId) { 181 Intent intent = new Intent(ACTION_PROFILE_PROVISIONING_COMPLETE); 182 try { 183 intent.setComponent(params.inferDeviceAdminComponentName(mUtils, mContext, userId)); 184 } catch (IllegalProvisioningArgumentException e) { 185 ProvisionLogger.loge("Failed to infer the device admin component name", e); 186 return null; 187 } 188 intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES | Intent.FLAG_RECEIVER_FOREGROUND); 189 addExtrasToIntent(intent, params); 190 return intent; 191 } 192 193 private Intent createDpcLaunchIntent(@NonNull ProvisioningParams params) { 194 Intent intent = new Intent(ACTION_PROVISIONING_SUCCESSFUL); 195 final String packageName = params.inferDeviceAdminPackageName(); 196 if (packageName == null) { 197 ProvisionLogger.loge("Device admin package name is null"); 198 return null; 199 } 200 intent.setPackage(packageName); 201 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 202 addExtrasToIntent(intent, params); 203 return intent; 204 } 205 206 private void addExtrasToIntent(Intent intent, ProvisioningParams params) { 207 intent.putExtra(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE, params.adminExtrasBundle); 208 } 209 210 private void storeProvisioningParams(ProvisioningParams params) { 211 params.save(getProvisioningParamsFile()); 212 } 213 214 private File getProvisioningParamsFile() { 215 return new File(mContext.getFilesDir(), PROVISIONING_PARAMS_FILE_NAME); 216 } 217 218 @VisibleForTesting 219 ProvisioningParams loadProvisioningParamsAndClearFile() { 220 File file = getProvisioningParamsFile(); 221 ProvisioningParams result = ProvisioningParams.load(file); 222 file.delete(); 223 return result; 224 } 225 } 226