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.preprovisioning; 18 19 import static com.android.internal.util.Preconditions.checkNotNull; 20 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.pm.PackageManager; 25 import android.os.Looper; 26 import android.os.UserHandle; 27 28 import com.android.internal.annotations.VisibleForTesting; 29 import com.android.managedprovisioning.common.Globals; 30 import com.android.managedprovisioning.common.NotificationHelper; 31 import com.android.managedprovisioning.common.ProvisionLogger; 32 import com.android.managedprovisioning.common.SettingsFacade; 33 import com.android.managedprovisioning.common.Utils; 34 import com.android.managedprovisioning.model.ProvisioningParams; 35 36 import java.io.File; 37 38 /** 39 * This controller manages all things related to the encryption reboot. 40 * 41 * <p>An encryption reminder can be scheduled using {@link #setEncryptionReminder}. This will store 42 * the provisioning data to disk and enable a HOME intent receiver. After the reboot, the HOME 43 * intent receiver calls {@link #resumeProvisioning} at which point a new provisioning intent is 44 * sent. The reminder can be cancelled using {@link #cancelEncryptionReminder}. 45 */ 46 public class EncryptionController { 47 private final Context mContext; 48 private final Utils mUtils; 49 private final SettingsFacade mSettingsFacade; 50 private final ComponentName mHomeReceiver; 51 private final NotificationHelper mNotificationHelper; 52 private final int mUserId; 53 54 private boolean mProvisioningResumed = false; 55 56 private final PackageManager mPackageManager; 57 58 private static EncryptionController sInstance; 59 60 private static final String PROVISIONING_PARAMS_FILE_NAME 61 = "encryption_controller_provisioning_params.xml"; 62 getInstance(Context context)63 public static synchronized EncryptionController getInstance(Context context) { 64 if (sInstance == null) { 65 sInstance = new EncryptionController(context); 66 } 67 return sInstance; 68 } 69 EncryptionController(Context context)70 private EncryptionController(Context context) { 71 this(context, 72 new Utils(), 73 new SettingsFacade(), 74 new ComponentName(context, PostEncryptionActivity.class), 75 new NotificationHelper(context), 76 UserHandle.myUserId()); 77 } 78 79 @VisibleForTesting EncryptionController(Context context, Utils utils, SettingsFacade settingsFacade, ComponentName homeReceiver, NotificationHelper resumeNotificationHelper, int userId)80 EncryptionController(Context context, 81 Utils utils, 82 SettingsFacade settingsFacade, 83 ComponentName homeReceiver, 84 NotificationHelper resumeNotificationHelper, 85 int userId) { 86 mContext = checkNotNull(context, "Context must not be null").getApplicationContext(); 87 mSettingsFacade = checkNotNull(settingsFacade); 88 mUtils = checkNotNull(utils, "Utils must not be null"); 89 mHomeReceiver = checkNotNull(homeReceiver, "HomeReceiver must not be null"); 90 mNotificationHelper = checkNotNull(resumeNotificationHelper, 91 "ResumeNotificationHelper must not be null"); 92 mUserId = userId; 93 94 mPackageManager = context.getPackageManager(); 95 } 96 97 /** 98 * Store a resume intent into persistent storage. Provisioning will be resumed after reboot 99 * using the stored intent. 100 * 101 * @param params the params to be stored. 102 */ setEncryptionReminder(ProvisioningParams params)103 public void setEncryptionReminder(ProvisioningParams params) { 104 ProvisionLogger.logd("Setting provisioning reminder for action: " 105 + params.provisioningAction); 106 params.save(getProvisioningParamsFile(mContext)); 107 // Only enable the HOME intent receiver for flows inside SUW, as showing the notification 108 // for non-SUW flows is less time cricital. 109 if (!mSettingsFacade.isUserSetupCompleted(mContext)) { 110 ProvisionLogger.logd("Enabling PostEncryptionActivity"); 111 mUtils.enableComponent(mHomeReceiver, mUserId); 112 // To ensure that the enabled state has been persisted to disk, we flush the 113 // restrictions. 114 mPackageManager.flushPackageRestrictionsAsUser(mUserId); 115 } 116 } 117 118 /** 119 * Cancel the encryption reminder to avoid further resumption of encryption. 120 */ cancelEncryptionReminder()121 public void cancelEncryptionReminder() { 122 ProvisionLogger.logd("Cancelling provisioning reminder."); 123 getProvisioningParamsFile(mContext).delete(); 124 mUtils.disableComponent(mHomeReceiver, mUserId); 125 } 126 127 /** 128 * Resume provisioning after encryption has happened. 129 * 130 * <p>If the device has already been set up, we show a notification to resume provisioning, 131 * otherwise we continue provisioning direclty. 132 * 133 * <p>Note that this method has to be called on the main thread. 134 */ resumeProvisioning()135 public void resumeProvisioning() { 136 // verify that this method was called on the main thread. 137 if (Looper.myLooper() != Looper.getMainLooper()) { 138 throw new IllegalStateException("resumeProvisioning must be called on the main thread"); 139 } 140 141 if (mProvisioningResumed) { 142 // If provisioning has already been resumed, don't resume it again. 143 // This can happen if the HOME intent receiver was launched multiple times or the 144 // BOOT_COMPLETED was received after the HOME intent receiver had already been launched. 145 return; 146 } 147 148 ProvisioningParams params = ProvisioningParams.load(getProvisioningParamsFile(mContext)); 149 150 if (params != null) { 151 Intent resumeIntent = new Intent(Globals.ACTION_RESUME_PROVISIONING); 152 resumeIntent.putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, params); 153 mProvisioningResumed = true; 154 String action = params.provisioningAction; 155 ProvisionLogger.logd("Provisioning resumed after encryption with action: " + action); 156 157 if (!mUtils.isPhysicalDeviceEncrypted()) { 158 ProvisionLogger.loge("Device is not encrypted after provisioning with" 159 + " action " + action + " but it should"); 160 return; 161 } 162 resumeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 163 164 if (mUtils.isProfileOwnerAction(action)) { 165 if (mSettingsFacade.isUserSetupCompleted(mContext)) { 166 mNotificationHelper.showResumeNotification(resumeIntent); 167 } else { 168 mContext.startActivity(resumeIntent); 169 } 170 } else if (mUtils.isDeviceOwnerAction(action)) { 171 mContext.startActivity(resumeIntent); 172 } else { 173 ProvisionLogger.loge("Unknown intent action loaded from the intent store: " 174 + action); 175 } 176 } 177 } 178 179 @VisibleForTesting getProvisioningParamsFile(Context context)180 File getProvisioningParamsFile(Context context) { 181 return new File(context.getFilesDir(), PROVISIONING_PARAMS_FILE_NAME); 182 } 183 } 184