How to make a mobile enterprise app unique on a device using the Android Management API
Tags: android, api, mobile
Published: 2021-03-16 06:17:43


Android devices are not always used as personal gadgets. Instead, they are sometimes used to fulfill specific business tasks, in which case there is no need for pre installed device properties. In this article, we explain how we at [Axmor](https://axmor.com/) block unnecessary functions without root rights, by making several simple adjustments to the Android Management API.

Most of us has, at some point or other, come into contact with corporate devices:

* client applications used to purchase tickets, order food in restaurants, register guests, etc.
* corporate applications used by sales managers, couriers, logisticians, inventory management systems, etc.

When utilized for such purposes, it is often required to limit the device in question to a single application. Specialized computers are sometimes used to perform the tasks listed above, but in fact, any tablet or smartphone can be turned into what Android development documents refer to as a [Dedicated device](https://developer.android.com/work/dpc/dedicated-devices?hl=en).

Our company develops a significant amount of enterprise applications, hence the frequent need to block functions on dedicated devices. One such example is the application that we created to assist industrial safety engineers. While traversing the factory floor and monitoring the serviceability of pipelines, the engineers needed a place to enter the collected data:
![app interface on a mobile phone](https://axmor.ru/wp-content/uploads/2020/02/slide_2-2.jpg)
![app interface on a mobile phone](https://axmor.ru/wp-content/uploads/2020/02/slide_3-1.jpg)
![app interface on a mobile phone](https://axmor.ru/wp-content/uploads/2020/02/slide_1-3.jpg)
The application allows the user to create reports, assess the status of pipelines, update the status of repair work, and check and confirm safety indicators. 

The client company purchased dozens of Android smartphones for this purpose and sent us the following requirements:

1. The primary application should always be present on the screen; the ability to launch other applications should be removed. Accordingly, the buttons “Back”, “Home” and “Recent applications” should be blocked.
2. The primary application should launch immediately upon rebooting the device.
3. Block the ability to change any settings on the device.
4. Block the ability to interact with the status bar, thereby removing the ability to change the settings.
5. Block the ability to open push notifications.
6. The device screen should not turn itself off.

Several of these requirements can be implemented with relative ease; however, a device without root rights does not currently permit blocking interactions with the status bar or push notifications. Furthermore, nothing can be done with the “Recent Applications” button.

Here the Android Management API and [device policy controller (DPC)](https://developer.android.com/work/dpc/build-dpc) come to the rescue, allowing corporate devices to be configured in the shortest possible time and any functions to be blocked at the user’s own discretion.


##Device Management Tools: Device policy controller and Android Management API

DPC is an application that can control the permissions of other applications and block various functions of the device. Therefore, by using a custom DPC, we can also hide the status bar and lock the system buttons on the device.
Writing one’s own DPC is a laborious task, since it involves the creation of an entire application, the sole function of which is to limit the user's capabilities when working with the device. In addition, any changes in the device policy will need to be implemented in the code, entailing the release of a new version of this application.
To lessen the burden on developers of said solutions, Google released its DPC, Android Device Policy (2017), which is managed using the Android Management API. To use the Android Management API, several steps need to be performed:
1. [Create a service account.](https://developers.google.com/android/management/service-account) This is an optional step and is needed to call the API methods from the code (from the backend or from the web console, which also needs to be manually written), since such calls require authentication through OAuth.
2. [Create an enterprise.](https://developers.google.com/android/management/create-enterprise) As the name suggests, it is a resource that represents an organization. To create it, you need a Gmail account that is not tied to any organization. An enterprise consists of policies and devices.
3. [Create a policy.](https://developers.google.com/android/management/create-policy) This is the set of rules and restrictions that will be applied to the device. When we change something in a policy, all devices to which it is applied automatically update their state in accordance with the changes without the need for additional actions on our part.
4. [Enroll and provision a device.](https://developers.google.com/android/management/provision-device) To apply the created policy to the device, it is necessary to create an EnrollmentToken bound to the selected policy. The device must then be reset to the factory settings, as installation of DPC as a Device owner can only occur during the initial system setup. This is effective for devices running Android 6.0 and above; details can be found in the official documentation. The easiest way to transfer the token to the device is to generate a QR code based on it. JSON for generating a QR code can be obtained from the qrCode field of the EnrollmentToken resource. To scan a QR code, simply tap 6 times in the same place on the first screen that appears following a factory reset (usually the language selection screen), after which you will be prompted to connect to the Internet and download a QR code scanner. If this method failed to work on the selected device, you can go to the data entry stage of the Google account and enter afw # setup there.

Google provides several options to use the Android Management API. The easiest way to complete all these steps is to go through the interactive [quickstart guide](https://colab.research.google.com/github/google/android-management-api-samples/blob/master/notebooks/quickstart.ipynb). To access the API from the code, you can use one of the libraries in [Java](https://developers.google.com/api-client-library/java/apis/androidmanagement/v1), [.NET](https://developers.google.com/api-client-library/dotnet/apis/androidmanagement/v1), [Python](https://developers.google.com/api-client-library/python/apis/androidmanagement/v1) or [Ruby](https://developers.google.com/api-client-library/ruby/apis/androidmanagement/v1).

##Using the Android Management API to create a SingleApp device

Let us now return to the list of the client’s requirements for the Dedicated device and consider how they can be implemented using policy. Firstly, to ensure that the selected application is always present on the screen and launches automatically after a reboot, it must replace the standard Home app. To achieve this the following block should be added to the policy:


```
"persistentPreferredActivities": [
      {
        "actions": [
          "android.intent.action.MAIN"
        ],
        "categories": [
          "android.intent.category.HOME",
          "android.intent.category.DEFAULT"
        ],
        "receiverActivity": "com.example.lockerApp"
      }
    ]

```
We also need to allow the application to run [lockTaskMode](https://developer.android.com/work/dpc/dedicated-devices/lock-task-mode), which will enable us to hide the “home” and “recent applications” buttons, preventing the user from exiting the application via use of the “Back” system button:
```
"applications": [
      {
        "defaultPermissionPolicy": "GRANT",
        "lockTaskAllowed": true,
        "packageName": "com.example.lockerApp"
      }
    ]
```
To start lockTaskMode, simply call the appropriate method in the code of the main Activity of your application:

```
fun startLockTask() {
   val am = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
   if (am.lockTaskModeState == ActivityManager.LOCK_TASK_MODE_NONE) {
       super.startLockTask()
   }
}

```
To hide the status bar, add to policy
```
"statusBarDisabled": true
```

To ensure that the screen remains permanently ‘on’, simply add the android flag: `keepScreenOn = "true"` to the root ViewGroup Activity. The resulting JSON policy will look something like this:
```
{
    "adjustVolumeDisabled": true,
    "appAutoUpdatePolicy": "ALWAYS",
    "applications": [
      {
        "defaultPermissionPolicy": "GRANT",
        "installType": "FORCE_INSTALLED",
        "lockTaskAllowed": true,
        "packageName": "com.example.lockerApp"
      }
    ],
   "persistentPreferredActivities": [
      {
        "actions": [
          "android.intent.action.MAIN"
        ],
        "categories": [
          "android.intent.category.HOME",
          "android.intent.category.DEFAULT"
        ],
        "receiverActivity": "com.example.lockerApp"
      }
    ],
    "playStoreMode": "WHITELIST",
    "safeBootDisabled": true,
    "screenCaptureDisabled": true,
    "statusBarDisabled": true,
    "systemUpdate": {
      "endMinutes": 240,
      "startMinutes": 120,
      "type": "WINDOWED"
    }
  }
```
Various examples of policies can be found [here](https://developers.google.com/android/management/policies/dedicated-devices). And here are [all available policy parameters](https://developers.google.com/android/management/reference/rest/v1/enterprises.policies). 

Here is another problem that we came up against when developing a solution. In one of our projects, the customer needed a functionality to exit the device’s locked mode, so as to fully use the device without the need to revert to factory settings. This can be achieved via the creation of two different policies: `policy_locked` and `policy_unlocked`, switching between them via the device’s backend. However, it is impossible to directly obtain the internal id in the Android Management API from the device, thus preventing operations being performed with it. Prior to Android 10, a device could be found in the general list by its serial number, stored in `Device.hardwareInfo.serialNumber`, having cross-referenced it with the serial number obtained through `Build.getSerial ()` or `Build.SERIAL`; however, from Android 10 onwards, it is [prohibited to receive the serial number for non-system applications](https://developer.android.com/preview/privacy/data-identifiers%20%20%20#device-ids).


Google has provided a [solution](https://stackoverflow.com/a/55251103/7035703) to this issue on Stackoverflow. They advise using [managed configurations](https://developer.android.com/work/managed-configurations), which are in fact additional parameters that can be transferred to the device by writing them into the policy. Of course, in this case each device requires its own unique policy. To circumvent the need to enter the policies manually, [Pub/Sub](https://developer.android.com/work/managed-configurations) notifications can be used, thus enabling the user to receive a notification when registering a new device and immediately record its id in the managed configurations. Perhaps not the easiest way to go about things, but certainly the best. It is not possible to prescribe a unique id for a device manually when generating a QR code, because managed configurations are designed in such a way that things can be written there only after the device has been registered.

##How to publish an enterprise application on Google Play

We now have a policy that is capable of blocking everything on the device, except for the chosen application. So, is our work here done? Not yet. Our application still needs to be installed on the device; however, uploading it to Google Play is clearly not an option since we do not want to make it available to a wider audience. In theory, it is of course possible to send an apk to the customer and ask them to install the application manually, but in practice doing this for each device is a tedious process that will need to be repeated every time the application is updated.

The solution to this problem is to use [Managed Google Play](https://support.google.com/googleplay/work/answer/6138458?visit_id=637090644612667436-340264434&rd=1) and set the application as a [private app](https://support.google.com/googleplay/android-developer/answer/6145139). To do this, first download the application on Google Play, then go to Store Presence -> Pricing & Distribution -> User programs -> Managed Google Play, check Turn on advanced managed Google Play features -> Privately target this app to a list of organizations. At this point, select the id of the enterprise for which the devices are being registered. Now, if the `installType` field is written as `FORCE_INSTALLED` in the policy for our application, it will be installed automatically when the device is registered.


##Conclusion
Despite several shortcomings, the Android Management API is currently the easiest and most convenient way to create a Dedicated device or single application device in the Android OS. 

The large array of customization opportunities allows the direct execution of code to be delegated to the creation of restrictions on the device to the finished DPC, which can significantly reduce the development time. While working on our mobile projects, this API allows [Axmor](https://axmor.com/) specialists to reduce the time spent on “routine” work such as implementing restrictions, and instead devote more attention to the Single App itself.