Application Profiles in an Android  Enterprise Mobility Management (EMM) Environment with Flutter

Application Profiles in an Android Enterprise Mobility Management (EMM) Environment with Flutter

·

6 min read

Application Profiles in an Android  Enterprise Mobility Management (EMM) Environment with Flutter

I really wanted to title this post, "You don't control me, yet you do". Also, if this blog has been somewhat neglected it is because of a mix of pandemic, baby at home and work eating into all my free time.

As a general reminder I try not to rehash documentation that demonstrates how to do something and will provide a link to it. This means you will probably want to review the links in each blog post if you have questions about something not directly covered. I guess what I am saying is that if you feel like the blog goes from step 1 to profit it probably means you might have missed reviewing a link's content but feel free to ask me and I'll help where I can.

Application Profiles in an Android  Enterprise Mobility Management (EMM) Environment with Flutter

I recently had a project with an application that was being deployed to a Google Play for Work managed Play Store. What this means is that this Private Google Play store will be connected to an organization ( s ) and the application will be private to this store. Likewise, the organization(s) will be managing all the devices via an EMM solution which is just a fancy work for "controlling the devices".

To give a short and general synopsis of application. It will be installed on a device that I control into different clients' retail location. At first I was building the application for each client, producing an APK and manually installing it on the device. This works in the beginning but not at scale.

There are a couple options but if you take the requirements that we want one compiled app apk and we don't want to have logins and to make this as simple as possible for the client for installs and upgrades, there is only one and that is the use of managed configurations. This is a nifty process where the EMM tool can read the managed configuration data for an app and allow the user to create different configurations. This is similar to having an xml/json config file for each client that get pushed to the device on install and the device reads that data. The great part is that a good EMM tool makes this easy to create these configs and remotely push them to devices on install of the application or pushed later if there are changes.

My first proof of concept process to handle this with three clients was to compile the application with environment variables. This was EASY to do and worked okay but I knew it was temporary.

flutter build apk --dart-define=environment=prod --dart-define=client_name="ACME FOOD" --dart-define=client_apk_url=https://myapp.com/download --dart-define=client_id=1 --dart-define=service_token=token --dart-define=service_url=https://myapp.com/ --dart-define=client_logo="yourlogo.com/logo.png"

Reading these values in the app in the main startup method was just as easy:

const clientId = int.fromEnvironment('client_id');
const serviceToken = String.fromEnvironment('service_token')
// ....

It worked and worked well if you want to produce APK's in a rather static environment and especially if you have a public app not under EMM. In fact, my scenario I'm supporting both ( who likes to remove good code ?) now but I will cover in the next sections how to do this via EMM and managed configs.

I went down this route of how to read an EMM application config with a bunch of Flutter experience but not a ton of native application (Android) development experience. I knew that Flutter allows for the integration with the native device code (Kotlin or Java) but never had much of a reason (Kudos to the Flutter community and packages) to have to do this with the business applications I've been developing. Where is the first place to look? Google. Nothing Next place is Stack Overflow. Nothing. Yep, nothing. So I created a question and waited a couple weeks and...... Nothing. At this point I'm wondering if this is even possible but I take some time off over the holiday break to relax and came back recharged and ready to tackle it. Let's see how we can support this EMM config process.

The first link you should review is the one posted above. Here it is.

[

Set up managed configurations | Android Developers

Learn how to implement managed configurations that can be changed by other apps on the same device.

Application Profiles in an Android  Enterprise Mobility Management (EMM) Environment with FlutterAndroid Developers

Application Profiles in an Android  Enterprise Mobility Management (EMM) Environment with Flutter

](developer.android.com/work/managed-configur..)

You will want to add the restrictions your application will need to be configured. This is what the EMM tool reads. I won't go too deep into this as the link covers it but it amounts to creating a resource XML file with keys:

<?xml version="1.0" encoding="utf-8"?>
<restrictions xmlns:android="http://schemas.android.com/apk/res/android">

  <restriction
    android:key="api_url"
    android:title="@string/api_url_title"
    android:restrictionType="string"
    android:description="@string/api_url_description"
    android:defaultValue="EMPTY" />

</restrictions>

In the case above our application will support an api_url value that can be pushed remotely to the application on install. You could imagine a case where you have a development or production environment and you want the application to use a different url for each. Or you have a multi-tenant api where each client has their own server. Instead of hard coding it in the application it can read a config value and we can go on from there. In the event the url changes we don't need to necessarily reinstall the application, we can push a new config profile via the EMM tool.

In flutter there is no direct way to read this value but there is a way to execute native code. You can read more about it here.

In order for Flutter to make use of this data we must create a MethodChannel which creates a RestrictionsManager and gets access to the restriction data you defined prior. In my case I am passing into the channel the name of the key I want to retrive. To keep this simple I am just calling success and ignoring all errors.

import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import android.content.Context
import android.content.RestrictionsManager
import android.os.Bundle

class MainActivity: FlutterActivity() {
    private val CHANNEL = "com.yourdomain.something/managed" // you define this

   override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
      call, result ->
      var myRestrictionsMgr =
        activity?.getSystemService(Context.RESTRICTIONS_SERVICE) as RestrictionsManager
        var appRestrictions: Bundle = myRestrictionsMgr.applicationRestrictions
       result.success(appRestrictions.getString(call.method))
    }
  }
}

Now that we have a MethodChannel, it is trivial to invoke this via the platform.invokeMethod. The code is below and uses the URL for the channel and the name of the restriction key!

class Configs {
  static const platform = const MethodChannel('com.yourdomain.something/managed');
  static Future<String> fromMdmConfigString(String name, {String defaultValue: ""}) async {
    return await platform.invokeMethod(name);
  }
}
// somewhere in the app

void main() async {
  // other stuff
  var apiUrl = await Configs.fromMdmConfigString('api_url');
  var apiClient = ApiClient(apiUrl);
}

Finally, the big question is how do we pull it all together? I am going to demonstrate the next piece using Chrome which has a LOT of managed application config values but we will just concentrate on the proxy one for us because it is a string and similar to the above.

Create a managed configuration and enter some values:

Application Profiles in an Android  Enterprise Mobility Management (EMM) Environment with Flutter

Application Profiles in an Android  Enterprise Mobility Management (EMM) Environment with Flutter

Now we publish this managed configuration to the devices we want to target (in this case I'm using my account at the EMM provider we use - I will post about them later). We can really target anyone and any set of devices.

Application Profiles in an Android  Enterprise Mobility Management (EMM) Environment with Flutter

After publishing the device now has the EMM managed config data and the application can read it!