Developer Console

Integrate Your App

After you Obtain Credentials to use Amazon Device Messaging (ADM) and set it up in your development environment, integrate ADM with your app. See Overview of ADM for a high-level overview of the process of adopting ADM in your app and server code.

Testing notes

When testing your app's use of ADM, note the following:

  • First-generation Kindle Fire tablets do not support ADM.
  • Second-generation Kindle Fire tablets must be running the following system versions to support ADM. To see the system version, go to Settings > Device > About.
    • Kindle Fire HD 8.9" - System Version 8.3.0 or later
    • Kindle Fire HD 7" - System Version 7.3.0 or later
    • Kindle Fire (2nd Generation) - System Version 10.3.0 or later. On second-generation tablets, your corporate firewall may block network access for ADM. Your app must be able to communicate with servers outside your corporate network using UDP on one of the following ports: 49317, 33434, 40317
  • Other system versions of Fire tablets and Fire TV devices all support ADM.

Update your app manifest

Updating your AndroidManifest.xml file is the first step in receiving messages sent via ADM. You must make the following changes to your existing file.

  1. Open your AndroidManifest.xml file and add the Amazon namespace:

    xmlns:amazon="http://schemas.amazon.com/apk/res/android"
    
  2. To declare the permissions necessary to support ADM, after the manifest element, add the permission and uses-permission elements.

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:amazon="http://schemas.amazon.com/apk/res/android"
       package="[YOUR PACKAGE NAME]"
       android:versionCode="1"
       android:versionName="1.0">
    <!-- This permission ensures that no other application can intercept your ADM messages. -->
    <permission
         android:name="[YOUR PACKAGE NAME].permission.RECEIVE_ADM_MESSAGE"
         android:protectionLevel="signature" />
    <uses-permission android:name="[YOUR PACKAGE NAME].permission.RECEIVE_ADM_MESSAGE" />
    
    <!-- This permission allows your app access to receive push notifications from ADM. -->
    <uses-permission android:name="com.amazon.device.messaging.permission.RECEIVE" />
    
    <!-- ADM uses WAKE_LOCK to keep the processor from sleeping when a message is received. -->
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    ...
    </manifest>
    
  3. Explicitly enable ADM and declare whether your app requires ADM (android:required="true") or can work without ADM being present (android:required="false"). If you specify android:required="false", your app must degrade gracefully if ADM is unavailable. In the application node of the manifest, add the uses-library element.

    ...
    <application
          android:icon="@drawable/ic_launcher"
          android:label="@string/app_name"
          android:theme="@style/AppTheme">
    
          <!-- You must explicitly enable ADM and declare whether your app cannot work without
             ADM (android:required="true") or can work without ADM (android:required="false").
             If you specify android:required="false", your app must degrade gracefully if ADM
             is unavailable. -->
         <uses-library
            android:name="com.amazon.device.messaging"
         android:required="true"/>
    ...
    
  4. Declare a broadcast receiver to handle the REGISTRATION and RECEIVE intents that ADM sends. ADM requires that the receiver be defined in the AndroidManifest.xml file, rather than programmatically. Immediately after uses-library add the following elements.

     <!-- You must replace the names in the service and receiver tags
         with names that are appropriate to your package. -->
    
     <service
         android:name="[YOUR JOBSERVICE NAME]"
         android:permission="android.permission.BIND_JOB_SERVICE"
         android:exported="false" />
    
     <!-- This is needed for devices with older ADM versions -->
     <service
         android:name="[YOUR SERVICE NAME]"
         android:exported="false" />
    
     <receiver
         android:name="[YOUR RECEIVER NAME]"
    
         <!-- This permission ensures that only ADM can send your app registration broadcasts. -->
         android:permission="com.amazon.device.messaging.permission.SEND" >
    
         <!-- To interact with ADM, your app must listen for the following intents. -->
         <intent-filter>
            <action android:name="com.amazon.device.messaging.intent.REGISTRATION" />
            <action android:name="com.amazon.device.messaging.intent.RECEIVE" />
    
            <!-- Replace the name in the category tag with your app's package name. -->
            <category android:name="[YOUR PACKAGE NAME]" />
         </intent-filter>
     </receiver>
    
  5. After updating your AndroidManifest.xml file, you can confirm that the changes are correct for ADM by calling ADMManifest.checkManifestAuthoredProperly().

    If you receive a java.lang.NoClassDefFoundError: com.amazon.device.messaging.ADM error, check your manifest to verify that you have added the uses-library element in the location specified in step 3.

