CalAmp Mobile Framework Messaging API

Introduction

This document specifies the CalAmp Mobile Framework (CMF) Messaging API for the Android MDT.

Working examples can be found in CMFApiTest application which comes as part of CMF SDK distribution. The examples in this document are derived from the same.

The CalAmp Mobile Framework Messaging API is a Java layer to interface an Android application with an LMU and back-end server.

Description

Sequence Diagram

Communications between a user application (APP) and the CMF API are based on the Android Intent objects

752752

When the application needs to communicate with the LMU or send an LM Direct message to the outside world, it creates an Intent containing the data and broadcasts it. On reception of the Intent the API extracts the data and passes it to the LM Direct Stack for transmission.

When the API receives a message from the LM Direct Stack, it extracts the data and packs it into an Intent and broadcasts it to the applications with a suitable broadcast receivers. On reception of the intent, the applications can extract and process the data.

Instances of the LMD stack are available for communications with the:

  • LMU
  • back-end (inbound) server

API Library

The classes and actions required to generate the intents used for communication with CMF are available in a Java library. Using the library will make it easier to create applications that use the CMF facilities. To use the library copy the JAR file provided by CalAmp to the “libs” directory in your Android application.

The following classes are provided:

ClassUse
CmfLmuConfigParamReading and writing LMU configuration parameters
CmfLmuStatusGetting the LMU environmental status e.g. GPS position
CmfMsgSending and receiving LM Direct User messages
CmfPegActionGenerating PEG actions at the LMU
CmfPegTriggerGenerating MDT PEG triggers at the LMU
CmfTypesUseful constants and enumerations for generating CMF API intents
CmfLmuEventThe contents of a Mini Event Message (Message Type 10) received from the LMU as a result of PEG action 131
CmfConnectionStatusThe status of the connection between MDT and LMU, and the LMU’s wireless modem communications status and state
CmfLmuConnectionStatusLMU’s wireless modem carrier and RSSI
CmfLmuCommStateThe state of the LMU’s wireless modem
CmfNavigationInfoThe information on Navigation capabilities
CmfAppMsgReceiving LM Direct Application messages

Channels

The CMF API can be used to send and receive messages to servers on the Internet as well as the LMU. There may be alternative paths available to a server, for example cellular. The details defining these destinations, such as their address or URL, port number and so on, are set up in a configuration file and assigned to channel enumerations. When an application wants to send to a server it specifies the corresponding channel in an Intent Extra.

The following channels are planned:

ChannelEnumerationDestination
LMU_CORE0LMU core
LMU_HA1LMU hosted application
PULS2PULS
BES_CELL3Back end server via cellular

Some API functions such as the PEG actions and triggers are only valid for one destination. Functions that are only valid for one destination will ignore any channel Extra in the Intent.

Enumeration values for the channels are available in the CMF API library e.g. commsChannel.BES_CELL for channel 3.

Actions and Extras

CMF Intents filters and broadcast receivers need to be configured using CMF API actions. Intent actions are defined as ACTIONstring constants. To assign one of these actions to this attribute, prepend "com.calamp.mdt.cmf.action." to the string that follows ACTION. E.g. for ACTION_SEND_PEG_ACTION, use "com.calamp.mdt.cmf.action.SEND_PEG_ACTION" and for ACTION_SEND_TO_LMU, use "com.calamp.mdt.cmf.action.SEND_TO_LMU".

The following intent actions are defined:

ActionDescription
ACTION_SEND_PEG_ACTIONGenerate and send to the LMU a real time PEG action to be executed
ACTION_GENERATE_PEG_TRIGGERGenerate and send to the LMU an MDT specific PEG trigger
ACTION_UNACK_REQUEST_LMU_LOCATESingle request for a Locate Report from the LMU
ACTION_ACK_REQUEST_LMU_LOCATEKeep requesting a Locate Report from the LMU until an acknowledge is received.
ACTION_RECEIVED_LMU_LOCATEReceived the current status of the LMU in response to a request made with the ACTION_REQUEST_LMU_LOCATE action
ACTION_REQUEST_LMU_PARAMRequest to read a configuration parameter on the LMU
ACTION_RECEIVED_LMU_PARAMReceived a configuration parameter in response to a request made with the ACTION_REQUEST_LMU_PARAM action
ACTION_WRITE_LMU_PARAMWrite a configuration parameter to the LMU
ACTION_RECEIVED_LMU_EVENTReceived an event generated on the LMU by PEG action 131
ACTION_REQUEST_LMU_CONNECT_STATUSRequest for an LMU connection status
ACTION_RECEIVED_LMU_CONNECT_STATUSReceived an LMU connection status
ACTION_SEND_MSGSend an LM Direct user message
ACTION_RECEIVED_MSGReceived an LM Direct user message
ACTION_RECEIVED_APP_MSGReceived an LM Direct application message
ACTION_REQUEST_NAVIGATION_INFORequest information on Navigation capability
ACTION_RESPONSE_NAVIGATION_INFOResponse holding the information on Navigation capability

The action string constants for the actions above are available in the CMF API library. To use them you must import com.calamp.mdt.cmfapi.CmfTypes. Then, for example, for ACTION_SEND_PEG_ACTION the string constant is CmfTypes.ACTION_SEND_PEG_ACTION.

CMF Intents may contain additional information in form of extras. Intent extras are defined as EXTRAstring constants. To assign one of these extras to this attribute, prepend "com.calamp.mdt.cmf.extra." to the string that follows EXTRA. For example, for EXTRA_DATA_NAME, use "com.calamp.mdt.cmf.extra.DATA_NAME".

The following intent extras are defined:

ExtraDescription
EXTRA_DATA_NAMEA generic extra. The contents varies with the action.
EXTRA_CHANNELThe channel for the action destination

The extra string constants are also available in the CMF API library.

Refer to the specific Examples sections for the use of intent actions and extras.

FMP API Intents

There is a special case of intent that makes use of the Fleet Management Protocol Api library. Using these intents creates the action that is a mimic of the reception of a FMP message from a server or LMU. Currently the following actions use the FMP Api library to be executed:

ActionDescription
Action MDT RequestGenerates a MDT action request. The extra data is an enumeration that determines what MDT action is to be performed. This is a clone of the MDT receiving a unit request message from the LMU or back end server.

LMU API Intents

ACTION_SEND_PEG_ACTION

Generate and send to the LMU a Real Time PEG action to be executed. This works exactly the same as the unit request message action code 3 as documented in the LM-Direct manual.

Class Public Methods:
CmfPegAction() // the constructor
void setCode(byte actionCode )
setModifier(byte actionModifier )
byte getCode()
byte getModifier()

