/* * Copyright 2014, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.managedprovisioning; import static android.app.admin.DeviceAdminReceiver.ACTION_PROFILE_PROVISIONING_COMPLETE; import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE; import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE; import android.app.Activity; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.UserHandle; import com.android.managedprovisioning.common.IllegalProvisioningArgumentException; import com.android.managedprovisioning.common.Utils; import com.android.managedprovisioning.model.ProvisioningParams; import com.android.managedprovisioning.parser.MessageParser; /* * This class is used to make sure that we start the MDM after we shut the setup wizard down. * The shut down of the setup wizard is initiated in the DeviceOwnerProvisioningActivity or * ProfileOwnerProvisioningActivity by calling * {@link DevicePolicyManager.setUserProvisioningState()}. This will cause the * Setup wizard to shut down and send a ACTION_PROVISIONING_FINALIZATION intent. This intent is * caught by this receiver instead which will send the * ACTION_PROFILE_PROVISIONING_COMPLETE broadcast to the MDM, which can then present it's own * activities. */ public class FinalizationActivity extends Activity { private ProvisioningParams mParams; private static final String INTENT_STORE_NAME = "finalization-receiver"; private final Utils mUtils = new Utils(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class); int currentState = dpm.getUserProvisioningState(); switch (currentState) { case DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE: case DevicePolicyManager.STATE_USER_SETUP_COMPLETE: case DevicePolicyManager.STATE_USER_PROFILE_COMPLETE: finalizeProvisioning(dpm); break; case DevicePolicyManager.STATE_USER_UNMANAGED: case DevicePolicyManager.STATE_USER_SETUP_FINALIZED: // Nothing to do in these cases. ProvisionLogger.logw("Received ACTION_PROVISIONING_FINALIZATION intent, but " + "nothing to do in state: " + currentState); break; } finish(); } public static void storeProvisioningParams(Context context, ProvisioningParams params) { Intent intent = new MessageParser().getIntentFromProvisioningParams(params); getIntentStore(context).save(intent); } private void finalizeProvisioning(DevicePolicyManager dpm) { mParams = loadProvisioningParamsAndClearIntentStore(); Intent provisioningCompleteIntent = getProvisioningCompleteIntent(dpm); if (provisioningCompleteIntent == null) { return; } // It maybe the case that mParams is null at this point - we expect this to be the case if // ManagedProvisioning wasn't invoked to perform setup. We'll simply trigger the normal // broadcast so that the installed DPC knows that user-setup completed. Concrete use-case // is a user being setup through DevicePolicyManager.createAndInitializeUser() by the // device-owner, which sets profile-owner and then launches the user. Setup-wizard on the // user runs, and at the end of it's activity flow will trigger the finalization intent, // which then allows us to notify the DPC that profile setup is complete. if (mParams != null && mParams.provisioningAction.equals(ACTION_PROVISION_MANAGED_PROFILE)) { // For the managed profile owner case, we need to send the provisioning complete // intent to the mdm. Once it has been received, we'll send // ACTION_MANAGED_PROFILE_PROVISIONED in the parent. finalizeManagedProfileOwnerProvisioning(provisioningCompleteIntent); } else { // For managed user and device owner, we just need to send the provisioning complete // intent to the mdm. sendBroadcast(provisioningCompleteIntent); } mUtils.markUserProvisioningStateFinalized(this, mParams); } private void finalizeManagedProfileOwnerProvisioning(Intent provisioningCompleteIntent) { UserHandle managedUserHandle = mUtils.getManagedProfile(this); if (managedUserHandle == null) { ProvisionLogger.loge("Failed to retrieve the userHandle of the managed profile."); return; } BroadcastReceiver mdmReceivedSuccessReceiver = new MdmReceivedSuccessReceiver( mParams.accountToMigrate, mParams.deviceAdminComponentName.getPackageName()); sendOrderedBroadcastAsUser(provisioningCompleteIntent, managedUserHandle, null, mdmReceivedSuccessReceiver, null, Activity.RESULT_OK, null, null); } private Intent getProvisioningCompleteIntent(DevicePolicyManager dpm) { Intent intent = new Intent(ACTION_PROFILE_PROVISIONING_COMPLETE); try { // mParams maybe null for cases where DevicePolicyManager has directly set DO or PO and // ManagedProvisioning wasn't involved. In that case, we may still want to use the same // mechanism after setup-wizard to invoke the MDM, hence why we fallback to inspecting // device and profile-owner. if (mParams != null) { intent.setComponent(mParams.inferDeviceAdminComponentName(this)); } else if (dpm.getDeviceOwner() != null) { intent.setComponent(mUtils.findDeviceAdmin(dpm.getDeviceOwner(), null /* mdmComponentName */, this)); } else if (dpm.getProfileOwner() != null) { intent.setComponent(mUtils.findDeviceAdmin(dpm.getProfileOwner().getPackageName(), null /* mdmComponentName */, this)); } else { return null; } } catch (IllegalProvisioningArgumentException e) { ProvisionLogger.loge("Failed to infer the device admin component name", e); return null; } intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES | Intent.FLAG_RECEIVER_FOREGROUND); if (mParams != null && mParams.adminExtrasBundle != null) { intent.putExtra(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE, mParams.adminExtrasBundle); } return intent; } private ProvisioningParams loadProvisioningParamsAndClearIntentStore() { IntentStore intentStore = getIntentStore(this); Intent intent = intentStore.load(); if (intent == null) { ProvisionLogger.loge("Fail to retrieve ProvisioningParams from intent store."); return null; } intentStore.clear(); try { return new MessageParser().parse(intent, this); } catch (IllegalProvisioningArgumentException e) { ProvisionLogger.loge("Failed to parse provisioning intent", e); } return null; } private static IntentStore getIntentStore(Context context) { return new IntentStore(context, INTENT_STORE_NAME); } }