The following code is a complete example of an AndroidManifest.xml file enabled for ADM.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:amazon="http://schemas.amazon.com/apk/res/android"
   package="[YOUR PACKAGE NAME]"
   android:versionCode="1"
   android:versionName="1.0" >

   <uses-sdk
      android:minSdkVersion="15"
      android:targetSdkVersion="15" />

   <permission
      android:name="[YOUR PACKAGE NAME].permission.RECEIVE_ADM_MESSAGE"
      android:protectionLevel="signature" />
   <uses-permission android:name="[YOUR PACKAGE NAME].permission.RECEIVE_ADM_MESSAGE" />

   <uses-permission android:name="com.amazon.device.messaging.permission.RECEIVE" />

   <uses-permission android:name="android.permission.WAKE_LOCK" />

   <application
      android:icon="@drawable/ic_launcher"
      android:label="@string/app_name"
      android:theme="@style/AppTheme" >

      <uses-library
         android:name="com.amazon.device.messaging"
         android:required="true"/>

      <service
         android:name="[YOUR JOBSERVICE NAME]"
         android:permission="android.permission.BIND_JOB_SERVICE"
         android:exported="false" />

      <!-- This is needed for devices with older ADM versions -->
      <service
         android:name="[YOUR SERVICE NAME]"
         android:exported="false" />

      <receiver
         android:name="[YOUR RECEIVER NAME]"
         android:permission="com.amazon.device.messaging.permission.SEND" >

         <intent-filter>
         <action android:name="com.amazon.device.messaging.intent.REGISTRATION" />
         <action android:name="com.amazon.device.messaging.intent.RECEIVE" />

         <category android:name="[YOUR PACKAGE NAME]" />
         </intent-filter>

      </receiver>
   </application>
</manifest>

Store your API key as an asset

To enable your app to receive messages, your app must contain a valid API key. Obtain Credentials describes the process of creating an API key, and Overview of ADM discusses the API key in more detail.

For a pre-release or "debug" version of your app, you must create an API key and store it in your project. To add the API key to your app:

  1. Create a file called api_key.txt located inside your project's assets folder. Placing the file in this specific directory is required.
  2. Insert your API key as the only data in this api_key.txt file.

For a release or "production" version of your app, if your app uses the Appstore SDK, you must create an additional API key for the release version of your app. If using the older IAP SDK v2.0 and you sign your app using your own certificate, you must also create an API key for the release version of your app. In contrast, if using the IAP SDK v2.0, or no in-app purchasing SDK at all, and you allow Amazon to sign your app on your behalf, you do not need to create an additional API key. For a summary, see the following table.

Determine if you're required to create an API key for your release app.
App uses the Appstore SDK App signed with your own certificate API key required?
Yes
  Yes
  Yes
    No

Integrate ADM into your app's main activity

The following code shows how to use the methods in the com.amazon.device.messaging.ADM class to:

  • Create an instance of an ADM context
  • Initiate registration with ADM for this instance of your app
final ADM adm = new ADM(this);
if (adm.getRegistrationId() == null)
{
   // startRegister() is asynchronous; your app is notified via the
   // onRegistered() callback when the registration ID is available.
   adm.startRegister();
}

You should run this code each time your app starts, for example, by placing it within your onCreate() method. If your app is already registered, getRegistrationId() returns the registration ID for this instance of the app, and startRegister() isn't called. If this is the first startup of the app on a given device or if a previous registration failed, this code begins the registration process.

Implement handling for registration and messages

