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