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 */
16package com.android.managedprovisioning;
17
18import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE;
19import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME;
20
21import android.app.Notification;
22import android.app.NotificationManager;
23import android.app.PendingIntent;
24import android.content.BroadcastReceiver;
25import android.content.ComponentName;
26import android.content.Context;
27import android.content.Intent;
28import android.os.Bundle;
29
30/**
31 * Class that handles the resuming process that takes place after a reboot for encryption
32 * during the provisioning process.
33 */
34public class BootReminder extends BroadcastReceiver {
35    private static final int NOTIFY_ID = 1;
36
37    /*
38     * Profile owner parameters that are stored in the IntentStore for resuming provisioning.
39     */
40    private static final String PROFILE_OWNER_PREFERENCES_NAME =
41            "profile-owner-provisioning-resume";
42
43    private static final String[] PROFILE_OWNER_STRING_EXTRAS = {
44        // Key for the device admin package name
45        EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME
46    };
47
48    private static final String[] PROFILE_OWNER_PERSISTABLE_BUNDLE_EXTRAS = {
49        // Key for the admin extras bundle
50        EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE
51    };
52
53    private static final ComponentName PROFILE_OWNER_INTENT_TARGET =
54            ManagedProvisioningActivity.ALIAS_NO_CHECK_CALLER;
55
56    /*
57     * Device owner parameters that are stored in the IntentStore for resuming provisioning.
58     */
59    private static final String DEVICE_OWNER_PREFERENCES_NAME =
60            "device-owner-provisioning-resume";
61
62    private static final ComponentName DEVICE_OWNER_INTENT_TARGET =
63            new ComponentName("com.android.managedprovisioning",
64                    "com.android.managedprovisioning.DeviceOwnerProvisioningActivity");
65
66    @Override
67    public void onReceive(Context context, Intent intent) {
68        if (android.content.Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
69
70            // Resume profile owner provisioning if applicable.
71            IntentStore profileOwnerIntentStore = getProfileOwnerIntentStore(context);
72            final Intent resumeProfileOwnerPrvIntent = profileOwnerIntentStore.load();
73            if (resumeProfileOwnerPrvIntent != null) {
74                if (EncryptDeviceActivity.isDeviceEncrypted()) {
75                    // Show reminder notification and then forget about it for next boot
76                    profileOwnerIntentStore.clear();
77                    setNotification(context, resumeProfileOwnerPrvIntent);
78                }
79            }
80
81            // Resume device owner provisioning if applicable.
82            IntentStore deviceOwnerIntentStore = getDeviceOwnerIntentStore(context);
83            Intent resumeDeviceOwnerPrvIntent = deviceOwnerIntentStore.load();
84            if (resumeDeviceOwnerPrvIntent != null) {
85                deviceOwnerIntentStore.clear();
86                resumeDeviceOwnerPrvIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
87                context.startActivity(resumeDeviceOwnerPrvIntent);
88            }
89        }
90    }
91
92    /**
93     * Schedule a provisioning reminder notification for the next reboot.
94     *
95     * {@code extras} should be a Bundle containing the
96     * {@link EncryptDeviceActivity.EXTRA_RESUME_TARGET}.
97     * This field has only two supported values {@link EncryptDeviceActivity.TARGET_PROFILE_OWNER}
98     * and {@link EncryptDeviceActivity.TARGET_DEVICE_OWNER}
99     *
100     * <p> In case of TARGET_PROFILE_OWNER {@code extras} should further contain a value for at
101     * least the key: {@link EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME}, a {@link String} which
102     * specifies the package to set as profile owner.
103     *
104     * <p>
105     * See {@link MessageParser} for the TARGET_DEVICE_OWNER case.
106     * </ul>
107     *
108     * <p> These fields will be persisted and restored to the provisioner after rebooting. Any other
109     * key/value pairs will be ignored.
110     */
111    public static void setProvisioningReminder(Context context, Bundle extras) {
112        IntentStore intentStore;
113        String resumeTarget = extras.getString(EncryptDeviceActivity.EXTRA_RESUME_TARGET, null);
114        if (resumeTarget == null) {
115            return;
116        }
117        if (resumeTarget.equals(EncryptDeviceActivity.TARGET_PROFILE_OWNER)) {
118            intentStore = getProfileOwnerIntentStore(context);
119        } else if (resumeTarget.equals(EncryptDeviceActivity.TARGET_DEVICE_OWNER)) {
120            intentStore = getDeviceOwnerIntentStore(context);
121        } else {
122            ProvisionLogger.loge("Unknown resume target for bootreminder.");
123            return;
124        }
125        intentStore.save(extras);
126    }
127
128    /**
129     * Cancel all active provisioning reminders.
130     */
131    public static void cancelProvisioningReminder(Context context) {
132        getProfileOwnerIntentStore(context).clear();
133        getDeviceOwnerIntentStore(context).clear();
134        setNotification(context, null);
135    }
136
137    private static IntentStore getProfileOwnerIntentStore(Context context) {
138        return new IntentStore(context,PROFILE_OWNER_INTENT_TARGET, PROFILE_OWNER_PREFERENCES_NAME)
139                .setStringKeys(PROFILE_OWNER_STRING_EXTRAS)
140                .setPersistableBundleKeys(PROFILE_OWNER_PERSISTABLE_BUNDLE_EXTRAS);
141    }
142
143    private static IntentStore getDeviceOwnerIntentStore(Context context) {
144        return new IntentStore(context, DEVICE_OWNER_INTENT_TARGET, DEVICE_OWNER_PREFERENCES_NAME)
145                .setStringKeys(MessageParser.DEVICE_OWNER_STRING_EXTRAS)
146                .setLongKeys(MessageParser.DEVICE_OWNER_LONG_EXTRAS)
147                .setIntKeys(MessageParser.DEVICE_OWNER_INT_EXTRAS)
148                .setBooleanKeys(MessageParser.DEVICE_OWNER_BOOLEAN_EXTRAS)
149                .setPersistableBundleKeys(MessageParser.DEVICE_OWNER_PERSISTABLE_BUNDLE_EXTRAS);
150    }
151
152    /** Create and show the provisioning reminder notification. */
153    private static void setNotification(Context context, Intent intent) {
154        final NotificationManager notificationManager = (NotificationManager)
155                context.getSystemService(Context.NOTIFICATION_SERVICE);
156        if (intent == null) {
157            notificationManager.cancel(NOTIFY_ID);
158            return;
159        }
160        final PendingIntent resumePendingIntent = PendingIntent.getActivity(
161                context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
162        final Notification.Builder notify = new Notification.Builder(context)
163                .setContentIntent(resumePendingIntent)
164                .setContentTitle(context.getString(R.string.continue_provisioning_notify_title))
165                .setContentText(context.getString(R.string.continue_provisioning_notify_text))
166                .setSmallIcon(com.android.internal.R.drawable.ic_corp_statusbar_icon)
167                .setVisibility(Notification.VISIBILITY_PUBLIC)
168                .setColor(context.getResources().getColor(
169                        com.android.internal.R.color.system_notification_accent_color))
170                .setAutoCancel(true);
171        notificationManager.notify(NOTIFY_ID, notify.build());
172    }
173}
174