ManagedProvisioningActivity.java revision df8cba7fec4b96c113697d1ef67de2e2e196988e
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
17package com.android.managedprovisioning;
18
19import static android.app.admin.DeviceAdminReceiver.ACTION_PROFILE_PROVISIONING_COMPLETE;
20import static android.app.admin.DevicePolicyManager.EXTRA_DEVICE_ADMIN;
21import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME;
22import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME;
23import static com.android.managedprovisioning.UserConsentActivity.USER_CONSENT_KEY;
24
25import android.app.Activity;
26import android.app.ActivityManagerNative;
27import android.app.IActivityManager;
28import android.app.admin.DevicePolicyManager;
29import android.content.ComponentName;
30import android.content.Context;
31import android.content.Intent;
32import android.content.IntentFilter;
33import android.content.pm.IPackageManager;
34import android.content.pm.PackageManager;
35import android.content.pm.PackageManager.NameNotFoundException;
36import android.content.pm.UserInfo;
37import android.os.Bundle;
38import android.os.Process;
39import android.os.RemoteException;
40import android.os.ServiceManager;
41import android.os.UserHandle;
42import android.os.UserManager;
43import android.text.TextUtils;
44import android.view.LayoutInflater;
45import android.view.View;
46
47
48
49import com.android.managedprovisioning.task.DeleteNonRequiredAppsTask;
50
51import java.util.List;
52
53/**
54 * Handles managed profile provisioning: A device that already has a user, but needs to be set up
55 * for a secondary usage purpose (e.g using your personal device as a corporate device).
56 */
57// TODO: Proper error handling to report back to the user and potentially the mdm.
58public class ManagedProvisioningActivity extends Activity {
59
60    private static final int USER_CONSENT_REQUEST_CODE = 1;
61    private static final int ENCRYPT_DEVICE_REQUEST_CODE = 2;
62
63    private String mMdmPackageName;
64    private ComponentName mActiveAdminComponentName;
65    private String mDefaultManagedProfileName;
66
67    private IPackageManager mIpm;
68    private UserInfo mManagedProfileUserInfo;
69    private UserManager mUserManager;
70
71    private Boolean userConsented;
72
73    @Override
74    protected void onCreate(Bundle savedInstanceState) {
75        super.onCreate(savedInstanceState);
76
77        ProvisionLogger.logd("Managed provisioning activity ONCREATE");
78
79        PackageManager pm = getPackageManager();
80        if (!pm.hasSystemFeature(PackageManager.FEATURE_MANAGED_PROFILES)) {
81            showErrorAndClose(R.string.managed_provisioning_not_supported,
82                    "Exiting managed provisioning, managed profiles feature is not available");
83            return;
84        }
85
86        // Initialize member variables from the intent, stop if the intent wasn't valid.
87        try {
88            initialize(getIntent());
89        } catch (ManagedProvisioningFailedException e) {
90            showErrorAndClose(R.string.managed_provisioning_error_text, e.getMessage());
91            return;
92        }
93
94        // TODO: update UI
95        final LayoutInflater inflater = getLayoutInflater();
96        final View contentView = inflater.inflate(R.layout.progress_profile_owner, null);
97        setContentView(contentView);
98
99        mIpm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
100        mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
101
102        if (!alreadyHasManagedProfile()) {
103            // Ask for user consent.
104            Intent userConsentIntent = new Intent(this, UserConsentActivity.class);
105            startActivityForResult(userConsentIntent, USER_CONSENT_REQUEST_CODE);
106            // Wait for user consent, in onActivityResult
107        } else {
108            showErrorAndClose(R.string.managed_profile_already_present,
109                    "The device already has a managed profile, nothing to do.");
110        }
111    }
112
113    private void initialize(Intent intent)
114            throws ManagedProvisioningFailedException {
115        mMdmPackageName = intent.getStringExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME);
116        // Validate package name
117        if (TextUtils.isEmpty(mMdmPackageName)) {
118            throw new ManagedProvisioningFailedException("Missing intent extra: "
119                    + EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME);
120        } else {
121            // Check if the package is installed
122            try {
123                this.getPackageManager().getPackageInfo(mMdmPackageName, 0);
124            } catch (NameNotFoundException e) {
125                throw new ManagedProvisioningFailedException("Mdm "+ mMdmPackageName
126                        + " is not installed. " + e);
127            }
128        }
129
130        mActiveAdminComponentName = intent.getParcelableExtra(EXTRA_DEVICE_ADMIN);
131        if (mActiveAdminComponentName == null) {
132            throw new ManagedProvisioningFailedException("Missing intent extra: "
133                    + EXTRA_DEVICE_ADMIN);
134        }
135
136        mDefaultManagedProfileName = getIntent()
137                .getStringExtra(EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME);
138        // Validate profile name
139        if (TextUtils.isEmpty(mDefaultManagedProfileName)) {
140            throw new ManagedProvisioningFailedException("Missing intent extra: "
141                    + EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME);
142        }
143    }
144
145    @Override
146    public void onBackPressed() {
147        // TODO: Handle this graciously by stopping the provisioning flow and cleaning up.
148    }
149
150    @Override
151    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
152
153        // Wait for the user to consent before starting managed profile provisioning.
154        if (requestCode == USER_CONSENT_REQUEST_CODE) {
155            if (resultCode == RESULT_OK) {
156                userConsented = data.getBooleanExtra(USER_CONSENT_KEY, false);
157
158                // Only start provisioning if the user has consented.
159                if (!userConsented) {
160                    ProvisionLogger.logd("User did not consent to profile creation, "
161                            + "cancelling provisioing");
162                    finish();
163                    return;
164                }
165
166                // Ask to encrypt the device before proceeding
167                if (!EncryptDeviceActivity.isDeviceEncrypted()) {
168                    Intent encryptIntent = new Intent(this, EncryptDeviceActivity.class)
169                            .putExtra(EncryptDeviceActivity.EXTRA_RESUME, getIntent().getExtras());
170                    startActivityForResult(encryptIntent, ENCRYPT_DEVICE_REQUEST_CODE);
171                    return;
172                }
173
174                startManagedProfileProvisioning();
175            }
176            if (resultCode == RESULT_CANCELED) {
177                ProvisionLogger.logd("User consent cancelled.");
178                finish();
179            }
180        } else if (requestCode == ENCRYPT_DEVICE_REQUEST_CODE) {
181            if (resultCode == RESULT_CANCELED) {
182                // Move back to user consent screen
183                Intent userConsentIntent = new Intent(this, UserConsentActivity.class);
184                startActivityForResult(userConsentIntent, USER_CONSENT_REQUEST_CODE);
185            }
186        }
187    }
188
189    /**
190     * This is the core method of this class. It goes through every provisioning step.
191     */
192    private void startManagedProfileProvisioning() {
193
194        ProvisionLogger.logd("Starting managed profile provisioning");
195        // Work through the provisioning steps in their corresponding order
196
197        try {
198            createProfile(mDefaultManagedProfileName);
199            DeleteNonRequiredAppsTask deleteTask =
200                    new DeleteNonRequiredAppsTask(this,
201                            mMdmPackageName, mManagedProfileUserInfo.id,
202                            new DeleteNonRequiredAppsTask.Callback() {
203
204                                @Override
205                                public void onSuccess() {
206                                    setUpProfileAndFinish();
207                                }
208
209                                @Override
210                                public void onError() {
211                                    cleanup();
212                                    showErrorAndClose(R.string.managed_provisioning_error_text,
213                                            "Delete non required apps task failed.");
214                                }},
215                            R.array.required_apps_managed_profile,
216                            R.array.vendor_required_apps_managed_profile);
217            deleteTask.run();
218        } catch (ManagedProvisioningFailedException e) {
219            cleanup();
220            showErrorAndClose(R.string.managed_provisioning_error_text,
221                    "Could not finish managed profile provisioning: " + e.getMessage());
222        }
223    }
224
225    /**
226     * Called when the new profile is ready for provisioning (the profile is created and all the
227     * apps not needed have been deleted).
228     */
229    private void setUpProfileAndFinish() {
230        try {
231            installMdmOnManagedProfile();
232            setMdmAsActiveAdmin();
233            setMdmAsManagedProfileOwner();
234            startManagedProfile();
235            forwardIntentsToPrimaryUser();
236            sendProvisioningCompleteToManagedProfile(this);
237            ProvisionLogger.logd("Finishing managed profile provisioning.");
238            finish();
239        } catch (ManagedProvisioningFailedException e) {
240            cleanup();
241            showErrorAndClose(R.string.managed_provisioning_error_text,
242                    "Could not finish managed profile provisioning: " + e.getMessage());
243        }
244    }
245
246    private void createProfile(String profileName) throws ManagedProvisioningFailedException {
247
248        ProvisionLogger.logd("Creating managed profile with name " + profileName);
249
250        mManagedProfileUserInfo = mUserManager.createProfileForUser(profileName,
251                UserInfo.FLAG_MANAGED_PROFILE | UserInfo.FLAG_DISABLED,
252                Process.myUserHandle().getIdentifier());
253
254        if (mManagedProfileUserInfo == null) {
255            if (UserManager.getMaxSupportedUsers() == mUserManager.getUserCount()) {
256                throw new ManagedProvisioningFailedException(
257                        "Profile creation failed, maximum number of users reached.");
258            } else {
259                throw new ManagedProvisioningFailedException(
260                        "Couldn't create profile. Reason unknown.");
261            }
262        }
263    }
264
265    /**
266     * Performs cleanup of the device on failure.
267     */
268    private void cleanup() {
269        // The only cleanup we need to do is remove the profile we created.
270        if (mManagedProfileUserInfo != null) {
271            ProvisionLogger.logd("Removing managed profile");
272            mUserManager.removeUser(mManagedProfileUserInfo.id);
273        }
274    }
275
276    /**
277     * Initializes the user that underlies the managed profile.
278     * This is required so that the provisioning complete broadcast can be sent across to the
279     * profile and apps can run on it.
280     */
281    private void startManagedProfile() throws ManagedProvisioningFailedException {
282        ProvisionLogger.logd("Starting user in background");
283        IActivityManager iActivityManager = ActivityManagerNative.getDefault();
284        try {
285            boolean success = iActivityManager.startUserInBackground(mManagedProfileUserInfo.id);
286            if (!success) {
287                throw new ManagedProvisioningFailedException("Could not start user in background");
288            }
289        } catch (RemoteException neverThrown) {
290            // Never thrown, as we are making local calls.
291            ProvisionLogger.loge("This should not happen.", neverThrown);
292        }
293    }
294
295    private void installMdmOnManagedProfile() throws ManagedProvisioningFailedException {
296
297        ProvisionLogger.logd("Installing mobile device management app " + mMdmPackageName +
298              " on managed profile");
299
300        try {
301            int status = mIpm.installExistingPackageAsUser(
302                mMdmPackageName, mManagedProfileUserInfo.id);
303            switch (status) {
304              case PackageManager.INSTALL_SUCCEEDED:
305                  return;
306              case PackageManager.INSTALL_FAILED_USER_RESTRICTED:
307                  // Should not happen because we're not installing a restricted user
308                  throw new ManagedProvisioningFailedException(
309                          "Could not install mobile device management app on managed profile " +
310                          "because the user is restricted");
311              case PackageManager.INSTALL_FAILED_INVALID_URI:
312                  // Should not happen because we already checked
313                  throw new ManagedProvisioningFailedException(
314                          "Could not install mobile device management app on managed profile " +
315                          "because the package could not be found");
316              default:
317                  throw new ManagedProvisioningFailedException(
318                          "Could not install mobile device management app on managed profile. " +
319                          "Unknown status: " + status);
320            }
321        } catch (RemoteException neverThrown) {
322            // Never thrown, as we are making local calls.
323            ProvisionLogger.loge("This should not happen.", neverThrown);
324        }
325    }
326
327    private void setMdmAsManagedProfileOwner() throws ManagedProvisioningFailedException {
328
329        ProvisionLogger.logd("Setting package " + mMdmPackageName + " as managed profile owner.");
330
331        DevicePolicyManager dpm =
332                (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
333        if (!dpm.setProfileOwner(
334                mMdmPackageName, mDefaultManagedProfileName, mManagedProfileUserInfo.id)) {
335            ProvisionLogger.logw("Could not set profile owner.");
336            throw new ManagedProvisioningFailedException("Could not set profile owner.");
337        }
338    }
339
340    private void setMdmAsActiveAdmin() {
341
342        ProvisionLogger.logd("Setting package " + mMdmPackageName + " as active admin.");
343
344        DevicePolicyManager dpm =
345                (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
346        dpm.setActiveAdmin(mActiveAdminComponentName, true /* refreshing*/,
347                mManagedProfileUserInfo.id);
348    }
349
350    public void showErrorAndClose(int resourceId, String logText) {
351        ProvisionLogger.loge(logText);
352        new ManagedProvisioningErrorDialog(getString(resourceId))
353              .show(getFragmentManager(), "ErrorDialogFragment");
354    }
355
356    boolean alreadyHasManagedProfile() {
357        UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
358        List<UserInfo> profiles = userManager.getProfiles(getUserId());
359        for (UserInfo userInfo : profiles) {
360            if (userInfo.isManagedProfile()) return true;
361        }
362        return false;
363    }
364
365    private void sendProvisioningCompleteToManagedProfile(Context context) {
366        UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
367        UserHandle userHandle = userManager.getUserForSerialNumber(
368                mManagedProfileUserInfo.serialNumber);
369
370        Intent completeIntent = new Intent(ACTION_PROFILE_PROVISIONING_COMPLETE);
371        completeIntent.setComponent(mActiveAdminComponentName);
372        completeIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES |
373            Intent.FLAG_RECEIVER_FOREGROUND);
374        context.sendBroadcastAsUser(completeIntent, userHandle);
375
376        ProvisionLogger.logd("Provisioning complete broadcast has been sent to user "
377            + userHandle.getIdentifier());
378      }
379
380    private void forwardIntentsToPrimaryUser() {
381        ProvisionLogger.logd("Setting forwarding intent filters");
382        PackageManager pm = getPackageManager();
383
384        IntentFilter mimeTypeTelephony = new IntentFilter();
385        mimeTypeTelephony.addAction("android.intent.action.DIAL");
386        mimeTypeTelephony.addCategory("android.intent.category.DEFAULT");
387        mimeTypeTelephony.addCategory("android.intent.category.BROWSABLE");
388        try {
389            mimeTypeTelephony.addDataType("vnd.android.cursor.item/phone");
390            mimeTypeTelephony.addDataType("vnd.android.cursor.item/person");
391            mimeTypeTelephony.addDataType("vnd.android.cursor.dir/calls");
392        } catch (IntentFilter.MalformedMimeTypeException e) {
393            //will not happen
394        }
395        pm.addForwardingIntentFilter(mimeTypeTelephony, false /*non-removable*/,
396                mManagedProfileUserInfo.id, UserHandle.USER_OWNER);
397
398        IntentFilter callDial = new IntentFilter();
399        callDial.addAction("android.intent.action.DIAL");
400        callDial.addAction("android.intent.action.CALL");
401        callDial.addAction("android.intent.action.VIEW");
402        callDial.addCategory("android.intent.category.DEFAULT");
403        callDial.addCategory("android.intent.category.BROWSABLE");
404        callDial.addDataScheme("tel");
405        callDial.addDataScheme("voicemail");
406        callDial.addDataScheme("sip");
407        callDial.addDataScheme("tel");
408        pm.addForwardingIntentFilter(callDial, false /*non-removable*/, mManagedProfileUserInfo.id,
409                UserHandle.USER_OWNER);
410
411        IntentFilter callDialNoData = new IntentFilter();
412        callDialNoData.addAction("android.intent.action.DIAL");
413        callDialNoData.addAction("android.intent.action.CALL");
414        callDialNoData.addAction("android.intent.action.CALL_BUTTON");
415        callDialNoData.addCategory("android.intent.category.DEFAULT");
416        callDialNoData.addCategory("android.intent.category.BROWSABLE");
417        pm.addForwardingIntentFilter(callDialNoData, false /*non-removable*/,
418                mManagedProfileUserInfo.id, UserHandle.USER_OWNER);
419
420        IntentFilter smsMms = new IntentFilter();
421        smsMms.addAction("android.intent.action.VIEW");
422        smsMms.addAction("android.intent.action.SENDTO");
423        smsMms.addCategory("android.intent.category.DEFAULT");
424        smsMms.addCategory("android.intent.category.BROWSABLE");
425        smsMms.addDataScheme("sms");
426        smsMms.addDataScheme("smsto");
427        smsMms.addDataScheme("mms");
428        smsMms.addDataScheme("mmsto");
429        pm.addForwardingIntentFilter(smsMms, false /*non-removable*/, mManagedProfileUserInfo.id,
430                UserHandle.USER_OWNER);
431
432        IntentFilter setPassword = new IntentFilter();
433        setPassword.addAction("android.app.action.SET_NEW_PASSWORD");
434        setPassword.addCategory("android.intent.category.DEFAULT");
435        pm.addForwardingIntentFilter(setPassword, false /*non-removable*/,
436                mManagedProfileUserInfo.id, UserHandle.USER_OWNER);
437
438        IntentFilter send = new IntentFilter();
439        send.addAction("android.intent.action.SEND");
440        send.addAction("android.intent.action.SEND_MULTIPLE");
441        send.addCategory("android.intent.category.DEFAULT");
442        try {
443            send.addDataType("*/*");
444        } catch (IntentFilter.MalformedMimeTypeException e) {
445            //will not happen
446        }
447        pm.addForwardingIntentFilter(send, false /*non-removable*/,
448                UserHandle.USER_OWNER, mManagedProfileUserInfo.id);
449
450        IntentFilter getContent = new IntentFilter();
451        getContent.addAction("android.intent.action.GET_CONTENT");
452        getContent.addCategory("android.intent.category.DEFAULT");
453        getContent.addCategory("android.intent.category.OPENABLE");
454        try {
455            getContent.addDataType("*/*");
456        } catch (IntentFilter.MalformedMimeTypeException e) {
457            //will not happen
458        }
459        pm.addForwardingIntentFilter(getContent, false /*non-removable*/,
460                mManagedProfileUserInfo.id, UserHandle.USER_OWNER);
461
462        IntentFilter openDocument = new IntentFilter();
463        openDocument.addAction("android.intent.action.OPEN_DOCUMENT");
464        openDocument.addCategory("android.intent.category.DEFAULT");
465        openDocument.addCategory("android.intent.category.OPENABLE");
466        try {
467            openDocument.addDataType("*/*");
468        } catch (IntentFilter.MalformedMimeTypeException e) {
469            //will not happen
470        }
471        pm.addForwardingIntentFilter(openDocument, false /*non-removable*/,
472                mManagedProfileUserInfo.id, UserHandle.USER_OWNER);
473
474        IntentFilter pick = new IntentFilter();
475        pick.addAction("android.intent.action.PICK");
476        pick.addCategory("android.intent.category.DEFAULT");
477        try {
478            pick.addDataType("*/*");
479        } catch (IntentFilter.MalformedMimeTypeException e) {
480            //will not happen
481        }
482        pm.addForwardingIntentFilter(pick, false /*non-removable*/,
483                mManagedProfileUserInfo.id, UserHandle.USER_OWNER);
484
485        IntentFilter pickNoData = new IntentFilter();
486        pickNoData.addAction("android.intent.action.PICK");
487        pickNoData.addCategory("android.intent.category.DEFAULT");
488        pm.addForwardingIntentFilter(pickNoData, false /*non-removable*/,
489                mManagedProfileUserInfo.id, UserHandle.USER_OWNER);
490    }
491
492    /**
493     * Exception thrown when the managed provisioning has failed completely.
494     *
495     * Note: We're using a custom exception to avoid catching subsequent exceptions that might be
496     * significant.
497     */
498    private class ManagedProvisioningFailedException extends Exception {
499      public ManagedProvisioningFailedException(String message) {
500          super(message);
501      }
502    }
503}
504
505