blob: 2948d2f2074dbdd9fb27e1d8906508d7bafcfaf1 [file] [log] [blame]
/*
* 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;
}
}