import com.calamp.mdt.cmfapi.CmfPegAction;
import com.calamp.mdt.cmfapi.CmfTypes;

// create a real time PEG action to restart timer 0
CmfPegAction txPegAction = new CmfPegAction();
// populate the peg action with data
byte actionCode = 13; //PEG action code to Start/Restart Timer
byte actionModifier = 0; //Timer 0
txPegAction.setCode(actionCode);
txPegAction.setModifier(actionModifier);
// create an intent
Intent intent = new Intent(CmfTypes.ACTION_SEND_PEG_ACTION);
// add the action to the intent
intent.putExtra(CmfTypes.EXTRA_DATA_NAME,txPegAction);
// broadcast the intent
sendBroadcast(intent);

ACTION_GENERATE_PEG_TRIGGER

Generate and send to the LMU an MDT specific PEG trigger. This is used to inform the PEG script that an event has occurred on the MDT that requires some form of action. The trigger modifier field in the transmission class can be used to identify 255 unique MDT triggers.

Class Public Methods:
CmfPegTrigger() // the constructor
void setModifier( byte triggerModifier )
byte getModifier()

import com.calamp.mdt.cmfapi.CmfPegTrigger;
import com.calamp.mdt.cmfapi.CmfTypes;

// create a MDT trigger
CmfPegTrigger txPegTrigger = new CmfPegTrigger();
byte triggerModifier = 0;
txPegTrigger.setModifier(triggerModifier);
//create intent, add data and broadcast
Intent intent = new Intent(CmfTypes.ACTION_GENERATE_PEG_TRIGGER);
intent.putExtra(CmfTypes.EXTRA_DATA_NAME,txPegTrigger);
sendBroadcast(intent);

ACTION_UNACK_REQUEST_LMU_LOCATE

Request a Locate Report from the LMU using an LM Direct Unacknowledged request. This works exactly the same as the unit request message action code 10 as documented in the LM-Direct manual. The status information returned in the Locate Report message is displayed in the table below.

Time of FixThe last known time of fix from the GPS satellites. This value is reported in seconds from Jan. 1, 1970
LatitudeThe latitude reading of the GPS receiver measured in degrees with a 1x10^-7 degree lsb, signed 2’s complement.
LongitudeThe longitude reading of the GPS receiver measured in degrees with a 1x10^-7 degree lsb, signed 2’s complement.
AltitudeThe altitude reading of the GPS receiver measured in centimeters above the WGS-84 Datum, signed 2’s complement.
SpeedThe speed as reported by the GPS receiver measured in centimeters per second
HeadingThe heading value reported in degrees from true North
SatellitesThe number of satellites used in the GPS solution
Fix StatusThe current fix status of the GPS receiver bitmapped as follows:
Bit 0 – Predicted: Bit is set when the position update has a horizontal position accuracy estimate that is less that the Horizontal Position Accuracy Threshold defined in S-Register 142 (and the threshold is non-zero).
Bit 1 – Differentially Corrected: This bit is set when WAAS DGPS is enabled (S-Register 139) and the position has been differentially corrected.
Bit 2 – Last Known: This bit is set when the current GPS fix is invalid but a previous fix’s value is available.
Bit 3 – Invalid Fix: This bit is set only after a power-up or reset before a valid fix is obtained.
Bit 4 – 2D Fix: This bit is set when 3 or fewer satellites are seen/used in the GPS fix. (i.e. with 3 satellites or less, an altitude value cannot be calculated)
Bit 5 – Historic: This bit is set when the message has been logged by the LMU.
Bit 6 – Invalid Time: This bit is set only after a power-up or reset before a valid time-sync has been obtained.
CarrierThe identifier of the Carrier/Operator the wireless modem is currently using
RSSIThe received signal strength of the wireless modem in dBm. This value is signed in a 2’s complement format.
Comm StateThe current state of the wireless modem bit mapped as follows
Bit 0 – Available
Bit 1 – Network Service
Bit 2 – Data Service
Bit 3 – Connected (PPP Session Up)
Bit 4 – Voice Call is Active
Bit 5 – Roaming
Bit 6 – 3G Network (ie UMTS)
Bit 7 – Not Used
HDOPThe GPS Horizontal Dilution of Precision - it is a unit-less value reported with a 0.1 lsb.
InputsThe current state of the inputs, bit mapped as follows:
Bit 0 – Ignition
Bit 1 – Input 1
Bit 2 – Input 2
Bit 3 – Input 3
Bit 4 – Input 4
Bit 5 – Input 5
Bit 6 – Input 6
Bit 7 – Input 7
Unit StatusStatus of key modules within the unit:
Bit 0 – HTTP OTA Update Status (0=Ok, 1=Error),
Bit 1 – GPS Antenna Status (0=OK, 1=Error)
Bit 2 – GPS Receiver Self-Test (0=OK, 1=Error)
Bit 3 – GPS Receiver Tracking (0=Yes, 1=No)
Bit 4 – Reserved, Currently Unused
Bit 5 – Reserved, Currently Unused
Bit 6 – Reserved, Currently Unused
Bit 7 – Unused
import com.calamp.mdt.cmfapi.CmfTypes;

// Request a Locate Report from the LMU.
//Create intent and broadcast. There is no data required to be sent with
//this request.
Intent intent = new Intent(CmfTypes.ACTION_UNACK_REQUEST_LMU_LOCATE);
sendBroadcast(intent);

ACTION_ACK_REQUEST_LMU_LOCATE

Request a Locate Report from the LMU using an LM Direct Acknowledged request. This is similar to the previous intent, except that the request will be retried if there is no response. The status information returned in the Locate Report message is the same as that returned for the unacknowledged request.

import com.calamp.mdt.cmfapi.CmfTypes;

Intent intent = new Intent(CmfTypes.ACTION_ACK_REQUEST_LMU_LOCATE);
sendBroadcast(intent);

ACTION_RECEIVED_LMU_LOCATE

Receives the current status of the LMU in response to a request made with the ACTION_UNACK_REQUEST_LMU_LOCATE action. This contains the same information as the Locate Report Message (Message Type 8) as documented in the LM-Direct manual.

Class Public Methods:
CmfLmuStatus() // the constructor
long getUpdateTime()
long getFixTime()
int getLatitude()
int getLongitude()
int getAltitude()
long getSpeed()
int getHeading()
int getSatellites()
byte getFixStatus()
int getCarrier()
int getRssi()
byte getCommState()
int getHdop()
byte getInputs()
byte getUnitStatus()

import com.calamp.mdt.cmfapi.CmfLmuStatus;
import com.calamp.mdt.cmfapi.CmfTypes;

