1 /* 2 * Copyright 2014, 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 package com.android.managedprovisioning; 17 18 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE; 19 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE; 20 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE; 21 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME; 22 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME; 23 24 import android.app.Notification; 25 import android.app.NotificationManager; 26 import android.app.PendingIntent; 27 import android.content.BroadcastReceiver; 28 import android.content.ComponentName; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.os.Bundle; 32 33 /** 34 * Class that handles the resuming process that takes place after a reboot during the provisioning 35 * process. The reboot could be an unexpected reboot or a reboot during the encryption process. 36 */ 37 public class BootReminder extends BroadcastReceiver { 38 private static final int NOTIFY_ID = 1; 39 40 /* 41 * Profile owner parameters that are stored in the IntentStore for resuming provisioning after 42 * encryption. 43 */ 44 private static final String PROFILE_OWNER_ENCRYPTION_PREFERENCES_NAME = 45 "profile-owner-encryption-resume"; 46 47 /* 48 * Profile owner parameters that are stored in the IntentStore for resuming provisioning after 49 * Setup wizard has shutdown. 50 */ 51 private static final String PROFILE_OWNER_FINALIZING_PREFERENCES_NAME = 52 "profile-owner-finalizing-resume"; 53 54 private static final ComponentName PROFILE_OWNER_INTENT_TARGET = 55 ProfileOwnerPreProvisioningActivity.ALIAS_NO_CHECK_CALLER; 56 57 /* 58 * Device owner parameters that are stored in the IntentStore for resuming provisioning after 59 * encryption. 60 */ 61 private static final String DEVICE_OWNER_ENCRYPTION_PREFERENCES_NAME = 62 "device-owner-encryption-resume"; 63 64 // Device owner parameter stored for resuming provisioning after unexpected device reboot during 65 // finalizing stage. 66 private static final String DEVICE_OWNER_FINALIZING_PREFERENCES_NAME = 67 "device-owner-finalizing-resume"; 68 69 private static final ComponentName DEVICE_OWNER_INTENT_TARGET = 70 new ComponentName("com.android.managedprovisioning", 71 "com.android.managedprovisioning.DeviceOwnerPreProvisioningActivity"); 72 73 private static final ComponentName HOME_RECEIVER_INTENT_TARGET = 74 new ComponentName("com.android.managedprovisioning", 75 "com.android.managedprovisioning.HomeReceiverActivity"); 76 77 @Override onReceive(Context context, Intent intent)78 public void onReceive(Context context, Intent intent) { 79 if (android.content.Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { 80 81 // Resume profile owner provisioning if applicable. 82 IntentStore profileOwnerIntentStore = 83 getProfileOwnerEncryptionResumptionIntentStore(context); 84 final Intent resumeProfileOwnerPrvIntent = profileOwnerIntentStore.load(); 85 if (resumeProfileOwnerPrvIntent != null && EncryptDeviceActivity.isDeviceEncrypted()) { 86 profileOwnerIntentStore.clear(); 87 if (Utils.isUserSetupCompleted(context)) { 88 // Show reminder notification and then forget about it for next boot 89 setNotification(context, resumeProfileOwnerPrvIntent); 90 } else { 91 resumeProfileOwnerPrvIntent.setAction(ACTION_PROVISION_MANAGED_PROFILE); 92 TrampolineActivity.startActivity(context, resumeProfileOwnerPrvIntent); 93 } 94 } 95 96 // Resume device owner provisioning after encryption if applicable. 97 IntentStore deviceOwnerIntentStore = 98 getDeviceOwnerEncryptionResumptionIntentStore(context); 99 Intent resumeDeviceOwnerPrvIntent = deviceOwnerIntentStore.load(); 100 if (resumeDeviceOwnerPrvIntent != null) { 101 deviceOwnerIntentStore.clear(); 102 resumeDeviceOwnerPrvIntent.setAction( 103 DeviceOwnerPreProvisioningActivity.LEGACY_ACTION_PROVISION_MANAGED_DEVICE); 104 TrampolineActivity.startActivity(context, resumeDeviceOwnerPrvIntent); 105 } 106 } 107 } 108 109 /** 110 * Schedule a provisioning reminder notification for the next reboot. 111 * 112 * {@code extras} should be a Bundle containing the 113 * {@link EncryptDeviceActivity.EXTRA_RESUME_TARGET}. 114 * This field has only two supported values {@link EncryptDeviceActivity.TARGET_PROFILE_OWNER} 115 * and {@link EncryptDeviceActivity.TARGET_DEVICE_OWNER} 116 * 117 * <p> In case of TARGET_PROFILE_OWNER {@code extras} should further contain a value for at 118 * least the key: {@link EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}, a {@link String} 119 * which, when unflattened to a componentName, specifies the component to set as profile owner. 120 * 121 * <p> 122 * See {@link MessageParser} for the TARGET_DEVICE_OWNER case. 123 * </ul> 124 * 125 * <p> These fields will be persisted and restored to the provisioner after rebooting. Any other 126 * key/value pairs will be ignored. 127 */ setProvisioningReminder(Context context, Bundle extras)128 public static void setProvisioningReminder(Context context, Bundle extras) { 129 IntentStore intentStore; 130 String resumeTarget = extras.getString(EncryptDeviceActivity.EXTRA_RESUME_TARGET, null); 131 if (resumeTarget == null) { 132 return; 133 } 134 if (resumeTarget.equals(EncryptDeviceActivity.TARGET_PROFILE_OWNER)) { 135 intentStore = getProfileOwnerEncryptionResumptionIntentStore(context); 136 } else if (resumeTarget.equals(EncryptDeviceActivity.TARGET_DEVICE_OWNER)) { 137 intentStore = getDeviceOwnerEncryptionResumptionIntentStore(context); 138 } else { 139 ProvisionLogger.loge("Unknown resume target for bootreminder."); 140 return; 141 } 142 intentStore.save(extras); 143 } 144 145 /** 146 * Cancel all active provisioning reminders. 147 */ cancelProvisioningReminder(Context context)148 public static void cancelProvisioningReminder(Context context) { 149 getProfileOwnerEncryptionResumptionIntentStore(context).clear(); 150 getDeviceOwnerEncryptionResumptionIntentStore(context).clear(); 151 setNotification(context, null); 152 } 153 getProfileOwnerIntentStore(Context context, ComponentName intentTarget, String storeName)154 private static IntentStore getProfileOwnerIntentStore(Context context, 155 ComponentName intentTarget, String storeName) { 156 return new IntentStore(context, intentTarget, storeName) 157 .setComponentNameKeys(MessageParser.PROFILE_OWNER_COMPONENT_NAME_EXTRAS) 158 .setStringKeys(MessageParser.PROFILE_OWNER_STRING_EXTRAS) 159 .setPersistableBundleKeys(MessageParser.PROFILE_OWNER_PERSISTABLE_BUNDLE_EXTRAS) 160 .setAccountKeys(MessageParser.PROFILE_OWNER_ACCOUNT_EXTRAS); 161 } 162 getDeviceOwnerIntentStore(Context context, ComponentName intentTarget, String storeName)163 private static IntentStore getDeviceOwnerIntentStore(Context context, 164 ComponentName intentTarget, String storeName) { 165 return new IntentStore(context, intentTarget, storeName) 166 .setComponentNameKeys(MessageParser.DEVICE_OWNER_COMPONENT_NAME_EXTRAS) 167 .setStringKeys(MessageParser.DEVICE_OWNER_STRING_EXTRAS) 168 .setLongKeys(MessageParser.DEVICE_OWNER_LONG_EXTRAS) 169 .setIntKeys(MessageParser.DEVICE_OWNER_INT_EXTRAS) 170 .setBooleanKeys(MessageParser.DEVICE_OWNER_BOOLEAN_EXTRAS) 171 .setPersistableBundleKeys(MessageParser.DEVICE_OWNER_PERSISTABLE_BUNDLE_EXTRAS); 172 } 173 getDeviceOwnerEncryptionResumptionIntentStore(Context context)174 private static IntentStore getDeviceOwnerEncryptionResumptionIntentStore(Context context) { 175 return getDeviceOwnerIntentStore(context, DEVICE_OWNER_INTENT_TARGET, 176 DEVICE_OWNER_ENCRYPTION_PREFERENCES_NAME); 177 } 178 getDeviceOwnerFinalizingIntentStore(Context context)179 protected static IntentStore getDeviceOwnerFinalizingIntentStore(Context context) { 180 return getDeviceOwnerIntentStore(context, HOME_RECEIVER_INTENT_TARGET, 181 DEVICE_OWNER_FINALIZING_PREFERENCES_NAME); 182 } 183 getProfileOwnerEncryptionResumptionIntentStore(Context context)184 private static IntentStore getProfileOwnerEncryptionResumptionIntentStore(Context context) { 185 return getProfileOwnerIntentStore(context, PROFILE_OWNER_INTENT_TARGET, 186 PROFILE_OWNER_ENCRYPTION_PREFERENCES_NAME); 187 } 188 getProfileOwnerFinalizingIntentStore(Context context)189 protected static IntentStore getProfileOwnerFinalizingIntentStore(Context context) { 190 return getProfileOwnerIntentStore(context, HOME_RECEIVER_INTENT_TARGET, 191 PROFILE_OWNER_FINALIZING_PREFERENCES_NAME); 192 } 193 194 /** Create and show the provisioning reminder notification. */ setNotification(Context context, Intent intent)195 private static void setNotification(Context context, Intent intent) { 196 final NotificationManager notificationManager = (NotificationManager) 197 context.getSystemService(Context.NOTIFICATION_SERVICE); 198 if (intent == null) { 199 notificationManager.cancel(NOTIFY_ID); 200 return; 201 } 202 final PendingIntent resumePendingIntent = PendingIntent.getActivity( 203 context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); 204 final Notification.Builder notify = new Notification.Builder(context) 205 .setContentIntent(resumePendingIntent) 206 .setContentTitle(context.getString(R.string.continue_provisioning_notify_title)) 207 .setContentText(context.getString(R.string.continue_provisioning_notify_text)) 208 .setSmallIcon(com.android.internal.R.drawable.ic_corp_statusbar_icon) 209 .setVisibility(Notification.VISIBILITY_PUBLIC) 210 .setColor(context.getResources().getColor( 211 com.android.internal.R.color.system_notification_accent_color)) 212 .setAutoCancel(true); 213 notificationManager.notify(NOTIFY_ID, notify.build()); 214 } 215 } 216