blob: 9dcf9a3cf5a5a3cb66d4367ab0283d574926ee84 [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.preprovisioning;
import static com.android.internal.util.Preconditions.checkNotNull;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Looper;
import android.os.UserHandle;
import com.android.internal.annotations.VisibleForTesting;
import com.android.managedprovisioning.common.Globals;
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 java.io.File;
/**
* This controller manages all things related to the encryption reboot.
*
* <p>An encryption reminder can be scheduled using {@link #setEncryptionReminder}. This will store
* the provisioning data to disk and enable a HOME intent receiver. After the reboot, the HOME
* intent receiver calls {@link #resumeProvisioning} at which point a new provisioning intent is
* sent. The reminder can be cancelled using {@link #cancelEncryptionReminder}.
*/
public class EncryptionController {
private final Context mContext;
private final Utils mUtils;
private final SettingsFacade mSettingsFacade;
private final ComponentName mHomeReceiver;
private final NotificationHelper mNotificationHelper;
private final int mUserId;
private boolean mProvisioningResumed = false;
private final PackageManager mPackageManager;
private static EncryptionController sInstance;
private static final String PROVISIONING_PARAMS_FILE_NAME
= "encryption_controller_provisioning_params.xml";
public static synchronized EncryptionController getInstance(Context context) {
if (sInstance == null) {
sInstance = new EncryptionController(context);
}
return sInstance;
}
private EncryptionController(Context context) {
this(context,
new Utils(),
new SettingsFacade(),
new ComponentName(context, PostEncryptionActivity.class),
new NotificationHelper(context),
UserHandle.myUserId());
}
@VisibleForTesting
EncryptionController(Context context,
Utils utils,
SettingsFacade settingsFacade,
ComponentName homeReceiver,
NotificationHelper resumeNotificationHelper,
int userId) {
mContext = checkNotNull(context, "Context must not be null").getApplicationContext();
mSettingsFacade = checkNotNull(settingsFacade);
mUtils = checkNotNull(utils, "Utils must not be null");
mHomeReceiver = checkNotNull(homeReceiver, "HomeReceiver must not be null");
mNotificationHelper = checkNotNull(resumeNotificationHelper,
"ResumeNotificationHelper must not be null");
mUserId = userId;
mPackageManager = context.getPackageManager();
}
/**
* Store a resume intent into persistent storage. Provisioning will be resumed after reboot
* using the stored intent.
*
* @param params the params to be stored.
*/
public void setEncryptionReminder(ProvisioningParams params) {
ProvisionLogger.logd("Setting provisioning reminder for action: "
+ params.provisioningAction);
params.save(getProvisioningParamsFile(mContext));
// Only enable the HOME intent receiver for flows inside SUW, as showing the notification
// for non-SUW flows is less time cricital.
if (!mSettingsFacade.isUserSetupCompleted(mContext)) {
ProvisionLogger.logd("Enabling PostEncryptionActivity");
mUtils.enableComponent(mHomeReceiver, mUserId);
// To ensure that the enabled state has been persisted to disk, we flush the
// restrictions.
mPackageManager.flushPackageRestrictionsAsUser(mUserId);
}
}
/**
* Cancel the encryption reminder to avoid further resumption of encryption.
*/
public void cancelEncryptionReminder() {
ProvisionLogger.logd("Cancelling provisioning reminder.");
getProvisioningParamsFile(mContext).delete();
mUtils.disableComponent(mHomeReceiver, mUserId);
}
/**
* Resume provisioning after encryption has happened.
*
* <p>If the device has already been set up, we show a notification to resume provisioning,
* otherwise we continue provisioning direclty.
*
* <p>Note that this method has to be called on the main thread.
*/
public void resumeProvisioning() {
// verify that this method was called on the main thread.
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new IllegalStateException("resumeProvisioning must be called on the main thread");
}
if (mProvisioningResumed) {
// If provisioning has already been resumed, don't resume it again.
// This can happen if the HOME intent receiver was launched multiple times or the
// BOOT_COMPLETED was received after the HOME intent receiver had already been launched.
return;
}
ProvisioningParams params = ProvisioningParams.load(getProvisioningParamsFile(mContext));
if (params != null) {
Intent resumeIntent = new Intent(Globals.ACTION_RESUME_PROVISIONING);
resumeIntent.putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, params);
mProvisioningResumed = true;
String action = params.provisioningAction;
ProvisionLogger.logd("Provisioning resumed after encryption with action: " + action);
if (!mUtils.isPhysicalDeviceEncrypted()) {
ProvisionLogger.loge("Device is not encrypted after provisioning with"
+ " action " + action + " but it should");
return;
}
resumeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (mUtils.isProfileOwnerAction(action)) {
if (mSettingsFacade.isUserSetupCompleted(mContext)) {
mNotificationHelper.showResumeNotification(resumeIntent);
} else {
mContext.startActivity(resumeIntent);
}
} else if (mUtils.isDeviceOwnerAction(action)) {
mContext.startActivity(resumeIntent);
} else {
ProvisionLogger.loge("Unknown intent action loaded from the intent store: "
+ action);
}
}
}
@VisibleForTesting
File getProvisioningParamsFile(Context context) {
return new File(context.getFilesDir(), PROVISIONING_PARAMS_FILE_NAME);
}
}