| /* |
| * Copyright 2016, 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.finalization; |
| |
| import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE; |
| import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE; |
| |
| import static com.android.internal.util.Preconditions.checkNotNull; |
| import static com.android.managedprovisioning.finalization.SendDpcBroadcastService.EXTRA_PROVISIONING_PARAMS; |
| |
| import android.app.NotificationManager; |
| import android.app.admin.DevicePolicyManager; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.PackageManager.NameNotFoundException; |
| import android.os.UserHandle; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.managedprovisioning.R; |
| import com.android.managedprovisioning.analytics.DeferredMetricsReader; |
| import com.android.managedprovisioning.analytics.InstantMetricsWriter; |
| import com.android.managedprovisioning.analytics.MetricsWriterFactory; |
| import com.android.managedprovisioning.common.NotificationHelper; |
| import com.android.managedprovisioning.common.ProvisionLogger; |
| import com.android.managedprovisioning.common.SettingsFacade; |
| import com.android.managedprovisioning.common.Utils; |
| import com.android.managedprovisioning.model.ProvisioningParams; |
| import com.android.managedprovisioning.provisioning.Constants; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| |
| /** |
| * Controller for the finalization of managed provisioning. |
| * |
| * <p>This controller is invoked when the active provisioning is completed via |
| * {@link #provisioningInitiallyDone(ProvisioningParams)}. In the case of provisioning during SUW, |
| * it is invoked again when provisioning is finalized via {@link #provisioningFinalized()}.</p> |
| */ |
| public class FinalizationController { |
| private static final String PROVISIONING_PARAMS_FILE_NAME = |
| "finalization_activity_provisioning_params.xml"; |
| |
| private final Context mContext; |
| private final Utils mUtils; |
| private final SettingsFacade mSettingsFacade; |
| private final UserProvisioningStateHelper mUserProvisioningStateHelper; |
| private final ProvisioningIntentProvider mProvisioningIntentProvider; |
| private final NotificationHelper mNotificationHelper; |
| private final DeferredMetricsReader mDeferredMetricsReader; |
| |
| public FinalizationController(Context context, |
| UserProvisioningStateHelper userProvisioningStateHelper) { |
| this( |
| context, |
| new Utils(), |
| new SettingsFacade(), |
| userProvisioningStateHelper, |
| new NotificationHelper(context), |
| new DeferredMetricsReader( |
| Constants.getDeferredMetricsFile(context), |
| new InstantMetricsWriter())); |
| } |
| |
| public FinalizationController(Context context) { |
| this( |
| context, |
| new Utils(), |
| new SettingsFacade(), |
| new UserProvisioningStateHelper(context), |
| new NotificationHelper(context), |
| new DeferredMetricsReader( |
| Constants.getDeferredMetricsFile(context), |
| new InstantMetricsWriter())); |
| } |
| |
| @VisibleForTesting |
| FinalizationController(Context context, |
| Utils utils, |
| SettingsFacade settingsFacade, |
| UserProvisioningStateHelper helper, |
| NotificationHelper notificationHelper, |
| DeferredMetricsReader deferredMetricsReader) { |
| mContext = checkNotNull(context); |
| mUtils = checkNotNull(utils); |
| mSettingsFacade = checkNotNull(settingsFacade); |
| mUserProvisioningStateHelper = checkNotNull(helper); |
| mProvisioningIntentProvider = new ProvisioningIntentProvider(); |
| mNotificationHelper = checkNotNull(notificationHelper); |
| mDeferredMetricsReader = checkNotNull(deferredMetricsReader); |
| } |
| |
| /** |
| * This method is invoked when the provisioning process is done. |
| * |
| * <p>If provisioning happens as part of SUW, we rely on {@link #provisioningFinalized()} to be |
| * called at the end of SUW. Otherwise, this method will finalize provisioning. If called after |
| * SUW, this method notifies the DPC about the completed provisioning; otherwise, it stores the |
| * provisioning params for later digestion.</p> |
| * |
| * <p>Note that fully managed device provisioning is only possible during SUW. |
| * |
| * @param params the provisioning params |
| */ |
| public void provisioningInitiallyDone(ProvisioningParams params) { |
| if (!mUserProvisioningStateHelper.isStateUnmanagedOrFinalized()) { |
| // In any other state than STATE_USER_UNMANAGED and STATE_USER_SETUP_FINALIZED, we've |
| // already run this method, so don't do anything. |
| // STATE_USER_SETUP_FINALIZED can occur here if a managed profile is provisioned on a |
| // device owner device. |
| ProvisionLogger.logw("provisioningInitiallyDone called, but state is not finalized or " |
| + "unmanaged"); |
| return; |
| } |
| |
| mUserProvisioningStateHelper.markUserProvisioningStateInitiallyDone(params); |
| if (ACTION_PROVISION_MANAGED_PROFILE.equals(params.provisioningAction)) { |
| if (params.isOrganizationOwnedProvisioning) { |
| setProfileOwnerCanAccessDeviceIds(); |
| } |
| if (!mSettingsFacade.isDuringSetupWizard(mContext)) { |
| // If a managed profile was provisioned after SUW, notify the DPC straight away. |
| notifyDpcManagedProfile(params); |
| } |
| } |
| if (mSettingsFacade.isDuringSetupWizard(mContext)) { |
| // Store the information and wait for provisioningFinalized to be called |
| storeProvisioningParams(params); |
| } |
| } |
| |
| private void setProfileOwnerCanAccessDeviceIds() { |
| final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); |
| final int managedProfileUserId = mUtils.getManagedProfile(mContext).getIdentifier(); |
| final ComponentName admin = dpm.getProfileOwnerAsUser(managedProfileUserId); |
| if (admin != null) { |
| try { |
| final Context profileContext = mContext.createPackageContextAsUser( |
| mContext.getPackageName(), 0 /* flags */, |
| UserHandle.of(managedProfileUserId)); |
| final DevicePolicyManager profileDpm = |
| profileContext.getSystemService(DevicePolicyManager.class); |
| profileDpm.setProfileOwnerCanAccessDeviceIds(admin); |
| } catch (NameNotFoundException e) { |
| ProvisionLogger.logw("Error setting access to Device IDs: " + e.getMessage()); |
| } |
| } |
| } |
| |
| @VisibleForTesting |
| PrimaryProfileFinalizationHelper getPrimaryProfileFinalizationHelper( |
| ProvisioningParams params) { |
| return new PrimaryProfileFinalizationHelper(params.accountToMigrate, |
| params.keepAccountMigrated, mUtils.getManagedProfile(mContext), |
| params.inferDeviceAdminPackageName(), mUtils, |
| mUtils.isAdminIntegratedFlow(params)); |
| } |
| |
| /** |
| * This method is invoked when provisioning is finalized. |
| * |
| * <p>This method has to be invoked after {@link #provisioningInitiallyDone(ProvisioningParams)} |
| * was called. It is commonly invoked at the end of SUW if provisioning occurs during SUW. It |
| * loads the provisioning params from the storage, notifies the DPC about the completed |
| * provisioning and sets the right user provisioning states.</p> |
| */ |
| void provisioningFinalized() { |
| mDeferredMetricsReader.dumpMetricsAndClearFile(); |
| |
| if (mUserProvisioningStateHelper.isStateUnmanagedOrFinalized()) { |
| ProvisionLogger.logw("provisioningInitiallyDone called, but state is finalized or " |
| + "unmanaged"); |
| return; |
| } |
| |
| final ProvisioningParams params = loadProvisioningParamsAndClearFile(); |
| if (params == null) { |
| ProvisionLogger.logw("FinalizationController invoked, but no stored params"); |
| return; |
| } |
| |
| if (mUtils.isAdminIntegratedFlow(params)) { |
| // Don't send ACTION_PROFILE_PROVISIONING_COMPLETE broadcast to DPC or launch DPC by |
| // ACTION_PROVISIONING_SUCCESSFUL intent if it's admin integrated flow. |
| if (params.provisioningAction.equals(ACTION_PROVISION_MANAGED_PROFILE)) { |
| getPrimaryProfileFinalizationHelper(params) |
| .finalizeProvisioningInPrimaryProfile(mContext, null); |
| } else if (ACTION_PROVISION_MANAGED_DEVICE.equals(params.provisioningAction)) { |
| mNotificationHelper.showPrivacyReminderNotification( |
| mContext, NotificationManager.IMPORTANCE_DEFAULT); |
| } |
| mProvisioningIntentProvider.launchFinalizationScreen(mContext, params); |
| } else { |
| if (params.provisioningAction.equals(ACTION_PROVISION_MANAGED_PROFILE)) { |
| notifyDpcManagedProfile(params); |
| } else { |
| // For managed user and device owner, we send the provisioning complete intent and |
| // maybe launch the DPC. |
| final int userId = UserHandle.myUserId(); |
| final Intent provisioningCompleteIntent = mProvisioningIntentProvider |
| .createProvisioningCompleteIntent(params, userId, mUtils, mContext); |
| if (provisioningCompleteIntent == null) { |
| return; |
| } |
| mContext.sendBroadcast(provisioningCompleteIntent); |
| |
| mProvisioningIntentProvider.maybeLaunchDpc(params, userId, mUtils, mContext); |
| |
| if (ACTION_PROVISION_MANAGED_DEVICE.equals(params.provisioningAction)) { |
| mNotificationHelper.showPrivacyReminderNotification( |
| mContext, NotificationManager.IMPORTANCE_DEFAULT); |
| } |
| } |
| } |
| |
| mUserProvisioningStateHelper.markUserProvisioningStateFinalized(params); |
| } |
| |
| /** |
| * Start a service which notifies the DPC on the managed profile that provisioning has |
| * completed. When the DPC has received the intent, send notify the primary instance that the |
| * profile is ready. The service is needed to prevent the managed provisioning process from |
| * getting killed while the user is on the DPC screen. |
| */ |
| private void notifyDpcManagedProfile(ProvisioningParams params) { |
| mContext.startService( |
| new Intent(mContext, SendDpcBroadcastService.class) |
| .putExtra(EXTRA_PROVISIONING_PARAMS, params)); |
| } |
| |
| private void storeProvisioningParams(ProvisioningParams params) { |
| params.save(getProvisioningParamsFile()); |
| } |
| |
| private File getProvisioningParamsFile() { |
| return new File(mContext.getFilesDir(), PROVISIONING_PARAMS_FILE_NAME); |
| } |
| |
| @VisibleForTesting |
| ProvisioningParams loadProvisioningParamsAndClearFile() { |
| File file = getProvisioningParamsFile(); |
| ProvisioningParams result = ProvisioningParams.load(file); |
| file.delete(); |
| return result; |
| } |
| } |