ADM has three classes that you must extend. Refer to the following table to understand the function of each. You can also review the ADM API reference.

Class Description
com.amazon.device.messaging.ADMMessageHandlerJobBase For handling messages on the latest Fire devices
com.amazon.device.messaging.ADMMessageHandlerBase For handling messages on older Fire devices
com.amazon.device.messaging.ADMMessageReceiver For forwarding messages to the appropriate message handling class

The broadcast receiver declared in your manifest both listens for intents and invokes your app when intents arrive. Your app communicates with the broadcast receiver via the following callback methods defined in the com.amazon.device.messaging.ADMMessageHandlerJobBase and com.amazon.device.messaging.ADMMessageHandlerBase classes:

Callback Method Description
onRegistered Called when the registration ID for the app instance is ready. Your app must transmit this registration ID to your server. Doing so enables your server to send messages to the app instance.
onUnregistered Called if your app instance becomes unregistered from ADM.
onRegistrationError Called if your app's ADM registration request fails for any reason, such as no Amazon user being signed in to the device.
onMessage Called when the ADM client delivers a message to your app instance.

Your app must override these callbacks when you implement subclasses of com.amazon.device.messaging.ADMMessageHandlerJobBase, com.amazon.device.messaging.ADMMessageHandlerBase and com.amazon.device.messaging.ADMMessageReceiver, as shown in the following code examples:

  1. Check if the current device has been updated to support new ADM service.

     ADMLatestAvailable = false ;
     try{
         Class.forName( "com.amazon.device.messaging.ADMMessageHandlerJobBase" );
         ADMLatestAvailable = true ;
     }
     catch (ClassNotFoundException e)
     {
         // Handle the exception.
     }
    
  2. Create your receiver and service implementations.

     public class Receiver extends ADMMessageReceiver
     {   
         public Receiver()
         {   // This is needed for backward compatibility
            super(MyADMLegacyMessageHandler.class);
            // Where available, prefer using the new job based
            if (ADMLatestAvailable) {
                registerJobServiceClass(MyADMMessageHandler.class, <JOB_ID>)
            }
         }
         // Nothing else is required here; your broadcast receiver automatically
         // forwards intents to your service for processing.
     }
    
     public class MyADMMessageHandler extends ADMMessageHandlerJobBase
     {
    
         @Override
         protected void onRegistered(final Context context, final String newRegistrationId)
         {
             // You start the registration process by calling startRegister() in your Main
             // Activity. When the registration ID is ready, ADM calls onRegistered() on
             // your app. Transmit the passed-in registration ID to your server, so your
             // server can send messages to this app instance. onRegistered() is also
             // called if your registration ID is rotated or changed for any reason; your
             // app should pass the new registration ID to your server if this occurs.
             // Your server needs to be able to handle a registration ID up to 1536 characters
             // in length.
    
             // The following is an example of sending the registration ID to your
             // server via a header key/value pair over HTTP.
             URL url = new URL(YOUR_WEBSERVER_URL);
             HttpURLConnection con = (HttpURLConnection) url.openConnection();
             con.setDoInput(true);
             con.setUseCaches(false);
             con.setRequestMethod("POST");
             con.setRequestProperty("RegistrationId", newRegistrationId);
             con.getResponse();
         }
    
         @Override
         protected void onUnregistered(final Context context, final String registrationId)
         {
             // If your app is unregistered on this device, inform your server that
             // this app instance is no longer a valid target for messages.
         }
    
         @Override
         protected void onRegistrationError(final Context context, final String errorId)
         {
             // You should consider a registration error fatal. In response, your app may
             // degrade gracefully, or you may wish to notify the user that this part of
             // your app's functionality is not available.
         }
    
         @Override
         protected void onMessage(final Context context, final Intent intent)
         {
             // Extract the message content from the set of extras attached to
             // the com.amazon.device.messaging.intent.RECEIVE intent.
    
             // Create strings to access the message and timeStamp fields from the JSON data.
             final String msgKey = getString(R.string.json_data_msg_key);
             final String timeKey = getString(R.string.json_data_time_key);
    
             // Obtain the intent action that will be triggered in onMessage() callback.
             final String intentAction = getString(R.string.intent_msg_action);
    
             // Obtain the extras that were included in the intent.
             final Bundle extras = intent.getExtras();
    
             // Extract the message and time from the extras in the intent.
             // ADM makes no guarantees about delivery or the order of messages.
             // Due to varying network conditions, messages may be delivered more than once.
             // Your app must be able to handle instances of duplicate messages.
             final String msg = extras.getString(msgKey);
             final String time = extras.getString(timeKey);
         }
     }
    

