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             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