//Create BroadcastReceiver to handle incoming LMU location status
private BroadcastReceiver lmuStatusReceiver = new BroadcastReceiver() {
            @Override
             public void onReceive(Context context, Intent rxIntent) {
                          Object data =
                                        rxIntent.getParcelableExtra(CmfTypes.EXTRA_DATA_NAME);
                           if(data instanceof CmfLmuStatus) {
                                        Log.d(TAG, "LMU Status received at lat: " +((CmfLmuStatus)data).getLatitude() + 
                                        " lon: " + ((CmfLmuStatus)data).getLongitude());
                           // processing of the received LMU Status data
                          // ...
               }
       }
};
registerReceiver(lmuStatusReceiver, 
              new IntentFilter(CmfTypes.ACTION_RECEIVED_LMU_LOCATE));

ACTION_REQUEST_LMU_PARAM

Request to read a configuration parameter on the LMU. The response with the LMU parameter value is returned via the ACTION_RECEIVED_LMU_PARAM action.

Class Public Methods:
CmfLmuConfigParam() // the constructor
void setId(int ID)
void setIndex(byte Index)
void setData(byte[] Data)
int getId()
byte getIndex()
byte[] getData()

import com.calamp.mdt.cmfapi.CmfLmuConfigParam;
import com.calamp.mdt.cmfapi.CmfTypes;

// Request the value of the LMU’s Accumulator 10
CmfLmuConfigParam txConfigParam = new CmfLmuConfigParam();
intparamID = 2560; //Config parameter ID of Accumulators
byte paramIdx = 10; //Config parameter index of Accumulator 10.
txConfigParam.setId(paramID);
txConfigParam.setIndex(paramIdx);
//create intent, add data and broadcast
Intent intent = new Intent(CmfTypes.ACTION_REQUEST_LMU_PARAM);
intent.putExtra(CmfTypes.EXTRA_DATA_NAME,txConfigParam);
sendBroadcast(intent);

ACTION_RECEIVED_LMU_PARAM

Response to a read configuration parameter request via the ACTION_REQUEST_LMU_PARAM action.

import com.calamp.mdt.cmfapi.CmfLmuConfigParam;
import com.calamp.mdt.cmfapi.CmfTypes;

//Create BroadcastReceiver to handle incoming LMU status
private BroadcastReceiver lmuParamReceiver = new BroadcastReceiver() {
     @Override
     public void onReceive(Context context, Intent rxIntent) {
          Object data = 
               rxIntent.getParcelableExtra(CmfTypesEXTRA_DATA_NAME);
          if(data instanceof CmfLmuConfigParam) {
               Log.d(TAG, "LMU Config Param received id: " + 
                    ((CmfLmuConfigParam)data).getID() + " index: " + 
                    ((CmfLmuConfigParam)data).getIndex());
               // processing of the LMU Configuration Parameter
               // information ...
          }
     }
};
registerReceiver(lmuParamReceiver, 
     new IntentFilter(CmfTypes.ACTION_RECEIVED_LMU_PARAM));

ACTION_WRITE_LMU_PARAM

Write a configuration parameter to the LMU.

import com.calamp.mdt.cmfapi.CmfLmuConfigParam;
import com.calamp.mdt.cmfapi.CmfTypes;

// Set the threshold of the LMU’s Accumulator 0 to 100
CmfLmuConfigParam txConfigParam = new CmfLmuConfigParam();
intparamID = 266; //parameter ID of Accumulators
byte paramIdx = 0; //parameter index of Accumulator 0.
byte[] paramValue = new byte[] { 0, 0, 0, 0x64 };
txConfigParam.setId(paramID);
txConfigParam.setIndex(paramIdx);
txConfigParam.setData(paramValue);
//create intent, add data and broadcast
Intent intent = new Intent(CmfTypes.ACTION_WRITE_LMU_PARAM);
intent.putExtra(CmfTypes.EXTRA_DATA_NAME,txConfigParam);
sendBroadcast(intent);

ACTION_WRITE_LMU_PARAM_W_UPDATE_MSG

Write a configuration parameter to the LMU along with update messages.

import com.calamp.mdt.cmfapi.CmfLmuConfigParam;
import com.calamp.mdt.cmfapi.CmfTypes;

// Set the threshold of the LMU’s Accumulator 0 to 100
CmfLmuConfigParam txConfigParam = new CmfLmuConfigParam();
intparamID = 266; //parameter ID of Accumulators
byte paramIdx = 0; //parameter index of Accumulator 0.
byte[] paramValue = new byte[] { 0, 0, 0, 0x64 };
txConfigParam.setId(paramID);
txConfigParam.setIndex(paramIdx);
txConfigParam.setData(paramValue);
//create intent, add data and broadcast
Intent intent = new Intent(CmfTypes.ACTION_WRITE_LMU_PARAM_W_UPDATE_MSG);
intent.putExtra(CmfTypes.EXTRA_DATA_NAME,txConfigParam);
sendBroadcast(intent);

ACTION_RECEIVED_LMU_EVENT

Receives the last event generated on the LMU by PEG action 131. This contains the same information as the Mini Event Message (Message Type 10) as documented in the LM-Direct manual.

Class Public Methods:
CmfLmuEvent() // the constructor
long getUpdateTime()
int getLatitude()
int getLongitude()
int getHeading()
int getSpeed()
byte getSatellites()
boolean isGpsFixTimeValid()
boolean isGpsFixValid()
boolean isGpsFixLastKnown()
boolean isGpsFixHistoric()
boolean isCommAvailable()
boolean isNetworkServiceAvailable()
boolean isDataServiceAvailable()
boolean isCommConnected()
boolean isVoiceCallOn()
boolean isRoamingOn()
boolean isGpsAntennaOperational()
boolean isGpsTrackingOn()
byte getInputs()
byte getEventCode()
byte getAccumCount()
byte getAccumFormat()
int[] getAccums()

📘

Note

The list of accumulators returned by the getAccums() method contains 32-bit values (int), which may need to be converted to some different data types, depending on the nature of the data stored in the accumulators.

import com.calamp.mdt.cmfapi.CmfLmuEvent;
import com.calamp.mdt.cmfapi.CmfTypes;

