| /* |
| * 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); |
| } |
| } |