Tasks for legacy configuration handling
To handle app-specific configuration using the legacy mechanism in your app, do the following high-level tasks:
-
Check at runtime if your app is wrapped.
This check is typically necessary if you are a third-party app developer using the same source code to create a Google Play app and an in-house AppConnect app. Only wrapped AppConnect apps can receive app-specific configuration from an Ivanti server.
If you are developing an app that will be distributed only as an in-house app, not from Google Play, you will not use this check.
-
Add a service to AndroidManifest.xml.
Add a service for handling configuration intents to your app’s AndroidManifest.xml file. In the service, you specify an intent-filter for the intent with the action "com.mobileiron.HANDLE_CONFIG".
-
Create a class that extends IntentService.
The name of the class matches the android:name attribute of the service you add to the AndroidManifest.xml file. You implement the onHandleIntent() method, in which you asynchronously receive the configuration’s key-value pairs when they change or you request them.
-
Request the configuration when your app starts.
When your app starts, request the configuration, which your app will receive asynchronously in its onHandleIntent() method.
-
Specify app configuration and policies in .properties files.
You can include .properties files in your app that list your app’s key-value pairs and data loss prevention (DLP) policies. When the Ivanti server administrator uploads your app to the server, these files cause the server to automatically configure the key-value pairs and DLP policies.
Check at runtime if your app is wrapped
If you are a third-party developer, you sometimes develop an app in which the same source code is used in these ways:
-
as a wrapped app distributed from the Ivanti server’s App Catalog
This secure AppConnect app is for enterprise device users.
-
as an unwrapped app distributed from Google Play
This unsecured app is for general distribution.
An app that serves both these markets typically behaves differently depending on whether it is a wrapped, secure AppConnect app.
For example:
-
If a wrapped app expects key-value pairs from the Ivanti server, but does not receive the expected pairs or valid values, it should take appropriate actions.
As a best practice, if your app expects a login ID from the server, but does not receive one, do not allow the device user to enter the ID manually. See Use only a login ID from the Ivanti server if one is expected.
-
If an app is not wrapped, it cannot get its configuration from the Ivanti server. It gets configurable information another way, such as prompting the device user to enter it.
For example, the unwrapped app prompts the user to enter a login ID.
To determine at runtime whether the app is running as a wrapped app, check this Android system property:
"com.mobileiron.wrapped"
For example, use the following expression:
Boolean.parseBoolean(System.getProperty("com.mobileiron.wrapped", "false"))
The expression returns true if the app is wrapped. Otherwise, it returns false.
Add a service to AndroidManifest.xml
Add a <service> element to your app’s AndroidManifest.xml file. This service is for handling the configuration intent sent from the Ivanti client app.
For example, in HelloAppConnect, the lines in AndroidManifest.xml are:
<service android:permission="com.mobileiron.CONFIG_PERMISSION” android:enabled="true" android:name=".AppConnectConfigService"> <intent-filter> <action android:name="com.mobileiron.HANDLE_CONFIG"/> </intent-filter> </service>
Do the following in the <service> element:
-
Set the attribute android:permission="com.mobileiron.CONFIG_PERMISSION” to ensure that only the Ivanti client app can start this service.
This permission is necessary only if you are a third-party app developer planning to distribute an unwrapped version of your app. For more information, see App for testing legacy configuration handling.
-
Ensure that the attribute android:exported is "true", which it is by default.
-
Ensure that the attribute android:enabled is "true", which it is by default.
-
Ensure that the attribute android:enabled of the <application> element that contains the <service> element is "true", which it is by default.
-
Set the android:name attribute of the <service> element to the name of the class in your app that extends IntentService. This class in your app implements onHandleIntent() to handle configuration updates.
-
Add an <intent-filter> element that contains an <action> element.
-
Set the android:name attribute of the <action> element to "com.mobileiron.HANDLE_CONFIG".
Create a class that extends IntentService
Create a class that extends IntentService. This class handles the intent with the action "com.mobileiron.HANDLE_CONFIG".
Do the following:
1. | Name the class the same name you specified in the android:name attribute of the <service> element in the AndroidManifest.xml file. |
2. | Implement onHandleConfig() to handle the received Intent object with the action "com.mobileiron.HANDLE_CONFIG". |
Implement onHandleConfig()
Implementing onHandleConfig() involves the following steps. Code samples are from HelloAppConnect.
1. | Get the Bundle object contained in the received Intent object. |
For example:
Bundle config = intent.getBundleExtra("config");
If no configuration exists on server for your app, the Bundle object is null. If a configuration exists on server, but contains no key-value pairs, the Bundle object is not null, but its keyset() method returns an empty set.
2. | Extract the key-value pairs in the Bundle object. |
For example:
Map<String, String> map = new HashMap<String, String>(); for (String key: config.keySet()) { map.put(key, config.getString(key)); }
3. | Process the key-value pairs according to your app’s requirements. |
4. | If the app successfully processes the key-value pairs, start a service. Pass the service the “success” Intent object. The “success” Intent object is in the received Intent object, and has the extended data name "configAppliedIntent". |
For example:
startService ((Intent)intent.getParcelableExtra("configAppliedIntent"));
5. | If the app fails to process the key-value pairs, start a service. Pass the service the “error” Intent object. The “error” Intent object is in the received Intent object, and has the extended data name "configErrorIntent". Include a string in the “error” Intent object, where the string value describes the error condition. |
For example:
Intent i = (Intent)intent.getParcelableExtra("configErrorIntent"); i.putExtra("errorString", "This is a sample error message."); startService(i);
Reasons for returning an error
If your app fails to process the key-value pairs, it starts a service, passing the “error” Intent object. Some reasons for failure are:
-
A value is not valid for its key.
For example, if the key is “emailAddress”, but the value does not include the @ character, return an error.
-
A value is empty.
Typically, if a key is included in the Ivanti server configuration for your app, your app expects a value. If the Ivanti server administrator did not enter a value, return an error.
-
Your app encounters a system error while processing a key-value pair.
Your app determines whether a system error impacts key-value processing to warrant an error return.
When the app returns an error, how it continues to operate depends on your app’s design and requirements.
Request the configuration when your app starts
When your app starts, request the app-specific configuration. Do the following:
- Create an Intent object.
- Set the Intent object’s action to "com.mobileiron.REQUEST_CONFIG".
- Add your app’s package name as extended data to the Intent object.
- Call startService() with the Intent object.
- Handle the asynchronous response in onHandleConfig().
For example, based on code in HelloAppConnect:
Intent intent = new Intent("com.mobileiron.REQUEST_CONFIG"); intent.putExtra("packageName", ctx.getPackageName()); ctx.startService(intent);
Request the configuration only once, when your app starts. After that, whenever the Ivanti server administrator updates the configuration on the server, your app automatically receives the Intent object with the action "com.mobileiron.HANDLE_CONFIG".
Specify app configuration and policies in .properties files
You can include the following .properties files with your app:
-
appconnectconfig.properties
This file specifies your app’s configuration keys and their default values, if any. Providing this .properties file causes the Ivanti server to automatically configure the keys and their default values on the server.
-
appconnectpolicy.properties
This file specifies the default data loss prevention policy for screen capture for the app. Specifically, it specifies whether screen capture is allowed in the app. The policy is enforced by the AppConnect wrapping technology.
If your app contains these .properties files, the Ivanti server automatically configures the key-value pairs and the screen capture policy that you specified. This automatic configuration occurs when the Ivanti server administrator uploads your app to the server’s App Catalog.
The administrator can then change the default values on the server as necessary for that enterprise.
File location of the .properties files
Put the .properties files in this directory in your app:
<application root directory>/res/raw
Example of the appconnectconfig.properties file
An example of an appconnectconfig.properties file is available in HelloAppConnect.
It contains the following:
# This sample appconnectconfig.properties file uses rules found at
# http://en.wikipedia.org/wiki/.properties.
server=www.myCompanyApplicationServer.com
port=8080
# In the following example, the resulting property value contains only single spaces.
# It contains no other whitespace.
# Therefore, the value is: "I'm also demonstrating a multi-line property!"
name\ with\ spaces:I'm also demonstrating \
a multi-line property!
# Use an empty value for keys that have no default value.
nodefault=
! You can also start comments with exclamation marks.
# You can use these Core variables for values:
# $USERID$, $EMAIL$, $PASSWORD$,
# $USER_CUSTOM1$, $USER_CUSTOM2$, $USER_CUSTOM3$, $USER_CUSTOM4$
# You can use these Ivanti Neurons for MDM variables for values:
# ${userUID}, ${userEmailAddress}
# ${USER_CUSTOM1}, ${USER_CUSTOM2}, ${USER_CUSTOM3}, ${USER_CUSTOM4}
userid=$USERID$
email=$EMAIL$
user_custom1=$USER_CUSTOM1$
combined=$USERID$::$EMAIL$
Format of the appconnectconfig.properties file
Use the rules for well-formed Java property files given in the Java Properties class. For example, use the characters = or : or a space to separate the key from the value. Use \ before each of these characters if the character is part of the key.
The values that you specify are the default values for the key. If the value has no default, leave the value empty.
A value can be any string. The value can also use one of the following server variables:
Core variable |
Ivanti Neurons for MDM variable |
Description |
$USERID$ |
${userUID} |
The device user’s enterprise user ID, typically an LDAP ID. |
$PASSWORD$ |
Not available |
The device user’s enterprise user password, typically an LDAP password. |
$EMAIL$ |
${userEmailAddress} |
The device user’s enterprise email address. |
$USER_CUSTOM1$ $USER_CUSTOM2$ $USER_CUSTOM3$ $USER_CUSTOM4$ |
${USER_CUSTOM1} ${USER_CUSTOM2} ${USER_CUSTOM3} ${USER_CUSTOM4} |
Custom variables that the Ivanti server administrator sets up. Only use these variables if you are developing an app for a specific Ivanti customer. Contact the server administrator to determine the values of these variables. |
You can also specify values that are combinations of text and server variables. For example, using Core variables:
- $USERID$::$EMAIL$
- $USERID$@somedomain.com
Use server variables for default values in your appconnectconfig.properties only if you know what kind of server (Core or Ivanti Neurons for MDM) your app will be used with. If you don’t know, leave the value empty. The server administrator will fill in the value.
Example of the appconnectpolicy.properties file
An example of an appconnectpolicy.properties file is available in HelloAppConnect.
It contains the following:
# A sample appconnectpolicy.properties file
screencapture=disable
Format of the appconnectpolicy.properties file
To disable screen capture in the app, include the following line in appconnectpolicy.properties:
screencapture=disable
To allow screen capture:
screencapture=allow