//Create BroadcastReceiver to handle incoming LMU event
private BroadcastReceiver lmuEventReceiver = new BroadcastReceiver() {
     @Override
     public void onReceive(Context context, Intent rxIntent) {
          Object data =
               rxIntent.getParcelableExtra(CmfTypes.EXTRA_DATA_NAME);
          if(data instanceof CmfLmuEvent) {
               Log.d(TAG, "LMU Event received at lat: " +
                    ((CmfLmuEvent)data).getLatitude() + " lon: " +
                    ((CmfLmuEvent)data).getLongitude());
               // processing of the received LMU Event data
               // ...
          }
     }
};
registerReceiver(lmuEventReceiver,
     new IntentFilter(CmfTypes.ACTION_RECEIVED_LMU_EVENT));

ACTION_REQUEST_LMU_CONNECT_STATUS

Request an LMU connection status. The response with the LMU connection status is returned via the ACTION_RECEIVED_LMU_CONNECT_STATUS action.

import com.calamp.mdt.cmfapi.CmfTypes;

sendBroadcast(new Intent(CmfTypes.ACTION_REQUEST_LMU_CONNECT_STATUS));

ACTION_RECEIVED_LMU_CONNECT_STATUS

Receives an LMU connection status.
LMU connection statuses are generated automatically each time the MDT (re)connects to or disconnects from an LMU, or in response to requests made using the ACTION_REQUEST_LMU_CONNECT_STATUS action.

Class Public Member and Method:
CmfConnectionStatus() // the constructor
CmfLmuConnectionStatus lmuConnectionStatus;
boolean isConnectedToLmu()

The CmfLmuConnectionStatus member class has one Public Member and three Methods:

CmfLmuCommState commState;
int getCarrier()
int getRssi()
String getLmuMobileId()

Finally, the CmfLmuCommState member class has the following Public Methods:

boolean isCommAvailable()
boolean isNetworkServiceAvailable()
boolean isDataServiceAvailable()
boolean isCommConnected()
boolean isVoiceCallOn()
boolean isRoamingOn()

📘

Note

The lmuConnectionStatus Public Member will be null when the MDT is not connected to an LMU (i.e. when isConnectedToLmu() returns false);

import com.calamp.mdt.cmfapi.CmfConnectionStatus
import com.calamp.mdt.cmfapi.CmfTypes;

//Create BroadcastReceiver to handle incoming LMU connection status
private BroadcastReceiver lmuConnectStatusRcvr = new BroadcastReceiver() {
     @Override
     public void onReceive(Context context, Intent rxIntent) {
          Object data =
               rxIntent.getParcelableExtra(CmfTypes.EXTRA_DATA_NAME);
          if(data instanceof CmfConnectionStatus) {
            CmfConnectionStatus cs = (CmfConnectionStatus)data;
            Log.d(TAG, "isConnectedToLmu: " + cs.isConnectedToLmu());
            if(cs.isConnectedToLmu()) {
               Log.d(TAG, "getCarrier: " +
                    cs.lmuConnectionStatus.getCarrier());
               Log.d(TAG, "getRssi: " +
                    cs.lmuConnectionStatus.getRssi());
               Log.d(TAG, "isCommConnected: " +
                    cs.lmuConnectionStatus.commState.isCommConnected());
               // processing of the received LMU Event data
               // ...
          }
     }
};
registerReceiver(lmuConnectStatusRcvr,
     new IntentFilter(CmfTypes. ACTION_RECEIVED_LMU_CONNECT_STATUS));

ACTION_LMU_SEND_UNIT_REQUEST

Generate a Unit Request Message (Message Type 7).
Caller should make use of the following keys to add extras to this action.

KeyTabDescription
EXTRA_UNIT_REQUEST_ACTION_CODEbyte (Java)Key for adding an action code.
EXTRA_UNIT_REQUEST_DATAF_8byte (Java)Key for adding a Data8 field.
EXTRA_UNIT_REQUEST_DATAF_16int (Java)Key for adding a Data16 field.
EXTRA_UNIT_REQUEST_DATAF_32int (Java)Key for adding a Data32 field.
import com.calamp.mdt.cmfapi.CmfTypes;

// actionCode, d8Val, d16Val and d32Val are input arguments

// create a new intent and fill the extras into it
Intent intent = new Intent(CmfTypes.ACTION_LMU_SEND_UNIT_REQUEST);
intent.putExtra(CmfTypes.EXTRA_UNIT_REQUEST_ACTION_CODE, (byte)actionCode);
intent.putExtra(CmfTypes.EXTRA_UNIT_REQUEST_DATAF_8, (byte)d8Val);
intent.putExtra(CmfTypes.EXTRA_UNIT_REQUEST_DATAF_16, d16Val);
intent.putExtra(CmfTypes.EXTRA_UNIT_REQUEST_DATAF_32, d32Val);

// now broadcast the intent
sendBroadcast(intent);

ACTION_RECEIVED_LMU_OTHER_MSG

An Action that can be listened to in order to receive raw bytes of a LMD message.
This Action is broadcast only for the message types that do not get handled by any other CMF API Intent. Caller can invoke getByteArrayExtra(EXTRA_RECEIVED_LMD_OTHER_MSG) on the received Intent to extract the raw bytes of LMD message.
Where EXTRA_RECEIVED_LMD_OTHER_MSG is the key to retrieve extra.

Please refer to CMFApiTest application for sample code implementation. Please note that the EXTRA_RECEIVED_LMD_OTHER_MSG provides a fixed size buffer which can have a series of null characters appended to the bytes of actual message buffer and displayed in the CMFApiTest application.

import com.calamp.mdt.cmfapi.CmfTypes;

// create an instance of a Receiver and register to listen to this
// Intent
RawBytesReceiver rawBytesReceiver = new RawBytesReceiver();
     registerReceiver(rawBytesReceiver,
          new IntentFilter(CmfTypes.ACTION_RECEIVED_LMD_OTHER_MSG))
          
// Receiver implementation that listens to this Intent 
private class RawBytesReceiver extends BroadcastReceiver {

     @Override
     public void onReceive(Context context, Intent intent) {
          byte[] rawBytes = intent.getByteArrayExtra(
               CmfTypes.EXTRA_RECEIVED_LMD_OTHER_MSG));
          // consume the bytes here…
     }
   }
}

ACTION_REQUEST_MULTIPLE_LMU_PARAM

This is a request to read multiple configuration parameters from the LMU. The response with the LMU parameter values is returned via the ACTION_RECEIVE_MULTIPLE_LMU_PARAM action.

If there is only one parameter in this request then intents for both ACTION_RECEIVE_MULTIPLE_LMU_PARAM and ACTION_RECEIVED_LMU_PARAM will be generated when the read report is received. This is done to maintain backward compatibility with older applications listening for ACTION_RECEIVED_LMU_PARAM. Applications that implement ACTION_REQUEST_MULTIPLE_LMU_PARAM should be updated at the same time to only listen for ACTION_RECEIVE_MULTIPLE_LMU_PARAM.

