Home | History | Annotate | Download | only in finalization
      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