| /* |
| * Copyright 2014, 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; |
| |
| import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE; |
| import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE; |
| import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME; |
| import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME; |
| |
| import android.app.Notification; |
| import android.app.NotificationManager; |
| import android.app.PendingIntent; |
| import android.content.BroadcastReceiver; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.os.Bundle; |
| |
| /** |
| * Class that handles the resuming process that takes place after a reboot during the provisioning |
| * process. The reboot could be an unexpected reboot or a reboot during the encryption process. |
| */ |
| public class BootReminder extends BroadcastReceiver { |
| private static final int NOTIFY_ID = 1; |
| |
| /* |
| * Profile owner parameters that are stored in the IntentStore for resuming provisioning. |
| */ |
| private static final String PROFILE_OWNER_PREFERENCES_NAME = |
| "profile-owner-provisioning-resume"; |
| |
| private static final String[] PROFILE_OWNER_STRING_EXTRAS = { |
| // Key for the device admin package name |
| EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME |
| }; |
| |
| private static final String[] PROFILE_OWNER_COMPONENT_NAME_EXTRAS = { |
| // Key for the device admin component name |
| EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME |
| }; |
| |
| private static final String[] PROFILE_OWNER_PERSISTABLE_BUNDLE_EXTRAS = { |
| // Key for the admin extras bundle |
| EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE |
| }; |
| |
| private static final String[] PROFILE_OWNER_ACCOUNT_EXTRAS = { |
| // Key for the account extras |
| EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE |
| }; |
| |
| private static final ComponentName PROFILE_OWNER_INTENT_TARGET = |
| ProfileOwnerPreProvisioningActivity.ALIAS_NO_CHECK_CALLER; |
| |
| /* |
| * Device owner parameters that are stored in the IntentStore for resuming provisioning after |
| * encryption. |
| */ |
| private static final String DEVICE_OWNER_ENCRYPTION_PREFERENCES_NAME = |
| "device-owner-encryption-resume"; |
| |
| // Device owner parameter stored for resuming provisioning after unexpected device reboot during |
| // finalizing stage. |
| private static final String DEVICE_OWNER_FINALIZING_PREFERENCES_NAME = |
| "device-owner-finalizing-resume"; |
| |
| private static final ComponentName DEVICE_OWNER_INTENT_TARGET = |
| new ComponentName("com.android.managedprovisioning", |
| "com.android.managedprovisioning.DeviceOwnerProvisioningActivity"); |
| |
| private static final ComponentName HOME_RECEIVER_INTENT_TARGET = |
| new ComponentName("com.android.managedprovisioning", |
| "com.android.managedprovisioning.HomeReceiverActivity"); |
| |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| if (android.content.Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { |
| |
| // Resume profile owner provisioning if applicable. |
| IntentStore profileOwnerIntentStore = getProfileOwnerIntentStore(context); |
| final Intent resumeProfileOwnerPrvIntent = profileOwnerIntentStore.load(); |
| if (resumeProfileOwnerPrvIntent != null) { |
| if (EncryptDeviceActivity.isDeviceEncrypted()) { |
| // Show reminder notification and then forget about it for next boot |
| profileOwnerIntentStore.clear(); |
| setNotification(context, resumeProfileOwnerPrvIntent); |
| } |
| } |
| |
| // Resume device owner provisioning after encryption if applicable. |
| IntentStore deviceOwnerIntentStore = getDeviceOwnerEncryptionResumptionIntentStore(context); |
| Intent resumeDeviceOwnerPrvIntent = deviceOwnerIntentStore.load(); |
| if (resumeDeviceOwnerPrvIntent != null) { |
| deviceOwnerIntentStore.clear(); |
| resumeDeviceOwnerPrvIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| context.startActivity(resumeDeviceOwnerPrvIntent); |
| } |
| } |
| } |
| |
| /** |
| * Schedule a provisioning reminder notification for the next reboot. |
| * |
| * {@code extras} should be a Bundle containing the |
| * {@link EncryptDeviceActivity.EXTRA_RESUME_TARGET}. |
| * This field has only two supported values {@link EncryptDeviceActivity.TARGET_PROFILE_OWNER} |
| * and {@link EncryptDeviceActivity.TARGET_DEVICE_OWNER} |
| * |
| * <p> In case of TARGET_PROFILE_OWNER {@code extras} should further contain a value for at |
| * least the key: {@link EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}, a {@link String} |
| * which, when unflattened to a componentName, specifies the component to set as profile owner. |
| * |
| * <p> |
| * See {@link MessageParser} for the TARGET_DEVICE_OWNER case. |
| * </ul> |
| * |
| * <p> These fields will be persisted and restored to the provisioner after rebooting. Any other |
| * key/value pairs will be ignored. |
| */ |
| public static void setProvisioningReminder(Context context, Bundle extras) { |
| IntentStore intentStore; |
| String resumeTarget = extras.getString(EncryptDeviceActivity.EXTRA_RESUME_TARGET, null); |
| if (resumeTarget == null) { |
| return; |
| } |
| if (resumeTarget.equals(EncryptDeviceActivity.TARGET_PROFILE_OWNER)) { |
| intentStore = getProfileOwnerIntentStore(context); |
| } else if (resumeTarget.equals(EncryptDeviceActivity.TARGET_DEVICE_OWNER)) { |
| intentStore = getDeviceOwnerEncryptionResumptionIntentStore(context); |
| } else { |
| ProvisionLogger.loge("Unknown resume target for bootreminder."); |
| return; |
| } |
| intentStore.save(extras); |
| } |
| |
| /** |
| * Cancel all active provisioning reminders. |
| */ |
| public static void cancelProvisioningReminder(Context context) { |
| getProfileOwnerIntentStore(context).clear(); |
| getDeviceOwnerEncryptionResumptionIntentStore(context).clear(); |
| setNotification(context, null); |
| } |
| |
| private static IntentStore getProfileOwnerIntentStore(Context context) { |
| return new IntentStore(context,PROFILE_OWNER_INTENT_TARGET, PROFILE_OWNER_PREFERENCES_NAME) |
| .setComponentNameKeys(PROFILE_OWNER_COMPONENT_NAME_EXTRAS) |
| .setStringKeys(PROFILE_OWNER_STRING_EXTRAS) |
| .setPersistableBundleKeys(PROFILE_OWNER_PERSISTABLE_BUNDLE_EXTRAS) |
| .setAccountKeys(PROFILE_OWNER_ACCOUNT_EXTRAS); |
| } |
| |
| private static IntentStore getDeviceOwnerIntentStore(Context context, |
| ComponentName intentTarget, String storeName) { |
| return new IntentStore(context, intentTarget, storeName) |
| .setComponentNameKeys(MessageParser.DEVICE_OWNER_COMPONENT_NAME_EXTRAS) |
| .setStringKeys(MessageParser.DEVICE_OWNER_STRING_EXTRAS) |
| .setLongKeys(MessageParser.DEVICE_OWNER_LONG_EXTRAS) |
| .setIntKeys(MessageParser.DEVICE_OWNER_INT_EXTRAS) |
| .setBooleanKeys(MessageParser.DEVICE_OWNER_BOOLEAN_EXTRAS) |
| .setPersistableBundleKeys(MessageParser.DEVICE_OWNER_PERSISTABLE_BUNDLE_EXTRAS); |
| } |
| |
| private static IntentStore getDeviceOwnerEncryptionResumptionIntentStore(Context context) { |
| return getDeviceOwnerIntentStore(context, DEVICE_OWNER_INTENT_TARGET, |
| DEVICE_OWNER_ENCRYPTION_PREFERENCES_NAME); |
| } |
| |
| protected static IntentStore getDeviceOwnerFinalizingIntentStore(Context context) { |
| return getDeviceOwnerIntentStore(context, HOME_RECEIVER_INTENT_TARGET, |
| DEVICE_OWNER_FINALIZING_PREFERENCES_NAME); |
| } |
| |
| /** Create and show the provisioning reminder notification. */ |
| private static void setNotification(Context context, Intent intent) { |
| final NotificationManager notificationManager = (NotificationManager) |
| context.getSystemService(Context.NOTIFICATION_SERVICE); |
| if (intent == null) { |
| notificationManager.cancel(NOTIFY_ID); |
| return; |
| } |
| final PendingIntent resumePendingIntent = PendingIntent.getActivity( |
| context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); |
| final Notification.Builder notify = new Notification.Builder(context) |
| .setContentIntent(resumePendingIntent) |
| .setContentTitle(context.getString(R.string.continue_provisioning_notify_title)) |
| .setContentText(context.getString(R.string.continue_provisioning_notify_text)) |
| .setSmallIcon(com.android.internal.R.drawable.ic_corp_statusbar_icon) |
| .setVisibility(Notification.VISIBILITY_PUBLIC) |
| .setColor(context.getResources().getColor( |
| com.android.internal.R.color.system_notification_accent_color)) |
| .setAutoCancel(true); |
| notificationManager.notify(NOTIFY_ID, notify.build()); |
| } |
| } |