Update 'Set up Managed Configurations' guide to include examples for restriction bundles and bundle_arrays. Include table for restriction entry types and usage. Link to EMM doc for building DPC. Formatting. bug: 26151150 bug: 29966701 Change-Id: I89f732709fadd511b968515ab16f07dd46195f0e
518 lines
18 KiB
Plaintext
518 lines
18 KiB
Plaintext
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>
|