This is the fourth part in a series of blog posts looking at integrating Zebra Android devices as IoT clients in a public cloud infrastructure.  For other posts in this series, please see the links below:


This post expands on the concepts already discussed in Part 3 – AWS IoT Core Data Processing


In part 3 of this post series, we covered simulating an MQTT message using the MQTT client handily provided by AWS and in this post we will replace that test client with an MQTT client on a real device.  To achieve this we will lean heavily on an official AWS Android PubSub example which uses the AWS SDK for IoT to connect an Android device to AWS and generates its own certificates to do so.  Full credit to the original authors.


Setting up permissions to creating the client identity and keys

To connect the client (or ‘thing’ in AWS parlance) it is necessary to generate a certificate and key, the private key remaining on the device and the certificate being placed on the server to ensure secure connection with the client.  I will duplicate the steps given by the official official AWS Android PubSub example but alternative ways do exist, e.g. generating the key and certificate in the cloud.

  1. In the Amazon Cognito Console, press the Manage Identity Pools button and on the resulting page press the Create new identity pool button
  2. Give your identity pool a name and ensure that ‘Enable access to unauthenticated identities’ under the Unauthenticated identities section is checked.  This allows the sample application to assume the unauthenticated role associated with this identity pool.  Press the Create Pool button to create your identity pool.  This tutorial uses ‘ent_android_iot’ for the identity pool name.


  1. As part of creating the identity pool, Cognito will setup two roles in Identity and Access Management (IAM).  These will be named something similar to: Cognito_<<PoolName>>Auth_Role and Cognito_<<PoolName>>UnauthRole. You can view them by pressing the View Details button on the console.  Now press the Allow button to create the roles.  This tutorial uses ‘Cognito_ent_android_iotAuth_Role’ and ‘Cognito_ent_android_iotUnauth_Role’.

  1. Note the Identity pool ID value.  It should look similar to: ‘us-west-2: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx". Also, note the region that is being used. These will be used in the application code later.
  2. Next, we attach a policy to the unauthenticated role to setup permissions to access the required AWS IoT APIs.  This is done by first creating the IAM Policy shown below in the IAM Console and then attaching it to the unauthenticated role. In the IAM console, search for the pool name that you created and click on the link for the unauth role.
  1. Click on the “Add inline policy” button and add the following policy using the JSON tab. Click on “Review Policy”, give the policy a descriptive name and then click on “Create Policy”.  This policy allows the sample app to create a new certificate including a private key and attach a policy to the certificate.


  "Version": "2012-10-17",

  "Statement": [


      "Effect": "Allow",

      "Action": [




      "Resource": [







Note from the tutorial on which this post is based:


To keep this example simple it makes use of unauthenticated users in the identity pool. This can be used for getting started and prototypes but unauthenticated users should typically only be given read-only permissions if used in production applications. More information on Cognito identity pools can be found here, information on AWS IAM roles and policies can be found here, and information on AWS IoT policies can be found here.

Creating the Policy

The configuration we have setup up to this point will enable the Sample App to connect to the AWS IoT platform using Cognito and upload certificates and policies. Next, we will need to create a policy that we will attach to the Device Certificate that will authorize the certificate to connect to the AWS IoT message broker and perform publish, subscribe and receive operations. To create the policy in AWS IoT

  1. Navigate to the AWS IoT Console and press the Get Started button.  On the resulting page click on Secure on the side panel and then click on Policies
  2. Click on Create
  3. Give the policy a name (this tutorial uses ‘ent-andorid-policy’).  Note this name as you will use it in the application.
  4. Click on Advanced Mode and replace the default policy with the following text and then click the Create button.


  "Version": "2012-10-17",

  "Statement": [


      "Effect": "Allow",

      "Action": "iot:Connect",

      "Resource": "*"



      "Effect": "Allow",

      "Action": [





      "Resource": "*"




Note from the tutorial on which this post is based:



To keep things simple, This policy allows access to all the topics under your AWS IoT account. This can be used for getting started and prototypes. In product, you should scope this policy down to specific topics.  Specify them explicitly as ARNs in the resource section: "Resource": "arn:aws:iot:<REGION>:<ACCOUNT ID>:topic/<<mytopic/mysubtopic>>".


Creating the client identity and keys

I have put together a sample client which can be used to connect to supported cloud IoT solutions and send both dummy and real data.  Please clone the client app from GitHub and run it on a device, being sure to grant any requested runtime permissions.

The sample app depends on the AWS IoT client API which is available from gradle:



implementation 'com.amazonaws:aws-android-sdk-iot:2.8.+'


Firstly, configure the sample app with the connection information required for AWS


Setting Description

The custom endpoint allows you to connect to AWS IoT and will be in the form

You can find this by loading the AWS IoT dashboard then clicking Settings.

Cognito Pool This is the identity pool ID you noted down earlier, in the form us-west-2: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. You can retrieve it from the Cognito console and clicking Manage Identity Pools
Policy Name This is the policy name you created previously, this tutorial uses ‘ent-android-policy’
Cloud Region This tutorial uses us-west-2.  Note that it should be one of these regions.


You can either modify the connection settings using the UI of the sample app or (far more likely) modify the file.

To create the client identity and keys:

  1. Launch the application
  2. Select the AWS radio button
  3. Click ‘Connect’
  4. You should see a message that says “Test client connect failed: Certificates have not been created, please try again”.  This is a good thing.


  1. Click ‘Connect’ again
  2. Now the client should successfully connect



If at any point you want to regenerate the private key and certificate then you will need to uninstall and reinstall the client application.  In a production app you would want to implement a more seamless way of rotating keys(!)

The code to connect the client to the AWS IoT endpoint is as follows:



mqttManager.connect(clientKeyStore, new AWSIotMqttClientStatusCallback() {


    public void onStatusChanged(final AWSIotMqttClientStatus status,

                                final Throwable throwable) {

        if (status == AWSIotMqttClientStatus.Connecting) {

            Log.d(TAG, "Connecting...");

        } else if (status == AWSIotMqttClientStatus.Connected) {

            connected = true;

            Log.d(TAG, "Connected");

        } else {

            //  Handle other statuses





Observe that if you now load up the AWS IoT dashboard and click on Secure, then Certificates, a certificate will have been created.


Similarly, notice how a policy has also been created under the Policies tab

Sending device data from the client to AWS IoT

At this point, everything is configured to send either dummy or real device data from the client and process it within AWS IoT.  The following steps detail how to use the sample client available from GitHub to send both dummy and real data from the device.


Sending dummy data

After generating the AWS client certificate and key and successfully connecting to AWS, tap the ‘Send Data’ tab.  From this table enable the “Send Dummy Data” switch and you should see something like the below



Scroll down to the bottom of the view and click the “Send Dummy Data” button, you should see an indication that the data was successfully sent


This is publishing a message to the MQTT topic defined in, this tutorial uses ‘deviceTelemetry’.



final String topic = UserConfig.AWS_TOPIC;

long dt = System.currentTimeMillis();

DateFormat formatter = new SimpleDateFormat("YYYY-MM-dd'T'HH:mm:ss");

String dateTime = formatter.format(dt);

final String message = "{\n" +

        "\"deviceId\": \"" + deviceId + "\",\n" +

        "\"dateTime\": \"" + dateTime + "\",\n" +

        "\"model\": \"" + model + "\",\n" +

        "\"lat\": \"" + lat + "\",\n" +

        "\"lng\": \"" + lng + "\",\n" +

        "\"battLevel\": \"" + battLevel + "\",\n" +

        "\"battHealth\": \"" + battHealth + "\",\n" +

        "\"osVersion\": \"" + osVersion + "\",\n" +

        "\"patchLevel\": \"" + patchLevel + "\",\n" +

        "\"releaseVersion\": \"" + releaseVersion + "\"\n" +


mqttManager.publishString(message, topic, AWSIotMqttQos.QOS0);


Sending real data

To send real data, first select the ‘AWS’ radio button from the ‘Connect’ tab and disconnect from the test connection.  In the Send data pane, slide the ‘Send Real Data’ toggle to on.



A WorkManager has now been started which will send device data in the background about every 15 minutes, though this frequency will be impacted by the device going into doze so you may not see messages this frequently.


The code to create the periodic request is given below and every roughly 15 minutes the worker is will connect to the cloud IoT service, publish the real device data and then disconnect from the cloud IoT service.  Because the worker needs to connect, we pass the required connection information.



PeriodicWorkRequest.Builder sendRealDataBuilder =

        new PeriodicWorkRequest.Builder(SendRealDataWorker.class,

                PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS, TimeUnit.MILLISECONDS);

//  Pass the MQTT connection information to the worker.  The long-term worker is responsible for

//  making and breaking the MQTT connection when needed.

Data metaData = new Data.Builder().putInt(MQTT_SERVER_ENDPOINT, radioGroup.getCheckedRadioButtonId())

        .putString(MQTT_DEVICE_ID, txtDeviceId.getText().toString())    //  Common

        .putString(MQTT_CLOUD_REGION, txtCloudRegion.getText().toString())  //  Common

        .putString(MQTT_AWS_ENDPOINT, txtEndpoint.getText().toString()) //  AWS

        .putString(MQTT_COGNITO_POOL_ID, txtCognitoPoolId.getText().toString()) //  AWS

        .putString(MQTT_POLICY_NAME, txtPolicyName.getText().toString())    //  AWS


PeriodicWorkRequest sendRealDataWork = sendRealDataBuilder.



sendRealDataWorkId = sendRealDataWork.getId();


The actual work that gets performed every roughtly 15 minutes is defined in the doWork() function of the SendRealDataWorker class.

The following data is sent periodically:

  • Device build information (model, os version, patch level, release version)
  • Device location (lat, long)
  • Device battery status (level, health)

Note that battery health is only available on Zebra Android devices,