page.title=Ensuring Compatibility with Managed Profiles page.metaDescription=Learn how to make sure your apps operate smoothly in a corporate environment by following some best practices. @jd:body
The Android platform allows devices to have managed profiles. A managed profile is controlled by an administrator, and the functionality available to it is set separately from the functionality of the user's primary profile. This approach lets enterprises control the environment where company-specific apps and data are running on a user's device, while still letting users use their personal apps and profiles.
This lesson shows you how to modify your application so it functions reliably on a device with managed profiles. You don't need to do anything besides the ordinary app-development best practices. However, some of these best practices become especially important on devices with managed profiles. This document highlights the issues you need to be aware of.
Users often want to use their personal devices in an enterprise setting. This situation can present enterprises with a dilemma. If the user can use their own device, the enterprise has to worry that confidential information (like employee emails and contacts) are on a device the enterprise does not control.
To address this situation, Android 5.0 (API level 21) allows enterprises to set up managed profiles. If a device has a managed profile, the profile's settings are under the control of the enterprise administrator. The administrator can choose which apps are allowed for that profile, and can control just what device features are available to the profile.
If a device has a managed profile, there are implications for apps running on the device, no matter which profile the app is running under:
On a device with a managed profile, there are restrictions on whether intents can cross from one profile to another. In most cases, when an intent is fired off, it is handled on the same profile where it is fired. If there is no handler for the intent on that profile, the intent is not handled and the app that fired it may shut down unexpectedly—even if there's a handler for the intent on the other profile.
The profile administrator can choose which intents are allowed to cross from one profile to another. Since the administrator makes this decision, there's no way for you to know in advance which intents are allowed to cross this boundary. The administrator sets this policy, and is free to change it at any time.
Before your app starts an activity, you should verify that there is a
suitable resolution. You
can verify that there is an acceptable resolution by calling {@link
android.content.Intent#resolveActivity Intent.resolveActivity()}. If there is no
way to resolve the intent, the method returns
null
. If the method returns non-null, there is at least one way to
resolve the intent, and it is safe to fire off the intent. In this case, the
intent could be resolvable either
because there is a handler on the current profile, or because the intent is
allowed to cross to a handler on the other profile. (For more information about
resolving intents, see Common Intents.)
For example, if your app needs to set timers, it would need to check that there's a valid handler for the {@link android.provider.AlarmClock#ACTION_SET_TIMER} intent. If the app cannot resolve the intent, it should take an appropriate action (such as showing an error message).
public void startTimer(String message, int seconds) { // Build the "set timer" intent Intent timerIntent = new Intent(AlarmClock.ACTION_SET_TIMER) .putExtra(AlarmClock.EXTRA_MESSAGE, message) .putExtra(AlarmClock.EXTRA_LENGTH, seconds) .putExtra(AlarmClock.EXTRA_SKIP_UI, true); // Check if there's a handler for the intent if (timerIntent.resolveActivity(getPackageManager()) == null) { // Can't resolve the intent! Fail this operation cleanly // (perhaps by showing an error message) } else { // Intent resolves, it's safe to fire it off startActivity(timerIntent); } }
Sometimes an app needs to provide other apps with access to its own files. For example, an image gallery app might want to share its images with image editors. There are two ways you would ordinarily share a file: with a file URI or a content URI.
A file URI begins with the file:
prefix, followed by the
absolute path of the file on the device's storage. However, because the
managed profile and the personal profile use separate storage areas, a file URI
that is valid on one profile is not valid on the other. This situation
means that if you
attach a file URI to an intent, and the intent is handled on the other profile,
the handler is not able to access the file.
Instead, you should share files with content URIs. Content URIs identify the file in a more secure, shareable fashion. The content URI contains the file path, but also the authority that provides the file, and an ID number identifying the file. You can generate a content ID for any file by using a {@link android.support.v4.content.FileProvider}. You can then share that content ID with other apps (even on the other profile). The recipient can use the content ID to get access to the actual file.
For example, here's how you would get the content URI for a specific file URI:
// Open File object from its file URI File fileToShare = new File(fileUriToShare); Uri contentUriToShare = FileProvider.getUriForFile(getContext(), "com.example.myapp.fileprovider", fileToShare);
When you call the {@link
android.support.v4.content.FileProvider#getUriForFile getUriForFile()} method,
you must include the file provider's authority (in this example,
"com.example.myapp.fileprovider"
), which is specified in the
<provider>
element of your app manifest.
For more information about sharing files with content URIs, see
Sharing
Files.
You should test your app in a managed-profile environment to catch problems that would cause your app to fail on a device with managed profiles. In particular, testing on a managed-profile device is a good way to make sure that your app handles intents properly: not firing intents that can't be handled, not attaching URIs that don't work cross-profile, and so on.
We have provided a sample app, BasicManagedProfile, which you can use to set up a managed profile on an Android device that runs Android 5.0 (API level 21) and higher. This app offers you a simple way to test your app in a managed-profile environment. You can also use this app to configure the managed profile as follows:
If you manually install an app over a USB cable to a device which has a managed profile, the app is installed on both the managed and the unmanaged profile. Once you have installed the app, you can test the app under the following conditions:
There are a few tricks that you may find helpful in testing on a managed-profile device.
--user
flag, which lets you specify which user to run
as. By specifying a user, you can choose whether to run as the unmanaged or
managed profile. For
more information, see ADB
Shell Commands.list users
command. The first number in the output string is the
user ID, which you can use with the --user
flag. For more
information, see ADB Shell
Commands.For example, to find the users on a device, you would run this command:
$ adb shell pm list users UserInfo{0:Drew:13} running UserInfo{10:Work profile:30} running
In this case, the unmanaged profile ("Drew") has the user ID 0, and the managed profile has the user ID 10. To run an app in the work profile, you would use a command like this:
$ adb shell am start --user 10 \ -n "com.example.myapp/com.example.myapp.testactivity" \ -a android.intent.action.MAIN -c android.intent.category.LAUNCHER