Edit

Share via


Intune App SDK for Android - App Participation Features

The Microsoft Intune App SDK for Android lets you incorporate Intune app protection policies (also known as MAM policies) into your native Java/Kotlin Android app. An Intune-managed application integrates with the Intune App SDK. Intune administrators can easily deploy app protection policies to your Intune-managed app when Intune actively manages the app.

Note

This guide is divided into several distinct stages. Start by reviewing Plan the Integration.

Stage 7: App participation features

Stage Goals

  • Learn about app participation features offered by the Intune App SDK.
  • Integrate app participation features relevant to your app and users.
  • Test the integration of those features.

What are "App Participation Features"?

This SDK integration process attempts to minimize the amount of app-specific code that developers need to write. By successfully completing the prior stages of the SDK integration your app now enforces most app protection policy settings, such as file encryption, copy/paste restrictions, screenshot blocking, and data transfer restrictions.

However, some settings require app-specific code to enforce properly; these settings are called app participation features. Typically, the SDK doesn't have enough context about your application's code or the end user scenario to automatically enforce these settings, and thus relies on developers to call the SDK APIs appropriately.

App participation features aren't necessarily optional. Depending on your app's existing features, these features might be required. See Key Decisions for SDK integration for details.

Previous stages of this guide already describe several app participation features:

The rest of this guide describes the remaining set of app participation features:

  • Enforce policy restricting saving files to or opening files from local or cloud storage.
  • Enforce policy restricting content in notifications.
  • Enforce policy protecting backup data.
  • Enforce policy restricting screen capture (if your app has custom screen capture code).
  • Support App Protection CA.
  • Register for notifications from the SDK.
  • Apply custom application theming.
  • Use trusted certificates from Intune, ensuring chain of trust to on-premises endpoints.

App Participation Feature basics

The AppPolicy interface contains many methods that inform your app whether certain actions are allowed.

Most app participation features involve:

  • Identifying the right place in the app's code to check if an action is allowed.
  • Calling an AppPolicy method to check if an action is allowed, based on currently configured policy.
  • Depending on the result, either allowing the action to complete, or modifying the app behavior when the action is blocked.

To retrieve an AppPolicy instance, use one of the MAMPolicyManager methods, such as getPolicy(final Context context) or getPolicyForIdentityOID(final String oid).

Informational methods in AppPolicy

Not every method in AppPolicy is tied to an app participation feature. Some methods are informational, and give your app data on which policies are currently configured, even if those policies are automatically enforced by the SDK. These methods exist to give your app opportunities to present custom user experiences when specific policies are configured.

Example: Determine if screenshots are blocked

If the app has a control that lets the user take a screenshot, consider disabling or hiding that control if App Protection Policy blocks screenshots.

The app can check by calling MAMPolicyManager.getPolicy(currentActivity).getIsScreenCaptureAllowed().

Policy for limiting data transfer between apps and device or cloud storage locations

Many apps allow the end user to save data to or open data from local file storage or cloud storage services. The Intune App SDK lets IT administrators protect against data ingress and data leakage by restricting where apps can save data to and open data from.

Note

If your app allows saving to personal or cloud locations directly from the app or allows for data to be opened directly into the app, you must implement this Intune App SDK app participation feature** to enable IT administrators to block this saving and opening.

Saving to device or cloud storage

The getIsSaveToLocationAllowedForOID API lets the app know whether saving to certain locations is allowed for a given identity, based on the configured policy:

MAMPolicyManager.getPolicy(currentActivity).getIsSaveToLocationAllowedForOID(
SaveLocation service, String oid);

To determine whether the app should implement the getIsSaveToLocationAllowedForOID check, determine if the app supports data egress by reviewing the following table:

service Parameter: SaveLocation Enum Value Use Case Associated OID
ONEDRIVE The app is saving data to OneDrive. An OID for an account that is used for both cloud service authentication and Microsoft Entra authentication. If this account doesn't exist or the OID isn't known, use null.
SHAREPOINT The app is saving data to SharePoint. An OID for an account that is used for both cloud service authentication and Microsoft Entra authentication. If this account doesn't exist or the OID isn't known, use null.
BOX The app is saving data to Box. An OID for an account that is used for both cloud service authentication and Microsoft Entra authentication. If this account doesn't exist or the OID isn't known, use null.
LOCAL The app is saving data to an external storage location on the device that isn't the app's private storage. This storage location isn't considered a cloud service and should always be used with a null OID parameter.
PHOTO_LIBRARY The app is saving data to Android local photo storage. Local photo storage isn't considered a cloud service and should always be used with a null OID parameter.
IMANAGE The app is saving data to iManage. An OID for an account that is used for both cloud service authentication and Microsoft Entra authentication. If this account doesn't exist or the OID isn't known, use null.
EGNYTE The app is saving data to Egnyte. An OID for an account that is used for both cloud service authentication and Microsoft Entra authentication. If this account doesn't exist or the OID isn't known, use null.
ACCOUNT_DOCUMENT The app is saving data to a location associated with an account within the app and isn't one of the specific cloud locations in this table.

Use this location to determine whether data can be passed between accounts within a multi-identity app.
An OID for an account used for Microsoft Entra authentication. If this account doesn't exist or the OID isn't known, use null.
OTHER The app is saving data to a location not specified in this table and that doesn't meet the criteria for ACCOUNT_DOCUMENT. The oid isn't evaluated for this location and should be null.

Files placed in private app storage that are either necessary for app operation or downloaded temporarily for display are always allowed; no need to check getIsSaveToLocationAllowedForOID. Do check SaveLocation.LOCAL for

  1. Files saved outside private app storage.
  2. Files downloaded to private app storage that aren't necessary for app operation (for example, when the user explicitly chooses to download to the device).