public class MyADMLegacyMessageHandler extends ADMMessageHandlerBase
{

    @Override
    protected void onRegistered(final String newRegistrationId)
    {
        // You start the registration process by calling startRegister() in your Main
        // Activity. When the registration ID is ready, ADM calls onRegistered() on
        // your app. Transmit the passed-in registration ID to your server, so your
        // server can send messages to this app instance. onRegistered() is also
        // called if your registration ID is rotated or changed for any reason; your
        // app should pass the new registration ID to your server if this occurs.
        // Your server needs to be able to handle a registration ID up to 1536 characters
        // in length.

        // The following is an example of sending the registration ID to your
        // server via a header key/value pair over HTTP.
        URL url = new URL(YOUR_WEBSERVER_URL);
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        con.setDoInput(true);
        con.setUseCaches(false);
        con.setRequestMethod("POST");
        con.setRequestProperty("RegistrationId", newRegistrationId);
        con.getResponse();
    }

    @Override
    protected void onUnregistered(final String registrationId)
    {
        // If your app is unregistered on this device, inform your server that
        // this app instance is no longer a valid target for messages.
    }

    @Override
    protected void onRegistrationError(final String errorId)
    {
        // You should consider a registration error fatal. In response, your app may
        // degrade gracefully, or you may wish to notify the user that this part of
        // your app's functionality is not available.
    }

    @Override
    protected void onMessage(final Intent intent)
    {
        // Extract the message content from the set of extras attached to
        // the com.amazon.device.messaging.intent.RECEIVE intent.

        // Create strings to access the message and timeStamp fields from the JSON data.
        final String msgKey = getString(R.string.json_data_msg_key);
        final String timeKey = getString(R.string.json_data_time_key);

        // Obtain the intent action that will be triggered in onMessage() callback.
        final String intentAction = getString(R.string.intent_msg_action);

        // Obtain the extras that were included in the intent.
        final Bundle extras = intent.getExtras();

        // Extract the message and time from the extras in the intent.
        // ADM makes no guarantees about delivery or the order of messages.
        // Due to varying network conditions, messages may be delivered more than once.
        // Your app must be able to handle instances of duplicate messages.
        final String msg = extras.getString(msgKey);
        final String time = extras.getString(timeKey);
    }
}

Gracefully degrade if ADM is unavailable

In your manifest file, you declare whether your app can (android:required="false") or cannot (android:required="true") work without ADM. If you specify android:required="false", your app must degrade gracefully if ADM is unavailable.

Designing your app to accommodate the absence of ADM allows you to build a single APK that can be installed and run on devices that may or may not include ADM.

To modify your app to gracefully degrade:

  1. Use code similar to the following to check for ADM.

    ADMAvailable = false ;
    try
    {
        Class.forName( "com.amazon.device.messaging.ADM" );
        ADMAvailable = true ;
    }
    catch (ClassNotFoundException e)
    {
        // Handle the exception.
    }
    
  2. Add the following code to any of your code that requires the ADM library runtime.

    if (ADMAvailable)
    {
        // Your code that requires ADM goes here.
    }
    
  3. In your AndroidManifest.xml file, verify that the application element specifies:

    uses-library android:name="com.amazon.device.messaging" android:required="false"
    

Test ADM integration

You can confirm that your app is set up with ADM by sending a test notification to a device. See Send a Message for more information.


Last updated: Oct 30, 2024