| Billy Lamberta | e1277eb | 2016-05-03 16:23:59 -0700 | [diff] [blame] | 1 | page.title=Set up Managed Profiles |
| Joe Fernandez | 108ccac | 2015-02-19 23:56:07 -0800 | [diff] [blame] | 2 | page.metaDescription=Learn how to make sure your apps operate smoothly in a corporate environment by following some best practices. |
| Billy Lamberta | e1277eb | 2016-05-03 16:23:59 -0700 | [diff] [blame] | 3 | page.image=images/work/cards/briefcase_600px.png |
| Joe Fernandez | 108ccac | 2015-02-19 23:56:07 -0800 | [diff] [blame] | 4 | |
| Andrew Solovay | 713cd74 | 2014-12-08 11:19:09 -0800 | [diff] [blame] | 5 | @jd:body |
| 6 | |
| Billy Lamberta | e1277eb | 2016-05-03 16:23:59 -0700 | [diff] [blame] | 7 | <div id="qv-wrapper"> |
| 8 | <div id="qv"> |
| 9 | <h2>In this document</h2> |
| 10 | <ol> |
| 11 | <li><a href="#prevent_failed_intents">Prevent Failed Intents</a></li> |
| 12 | <li><a href="#sharing_files">Share Files Across Profiles</a></li> |
| 13 | <li><a href="#testing_apps"> |
| 14 | Test your App for Compatibility with Managed Profiles</a></li> |
| 15 | </ol> |
| 16 | </div> |
| Andrew Solovay | 713cd74 | 2014-12-08 11:19:09 -0800 | [diff] [blame] | 17 | </div> |
| 18 | |
| 19 | <p>The Android platform allows devices to have |
| 20 | <a href="{@docRoot}about/versions/android-5.0.html#Enterprise">managed |
| 21 | profiles</a>. A managed profile is controlled by an administrator, and the |
| 22 | functionality available to it is set separately from the functionality of the |
| 23 | user's primary profile. This approach lets enterprises control the environment |
| 24 | where company-specific apps and data are running on a user's device, while still |
| 25 | letting users use their personal apps and profiles.</p> |
| 26 | |
| 27 | <p>This lesson shows you how to modify your application so it functions |
| 28 | reliably on a device with managed profiles. You don't need to do anything |
| 29 | besides the ordinary app-development best practices. However, some of these best |
| 30 | practices become especially important on devices with managed profiles. This |
| 31 | document highlights the issues you need to be aware of.</p> |
| 32 | |
| 33 | <h2 id="overview">Overview</h2> |
| 34 | |
| 35 | <p>Users often want to use their personal devices in an enterprise setting. This |
| 36 | situation can present enterprises with a dilemma. If the user can use their own |
| 37 | device, the enterprise has to worry that confidential information (like employee |
| 38 | emails and contacts) are on a device the enterprise does not control. </p> |
| 39 | |
| 40 | <p>To address this situation, Android 5.0 (API level 21) allows enterprises to |
| 41 | set up <i>managed profiles</i>. If a device has a managed profile, the profile's |
| 42 | settings are under the control of the enterprise administrator. The |
| 43 | administrator can choose which apps are allowed for that profile, and can |
| 44 | control just what device features are available to the profile.</p> |
| 45 | |
| 46 | <p>If a device has a managed profile, there are implications for apps |
| 47 | running on the device, no matter which profile the app is running under:</p> |
| 48 | |
| 49 | <ul> |
| 50 | |
| 51 | <li>By default, most intents do not cross from one profile to the other. If an |
| 52 | app running on profile fires an intent, there is no handler for the intent on |
| 53 | that profile, and the intent is not allowed to cross to the other profile |
| 54 | due to profile restrictions, the request fails and the app may shut down |
| 55 | unexpectedly.</li> |
| 56 | <li>The profile administrator can limit which system apps are available on the |
| 57 | managed profile. This restriction can also result in there being no handler for |
| 58 | some common intents on the managed profile.</li> |
| 59 | <li>Since the managed and unmanaged profiles have separate storage areas, a |
| 60 | file URI that is valid on one profile is not valid on the other. Any |
| 61 | intent fired on one profile might be handled on the other (depending on profile |
| 62 | settings), so it is not safe to attach file URIs to intents.</li> |
| 63 | |
| 64 | </ul> |
| 65 | |
| 66 | <h2 id="prevent_failed_intents">Prevent Failed Intents</h2> |
| 67 | |
| 68 | <p>On a device with a managed profile, there are restrictions on whether intents |
| 69 | can cross from one profile to another. In most cases, when an intent is fired |
| 70 | off, it is handled on the same profile where it is fired. If there is no handler |
| 71 | for the intent <em>on that profile</em>, the intent is not handled and the app |
| 72 | that fired it may shut down unexpectedly—even if there's a handler for the |
| 73 | intent on the other profile.</p> |
| 74 | |
| 75 | <p>The profile administrator can choose which intents are |
| 76 | allowed to cross from one profile to another. Since the administrator makes |
| 77 | this decision, there's no way for you |
| 78 | to know in advance <em>which</em> intents are allowed to cross this boundary. The |
| 79 | administrator sets this policy, and is free to change it at any time.</p> |
| 80 | |
| 81 | <p>Before your app starts an activity, you should verify that there is a |
| 82 | suitable resolution. You |
| 83 | can verify that there is an acceptable resolution by calling {@link |
| 84 | android.content.Intent#resolveActivity Intent.resolveActivity()}. If there is no |
| 85 | way to resolve the intent, the method returns |
| 86 | <code>null</code>. If the method returns non-null, there is at least one way to |
| 87 | resolve the intent, and it is safe to fire off the intent. In this case, the |
| 88 | intent could be resolvable either |
| 89 | because there is a handler on the current profile, or because the intent is |
| 90 | allowed to cross to a handler on the other profile. (For more information about |
| 91 | resolving intents, see <a |
| 92 | href="{@docRoot}guide/components/intents-common.html">Common Intents</a>.)</p> |
| 93 | |
| 94 | <p>For example, if your app needs to set timers, it would need to check that |
| 95 | there's a valid handler for the {@link |
| 96 | android.provider.AlarmClock#ACTION_SET_TIMER} intent. If the app cannot resolve |
| 97 | the intent, it should take an appropriate action (such as showing an error |
| 98 | message).</p> |
| 99 | |
| 100 | <pre>public void startTimer(String message, int seconds) { |
| 101 | |
| 102 | // Build the "set timer" intent |
| 103 | Intent timerIntent = new Intent(AlarmClock.ACTION_SET_TIMER) |
| 104 | .putExtra(AlarmClock.EXTRA_MESSAGE, message) |
| 105 | .putExtra(AlarmClock.EXTRA_LENGTH, seconds) |
| 106 | .putExtra(AlarmClock.EXTRA_SKIP_UI, true); |
| 107 | |
| 108 | // Check if there's a handler for the intent |
| 109 | <strong>if (timerIntent.resolveActivity(getPackageManager()) == null)</strong> { |
| 110 | |
| 111 | // Can't resolve the intent! Fail this operation cleanly |
| 112 | // (perhaps by showing an error message) |
| 113 | |
| 114 | } else { |
| 115 | // Intent resolves, it's safe to fire it off |
| 116 | startActivity(timerIntent); |
| 117 | |
| 118 | } |
| 119 | } |
| 120 | </pre> |
| 121 | |
| 122 | <h2 id="sharing_files">Share Files Across Profiles</h2> |
| 123 | |
| 124 | <p>Sometimes an app needs to provide other apps with access to its own files. |
| 125 | For example, an image gallery app might want to share its images with image |
| 126 | editors. There are two ways you would ordinarily share a file: with a <em>file |
| 127 | URI</em> or a <em>content URI</em>.</p> |
| 128 | |
| 129 | <p>A file URI begins with the <code>file:</code> prefix, followed by the |
| 130 | absolute path of the file on the device's storage. However, because the |
| 131 | managed profile and the personal profile use separate storage areas, a file URI |
| 132 | that is valid on one profile is not valid on the other. This situation |
| 133 | means that if you |
| 134 | attach a file URI to an intent, and the intent is handled on the other profile, |
| 135 | the handler is not able to access the file.</p> |
| 136 | |
| 137 | <p>Instead, you should share files with <em>content URIs</em>. Content URIs |
| 138 | identify the file in a more secure, shareable fashion. The content URI contains |
| 139 | the file path, but also the authority that provides the file, and an ID number |
| 140 | identifying the file. You can generate a content ID for any file by using a |
| 141 | {@link android.support.v4.content.FileProvider}. You can then share that content |
| 142 | ID with other apps (even on the other profile). The recipient can use the |
| 143 | content ID to get access to the actual file.</p> |
| 144 | |
| 145 | <p>For example, here's how you would get the content URI for a specific file |
| 146 | URI:</p> |
| 147 | |
| 148 | <pre>// Open File object from its file URI |
| 149 | File fileToShare = new File(<em>fileUriToShare</em>); |
| 150 | |
| 151 | Uri contentUriToShare = FileProvider.getUriForFile(getContext(), |
| 152 | <em>"com.example.myapp.fileprovider"</em>, fileToShare);</pre> |
| 153 | |
| 154 | <p>When you call the {@link |
| 155 | android.support.v4.content.FileProvider#getUriForFile getUriForFile()} method, |
| 156 | you must include the file provider's authority (in this example, |
| 157 | <code>"com.example.myapp.fileprovider"</code>), which is specified in the |
| 158 | <a href="{@docRoot}guide/topics/manifest/provider-element.html"><code><provider></code></a> |
| 159 | element of your app manifest. |
| 160 | For more information about sharing files with content URIs, see |
| 161 | <a href="{@docRoot}training/secure-file-sharing/index.html">Sharing |
| 162 | Files</a>.</p> |
| 163 | |
| 164 | <h2 id="testing_apps">Test your App for Compatibility with Managed Profiles</h2> |
| 165 | |
| 166 | <p>You should test your app in a managed-profile environment to |
| 167 | catch problems that would cause your app to fail on a device with |
| 168 | managed profiles. In particular, testing on a managed-profile device is a good |
| 169 | way to make sure that your app handles intents properly: not firing intents that |
| 170 | can't be handled, not attaching URIs that don't work cross-profile, and so |
| 171 | on.</p> |
| 172 | |
| 173 | <p>We have provided a sample app, <a |
| 174 | href="{@docRoot}samples/BasicManagedProfile/index.html">BasicManagedProfile</a>, |
| 175 | which you can use to set up a managed profile on an Android device that runs |
| 176 | Android 5.0 (API level 21) and higher. This app offers you a simple way to test |
| 177 | your app in a managed-profile environment. You can also use this app to |
| 178 | configure the managed profile as follows:</p> |
| 179 | |
| 180 | <ul> |
| 181 | |
| 182 | <li>Specify which default apps are available on the managed |
| 183 | profile</li> |
| 184 | |
| 185 | <li>Configure which intents are allowed to cross from one profile to |
| 186 | the other</li> |
| 187 | |
| 188 | </ul> |
| 189 | |
| 190 | <p>If you manually install an app over a USB cable to a device which has a |
| 191 | managed profile, the app is installed on both the managed and the unmanaged |
| 192 | profile. Once you have installed the app, you can test the app under the |
| 193 | following conditions:</p> |
| 194 | |
| 195 | <ul> |
| 196 | |
| 197 | <li>If an intent would ordinarily be handled by a default app (for example, |
| 198 | the camera app), try disabling that default app on the managed profile, and |
| 199 | verify that the app handles this appropriately.</li> |
| 200 | |
| 201 | <li>If you fire an intent expecting it to be handled by some other app, try |
| 202 | enabling and disabling that intent's permission to cross from one profile to |
| 203 | another. Verify that the app behaves properly under both circumstances. If the |
| 204 | intent is not allowed to cross between profiles, verify the app's behavior both |
| 205 | when there is a suitable handler on the app's profile, and when there is not. |
| 206 | For example, if your app fires a map-related intent, try each of the following |
| 207 | scenarios: |
| 208 | |
| 209 | <ul> |
| 210 | |
| 211 | <li>The device allows map intents to cross from one profile to the other, and |
| 212 | there is a suitable handler on the other profile (the profile the app is not |
| 213 | running on)</li> |
| 214 | |
| 215 | <li>The device does not allow map intents to cross between profiles, but there |
| 216 | is a suitable handler on the app's profile</li> |
| 217 | |
| 218 | <li>The device does not allow map intents to cross between profiles, and there |
| 219 | is no suitable handler for map intents on the device's profile</li> |
| 220 | |
| 221 | </ul> |
| 222 | </li> |
| 223 | |
| 224 | <li>If you attach content to an intent, verify that the intent behaves properly |
| 225 | both when it is handled on the app's profile, and when it crosses between |
| 226 | profiles.</li> |
| 227 | |
| 228 | </ul> |
| 229 | |
| 230 | <h3 id="testing_tips">Testing on managed profiles: Tips and tricks</h3> |
| 231 | |
| 232 | <p>There are a few tricks that you may find helpful in testing on a |
| 233 | managed-profile device.</p> |
| 234 | |
| 235 | <ul> |
| 236 | |
| 237 | <li>As noted, when you side-load an app on a managed profile device, it is |
| 238 | installed on both profiles. If you wish, you can delete the app from one profile |
| 239 | and leave it on the other.</li> |
| 240 | |
| 241 | <li>Most of the activity manager commands available in the <a |
| 242 | href="{@docRoot}tools/help/adb.html">Android Debug Bridge</a> (adb) shell |
| 243 | support the <code>--user</code> flag, which lets you specify which user to run |
| 244 | as. By specifying a user, you can choose whether to run as the unmanaged or |
| 245 | managed profile. For |
| Luan Nguyen | 1afb2d5 | 2015-01-16 08:21:57 -0800 | [diff] [blame] | 246 | more information, see <a href="{@docRoot}tools/help/shell.html#am">ADB |
| 247 | Shell Commands</a>.</li> |
| Andrew Solovay | 713cd74 | 2014-12-08 11:19:09 -0800 | [diff] [blame] | 248 | |
| 249 | <li>To find the active users on a device, use the adb package manager's |
| 250 | <code>list users</code> command. The first number in the output string is the |
| 251 | user ID, which you can use with the <code>--user</code> flag. For more |
| Luan Nguyen | 1afb2d5 | 2015-01-16 08:21:57 -0800 | [diff] [blame] | 252 | information, see <a href="{@docRoot}tools/help/shell.html#pm">ADB Shell |
| 253 | Commands</a>.</li> |
| Andrew Solovay | 713cd74 | 2014-12-08 11:19:09 -0800 | [diff] [blame] | 254 | |
| 255 | </ul> |
| 256 | |
| 257 | <p>For example, to find the users on a device, you would run this command:</p> |
| 258 | |
| 259 | <pre class="no-pretty-print">$ <strong>adb shell pm list users</strong> |
| 260 | UserInfo{0:Drew:13} running |
| 261 | UserInfo{10:Work profile:30} running</pre> |
| 262 | |
| 263 | <p>In this case, the unmanaged profile ("Drew") has the user ID 0, and the |
| 264 | managed profile has the user ID 10. To run an app in the work profile, you |
| 265 | would use a command like this:</p> |
| 266 | |
| 267 | <pre class="no-pretty-print">$ adb shell am start --user 10 \ |
| 268 | -n "<em>com.example.myapp/com.example.myapp.testactivity</em>" \ |
| 269 | -a android.intent.action.MAIN -c android.intent.category.LAUNCHER</pre> |