This request is generated by supplying the set of parameters to be queried in the intent extra. The set must be a Java “TreeSet” collection of CmfLmuConfigParam objects.

Class Public Methods:
CmfLmuParams() // the constructor for the set of parameters
CmfLmuConfigParam() // the constructor for each parameter
void setId(int ID)
void setIndex(byte Index)
void setData(byte[] Data)
int getId()
byte getIndex()
byte[] getData()
void setParamSet(TreeSet params)
TreeSet getParamSet()

import com.calamp.mdt.cmfapi.CmfLmuConfigParam;
import com.calamp.mdt.cmfapi.CmfLmuParams;
import com.calamp.mdt.cmfapi.CmfTypes;
// Request the value of all the LMU’s Accumulators
TreeSet<CmfLmuConfigParam> params = new TreeSet<CmfLmuConfigParam>();
int id = 2560;
for(short i = 0; i <= 63; ++i) {
     Log.d(TAG, "read param " + id + ", index " + i);
     CmfLmuConfigParam param = new CmfLmuConfigParam();
     param.setID(id);
     param.setIndex((byte) i);
     params.add(param);
}
CmfLmuParams txParams = new CmfLmuParams();
txParams.setParamSet(params);
Intent intent = new Intent(CmfTypes.ACTION_REQUEST_MULTIPLE_LMU_PARAM);
intent.putExtra(CmfTypes.EXTRA_DATA_NAME, txParams);
getActivity().sendBroadcast(intent);

ACTION_RECEIVE_MULTIPLE_LMU_PARAM

This is the response to a read configuration parameters request via the ACTION_REQUEST_MULTIPLE_LMU_PARAM action.

The extra for the ACTION_RECEIVE_MULTIPLE_LMU_PARAM intent contains a set of parameters where each parameter is an instance of the CmfLmuConfigParam class. If an invalid parameter ID or index is given in the read request sent to the LMU then the data byte array will be set to null.

import com.calamp.mdt.cmfapi.CmfLmuConfigParam;
import com.calamp.mdt.cmfapi.CmfLmuParams;
import com.calamp.mdt.cmfapi.CmfTypes;
// Create a BroadcastReceiver to handle the incoming LMU Parameter read
// report
private BroadcastReceiver lmuParamsReceiver = new BroadcastReceiver() {
     @Override
     public void onReceive(Context context, Intent rxIntent) {
          Object data = null;
          
          try {
              data = rxIntent.getParcelableExtra(CmfTypes.EXTRA_DATA_NAME);
          } catch (BadParcelableException e) {
              Log.i(TAG, "encountered a bad parcel. unknown msg type.");
          }
          if (data != null && data instanceof CmfLmuParams) {
              Log.d(TAG, "LMU Params report received");
              CmfLmuParams msg = (CmfLmuParams) data;
              Set<CmfLmuConfigParam> params = msg.getParamSet();
              for(CmfLmuConfigParam param : params) {
                                    int id = param.getId();
                                    int index = param.getIndex() & 0xFF;
                                    byte[] value = param.getData();
                                    if (null == value) {
                                            if(0 == index) {
                                                        Log.d(TAG, "bad parameter: " + id);
                                            } else {
                                                        Log.d(TAG, "Parameter " + id + " bad index: "
                                                                    + index);
                                            }
                                    } else {
                                                StringBuilder sb = new StringBuilder();
                                                for (byte b : value) {
                                                        sb.append(String.format("%02X", b));
                                    }
                                                Log.d(TAG, "Parameter ID " + id + " index " + index
                                                        + " data: 0x" + sb.toString());
                                    }
                            }
                    }
            }
};
getActivity().registerReceiver(lmuParamsReceiver,
                        new IntentFilter(CmfTypes.ACTION_RECEIVE_MULTIPLE_LMU_PARAM));

Server API Intents

These API Intents make it possible to exchange LM Direct User messages between the MDT and the Back End Server or other servers over the cellular channel, or with the LMU hosted application.

ACTION_SEND_MSG

Send an LM Direct User message. The channel is used to set the destination and path.
Class Public Methods:
CmfMsg() // the constructor
void setId(int ID)
void setData(byte[] Data)
int getId()
byte[] getData()

setId() and getId() are used to set and read the LM Direct User message ID. This field is optional and is not used by CMF. It could be used to select the application when there are multiple applications using these APIs.

import com.calamp.mdt.cmfapi.CmfMsg;
import com.calamp.mdt.cmfapi.CmfTypes;
import com.calamp.mdt.cmfapi.CmfTypes.commsChannel;

// create a message
CmfMsg txMsg = CmfMsg();
// populate the message
byte id = 0;
byte[] data = “Hello world”.getBytes();
byte channel = commsChannel.BES_CELL.ChannelByte();
txMsg.setId(id);
txMsg.setData(data);
// create an intent
Intent intent = new Intent(CmfTypes.ACTION_SEND_MSG);
// add the message to the intent
intent.putExtra(CmfTypes.EXTRA_DATA_NAME, txMsg);
// add the channel to the intent
intent.putExtra(CmfTypes.EXTRA_CHANNEL, channel);
// broadcast the intent
sendBroadcast(intent);

ACTION_RECEIVED_MSG

Received an LM Direct User message. The channel gives the source of the message.

private BroadcastReceiver rxMsgReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent rxIntent) {
                        Object data =
                        rxIntent.getParcelableExtra(CmfTypes.EXTRA_DATA_NAME);
                        byte chan = rxIntent.getByteExtra(CmfTypes.EXTRA_CHANNEL, 
                        commsChannel.INVALID.ChannelByte());
                        if(data instanceof CmfMsg) {
                                    CmfMsg msg = (CmfMsg)data;
                                    Log.d(TAG, "rxMsgReceiver msg: " + msg.getData());
                                    Log.d(TAG, "CmfMsg channel: " + chan + " id: " + msg.getId());
                        // processing of the received CmfMsg
                        // ...
                    }
            }
};
registerReceiver(rxMsgReceiver, 
            new IntentFilter(CmfTypes.ACTION_RECEIVED_MSG));

ACTION_RECEIVED_APP_MSG

Received an LM Direct Application message.
Class Public Methods:
CmfAppMsg() // the constructor
CmfLmuStatus getLmuStatus()
int getType()
byte[] getData()

getLmuStatus() returns an object of type CmfLmuStatus (defined in point 3.5) containing the current status of the LMU, or null if no LMU status data has been included into the received message.
getType() returns the type of the received LM Direct Application message.