Note

When checking the save policy, oid should be the OID of the account associated with the cloud service being saved to (this account isn't necessarily the same as the account that owns the document being saved).

Opening data from a local or cloud storage location

The getIsOpenFromLocationAllowedForOID API lets the app know whether opening from certain locations is allowed for a given identity, based on the configured policy:

MAMPolicyManager.getPolicy(currentActivity).getIsOpenFromLocationAllowedForOID(
OpenLocation location, String oid);

To determine whether the app should implement the getIsOpenFromLocationAllowedForOID check, determine if the app supports data ingress by reviewing the following table:

location Parameter: OpenLocation Enum Value Use Case Associated OID
ONEDRIVE_FOR_BUSINESS The app is opening data from OneDrive. An OID for an account that is used for both cloud service authentication and Microsoft Entra authentication. If this account doesn't exist or the OID isn't known, use null.
SHAREPOINT The app is opening data from SharePoint. An OID for an account that is used for both cloud service authentication and Microsoft Entra authentication. If this account doesn't exist or the OID isn't known, use null.
CAMERA The app is opening data from the device camera. A null value, because the device camera isn't a cloud service.
LOCAL The app is opening data from an external storage location on the device that isn't the app's private storage. Although external storage isn't a cloud service, an oid parameter is expected because it indicates ownership.

For identity-tagged files: oid should be the file owner's identity.
For files without an identity tag: oid should be null.
PHOTO_LIBRARY The app is opening data from Android local photo storage. Local photo storage isn't considered a cloud service and should always be used with a null OID parameter.
ACCOUNT_DOCUMENT The app is opening data from a location associated with an account within the app and isn't one of the specific cloud locations in this table.

Use this location to determine whether data can be passed between accounts within a multi-identity app.
An OID for an account used for Microsoft Entra authentication. If this account doesn't exist or the OID isn't known, use null.
OTHER The app is opening data from a location not specified in this table and that doesn't meet the criteria for ACCOUNT_DOCUMENT. The oid isn't evaluated for this location and should be null.

Note

When you check the open policy, oid should be the OID of the account associated with the file or cloud service being opened from (not necessarily the same as the account who is opening the document).

Tip

For convenience, the SDK provides the method AppPolicy.isOpenFromLocalStorageAllowed that takes a File parameter for a file in local storage. For policy enforcement, this method is functionally identical to calling AppPolicy.getIsOpenFromLocationAllowedForOID(OpenLocation.LOCAL, oid) except it handles parsing the file owner's oid from the File.

Sharing blocked dialog

The SDK provides a dialog to notify the user when MAM policy blocks a data transfer action.

The dialog should be displayed to the user whenever the getIsSaveToLocationAllowedForOID or getIsOpenFromLocationAllowedForOID API call results in the save/open action being blocked. The dialog displays a generic message and returns to the calling Activity when dismissed.

To display the dialog, add the following code:

MAMUIHelper.showSharingBlockedDialog(currentActivity)

Allow file sharing

If saving to public storage locations isn't allowed, your app should still let the user view files by downloading them to app private storage and then opening them with the system chooser.

Policy for restricting content inside notifications

For single-identity apps, the Intune App SDK's default behavior attempts to block all notifications when App Protection Policy restricts notifications.

The SDK's default behavior is limited. The SDK can't automatically honor the "Block org data" value, which is intended to remove only managed content from notifications. For multi-identity apps, the SDK can't determine which notifications contain managed content.

If your app displays notifications, and it's either multi-identity and/or it needs to honor the "Block org data" value, it must check the notification restriction policy for the account that's associated with the notification before showing the notification.

To determine if the policy is enforced, make the following call:

NotificationRestriction notificationRestriction =
    MAMPolicyManager.getPolicyForIdentityOID(notificationIdentityOid).getNotificationRestriction();

The returned NotificationRestriction enum has the following values:

NotificationRestriction Enum Expected App Behavior
BLOCKED The app mustn't show any notifications for the account associated with this policy. For single-identity apps, the Intune App SDK blocks all notifications automatically, and no extra code is required.
BLOCK_ORG_DATA The app must show a modified notification that doesn't contain organization data.
UNRESTRICTED The app should show all notifications.

If your app doesn't properly invoke getNotificationRestriction, the MAM SDK makes a best effort to restrict notifications automatically for single-identity apps only.

In this case, BLOCK_ORG_DATA is treated the same as BLOCKED and the notification isn't shown at all.

For more fine-grained control, check the value of getNotificationRestriction and modify app notifications appropriately.

Policy for protecting backup data

The Intune App SDK can block data upload to Android's built-in backup and restore feature. For more information about backup and restore in Android, see the Android API guide and the changes introduced in Android S / 12 in Change to backup and restore.

Auto Backup for Apps

Beginning with Android M, Android provides automatic full backups to Google Drive for apps, regardless of the app's target API.

Intune lets you use all the autobackup features that Android provides, including the ability to define custom rules in XML, with specific Intune integration guidance to ensure data protection applies.

Configuring backup behavior in the app's manifest

By default, android:allowBackup is set to true as outlined in enable and disable backup.

If the app doesn't require full backup and restore functionality, set android:allowBackup to false. In this case, no further action is necessary and corporate data stays within the app.

If your app requires full backup and restore functionality, set android:allowBackup to true and perform the following steps:

  1. If your app doesn't use its own custom BackupAgent, use the default MAMBackupAgent to enable automatic full backups that are Intune policy compliant. Place the following in the app manifest:

    <application
    ...
      android:fullBackupOnly="true"
      android:backupAgent="com.microsoft.intune.mam.client.app.backup.MAMDefaultBackupAgent"
      ...>
      </application>
    
  2. Optional. If you implement a custom BackupAgent, you must use MAMBackupAgent or MAMBackupAgentHelper. See the following sections. Consider switching to Intune's MAMDefaultBackupAgent, described in step 1, which provides easy backup on Android M and later.

  3. When you decide which type of full backup your app should receive (unfiltered, filtered, or none), set the attribute android:fullBackupContent to true, false, or an XML resource in your app.

  4. Then, you must copy the value for android:fullBackupContent into the com.microsoft.intune.mam.FullBackupContent metadata tag and, for apps that support the XML configuration format that's added in API 31, into the com.microsoft.intune.mam.DataExtractionRules metadata tag.

    • Example 1: If you want your app to have full backups without exclusions, you must set the attributes and metadata tags to true:

      <application
        ...
        android:fullBackupContent="true"
        ...>
      </application>
      ...
      <meta-data android:name="com.microsoft.intune.mam.FullBackupContent" android:value="true" />
      <meta-data android:name="com.microsoft.intune.mam.DataExtractionRules" android:value="true" />
      
    • Example 2: If you want your app to use its custom BackupAgent and opt out of full, Intune policy compliant, automatic backups, you must set the attributes and metadata tags to false:

      <application
        ...
        android:fullBackupContent="false"
        ...>
      </application>
      ...
      <meta-data android:name="com.microsoft.intune.mam.FullBackupContent" android:value="false" />
      <meta-data android:name="com.microsoft.intune.mam.DataExtractionRules" android:value="false" />
      
    • Example 3: If you want your app to have full backups according to your custom rules defined in an XML file, set the attribute and metadata tag to the same XML resource:

      <application
        ...
        android:fullBackupContent="@xml/my_full_backup_content_scheme"
        android:dataExtractionRules="@xml/my_data_extraction_rules_scheme"
        ...>
      </application>
      ...
      <meta-data android:name="com.microsoft.intune.mam.FullBackupContent" android:resource="@xml/my_full_backup_content_scheme" />
      <meta-data android:name="com.microsoft.intune.mam.DataExtractionRules" android:resource="@xml/my_data_extraction_rules_scheme" />
      

Key/Value Backup

The Key/Value Backup option is available to all APIs 8+ and uploads app data to the Android Backup Service. The amount of data per app is limited to 5 MB. If you use Key/Value Backup, you must use a BackupAgentHelper or a BackupAgent.

BackupAgentHelper

BackupAgentHelper is easier to implement than BackupAgent both in terms of native Android functionality and Intune MAM integration. BackupAgentHelper lets the developer register entire files and shared preferences to a FileBackupHelper and SharedPreferencesBackupHelper (respectively) which are then added to the BackupAgentHelper upon creation. Follow these steps to use a BackupAgentHelper with Intune MAM:

  1. To use multi-identity backup with a BackupAgentHelper, follow the Android guide to Extending BackupAgentHelper.

  2. Have your class extend the MAM equivalent of BackupAgentHelper, FileBackupHelper, and SharedPreferencesBackupHelper.

Android class MAM equivalent
BackupAgentHelper MAMBackupAgentHelper
FileBackupHelper MAMFileBackupHelper
SharedPreferencesBackupHelper MAMSharedPreferencesBackupHelper

Following these guidelines lets you perform successful multi-identity backup and restore.

BackupAgent

A BackupAgent lets you be much more explicit about what data is backed up. Because the developer is responsible for the implementation, more steps are required to ensure appropriate data protection from Intune. Because most of the work is on you as the developer, Intune integration is slightly more involved.

Integrate MAM:

  1. Carefully read the Android guide for Key/Value Backup and specifically Extending BackupAgent to ensure your BackupAgent implementation follows Android guidelines.

  2. Have your class extend MAMBackupAgent.

Multi-identity Backup:

  1. Before beginning your backup, check that the files or data buffers you plan to back up are permitted by the IT administrator to be backed up in multi-identity scenarios. Use isBackupAllowed in MAMFileProtectionManager and MAMDataProtectionManager to determine this behavior. If the file or data buffer isn't allowed to be backed up, then you shouldn't include it in your backup.

  2. At some point during your backup, if you want to back up the identities for the files you checked in step 1, you must call backupMAMFileIdentity(BackupDataOutput data, File … files) with the files from which you plan to extract data. This method automatically creates new backup entities and writes them to the BackupDataOutput for you. These entities are automatically consumed upon restore.

Multi-identity Restore: The Data Backup guide specifies a general algorithm for restoring your application’s data and provides a code sample in the Extending BackupAgent section. To perform a successful multi-identity restore, you must follow the general structure provided in this code sample with special attention to the following points:

  1. You must use a while(data.readNextHeader())* loop to go through the backup entities.

  2. You must call data.skipEntityData() if data.getKey() doesn't match the key you wrote in onBackup. Without this step, your restores might not succeed.

  3. Avoid returning while consuming backup entities in the while(data.readNextHeader())* construct, because any entities that we automatically write are lost in this case.

  • Where data is the local variable name for the MAMBackupDataInput that the app receives upon restore.

Custom Screen Capture Restrictions

If your app contains a custom screen capture feature that bypasses Android's Window-level FLAG_SECURE restriction, you must check screen capture policy before allowing full access to the feature. For example, if your app uses a custom rendering engine to render the current view to a PNG file, you must first check AppPolicy.getIsScreenCaptureAllowed().

Note

If the app doesn't contain any custom or non-Microsoft screen capture features, no action is required to restrict screen captures. Screen capture policy is automatically enforced at the Window level for all MAM integrated apps. Any attempts by the OS or another app to capture a Window in your app are blocked as required. For example, if a user attempts to capture your app's screen through Android's built-in screenshot or screen recording features, the capture is automatically restricted without participation from your app.

Support App Protection CA

App Protection CA (Conditional Access), also known as app-based CA, restricts access to resources. Intune App Protection Policies must manage your application before it can access these resources. Microsoft Entra ID enforces this policy by requiring the app to enroll with and be managed by Intune App Protection Policies before granting a token to access a Conditional Access-protected resource.

Note

App Protection CA support requires Microsoft Authentication Library (MSAL) version 1.0.0 or later.

Handle noncompliance with MSAL

When the app acquires a token for an account, the MSAL library might return or throw an MsalIntuneAppProtectionPolicyRequiredException to indicate noncompliance with app protection policy management. You can extract more parameters from the exception for use in remediating compliance (see MAMComplianceManager). After remediation succeeds, the app can attempt the token acquisition again through MSAL.

MAMComplianceManager

The MAMComplianceManager interface is used when the policy-required error is received from MSAL. It contains the [remediateCompliance] method that you should call to attempt to put the app into a compliant state. You can obtain a reference to the MAMComplianceManager as follows:

MAMComplianceManager mgr = MAMComponents.get(MAMComplianceManager.class);

// make use of mgr

The MAMComplianceManager instance returned is guaranteed not to be null.

package com.microsoft.intune.mam.policy;

public interface MAMComplianceManager {
    void remediateCompliance(String upn, String aadId, String tenantId, String authority, boolean showUX);
}

The remediateCompliance() method attempts to put the app under management to satisfy the conditions for Microsoft Entra ID to grant the requested token. The first four parameters can be extracted from the exception that the MSAL AuthenticationCallback.onError() method receives. The final parameter is a boolean that controls whether a user experience appears during the compliance attempt.

remediateCompliance displays a simple blocking progress dialog so apps don't need to show customized experiences during this operation. This dialog appears only while the compliance remediation is in progress. It doesn't display the final result. Your app can register a receiver for the COMPLIANCE_STATUS notification to handle the success or failure of the compliance remediation attempt. See Compliance status notifications for more detail.

remediateCompliance() might initiate a MAM enrollment as part of establishing compliance. The app might receive an enrollment notification if it has registered a notification receiver for enrollment notifications. The app's registered MAMServiceAuthenticationCallback has its acquireToken() method called to get a token for the enrollment. acquireToken() is called before the app acquires its own token. Any bookkeeping or account creation tasks that the app does after a successful token acquisition might not have been done so yet. The callback must be able to acquire a token in this case.

If you can't return a token from acquireToken(), the compliance remediation attempt fails.

If you call updateToken later with a valid token for the requested resource, the compliance remediation resumes immediately with the given token.

Note

Silent token acquisition is still possible in acquireToken() because the user has already been guided to install the broker and register the device before the MsalIntuneAppProtectionPolicyRequiredException exception occurs. This process results in the broker having a valid refresh token in its cache, which lets the broker acquire the requested token silently.

Here's a sample that receives the policy-required error in the AuthenticationCallback.onError() method, and calls the MAMComplianceManager to handle the error.

public void onError(@Nullable MsalException exc) {
    if (exc instanceof MsalIntuneAppProtectionPolicyRequiredException) {

        final MsalIntuneAppProtectionPolicyRequiredException policyRequiredException =
            (MsalIntuneAppProtectionPolicyRequiredException) ex;

        final String upn = policyRequiredException.getAccountUpn();
        final String aadId = policyRequiredException.getAccountUserId();
        final String tenantId = policyRequiredException.getTenantId();
        final String authority = policyRequiredException.getAuthorityURL();

        MAMComplianceManager complianceManager = MAMComponents.get(MAMComplianceManager.class);
        complianceManager.remediateCompliance(upn, aadId, tenantId, authority, showUX);
    }
}

Compliance status notifications

If the app registers for notifications of type COMPLIANCE_STATUS, the system sends a MAMComplianceNotification to inform the app of the final status of the compliance remediation attempt. See Register for notifications from the SDK for detail on registering.

public interface MAMComplianceNotification extends MAMUserNotification {
    MAMCAComplianceStatus getComplianceStatus();
    String getComplianceErrorTitle();
    String getComplianceErrorMessage();
}

The getComplianceStatus() method returns the result of the compliance remediation attempt as a value from the [MAMCAComplianceStatus] enum.

Status code Explanation
UNKNOWN Status is unknown. This status could indicate an unanticipated failure reason. More information might be found in the Company Portal logs.
COMPLIANT Compliance remediation succeeded and the app is now compliant with policy. The MSAL token acquisition should be retried.
NOT_COMPLIANT The attempt to remediate compliance failed. The app isn't compliant and MSAL token acquisition shouldn't be retried until the error condition is corrected. The MAMComplianceNotification includes extra error information.
SERVICE_FAILURE There was a failure while attempting to retrieve compliance data from the Intune service. More information might be found in the Company Portal logs.
NETWORK_FAILURE There was an error connecting to the Intune service. The app should retry its token acquisition when the network connection is restored.
CLIENT_ERROR The attempt to remediate compliance failed due to a client-related issue, such as missing or incorrect user token. The MAMComplianceNotification includes more error information.
PENDING The attempt to remediate compliance fails because the service doesn't send the status response before the time limit expires. The app should try its token acquisition again later.
COMPANY_PORTAL_REQUIRED The Company Portal must be installed on the device for compliance remediation to succeed. If it is already installed, the app must be restarted. A dialog prompts the user to restart the app.

If the compliance status is MAMCAComplianceStatus.COMPLIANT, the app should reinitiate its original token acquisition (for its own resource).

If the compliance remediation attempt failed, the getComplianceErrorTitle() and getComplianceErrorMessage() methods return localized strings that the app can display to the end user if it chooses. The app can't resolve most error cases. In general, fail account creation or sign-in and allow the user to try again later.

If a failure is persistent, the Company Portal logs might help determine the cause. The end user can submit the logs. For more information, see Upload and email logs.

Here's an example of registering a receiver using an anonymous class to implement the MAMNotificationReceiver interface:

final MAMNotificationReceiverRegistry notificationRegistry = MAMComponents.get(MAMNotificationReceiverRegistry.class);
// create a receiver
final MAMNotificationReceiver receiver = new MAMNotificationReceiver() {
    public boolean onReceive(MAMNotification notification) {
        if (notification.getType() == MAMNotificationType.COMPLIANCE_STATUS) {
            MAMComplianceNotification complianceNotification = (MAMComplianceNotification) notification;

            // take appropriate action based on complianceNotification.getComplianceStatus()

            // unregister this receiver if no longer needed
            notificationRegistry.unregisterReceiver(this, MAMNotificationType.COMPLIANCE_STATUS);
        }
        return true;
    }
};
// register the receiver
notificationRegistry.registerReceiver(receiver, MAMNotificationType.COMPLIANCE_STATUS);

Note

You must register the notification receiver before calling remediateCompliance() to avoid a race condition that could result in missing the notification.

Declaring support for App Protection CA

Once your app is ready to handle App CA remediation, you can tell Microsoft Identity your app is App CA ready. To do this in the MSAL application, build the Public Client with the Client Capabilities of "protapp"

{
      "client_id" : "[YOUR_CLIENT_ID]",
      "authorization_user_agent" : "DEFAULT",
      "redirect_uri" : "[YOUR_REDIRECT_URI]",
      "multiple_clouds_supported":true,
      "broker_redirect_uri_registered": true,
      "account_mode": "MULTIPLE",
      "client_capabilities": "protapp",
      "authorities" : [
        {
          "type": "AAD",
          "audience": {
            "type": "AzureADandPersonalMicrosoftAccount"
          }
        }
      ]
    }

Once the steps are completed, proceed to Validating App Protection CA.

Implementation Notes

Note

The app's MAMServiceAuthenticationCallback.acquireToken() method should pass false for the forceRefresh flag to acquireTokenSilentAsync().

AcquireTokenSilentParameters acquireTokenSilentParameters =
        builder.withScopes(Arrays.asList(scopes))
               .forceRefresh(false)
               .build();

acquireTokenSilentAsync(acquireTokenSilentParameters);

Note

If you want to show a custom blocking UX during the remediation attempt, you should pass false for the showUX parameter to remediateCompliance(). You must ensure that you show your UX and register your notification listener first before calling remediateCompliance(). This prevents a race condition where the notification could be missed if remediateCompliance() fails quickly. For example, the onCreate() or onMAMCreate() method of an Activity subclass is the ideal place to register the notification listener and then call remediateCompliance(). The parameters for remediateCompliance() can be passed to your UX as Intent extras. When the compliance status notification is received, you can display the result or finish the activity.

Note

remediateCompliance() registers the account and attempt enrollment. Once the main token is acquired, calling registerAccountForMAM() isn't necessary, but there's no harm in doing so. On the other hand, if the app fails to acquire its token and wishes to remove the user account, it must call unregisterAccountForMAM() to remove the account and prevent background enrollment retries.

Register for notifications from the SDK

The Intune App SDK guide discusses several scenarios where your app might be required to register for notifications from the SDK, such as:

This section describes every type of notification the SDK can send, when and why your application would want to listen for it, and how to implement a notification receiver.

Types of notifications

All SDK notifications implement the MAMNotification interface, which has a single function, getType(), that returns a MAMNotificationType enum.

Most notifications are MAMUserNotification instances, which provide information specific to a single identity. The identity's OID can be retrieved via the getUserOid() function, and the identity's UPN can be retrieved via getUserIdentity().

MAMEnrollmentNotification and MAMComplianceNotification further extend MAMUserNotification, which contains results for attempts to enroll a user or device with the MAM service and results for attempts to remediate compliance for App Protection CA, respectively.

Notification type Notification class Reason for notification Applicability Tips for handling Thread info
COMPLIANCE_STATUS MAMComplianceNotification Returns the result of a compliance remediation attempt. Apps that implement App Protection CA must handle this. Nondeterministic
MAM_ENROLLMENT_RESULT MAMEnrollmentNotification Returns the result of an enrollment attempt. All apps receive this. Nondeterministic
MANAGEMENT_REMOVED MAMUserNotification App is about to become unmanaged. Apps that use MAMDataProtectionManager must handle this. See MANAGEMENT_REMOVED. Never on UI thread
REFRESH_APP_CONFIG MAMUserNotification App configuration values might have changed. Apps that implement app configuration and cache app configuration data must handle this. Apps must invalidate and refresh any cached app configuration data. Nondeterministic
REFRESH_POLICY MAMUserNotification App protection policy might have changed. Apps that cache app protection policy must handle this. Apps must invalidate and update cached app protection policy data. Nondeterministic
WIPE_USER_DATA MAMUserNotification Wipe is about to occur (*). Apps that use MAMDataProtectionManager must handle this or WIPE_USER_AUXILIARY_DATA. See Selective Wipe. Never on UI thread
WIPE_USER_AUXILIARY_DATA MAMUserNotification Wipe is about to occur (*). Only multi-identity apps receive this.
Apps that use MAMDataProtectionManager must handle this or WIPE_USER_DATA.
See Selective Wipe. Never on UI thread
WIPE_COMPLETED MAMUserNotification Wipe has completed. Optional for all apps. Delivered after WIPE_USER_DATA or WIPE_USER_AUXILIARY_DATA.
If the app reports a failure from its wipe handler, this notification won't be sent.
Never on UI thread

(*) Wipes might occur for many reasons, for example:

  • Your app called unregisterAccountForMAM.
  • An IT admin initiated a remote wipe.
  • Admin-required Conditional Access policies weren't satisfied.

Warning

An app should never register for both the WIPE_USER_DATA and WIPE_USER_AUXILIARY_DATA notifications.

MANAGEMENT_REMOVED

The MANAGEMENT_REMOVED notification informs the app that a previously policy-managed account is about to become unmanaged. After the account is unmanaged, the app can no longer read that account's encrypted files, read the account's data that's encrypted with MAMDataProtectionManager, interact with the encrypted clipboard, or otherwise participate in the managed-app ecosystem.

This doesn't require wiping user data or signing out the user (if a wipe were required, a WIPE_USER_DATA notification would be sent). Many apps might not need to handle this notification, but apps that use MAMDataProtectionManager must handle this. See Data Buffer Protection for details.

When the SDK calls the app's MANAGEMENT_REMOVED receiver, the following is true:

  • The SDK has already decrypted previously encrypted files (but not protected data buffers) that belong to the app. Files in public locations on the SD card that don't directly belong to the app (for example, the Documents or Download folders) aren't decrypted.

  • Any new files or protected data buffers that the receiver method creates (or any other code that runs after the receiver starts) aren't encrypted.

  • The app still has access to encryption keys, so operations such as decrypting data buffers succeed.

After your app's receiver returns, it no longer has access to encryption keys.

Implementing MAMNotificationReceiver

To register for notifications from the SDK, your app must create a MAMNotificationReceiver and register it with MAMNotificationReceiverRegistry.

To register the receiver, call registerReceiver with your receiver and the desired notification type in your Application.onCreate method:

@Override
public void onCreate() {
  super.onCreate();
  MAMComponents.get(MAMNotificationReceiverRegistry.class)
    .registerReceiver(
      new ToastNotificationReceiver(),
      MAMNotificationType.WIPE_USER_DATA);
}

Your app's MAMNotificationReceiver implementation must include the onReceive(MAMNotification notification) method. This method invokes individually for each notification received, and it must return a boolean. Generally, this method should always return true, unless your application encountered a failure responding to a notification.

As with other types of Android receivers, your application has flexibility with handling notifications:

  • It can create distinct MAMNotificationReceiver implementations for distinct notification types. In this case, make sure to register each implementation and each notification type separately.
  • It can use a single MAMNotificationReceiver implementation that contains logic for responding to multiple distinct notification types. In this case, it must be registered for each type of notification it can respond to.
  • It can create multiple MAMNotificationReceiver implementations that each respond to the same notification type. In this case, both must be registered to the same notification type.

Tip

It's safe to block in MAMNotificationReceiver.onReceive because its callback doesn't run on the UI thread.

Custom Themes

A custom theme can be provided to the Intune App SDK; this custom theme applies to all SDK screens and dialogs. If a theme isn't provided, the default SDK theme is used.

Providing a Custom Theme

To provide a theme, add the following line of code in the Application.onMAMCreate method:

MAMThemeManager.setAppTheme(R.style.AppTheme);

In the example, replace R.style.AppTheme with the style theme that the SDK should apply.

Trusted Root Certificates Management

If the application requires SSL/TLS certificates issued by an on-premises or private certificate authority to provide secure access to internal websites and applications, the Intune App SDK has added support for certificate trust management with the API classes MAMTrustedRootCertsManager and MAMCertTrustWebViewClient.

Note

MAMCertTrustWebViewClient supports Android 10 or later.

Trusted Root Certificates Management provides support for:

  • SSLContext
  • SSLSocketFactory
  • TrustManager
  • WebView

Requirements

Note

Trusted Root Certificates Management can be used independently of the Microsoft Tunnel VPN Gateway, but you must license Microsoft MAM Tunnel for use.

Using Trusted Root Certificates from Intune to Establish Trust Anchors

Trusted Root Certificates Management lets your app use trusted root certificates from Intune in combination with certificates from the device.

The API classes MAMTrustedRootCertsManager and MAMCertTrustWebViewClient use the Intune trusted root certificates delivered via App Configuration Policy as a fallback option if the device’s trusted root certificate stores don't contain the required trusted root certificates to establish a secure connection to on-premises resources. This way, the app can use both device and Intune certificates to verify secure connections and communication with trusted sources.

To enhance its network security settings, an app can use the Network Security Configuration XML file. Trusted Root Certificates Management respects this extra security by verifying whether the app’s Network Security Configuration XML has any of these features:

  • Custom trust anchors with extra CAs such as self-signed certificates.
  • Domain-specific rules for limiting trusted CAs.
  • Pin sets for certificates for specific domains.

Note

For more information about Android Network Security Configuration, see Network security configuration.

If any of these applies to a domain that is being checked for trust, then Trusted Root Certificates Management skips the custom trust checks for this domain and let only the platform’s default trust managers do the checks.

Class MAMTrustedRootCertsManager

This class provides the following APIs:

  • createSSLContextForOID(String oid, String protocol): creates an SSLContext object that uses trusted root certificates for the specified identity and the specified SSL/TLS protocol. The returned SSLContext object from this class is already initialized correctly with X509TrustManager objects that use the combined trusted root certificates from the device and the MAM service.
  • createSSLSocketFactoryForOID(String oid, String protocol): creates an SSLSocketFactory object that uses trusted root certificates for the specified identity and the specified SSL/TLS protocol. The returned SSLSocketFactory object is referenced from the same SSLContext object in this class.
  • createX509TrustManagersForOID(String oid): creates an array of X509TrustManager objects that use the combined trusted root certificates from the device and the MAM service for the specified identity.

Note

The oid parameter is expected to be the Microsoft Entra user ID (OID) for a particular user who runs the application. If the user identifier is unknown beforehand, you can pass a value of null and MAM attempts to discover the correct identity from the thread or process in which these APIs are invoked. The identity must be set on the process or thread correctly for MAM to discover the identity. For more information about setting the active identity on a process or thread, see Stage 5: Multi-Identity.

Note

When the protocol parameter isn't provided, the platform uses the highest supported SSL/TLS protocol.

Here are some examples of using this class.

Example Using HttpsUrlConnection
// Create an SSL socket factory using supplying the optional parameters identity and protocol
SSLSocketFactory sslSocketFactory = MAMTrustedRootCertsManager.createSSLSocketFactoryForOID(oid, "TLSv1.3");

// Create a URL object for the desired endpoint
URL url = new URL("https://example.com");

// Open a connection using the URL object
HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();

// Set the SSL socket factory for the connection
httpsURLConnection.setSSLSocketFactory(sslSocketFactory);

// Perform any other configuration or operations on the connection as needed
...
Example Using OkHttpClient
// Get the TrustManager instances for an identity from the SDK
TrustManager[] trustManagers = MAMTrustedRootCertsManager.createX509TrustManagersForOID(oid);

// Get SSLContext from the platform
SSLContext sslContext = SSLContext.getInstance("TLSv1.3");

// Initialize the SSLContext with the trust managers from the Intune App SDK
sslContext.init(null, trustManagers, null);

// Create an OkHttpClient.Builder object
OkHttpClient.Builder builder = new OkHttpClient.Builder();

// Set the SSLSocketFactory and the trust managers from the SDK
builder.sslSocketFactory(sslContext.socketFactory, trustManagers[0] as X509TrustManager).build();

// Build an OkHttpClient object from the builder
OkHttpClient okHttpClient = builder.build();

// Create a Request object for the desired endpoint
Request request = new Request.Builder().url("https://example.com").build();

// Execute the request using the OkHttpClient object and get a Response object
Response response = okHttpClient.newCall(request).execute();

// Perform any other operations on the response as needed
...

Class MAMCertTrustWebViewClient

This class provides a custom implementation of the Android class android.webkit.WebViewClient. The class provides a way to handle the SSL error android.net.http.SslError.SSL_UNTRUSTED in WebView.

When handling the error, the class uses trusted root certificates that Intune configures and the MAM service provides. This approach checks the trustworthiness of the host from the target URL that generated the SSL error in WebView. If the custom implementation doesn't handle the SSL error, the system invokes the default behavior inherited from the superclass.

When you use this class, create an instance of it and then call WebView.setWebViewClient(WebViewClient) to register it with a WebView instance.

Here's an example of using this class.

Example Using WebView
// Get the MAM implementation of WebViewClient from the Intune App SDK
MAMCertTrustWebViewClient mamCertTrustWebViewClient = new MAMCertTrustWebViewClient();

// Set the MAM WebViewClient from the SDK as the current handler on the instance of WebView
webView.setWebViewClient(mamCertTrustWebViewClient);

// Perform any other operations on WebView
...

Exit Criteria

For more information, see Quickly testing with changing policy for ease of testing.

Validating save to and open from restrictions

Skip this section if you didn't implement Policy for limiting data transfer between apps and device or cloud storage locations.

Refamiliarize yourself with every scenario where your app can save data to cloud services or local data and open data from cloud services or local data.

For simplicity, these tests assume your app only includes support for saving to and opening data from OneDrive from a single location within the app. However, you must validate every combination: every supported save location against every place your app allows saving data, and every supported open location against every place your app allows opening data.

For these tests, install your app and the Intune Company Portal; sign-in with a managed account before starting the test. Also:

  • Set the managed account's policy as:
    • "Send org data to other apps" set to "Policy managed apps".
    • "Receive data from other apps" set to "Policy managed apps".
Scenario Preconditions Steps
Save to, fully allowed “Save copies of org data” policy set to Allow - Navigate to the part of your app where it can save data to OneDrive.
- Attempt to save a document to OneDrive using the same managed account logged into your app.
- Confirm the save is allowed.
Save to, exempted - “Save copies of org data” policy set to Block
- “Allow user to save copies to selected services” policy set to OneDrive only
- Navigate to the part of your app where it can save data to OneDrive.
- Attempt to save a document to OneDrive using the same managed account logged into your app.
- Confirm the save is allowed.
- If your app allows it, attempt to save the file to a different cloud storage location and confirm it is blocked.
Save to, blocked “Save copies of org data” policy set to Block - Navigate to the part of your app where it can save data to OneDrive.
- Attempt to save a document to OneDrive using the same managed account logged into your app.
- Confirm the save is blocked.
- If your app allows it, attempt to save the file to a different cloud storage location and confirm it is blocked.
Open from, fully allowed “Open data into Org documents” policy set to Allow - Navigate to the part of your app where it can open data from OneDrive.
- Attempt to open a document from OneDrive using the same managed account logged into your app’s storage.
- Confirm the open is allowed.
Open from, exempted - “Open data into Org documents” policy set to Block
- “Allow users to open data from selected services” policy set to OneDrive only
- Navigate to the part of your app where it can open data from OneDrive.
- Attempt to open a document from OneDrive using the same managed account logged into your app’s storage.
- Confirm the open is allowed.
- If your app allows it, attempt to open another file from a different cloud storage location and confirm it is blocked.
Open from, blocked “Open data into Org documents” policy set to Block - Navigate to the part of your app where it can open data from OneDrive.
- Attempt to open a document from OneDrive using the same managed account logged into your app’s storage.
- Confirm the open is blocked.
- If your app allows it, attempt to open another file from a different cloud storage location and confirm it is blocked.

Validating notification restrictions

Skip this section if you didn't implement Policy for restricting content inside notifications.

As far as App Protection Policy is concerned, your application might fire three different types of notifications:

  1. Notifications that don't contain any account data.
  2. Notifications that contain data that belongs to a managed account.
  3. Notifications that contain data that belongs to an unmanaged account.

If your application is single-identity, only the first two are relevant, since no protections are applied if the sole account is unmanaged.

You can validate notification restrictions by triggering all three types of notifications with different policy values configured.

For these tests, install your app and the Intune Company Portal; sign-in with a managed account before starting the test. If your app is multi-identity, also sign-in to your app with an unmanaged account.

Scenario Preconditions Steps
Full content blocked “Org data notifications” policy set to Block - Trigger your app to fire a notification with no account data.
- Confirm the notification doesn't display any content.
- Trigger your app to fire a notification with the managed account’s data.
- Confirm the notification doesn't display any content.
- Trigger your app to fire a notification with the unmanaged account’s data.
- Confirm the notification doesn't display any content.
Partial content blocked “Org data notifications” policy set to Block org data - Trigger your app to fire a notification with no account data.
- Confirm the notification displays its full content.
- Trigger your app to fire a notification with the managed account’s data.
- Confirm the notification redacts the managed account’s content.
- Trigger your app to fire a notification with the unmanaged account’s data.
- Confirm the notification displays its full content.
No content blocked “Org data notifications” policy set to Allow - Trigger your app to fire a notification with no account data.
- Confirm the notification displays its full content.
- Trigger your app to fire a notification with the managed account’s data.
- Confirm the notification displays its full content.
- Trigger your app to fire a notification with the unmanaged account’s data.
- Confirm the notification displays its full content.

Validating data backup and restore

Skip this section if you didn't implement Policy for protecting backup data.

Refamiliarize yourself with the content (files and key-value pairs) that your app configured for backup. You should validate that only expected content is part of the restore. Extra content in the restore can lead to a data leak.

For these tests, install your app and the Intune Company Portal; sign in with a managed account before you start the test. If your app is multi-identity, also sign in to your app with an unmanaged account.

Follow Android's official instructions for testing backup. These instructions differ for autobackup and key/value backups, so follow them closely.

Validating custom screen capture against policy

Skip this section if you didn't implement Custom Screen Capture Restrictions.

If your application has a feature that bypasses Android's Window-level FLAG_SECURE, validate that this feature is blocked by app protection policy screen capture restrictions.

For these tests, install your app and the Intune Company Portal; sign-in with a managed account before starting the test.

Scenario Preconditions Steps
Screen capture blocked “Screen capture and Google Assistant” policy set to Block - Navigate to the part of your app that uses the custom FLAG_SECURE code.
- Attempt to use that feature.
- Confirm the feature is blocked.
Screen capture allowed “Screen capture and Google Assistant” policy set to Allow - Navigate to the part of your app that uses the custom FLAG_SECURE code.
- Attempt to use that feature.
- Confirm the feature is allowed.

Validating App Protection CA

Skip this section if you didn't implement Support App Protection CA.

In addition to the typical validation steps of creating and assigning app protection policy to your app and test account, you must also create and assign an App Protection Conditional Access policy to your test account. See Set up app-based Conditional Access policies with Intune for details.

Test steps:

  1. Uninstall Microsoft Authenticator and Intune Company Portal before you start this test.
  2. Install your app.
  3. Sign-in to your app with your test account targeted with both app protection policy and app-based CA policy.
  4. Confirm your app prompts you to install the Company Portal.
  5. Sign-in again.
  6. Confirm your app prompts you to register your device. Follow the prompts. If your app doesn't prompt for registration here, confirm that your test device had uninstalled other SDK-enabled apps, Company Portal, and Authenticator first. If this still doesn't prompt, revisit the implementation instructions.
  7. Confirm you're able to access all app data after registering.

Validating notification receivers

Skip this section if you didn't implement Register for notifications from the SDK.

Validation steps depend on the types of notifications your app registered for. For all types of notifications, add logging to ensure that your receiver is properly invoked.

MAM_ENROLLMENT_RESULT can be triggered by signing in to your application with an account that app protection policy targets.

You can trigger REFRESH_APP_CONFIG and REFRESH_POLICY by updating the respective App Configuration Policy and App Protection Policy that target your test account and waiting for the SDK to receive updated policy.

Tip

See Quickly testing with changing policy to speed up this process.

You can trigger MANAGEMENT_REMOVED, WIPE_USER_DATA, WIPE_USER_AUXILIARY_DATA, and WIPE_COMPLETED notifications by issuing a selective wipe from Microsoft Intune.

Validating custom themes

Skip this section if you didn't implement Custom Themes.

You can validate custom theme support by inspecting the colors on the SDK's dialogs. The simplest dialog to check is the MAM PIN screen.

Preconditions:

  • Set the managed account's policy as:
    • "PIN for access" set to "Required".
  • Install your app and the Intune Company Portal.

Test steps:

  1. Launch your application and sign in with the test account.
  2. Confirm the MAM PIN screen appears and is themed based on the custom theme that you provided to the SDK.

Next Steps

If you followed this guide in order and completed all the Exit Criteria earlier in this article, your app is now fully integrated with the Intune App SDK and can enforce app protection policies. If you skipped either of the previous app participation sections, Stage 5: Multi-Identity and Stage 6: App Configuration, and you're unsure if your app should support these features, revisit Key Decisions for SDK integration.

App protection is now a core scenario for your app. Continue to refer to this guide and the Appendix as you continue to develop your app.