| page.title=Set up Managed Configurations | |
| page.metaDescription=Learn how to implement managed configurations that can be changed by other apps on the same device. | |
| page.image=images/work/cards/briefcase_600px.png | |
| @jd:body | |
| <div id="qv-wrapper"> | |
| <div id="qv"> | |
| <h2>In this document</h2> | |
| <ol> | |
| <li><a href="#define-configuration">Define Managed Configurations</a></li> | |
| <li><a href="#check-configuration">Check Managed Configurations</a></li> | |
| <li><a href="#listen-configuration">Listen for Managed Configuration Changes</a></li> | |
| </ol> | |
| </div> | |
| </div> | |
| <p> | |
| If you are developing apps for the enterprise market, you may need | |
| to satisfy particular requirements set by a company's policies. | |
| Managed configurations, previously known as <em>application restrictions</em>, | |
| allow the enterprise administrator to remotely specify settings for | |
| apps. This capability is particularly useful for enterprise-approved | |
| apps deployed to a managed profile. | |
| </p> | |
| <p>For example, an enterprise might require that approved apps allow the | |
| enterprise administrator to:</p> | |
| <ul> | |
| <li>Whitelist or blacklist URLs for a web browser</li> | |
| <li>Configure whether an app is allowed to sync content via cellular, or just | |
| by Wi-Fi</li> | |
| <li>Configure the app's email settings</li> | |
| </ul> | |
| <p> | |
| This guide shows how to implement managed configuration settings in | |
| your app. If you're an EMM developer, refer to the | |
| <a href="https://developers.google.com/android/work/build-dpc" | |
| >Build a Device Policy Controller</a> guide. | |
| </p> | |
| <p class="note"> | |
| <strong>Note:</strong> For historical reasons, these configuration settings are known as | |
| <em>restrictions,</em> and are implemented with files and classes that use this | |
| term (such as {@link android.content.RestrictionsManager}). However, these | |
| restrictions can actually implement a wide range of configuration options, | |
| not just restrictions on app functionality. | |
| </p> | |
| <h2 id="overview"> | |
| Remote Configuration Overview | |
| </h2> | |
| <p> | |
| Apps define the managed configuration options that can be remotely | |
| set by an administrator. These are arbitrary settings that can be | |
| changed by a managed configuration provider. If your app is running on an | |
| enterprise device's managed profile, the enterprise administrator | |
| can change your app's managed configuration. | |
| </p> | |
| <p> | |
| The managed configurations provider is another app running on the same device. | |
| This app is typically controlled by the enterprise administrator. The | |
| enterprise administrator communicates configuration changes to the managed | |
| configuration provider app. That app, in turn, changes the configurations on your app. | |
| </p> | |
| <p> | |
| To provide externally managed configurations: | |
| </p> | |
| <ul> | |
| <li>Declare the managed configurations in your app manifest. Doing | |
| so allows the enterprise administrator to read the app's | |
| configurations through Google Play APIs. | |
| </li> | |
| <li>Whenever the app resumes, use the {@link | |
| android.content.RestrictionsManager} object to check the current | |
| managed configurations, and change your app's UI and behavior to | |
| conform with those configurations. | |
| </li> | |
| <li>Listen for the | |
| {@link android.content.Intent#ACTION_APPLICATION_RESTRICTIONS_CHANGED | |
| ACTION_APPLICATION_RESTRICTIONS_CHANGED} intent. When you receive this | |
| broadcast, check the {@link android.content.RestrictionsManager} to see what | |
| the current managed configurations are, and make any necessary changes to your | |
| app's behavior. | |
| </li> | |
| </ul> | |
| <h2 id="define-configuration"> | |
| Define Managed Configurations | |
| </h2> | |
| <p> | |
| Your app can support any managed configuration you want to define. You declare the | |
| app's managed configurations in a <em>managed configurations file</em>, and declare | |
| the configurations file in the manifest. Creating a configurations file allows | |
| other apps to examine the managed configurations your app provides. Enterprise | |
| Mobility Management (EMM) partners can read your app's configurations by using | |
| Google Play APIs. | |
| </p> | |
| <p> | |
| To define your app's remote configuration options, put the following element | |
| in your manifest's | |
| <a href="{@docRoot}guide/topics/manifest/application-element.html"> | |
| <code><application></code></a> element: | |
| </p> | |
| <pre><meta-data android:name="android.content.APP_RESTRICTIONS" | |
| android:resource="@xml/app_restrictions" /> | |
| </pre> | |
| <p> | |
| Create a file named <code>app_restrictions.xml</code> in your app's | |
| <code>res/xml</code> directory. The structure of that file is described in | |
| the reference for {@link android.content.RestrictionsManager}. The file has a | |
| single top-level <code><restrictions></code> element, which contains | |
| one <code><restriction></code> child element for every configuration | |
| option the app has. | |
| </p> | |
| <p class="note"> | |
| <strong>Note:</strong> Do not create localized versions of the | |
| managed configuration file. Your app is only allowed to have a | |
| single managed configurations file, so configurations will be | |
| consistent for your app in all locales. | |
| </p> | |
| <p> | |
| In an enterprise environment, an EMM will typically use the managed | |
| configuration schema to generate a remote console for IT | |
| administrators, so the administrators can remotely configure your | |
| application. | |
| </p> | |
| <p> | |
| The managed configuration provider can query the app to find details | |
| on the app's available configurations, including their description | |
| text. The configurations provider and enterprise administrator can | |
| change your app's managed configurations at any time, even when the | |
| app is not running. | |
| </p> | |
| <p> | |
| For example, suppose your app can be remotely configured to allow or forbid | |
| it to download data over a cellular connection. Your app could have a | |
| <code><restriction></code> element like this: | |
| </p> | |
| <pre> | |
| <?xml version="1.0" encoding="utf-8"?> | |
| <restrictions xmlns:android="http://schemas.android.com/apk/res/android"> | |
| <restriction | |
| android:key="downloadOnCellular" | |
| android:title="@string/download_on_cell_title" | |
| android:restrictionType="bool" | |
| android:description="@string/download_on_cell_description" | |
| android:defaultValue="true" /> | |
| </restrictions> | |
| </pre> | |
| <p> | |
| You use each configuration's <code>android:key</code> attribute to | |
| read its value from a managed configuration bundle. For this reason, | |
| each configuration must have a unique key string, and the string | |
| <em>cannot</em> be localized. It must be specified with a string literal. | |
| </p> | |
| <p class="note"> | |
| <strong>Note:</strong> In a production app, <code>android:title</code> and | |
| <code>android:description</code> should be drawn from a localized resource | |
| file, as described in | |
| <a href="{@docRoot}guide/topics/resources/localization.html" | |
| >Localizing with Resources</a>. | |
| </p> | |
| <p id="nested-restrictions"> | |
| An app can define one or multiple nested restriction elements using | |
| the restriction types | |
| {@link android.content.RestrictionEntry#TYPE_BUNDLE bundle} and | |
| {@link android.content.RestrictionEntry#TYPE_BUNDLE_ARRAY bundle_array}. | |
| For example, an app with multiple VPN connection options could define | |
| each VPN server configuration in a bundle, with multiple bundles grouped | |
| together in a bundle array: | |
| </p> | |
| <pre> | |
| <?xml version="1.0" encoding="utf-8"?> | |
| <restrictions xmlns:android="http://schemas.android.com/apk/res/android" > | |
| <restriction | |
| android:key="vpn_configuration_list" | |
| android:restrictionType="bundle_array"> | |
| <restriction | |
| android:key="vpn_configuration" | |
| android:restrictionType="bundle"> | |
| <restriction | |
| android:key="vpn_server" | |
| android:restrictionType="string"/> | |
| <restriction | |
| android:key="vpn_username" | |
| android:restrictionType="string"/> | |
| <restriction | |
| android:key="vpn_password" | |
| android:restrictionType="string"/> | |
| </restriction> | |
| </restriction> | |
| </restrictions> | |
| </pre> | |
| <p> | |
| The supported types for the <code>android:restrictionType</code> element | |
| are listed in <a href="#restriction-types">Table 1</a> and documented in | |
| the reference for {@link android.content.RestrictionsManager} and | |
| {@link android.content.RestrictionEntry}. | |
| </p> | |
| <p class="table-caption" id="restriction-types"> | |
| <strong>Table 1.</strong> Restriction entry types and usage. | |
| </p> | |
| <table> | |
| <tbody> | |
| <tr> | |
| <th>Type</th> | |
| <th>android:restrictionType</th> | |
| <th>Typical usage</th> | |
| </tr> | |
| <tr> | |
| <td> | |
| {@link android.content.RestrictionEntry#TYPE_BOOLEAN TYPE_BOOLEAN} | |
| </td> | |
| <td><code>"bool"</code></td> | |
| <td> | |
| A boolean value, true or false. | |
| </td> | |
| </tr> | |
| <tr> | |
| <td> | |
| {@link android.content.RestrictionEntry#TYPE_STRING TYPE_STRING} | |
| </td> | |
| <td><code>"string"</code></td> | |
| <td> | |
| A string value, such as a name. | |
| </td> | |
| </tr> | |
| <tr> | |
| <td> | |
| {@link android.content.RestrictionEntry#TYPE_INTEGER TYPE_INTEGER} | |
| </td> | |
| <td><code>"integer"</code></td> | |
| <td> | |
| An integer with a value from | |
| {@link java.lang.Integer#MIN_VALUE MIN_VALUE} to | |
| {@link java.lang.Integer#MAX_VALUE MAX_VALUE}. | |
| </td> | |
| </tr> | |
| <tr> | |
| <td> | |
| {@link android.content.RestrictionEntry#TYPE_CHOICE TYPE_CHOICE} | |
| </td> | |
| <td><code>"choice"</code></td> | |
| <td> | |
| A string value, typically presented as a single-select list. | |
| </td> | |
| </tr> | |
| <tr> | |
| <td> | |
| {@link android.content.RestrictionEntry#TYPE_MULTI_SELECT TYPE_MULTI_SELECT} | |
| </td> | |
| <td><code>"multi-select"</code></td> | |
| <td> | |
| Use this for presenting a multi-select list where more than | |
| one entry can be selected, such as for choosing specific | |
| titles to white-list. | |
| </td> | |
| </tr> | |
| <tr> | |
| <td> | |
| {@link android.content.RestrictionEntry#TYPE_NULL TYPE_NULL} | |
| </td> | |
| <td><code>"hidden"</code></td> | |
| <td> | |
| Hidden restriction type. Use this type for information that | |
| needs to be transferred across but shouldn't be presented to | |
| the user in the UI. Stores a single string value. | |
| </td> | |
| </tr> | |
| <tr> | |
| <td>{@link android.content.RestrictionEntry#TYPE_BUNDLE TYPE_BUNDLE}</td> | |
| <td><code>"bundle"</code></td> | |
| <td> | |
| Use this for storing {@link android.os.Bundle bundles} of | |
| restrictions. Available in Android 6.0 (API level 23). | |
| </td> | |
| </tr> | |
| <tr> | |
| <td> | |
| {@link android.content.RestrictionEntry#TYPE_BUNDLE_ARRAY TYPE_BUNDLE_ARRAY} | |
| </td> | |
| <td><code>"bundle_array"</code></td> | |
| <td> | |
| Use this for storing arrays of restriction | |
| <a href="{@docRoot}reference/android/os/Bundle.html" | |
| >bundles</a>. Available in Android 6.0 (API level 23). | |
| </td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| <h2 id="check-configuration"> | |
| Check Managed Configurations | |
| </h2> | |
| <p> | |
| Your app is not automatically notified when other apps change its | |
| configuration settings. Instead, you need to check what the managed | |
| configurations are when your app starts or resumes, and listen for a | |
| system intent to find out if the configurations change while your | |
| app is running. | |
| </p> | |
| <p> | |
| To find out the current configuration settings, your app uses a | |
| {@link android.content.RestrictionsManager} object. Your app should | |
| check for the current managed configurations at the following times: | |
| </p> | |
| <ul> | |
| <li>When the app starts or resumes, in its | |
| {@link android.app.Activity#onResume onResume()} method | |
| </li> | |
| <li>When the app is notified of a configuration change, as described in | |
| <a href="#listen-configuration">Listen for Managed Configuration | |
| Changes</a> | |
| </li> | |
| </ul> | |
| <p> | |
| To get a {@link android.content.RestrictionsManager} object, get the current | |
| activity with {@link android.app.Fragment#getActivity getActivity()}, then | |
| call that activity's {@link android.app.Activity#getSystemService | |
| Activity.getSystemService()} method: | |
| </p> | |
| <pre>RestrictionsManager myRestrictionsMgr = | |
| (RestrictionsManager) getActivity() | |
| .getSystemService(Context.RESTRICTIONS_SERVICE);</pre> | |
| <p> | |
| Once you have a {@link android.content.RestrictionsManager}, you can get the | |
| current configuration settings by calling its | |
| {@link android.content.RestrictionsManager#getApplicationRestrictions | |
| getApplicationRestrictions()} method: | |
| </p> | |
| <pre>Bundle appRestrictions = myRestrictionsMgr.getApplicationRestrictions();</pre> | |
| <p class="note"> | |
| <strong>Note:</strong> For convenience, you can also fetch the current | |
| configurations with a {@link android.os.UserManager}, by calling | |
| {@link android.os.UserManager#getApplicationRestrictions | |
| UserManager.getApplicationRestrictions()}. This method behaves exactly the | |
| same as {@link android.content.RestrictionsManager#getApplicationRestrictions | |
| RestrictionsManager.getApplicationRestrictions()}. | |
| </p> | |
| <p> | |
| The {@link android.content.RestrictionsManager#getApplicationRestrictions | |
| getApplicationRestrictions()} method requires reading from data storage, so | |
| it should be done sparingly. Do not call this method every time you need to | |
| know the current configuration. Instead, you should call it once when your app | |
| starts or resumes, and cache the fetched managed configurations bundle. Then listen | |
| for the {@link android.content.Intent#ACTION_APPLICATION_RESTRICTIONS_CHANGED | |
| ACTION_APPLICATION_RESTRICTIONS_CHANGED} intent to find out if the configuration | |
| change while your app is active, as described in | |
| <a href="#listen-configuration">Listen for Managed Configuration Changes</a>. | |
| </p> | |
| <h3 id="read-configurations"> | |
| Reading and applying managed configurations | |
| </h3> | |
| <p> | |
| The {@link android.content.RestrictionsManager#getApplicationRestrictions | |
| getApplicationRestrictions()} method returns a {@link android.os.Bundle} | |
| containing a key-value pair for each configuration that has been set. The | |
| values are all of type <code>Boolean</code>, <code>int</code>, | |
| <code>String</code>, and <code>String[]</code>. Once you have the | |
| managed configurations {@link android.os.Bundle}, you can check the current | |
| configuration settings with the standard {@link android.os.Bundle} methods for | |
| those data types, such as {@link android.os.Bundle#getBoolean getBoolean()} | |
| or | |
| {@link android.os.Bundle#getString getString()}. | |
| </p> | |
| <p class="note"> | |
| <strong>Note:</strong> The managed configurations {@link android.os.Bundle} | |
| contains one item for every configuration that has been explicitly set by a | |
| managed configurations provider. However, you <em>cannot</em> assume that a | |
| configuration will be present in the bundle just because you defined a default | |
| value in the managed configurations XML file. | |
| </p> | |
| <p> | |
| It is up to your app to take appropriate action based on the current | |
| managed configuration settings. For example, if your app has a | |
| configuration specifying whether it can download data over a | |
| cellular connection, and you find that the configuration is set to | |
| <code>false</code>, you would have to disable data download except when | |
| the device has a Wi-Fi connection, as shown in the following example code: | |
| </p> | |
| <pre> | |
| boolean appCanUseCellular; | |
| if (appRestrictions.containsKey("downloadOnCellular")) { | |
| appCanUseCellular = appRestrictions.getBoolean("downloadOnCellular"); | |
| } else { | |
| // cellularDefault is a boolean using the restriction's default value | |
| appCanUseCellular = cellularDefault; | |
| } | |
| if (!appCanUseCellular) { | |
| // ...turn off app's cellular-download functionality | |
| // ...show appropriate notices to user | |
| }</pre> | |
| <p> | |
| To apply multiple <a href="#nested-restrictions">nested restrictions</a>, read | |
| the {@link android.content.RestrictionEntry#TYPE_BUNDLE_ARRAY bundle_array} | |
| restriction entry as a collection of {@link android.os.Parcelable} objects | |
| and cast as a {@link android.os.Bundle}. In this example, each VPN's configuration | |
| data is parsed and used to build a list of server connection choices: | |
| </p> | |
| <pre> | |
| // VpnConfig is a sample class used store config data, not defined | |
| List<VpnConfig> vpnConfigs = new ArrayList<>(); | |
| Parcelable[] parcelables = | |
| appRestrictions.getParcelableArray("vpn_configuration_list"); | |
| if (parcelables != null && parcelables.length > 0) { | |
| // iterate parcelables and cast as bundle | |
| for (int i = 0; i < parcelables.length; i++) { | |
| Bundle vpnConfigBundle = (Bundle) parcelables[i]; | |
| // parse bundle data and store in VpnConfig array | |
| vpnConfigs.add(new VpnConfig() | |
| .setServer(vpnConfigBundle.getString("vpn_server")) | |
| .setUsername(vpnConfigBundle.getString("vpn_username")) | |
| .setPassword(vpnConfigBundle.getString("vpn_password"))); | |
| } | |
| } | |
| if (!vpnConfigs.isEmpty()) { | |
| // ...choose a VPN configuration or prompt user to select from list | |
| }</pre> | |
| <h2 id="listen-configuration"> | |
| Listen for Managed Configuration Changes | |
| </h2> | |
| <p> | |
| Whenever an app's managed configurations are changed, the system fires the | |
| {@link android.content.Intent#ACTION_APPLICATION_RESTRICTIONS_CHANGED | |
| ACTION_APPLICATION_RESTRICTIONS_CHANGED} intent. Your app has to listen for | |
| this intent so you can change the app's behavior when the configuration settings | |
| change.</p> | |
| <p class="note"> | |
| <strong>Note:</strong> The {@link | |
| android.content.Intent#ACTION_APPLICATION_RESTRICTIONS_CHANGED | |
| ACTION_APPLICATION_RESTRICTIONS_CHANGED} intent is sent only to listeners | |
| that are dynamically registered, <em>not</em> to listeners that are declared | |
| in the app manifest. | |
| </p> | |
| <p> | |
| The following code shows how to dynamically register a broadcast receiver for | |
| this intent: | |
| </p> | |
| <pre>IntentFilter restrictionsFilter = | |
| new IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED); | |
| BroadcastReceiver restrictionsReceiver = new BroadcastReceiver() { | |
| @Override public void onReceive(Context context, Intent intent) { | |
| // Get the current configuration bundle | |
| Bundle appRestrictions = myRestrictionsMgr.getApplicationRestrictions(); | |
| // Check current configuration settings, change your app's UI and | |
| // functionality as necessary. | |
| } | |
| }; | |
| registerReceiver(restrictionsReceiver, restrictionsFilter); | |
| </pre> | |
| <p class="note"> | |
| <strong>Note:</strong> Ordinarily, your app does not need to be notified | |
| about configuration changes when it is paused. Instead, you should unregister | |
| your broadcast receiver when the app is paused. When the app resumes, you | |
| first check for the current managed configurations (as discussed in | |
| <a href="#check-configuration">Check Managed Configurations</a>), then register | |
| your broadcast receiver to make sure you're notified about configuration changes | |
| that happen while the app is active. | |
| </p> |