import com.calamp.mdt.cmfapi.CmfAppMsg;
import com.calamp.mdt.cmfapi.CmfLmuStatus;
import com.calamp.mdt.cmfapi.CmfTypes;

private BroadcastReceiver rxAppMsgReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent rxIntent) {
                        Object data = rxIntent.getParcelableExtra(CmfTypes.EXTRA_DATA_NAME);
                        if (data != null && data instanceof CmfAppMsg) {
                                    CmfAppMsg msg = (CmfAppMsg) data;
                                    Log.d(TAG, "rxAppMsgReceiver msg type: "
                                    + msg.getType());
                                    byte[] payload = msg.getData();
                                    Log.d(TAG, "payload length: " + payload.length);
                                    StringBuilder sb = new StringBuilder();
                                    for (byte b : payload) {
                                                sb.append(String.format("%02X", b));
                                    }
                                    Log.d(TAG, "payload in hex: " + sb);
                                    // processing of the received message payload
                                    // ...
                                    CmfLmuStatus status = msg.getLmuStatus();
                                    if(status != null) {
                                                Log.d(TAG, "fix time: " + status.getFixTime());
                                                Log.d(TAG, "latitude: " + status.getLatitude());
                                                Log.d(TAG, "longitude: " + status.getLongitude());
                                                // processing of the received CmfLmuStatus
                                                // ...
                                    }
                        }
            }
};
registerReceiver(rxAppMsgReceiver,
            new IntentFilter(CmfTypes.ACTION_RECEIVED_APP_MSG));

Local MDT Intents

These API Intents enable caller to retrieve information on the system local capabilities.

ACTION_REQUEST_NAVIGATION_INFO

Send a request to CMF for information on Navigation. The information is sent back via the ACTION_RESPONSE_NAVIGATION_NIFO.

import com.calamp.mdt.cmfapi.CmfTypes;
import com.calamp.mdt.cmfapi.CmfNavigationInfo;

// create an intent
Intent intent = new Intent(CmfTypes.ACTION_REQUEST_NAVIGATION_INFO);
// broadcast the intent
sendBroadcast(intent);

ACTION_RESPONSE_NAVIGATION_INFO

Received a response to Navigation information request. The information can be retrieved by using EXTRA_NAVIGATION_INFO extra.
Class Public Methods:
CmfNavigationInfo() // the constructor
boolean isNavigationAppInstalled()

isNavigationAppInstalled() is used to find if a navigation application is installed. A true value specifies presence of a navigation application (Magellan and Sygic applications are currently supported) and false otherwise.

import com.calamp.mdt.cmfapi.CmfTypes; 
import com.calamp.mdt.cmfapi.CmfNavigationInfo;

private BroadcastReceiver navigationInfoReceiver = new BroadcastReceiver() 
{
            @Override
            public void onReceive(Context context, Intent rxIntent) {
                        CmfNavigationInfo data = (CmfNavigationInfo)
                    rxIntent.getParcelableExtra(
                                CmfTypes.EXTRA_NAVIGATION_INFO);
                    
                        Log.d(TAG, “Navigation App installation status:” +
                                data.isNavigationAppInstalled());
            }
};


registerReceiver(navigationInfoReceiver, 
            new IntentFilter(CmfTypes.ACTION_RESPONSE_NAVIGATION_INFO));

ACTION_SET_BT_PEER

Send a request to set Bluetooth peer address and connect to it. The caller must put the address information into a EXTRA_BT_PEER_ADDRESS extra in the Intent.

import com.calamp.mdt.cmfapi.CmfTypes;

Intent intent = new Intent();
String address = …; // a valid BT device address

// set the action
intent.setAction(CmfTypes.ACTION_SET_BT_PEER);
// put device address as an extra. Type must be <code>String</code>
intent.putExtra(CmfTypes.EXTRA_BT_PEER_ADDRESS, address);

// send out the broadcast
getActivity().sendBroadcast(intent);

ACTION_QUERY_BT_PEER

Send a request to retrieve the existing Bluetooth peer address. The information is sent back via ACTION_RESPONSE_QUERY_BT_PEER information.

import com.calamp.mdt.cmfapi.CmfTypes;

Intent intent = new Intent();
// set the action
intent.setAction(CmfTypes.ACTION_QUERY_BT_PEER);

// send out the broadcast
getActivity().sendBroadcast(intent);

ACTION_REPONSE_QUERY_BT_PEER

Received a response to Bluetooth peer address query request. The address can be retrieved by using EXTRA_BT_PEER_ADDRESS extra.

import com.calamp.mdt.cmfapi.CmfTypes;

private final BroadcastReceiver peerUpdateEventReceiver = new BroadcastReceiver() 
{
        @Override
        public void onReceive(Context context, Intent intent) {
                if (intent.getAction()
                        .equals(CmfTypes.ACTION_REPONSE_QUERY_BT_PEER)) {
                        
            // peer address
                        String address = intent
                        .getStringExtra(CmfTypes.EXTRA_BT_PEER_ADDRESS);
                }
        }
};

ACTION_CONNECTION_INFO

Provides information on changes in state of connection between MDT and LMU.

Real-time information is provided using Android’s Broadcast which receivers can intercept using Action ACTION_CONNECTION_INFO. The receiver can query for type and state as per the following description.

Type information
Receivers can query for integer type extra using key EXTRA_CONNECTION_INFO_TYPE. The extra value can be one of the following:

Communication StatusInteger value
LMU_COMMUNICATION_STATE_DISCONNECTED1
LMU_COMMUNICATION_STATE_HW_PLUGGED2
LMU_COMMUNICATION_STATE_CONNECTED3
LMU_COMMUNICATION_STATE_HW_UNPLUGGED4

📘

Note

LMU_COMMUNICATION_STATE_HW_PLUGGED and LMU_COMMUNICATION_STATE_HW_UNPLUGGED are valid state specific to USB connection type.

import com.calamp.mdt.cmfapi.CmfTypes;

// register a receiver
IntentFilter intentFilter = new IntentFilter();
                intentFilter.addAction(CmfTypes.ACTION_CONNECTION_INFO);
registerReceiver(infoReceiver, intentFilter);

/**
    * Receiver that listens to connection info action.
    */
    private final BroadcastReceiver infoReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                    if (intent.getAction()
                            .equals(CmfTypes.ACTION_CONNECTION_INFO)) {
              
                            final int connState = intent
                                        .getIntExtra(CmfTypes.EXTRA_CONNECTION_INFO_STATUS, -1);

                            final int connType = intent
                                        .getIntExtra(CmfTypes.EXTRA_CONNECTION_INFO_TYPE, -1);
                            }
            }
};

ACTION_CHANGE_LED_TIMEOUT_AND_DEFAULT_STATE

The caller can set the default state of the LED on the MDT dock using this Action. The timeout sets how long any LED state lasts. The LED state is set by the ACTION_CHANGE_LED_STATE action documented in the next section. When the timeout expires the LED state returns to the LED state set in this action.

This action also restarts the timeout timer and cancels any repeating LED setting (see next section). The result is that if there are no LED commands issued after this action then after the timeout period the LED will be set to the default state defined in this action.

The LED actions described in this and the next section must not be broadcast too frequently because there is an impact on the performance of the dock serial link to the LMU. In fact there should be a minimum of 110ms between broadcasts of LED action intents. This is because each action results in two internal calls to the dock LED control, one for each LED, and each internal call is limited to once every 50ms, plus there is a 10ms gap between the two internal calls. Broadcasts made too frequently will result in some commands or parts of commands being discarded, and you may get a case where both LEDs are on and you have neither red nor green but a mix of both.

LED type information
Caller must specify the type of LED being targeted via EXTRA_LED_TYPE. The LED type must be one of the following:

LED typeInteger value
LED_TYPE_GREEN0
LED_TYPE_RED1

LED state information
Caller must specify the default LED state. The call to set the timeout and default LED state is ignored if this extra is not supplied. The state can be provided using EXTRA_LED_STATE and must be one of the following:

LED stateInteger value
LED_STATE_ON0
LED_STATE_OFF1
LED_STATE_BLINK2

Timeout
Optionally, caller can specify the timeout for LED state settings using EXTRA_LED_DEFAULT_TIMEOUT_S. This timeout is specified in seconds. A timeout value of zero means LED settings do not timeout. Zero is the default if the timeout extra is not added to the intent.

import com.calamp.mdt.cmfapi.CmfTypes;


Intent intent = new Intent(CmfTypes.ACTION_CHANGE_LED_TIMEOUT_AND_DEFAULT_STATE);

intent.putExtra(CmfTypes.EXTRA_LED_DEFAULT_TIMEOUT_S, timeoutSeconds);

// setting default to be Red LED on
intent.putExtra(CmfTypes.EXTRA_LED_STATE,
        CmfTypes.LEDState.LED_STATE_ON.enumeration());
intent.putExtra(CmfTypes.EXTRA_LED_TYPE,
        CmfTypes.LEDType.LED_TYPE_RED.enumeration());


sendBroadcast(intent);

Using adb to set timeout and default state
For provisioning the timeout and default state can be set using an adb command.
The following example shows how to set the timeout to 30 seconds, and the default state to be red LED on:

adb shell am broadcast -a com.calamp.mdt.cmf.action.CHANGE_LED_TIMEOUT_AND_DEFAULT_STATE ^
                                            --ei com.calamp.mdt.cmf.extra.LED_STATE 0 ^
                                            --ei com.calamp.mdt.cmf.extra.LED_TYPE 1 ^
                                            --ei com.calamp.mdt.cmf.extra.LED_DEFAULT_TIMEOUT_S 30

📘

Note

In the above example the caret (^) character is the command line continuation character and is not part of the adb command.

ACTION_CHANGE_LED_STATE

The caller can set the state of the LED on the MDT dock using this Action. The state lasts until the timeout period has expired. The LED returns to the default state when the timeout period expires. The timeout and default state are set using the ACTION_CHANGE_LED_TIMEOUT_AND_DEFAULT_STATE action.

The same restriction on the frequency of use of this action applies as for the ACTION_CHANGE_LED_TIMEOUT_AND_DEFAULT_STATE action. In summary you should not broadcast this intent more frequently than every 110ms. See the previous section for a more detailed explanation.

If the repeat time is non zero then the LED state is set at intervals set by the repeat time. Usually the repeat time will be a value that is less than the timeout period, so that the state will remain in effect until the MDT is powered off or removed from the dock.

The repeat time is not remembered when the MDT is restarted. If the MDT is powered off then the LED will revert to its default state, and a new action will need to be broadcast when the MDT is next powered on to change from the default state.

LED type information
Caller must specify the type of LED being targeted via EXTRA_LED_TYPE. The LED type must be one of the following:

LED typeInteger value
LED_TYPE_GREEN0
LED_TYPE_RED1

LED state information
Caller must specify a LED state for the LED state to change. The call to change LED state is ignored if this extra is not supplied. The state can be provided using EXTRA_LED_STATE and must be one of the following:

LED stateInteger value
LED_STATE_ON0
LED_STATE_OFF1
LED_STATE_BLINK2

Repeat time
Optionally, caller can specify a repeat time for the state change to be repeated using EXTRA_LED_REPEAT_TIME_S. This repeat time is specified in seconds. A repeat time of zero means that the LED state is only set once. The repeat time defaults to zero if this extra is not used.

import com.calamp.mdt.cmfapi.CmfTypes;


Intent intent = new Intent(CmfTypes.ACTION_CHANGE_LED_STATE);

intent.putExtra(CmfTypes.EXTRA_LED_REPEAT_TIME_S, repeatSeconds);

// switching on the Red LED
intent.putExtra(CmfTypes.EXTRA_LED_STATE,
        CmfTypes.LEDState.LED_STATE_ON.enumeration());
intent.putExtra(CmfTypes.EXTRA_LED_TYPE,
        CmfTypes.LEDType.LED_TYPE_RED.enumeration());
    
    
sendBroadcast(intent);

FMP ACTION MDT REQUEST

It is possible to generate a MDT request that will cause an action to be performed on the MDT. This intent duplicates the actions that can be performed with a FMP unit request message generated by the PEG script or the back end server. Please refer the Fleet Management Protocol document section Unit Request (25) for a description of the actions available to be performed.

Applications need to additionally include the FMPApi library to use this Api.

import com.calamp.mdt.fmpapi.FmpApiTypes;
import com.calamp.mdt.fmpapi.FmpUnitReq;

static int REBOOT_MDT = 0x14;

FmpUnitReq msg = new FmpUnitReq();
if(msg != null) {
        msg.setId(REBOOT_MDT);
        Intent intent = new Intent(FmpApiTypes.FMP_API_RX);
        intent.putExtra(FmpApiTypes.FMP_EXTRA, msg);
        getActivity().sendBroadcast(intent);
}

Appendix

CMF versions for each API

The following table displays the CMF version where each API was first supported.

APICMF version
Send PEG action1.1d
Generate PEG trigger1.1d
Unacknowledged request for LMU Locate report1.1d
Acknowledged request for LMU Locate report1.1d
Received LMU Locate report1.1d
Request LMU parameter1.1d
Received LMU parameter1.1d
Write LMU parameter1.1d
Received LMU Event report1.1e
Request LMU connection status1.1e
Received LMU connection status1.1e
Send message1.1d
Received message1.1d
Request Navigation information1.1f
Received Navigation information1.1f
Received Application message1.1g
Set BT peer address1.1h
Request BT peer address1.1h
Received BT peer address1.1h
Receive connection state information1.1h
Send LMU Unit Request2.0b
Received LMD other message2.0b
Change the state, default state, and timeout, of the dock LED2.0d
Request and receive multiple LMU parameters2.0d

Application Implementation Guidelines

The following sections give select recommendations on how applications should use CMF API’s .

Communicating to the LMU or a LM Direct server
It is advised to use the provided CMF API intents for all LMU and LM Direct server communication. CMF incorporates a non-volatile LM Direct message queue for acknowledged LM Direct messages. This queue ensures that message transmission is handled in a reliable way, and additionally limits the transmission rate to 5Hz (one message per 200ms). This rate limit ensures 32bit LMU products are able to correctly perform other tasks in addition to MDT communications. Utilising the CMF API intents also ensures that any unacknowledged LM Direct messages are inserted correctly into the message stream without causing corruption.

When generating acknowledged messages it is advised to limit the average throughput of LM Direct messages to 5Hz to ensure timely transmission of said messages. This throughput limit includes any queries of LMU status or connection, as these generate LM Direct messages. An application developer making use of CMF API’s can audit their usage by searching for ‘NVL: i enqueuer: j’ messages in the log cat debug.

To help manage the message queue size, an application should listen for the ‘ACTION_RECEIVED_LMU_CONNECT_STATUS’ broadcast intent. On receipt of this intent the LMU connection status can be queried by using the isConnectedToLmu() method. If the method returns false, the LMU has become disconnected. Applications should stop sending any time sensitive acknowledged messages to the LMU until the same method returns true.

An example of messages that should not be requested when the LMU is disconnected is found when using the intent ‘ACTION_ACK_REQUEST_LMU_LOCATE’. This intent generates a locate request message to retrieve the LMU status. When the LMU is disconnected from the MDT, these messages will be queued until it reconnects. These stale requests will then need to be de-queued and responded to before other, more current, message traffic can be processed. Instead it is advised to gate the broadcast of these intents, or other time sensitive LMU requests, until the ‘ACTION_RECEIVED_LMU_CONNECT_STATUS’ broadcast is received with the ‘isConnectedToLmu()’ method returning true . Applications can poll for this status with the broadcast ‘ACTION_REQUEST_LMU_CONNECT_STATUS’ safely without generating any queued messages when the LMU is disconnected.

Communication management
The CMF applications main task is LMU connection management. It is tasked with maintaining reliable and timely connection with an LMU and recovery of that connection should there be an error. Three areas where applications may want to interact with the connection management are:

  • Checking the LMU connection status
  • Determining which Bluetooth device to connect too
  • Back up recovery of the LMU connection should the CMF application fail for an unknown reason (Applications acting as an independent watchdog to the CMF application).

Checking the LMU connection status
The LMU connection status can be obtained from two separate CMF intents:

  • ‘ACTION_REQUEST_LMU_CONNECT_STATUS’ can be initiated at any time and generates a Locate request message to the LMU. The connection status is only returned via ‘ACTION_RECEIVED_LMU_CONNECT_STATUS’ once a locate report message is received from the LMU.
    ‘ACTION_RECEIVED_LMU_CONNECT_STATUS’ is also received unsolicited on any MDT-LMU connection change. In addition to the state of the LMU connection, this API gives LMU status information including on the cellular modem connection and GPS tracking.
    *‘ACTION_CONNECTION_INFO’ API is received unsolicited by an application on any connection change. An application must register to intercept this information. This API does not generate any LMU message traffic, and relies on a periodic echo request - response mechanism to determine LMU connectivity.

It is advised not to make frequent connectivity checks with ‘ACTION_REQUEST_LMU_CONNECT_STATUS’. Instead, applications should rely on receiving automatic ‘ACTION_CONNECTION_INFO’ or ‘ACTION_RECEIVED_LMU_CONNECT_STATUS’ notifications on LMU connection changes. This is especially important in applications where message traffic is a concern. Conservatively, message traffic is a concern when it is higher than three LM Direct messages per second.

Determining remote Bluetooth device for MDT connection
If CMF is configured to connect to an LMU via Bluetooth, it will automatically attempt to connect to the designated ‘peer’ Bluetooth device. If the ‘peer’ Bluetooth device is in range, the MDT will automatically connect. It is up to user applications to determine which device should be the ‘peer’ device and to manage the selection or changing of the peer device.

A suggested sequence for an application to manage LMU Bluetooth connection would be:

  1. Determine the need to find a ‘peer’ Bluetooth device. This could be because there is no peer device set (intent ‘ACTION_QUERY_RESPONSE_BT_PEER’ returns an empty (null) Bluetooth address); the peer device is not available (CMF fails to automatically connect within an acceptable timeframe); or a user requests to connect to a different LMU.

  2. Scan for available Bluetooth devices (See Android Bluetooth Guide).

  3. Determine which device to connect to based on the returned available Bluetooth device list.

  4. If the desired device is not already in the bonded list, bond with the selected device

  5. Set this device to be the peer device with CMF API ‘ACTION_SET_BT_PEER’.

  6. Confirm the peer device has been set correctly by sending API ‘ACTION_QUERY_BT_PEER’ and a checking the returned address from ‘ACTION_QUERY_RESPONSE_BT_PEER’.

  7. CMF will automatically connect to the peer device from this point whenever it is in range of the MDT.

Connection recovery
The CMF should be allowed to manage the LMU connection recovery. The Bluetooth and USB communication state machines within the Android OS are complex and interrupting these could result in much longer LMU reconnection times. The CMF provides two options for additional connection recovery, though both of these will be visible to the end user:

  • An LMU watchdog option that resets the MDT if communications with the LMU fail for a user configurable number of minutes. The watchdog can be enabled for Bluetooth and USB connections via a CMF configuration option within the cmf.cfg file. The watchdog will only be started if the MDT is charging on the docking station. This ensures that the MDT should be able to communicate with a nearby LMU. Please refer to CMF release notes for implementation details.
  • A CMF API ‘FMP ACTION MDT REQUEST’ that can reset the MDT. This API can be called by an application if a significant period has elapsed without the CMF connection API ‘ACTION_RECEIVED_LMU_CONNECT_STATUS‘ ever returning a connected response.