Authentication and connection

更新时间:
复制 MD 格式

A device must be authenticated before it can connect to IoT Platform. This topic describes how to initialize the Android Link SDK to connect a device to IoT Platform.

Prerequisites

Background information

The Android Link SDK supports device identity authentication using a device secret or ID²-based authentication.

  • Device secret:

    Authentication method

    Registration method

    Description

    One-device-one-secret

    Not applicable

    Each device is programmed with its own device certificate (ProductKey, DeviceName, and DeviceSecret).

    One-product-one-secret

    Pre-registration

    • Devices of the same product are programmed with the same product certificate (ProductKey and ProductSecret).

    • The Dynamic Registration feature must be enabled for the product.

    • The device obtains a DeviceSecret through dynamic registration.

    No pre-registration

    • Devices of the same product are programmed with the same product certificate (ProductKey and ProductSecret).

    • The Dynamic Registration feature must be enabled for the product.

    • The device obtains a combination of a ClientID and a DeviceToken through dynamic registration.

    Note

    For more information about the differences between pre-registration and no pre-registration for the one-product-one-secret method, see Differences between pre-registration and no pre-registration.

  • ID²-based authentication: ID² is a trusted identity for Internet of Things (IoT) devices. It provides security properties such as tamper resistance and anti-counterfeiting.

Note

For code implementation details, see the InitManager.java file in the demo.

One-device-one-secret

The following code provides an example of how to use the one-device-one-secret authentication method:

AppLog.setLevel(ALog.LEVEL_DEBUG);

final LinkKitInitParams params = new LinkKitInitParams();

String productKey = "${YourProductKey}";
String deviceName = "${YourDeviceName}";
String deviceSecret = "${YourDeviceSecret}";
String productSecret = "";

// Step 1: Construct the device certificate information.
DeviceInfo deviceInfo = new DeviceInfo();
deviceInfo.productKey = productKey;  // The product key.
deviceInfo.deviceName = deviceName;  // The device name.
deviceInfo.deviceSecret = deviceSecret;  // The device secret.
deviceInfo.productSecret = productSecret;  // The product secret.
params.deviceInfo = deviceInfo;

// Step 2: Set the global default domain name.
IoTApiClientConfig userData = new IoTApiClientConfig();
params.connectConfig = userData;

// Step 3: Cache the Thing Specification Language model.
Map<String, ValueWrapper> propertyValues = new HashMap<>();
/**
 * The data of the Thing Specification Language model is cached in this field. Do not delete this field or set it to null. Otherwise, features may become abnormal.
 * After a user calls the API to report Thing Specification Language model data, the data is cached.
 */
params.propertyValues = propertyValues;

// Step 4: Configure MQTT.
/**
 * Configure MQTT parameters, such as the endpoint. For more information, see the deviceinfo file description.
 * Parameters include the domain name, product key, and authentication security mode.
 */
IoTMqttClientConfig clientConfig = new IoTMqttClientConfig();
clientConfig.receiveOfflineMsg = false; // cleanSession=1. Do not accept offline messages.
// The MQTT endpoint information.
clientConfig.channelHost = "${YourMqttHostUrl}:8883";
params.mqttClientConfig = clientConfig;
// If the device frequently goes offline when the screen is off, see the "Set a custom heartbeat and resolve inaccurate heartbeats when the screen is off" section.

// Step 5: Configure advanced features. Except for the Thing Specification Language model, all other features are disabled by default.
IoTDMConfig ioTDMConfig = new IoTDMConfig();
// The Thing Specification Language model feature is enabled by default. onInitDone is returned only after the Thing Specification Language model is initialized, which includes requesting the model from the cloud.
ioTDMConfig.enableThingModel = true;
// The gateway feature is disabled by default. If you enable this feature, the gateway module is initialized during the initialization process to obtain the list of sub-devices from the cloud.
ioTDMConfig.enableGateway = false;
// Disabled by default. Specifies whether to enable the log push feature.
ioTDMConfig.enableLogPush = false;
params.ioTDMConfig = ioTDMConfig;

// Step 6: Configure the callback for processing mobile terminated messages.
LinkKit.getInstance().registerOnPushListener(new IConnectNotifyListener() {
    @Override
    public void onNotify(String s, String s1, AMessage aMessage) {
        // TODO: Process the callback for mobile terminated messages. For more information, see the documentation.
    }

    @Override
    public boolean shouldHandle(String s, String s1) {
        return true; // TODO: Configure this based on your needs. For more information, see the documentation.
    }

    @Override
    public void onConnectStateChange(String connectId, ConnectState connectState) {
        // The callback for connection state changes of the corresponding connection type. For specific connection states, see ConnectState in the SDK.
        AppLog.d(TAG, "onConnectStateChange() called with: connectId = [" + connectId + "], connectState = [" + connectState + "]");
    
        // The first connection to the cloud may fail. If the first connection fails, the SDK reports the ConnectState.CONNECTFAIL state. In this scenario, you can try to connect several times and then exit, or you can keep retrying until the connection is successful.
        // TODO: The following code provides a reference implementation for active retries when the first connection fails. You can uncomment the following code to enable it.
//      if(connectState == ConnectState.CONNECTFAIL){
//          try{
//              Thread.sleep(5000);
//              PersistentNet.getInstance().reconnect();
//          }catch (Exception e){
//              AppLog.d(TAG, "exception is " + e);
//          };
//          AppLog.d(TAG, "onConnectStateChange() try to reconnect when connect failed");
//      }
    
        // After the SDK successfully connects to the cloud, if the connection is interrupted due to network fluctuations, the SDK reports the ConnectState.DISCONNECTED state. In this case, the SDK automatically retries the connection. The retry interval increases from 1s, 2s, 4s, 8s, up to 128s. After reaching the maximum interval of 128s, the SDK continues to retry at 128s intervals until the connection is successful.
    }
});

// For devices that use the one-product-one-secret (no pre-registration) method, use the deviceToken and clientId for cloud connection.
// Step 7: Configure the one-product-one-secret (no pre-registration) method. This is disabled by default.
// MqttConfigure.deviceToken = DemoApplication.deviceToken;
// MqttConfigure.clientId = DemoApplication.clientId;

// Step 8: Configure H2 file upload.
/**
 * If you want to use HTTP/2 for file uploads, you must set the domain name. This is disabled by default.
 */
// IoTH2Config ioTH2Config = new IoTH2Config();
// ioTH2Config.clientId = "client-id";
// ioTH2Config.endPoint = "https://" + productKey + ioTH2Config.endPoint; // Production environment
// params.iotH2InitParams = ioTH2Config;

/**
 * Initialize the device connection.
 * onError: The initialization failed. If the initialization fails due to network issues, you must retry the initialization.
 * onInitDone: The initialization is successful.
 */
LinkKit.getInstance().init(getAppContext(), params, new ILinkKitConnectListener() {
    @Override
    public void onError(AError error) {
        ALog.d(TAG, "onError() called with: error = [" + (error) + "]");
    }

    @Override
    public void onInitDone(Object data) {
        ALog.d(TAG, "onInitDone() called with: data = [" + data + "]");
        // TODO: Start your own services.
    }
});

One-product-one-secret

The one-product-one-secret method, also known as dynamic registration, is used to obtain a device secret from IoT Platform. This method includes two modes: no pre-registration and pre-registration. Before you use this feature, make sure that:

  • You have created a product in the IoT Platform console and enabled dynamic registration.

  • In the deviceinfo file of the demo, the `deviceSecret` field is empty and the `productSecret` field is not empty.

  • Ensure that you have executed steps 1, 2, and 3 in the following sample code.

  • After dynamic registration succeeds or fails, disconnect the persistent connection that was used for the registration. For more information, see Step 4.

  • For device security, after you obtain a device secret using the one-product-one-secret authentication method, save the secret to the device for persistence. Then, use the one-device-one-secret procedure to connect to IoT Platform.

The following describes the differences between using pre-registration and not using pre-registration.

Difference

Pre-registration

No pre-registration

Communication protocol

MQTT, HTTPS

MQTT

Region support

  • Dynamic registration based on MQTT: all regions supported by IoT Platform.

  • Dynamic registration based on HTTPS: only the China (Shanghai) region. This method is not recommended and is not covered in this sample code.

China (Shanghai), China (Beijing)

Returned device secret

DeviceSecret. For information about how to use it, see Step 1 in the one-device-one-secret example.

The ClientID and DeviceToken of the device. Save them to the device for persistence so they can be used for cloud connection and other features. For information about how to use them, see Step 7 in the one-device-one-secret example.

Add device

You must pre-register the DeviceName in IoT Platform.

You do not need to pre-register the DeviceName in IoT Platform.

Usage limits

  • A device certificate can be used to activate only one physical device. If physical device A is activated with a DeviceName, but you need to use the same DeviceName for physical device B, you can delete device A in the IoT Platform console. This invalidates the DeviceSecret of device A. Then, you can add a new device with the original DeviceName to activate physical device B.

  • If a device needs to be reactivated because its DeviceSecret is lost, call the ResetThing API operation to reset the device status to inactive. Then, reconnect the device to the network for activation. In this case, the DeviceSecret issued by IoT Platform remains unchanged.

IoT Platform allows up to five physical devices to be activated with the same ProductKey, ProductSecret, and DeviceName. Different ClientIDs and DeviceTokens are issued for different physical devices.

Note

For code implementation details, see the DemoApplication.java file in the demo.

The following code provides an example:

String productKey = "${YourProductKey}";
String deviceName = "${YourDeviceName}";
String deviceSecret = "${YourDeviceSecret}";
String productSecret = "";
MqttInitParams initParams = new MqttInitParams(productKey, productSecret, deviceName, deviceSecret, MqttConfigure.MQTT_SECURE_MODE_TLS);

// Dynamic registration Step 1: Determine the one-product-one-secret type (no pre-registration or pre-registration).
// Case 1: If registerType is set to regnwl, it indicates that the device uses the one-product-one-secret (no pre-registration) method. This means you do not need to create a device.
// Case 2: If this field is empty or set to "register", it indicates that the device uses the one-product-one-secret (pre-registration) method. This means you need to create a device.
initParams.registerType = "";

// Dynamic registration Step 2: Set the endpoint for dynamic registration.
MqttConfigure.mqttHost = "${YourMqttHostUrl}:8883";;

// Dynamic registration Step 3: For Enterprise instances or public instances for which IoT Platform was activated on or after July 30, 2021.
// For public instances for which IoT Platform was activated before July 30, 2021, this field is an empty string "".
MqttConfigure.registerInstanceId = "${YourInstanceId}";

final Object lock = new Object();
LinkKit.getInstance().deviceDynamicRegister(this, initParams, new IOnCallListener() {
    @Override
    public void onSuccess(com.aliyun.alink.linksdk.channel.core.base.ARequest request, com.aliyun.alink.linksdk.channel.core.base.AResponse response) {
        ALog.i(TAG, "onSuccess() called with: request = [" + request + "], response = [" + response + "]");
        // response.data is byte array
        try {
            String responseData = new String((byte[]) response.data);
            JSONObject jsonObject = JSONObject.parseObject(responseData);
            String pk = jsonObject.getString("productKey");
            String dn = jsonObject.getString("deviceName");
            // Returned for one-product-one-secret (pre-registration).
            String deviceSecret = jsonObject.getString("deviceSecret");

            // Returned for one-product-one-secret (no pre-registration).
            String clientId = jsonObject.getString("clientId");
            String deviceToken = jsonObject.getString("deviceToken");

            // TODO: Save the user key. Do not perform cloud connection here. Perform the cloud connection operation only after Step 4 is complete (for example, in the onSuccess branch).

            // Allow the waiting API to continue.
            synchronized (lock){
                lock.notify();
            }

        } catch (Exception e) {
        }

    }

    @Override
    public void onFailed(com.aliyun.alink.linksdk.channel.core.base.ARequest request, com.aliyun.alink.linksdk.channel.core.base.AError error) {
        ALog.e(TAG, "onFailed() called with: request = [" + request + "], error = [" + error + "]");
        // Allow the waiting API to continue.
        synchronized (lock){
            lock.notify();
        }
    }

    @Override
    public boolean needUISafety() {
        return false;
    }
});

try{
    // Wait for the mobile terminated message. A reply is usually received within 1s.
    synchronized (lock){
        lock.wait(3000);
    }
    
    // Step 4: Exit dynamic registration.
    // Do not run the following function in the LinkKit.getInstance().deviceDynamicRegister callback. Otherwise, an error is reported.
    LinkKit.getInstance().stopDeviceDynamicRegister(10 * 1000, null, new IMqttActionListener() {
        @Override
        public void onSuccess(IMqttToken iMqttToken) {
            ALog.d(TAG, "onSuccess() called with: iMqttToken = [" + iMqttToken + "]");
            // TODO: Refer to the one-device-one-secret method to perform cloud connection and initialization here.
        }

        @Override
        public void onFailure(IMqttToken iMqttToken, Throwable throwable) {
            ALog.w(TAG, "onFailure() called with: iMqttToken = [" + iMqttToken + "], throwable = [" + throwable + "]");
        }
    });
}catch (Exception e){

};

ID²-based authentication

/**
 * If you create a device that uses iTLS authentication in IoT Platform, use this method for initialization.
 * The product requires ID² authorization.
 */
IoTMqttClientConfig clientConfig = new IoTMqttClientConfig(productKey, deviceName, deviceSecret);
clientConfig.channelHost = productKey + ".itls.cn-shanghai.aliyuncs.com:1883";
clientConfig.productSecret = productSecret;
clientConfig.secureMode = 8;
linkKitInitParams.mqttClientConfig = clientConfig;
// For Enterprise instances or public instances for which IoT Platform was activated on or after July 30, 2021, specify the instance ID as follows. Replace ${YourInstanceId} with the actual ID in the iot-******* format.
MqttConfigure.extraMqttClientIdItems=",instanceId=" + "${YourInstanceId}";   

In the sample code, the clientConfig.channelHost parameter is the device connection endpoint.

More settings

You can configure the following parameters for additional settings related to the device connection.

  • MQTT connection:

    Configuration item

    Description

    Related code

    Keepalive interval

    Sets the keepalive interval for the device. This setting allows the device to maintain a persistent connection with IoT Platform.

    Note
    • The default keepalive interval of the SDK is 65 seconds.

    • You can set the heartbeat interval to a value from 30 to 1,200 seconds.

    // The unit of interval is seconds.
    MqttConfigure.setKeepAliveInterval(int interval);

    QoS level

    Sets the Quality of Service (QoS) level. QoS is a protocol that ensures message delivery between IoT Platform and a device. Only the following levels are supported:

    • 0: At most once.

    • 1: At least once.

    MqttPublishRequest request = new MqttPublishRequest();
    // Supports 0 and 1. The default value is 0.
    request.qos = 0;
    request.isRPC = false;
    request.topic = topic.replace("request", "response");
    String resId = topic.substring(topic.indexOf("rrpc/request/")+13);
    request.msgId = resId;
    // TODO: Fill in this field based on your needs.
    request.payloadObj = "{\"id\":\"" + resId + "\", \"code\":\"200\"" + ",\"data\":{} }";

    Offline messages

    Use cleanSession to specify whether to receive offline messages.

    IoTMqttClientConfig clientConfig = new IoTMqttClientConfig();
    // receiveOfflineMsg = !cleanSession. By default, offline messages are not received. 
    clientConfig.receiveOfflineMsg = true;
     

    Client ID

    Use clientId to set the client ID. For more information about the client ID, see Client ID description.

    MqttConfigure.clientId = "abcdef******";

    Reconnection mechanism

    Use the automaticReconnect parameter to specify whether to reconnect.

    • true: Reconnect.

    • false: Do not reconnect.

    Parameter setting:

    MqttConfigure.automaticReconnect = true;
  • SDK deinitialization:

    To deinitialize the SDK, call the following synchronous API operation.

    Note

    Before you initialize the SDK, make sure that the previous deinitialization process is complete. Otherwise, the reinitialization fails.

    // Unregister notifyListener. The notifyListener object must be the same as the one used for registration.
    LinkKit.getInstance().unRegisterOnPushListener(notifyListener);
    LinkKit.getInstance().deinit();
  • Connection status and mobile terminated message listener:

    To listen for the online and offline status of the device and for all data sent from IoT Platform, configure the following listener.

    IConnectNotifyListener notifyListener = new IConnectNotifyListener() {
        @Override
        public void onNotify(String connectId, String topic, AMessage aMessage) {
            // Callback for mobile terminated data.
            // connectId: connection type. topic: mobile terminated topic. aMessage: mobile terminated data.
            // Data parsing is as follows:
            //String pushData = new String((byte[]) aMessage.data);
            // pushData example: {"method":"thing.service.test_service","id":"123374967","params":{"vv":60},"version":"1.0.0"}
            // method: service type. params: pushed data content.    
       }
        @Override
        public boolean shouldHandle(String connectId, String topic) {
            // Select whether to skip processing mobile terminated data for a specific topic.
            // If you skip a topic, onNotify will not receive mobile terminated data for that topic.
            return true; //TODO: Configure this based on your needs.
        }
        @Override
        public void onConnectStateChange(String connectId, ConnectState connectState) {
            // The callback for connection state changes of the corresponding connection type. For specific connection states, see ConnectState in the SDK.
            AppLog.d(TAG, "onConnectStateChange() called with: connectId = [" + connectId + "], connectState = [" + connectState + "]");
        
            // The first connection to the cloud may fail. If the first connection fails, the SDK reports the ConnectState.CONNECTFAIL state. In this scenario, you can try to connect several times and then exit, or you can keep retrying until the connection is successful.
            // TODO: The following code provides a reference implementation for active retries when the first connection fails. You can uncomment the following code to enable it.
    //      if(connectState == ConnectState.CONNECTFAIL){
    //          try{
    //              Thread.sleep(5000);
    //              PersistentNet.getInstance().reconnect();
    //          }catch (Exception e){
    //              AppLog.d(TAG, "exception is " + e);
    //          };
    //          AppLog.d(TAG, "onConnectStateChange() try to reconnect when connect failed");
    //      }
        
            // After the SDK successfully connects to the cloud, if the connection is interrupted due to network fluctuations, the SDK reports the ConnectState.DISCONNECTED state. In this case, the SDK automatically retries the connection. The retry interval increases from 1s, 2s, 4s, 8s, up to 128s. After reaching the maximum interval of 128s, the SDK continues to retry at 128s intervals until the connection is successful.
        }
    }
    // Register a listener for mobile terminated messages, including the persistent connection status and mobile terminated data.
    LinkKit.getInstance().registerOnPushListener(notifyListener);
    // Unregister the listener for mobile terminated messages. Make sure that the object is the same as the one used for registration.
    // LinkKit.getInstance().unRegisterOnPushListener(notifyListener);
                
    Note

    By default, the onNotify callback for mobile terminated data runs on the UI thread. Starting from version 1.7.3 of lp-iot-linkkit, you can use PersistentConnect.mNotifyReceivedMsgOnMainThread = false; to process mobile terminated messages on a non-UI thread. In scenarios with a high volume of mobile terminated messages or a busy UI thread, set this configuration item to false.

  • Log switch:

    You can turn on the internal log output switch of the SDK:

    PersistentNet.getInstance().openLog(true);
    ALog.setLevel(ALog.LEVEL_DEBUG);
       

    The Android SDK provides an interceptor that lets you rewrite the log function of the interceptor to implement custom log processing. For example, you can save logs to a specific file for persistence.

    The following code provides an example of log output:

            ALog.setLogDispatcher(new ILogDispatcher() {
                @Override
                public void log(int level, String prefix, String msg) {
                    switch (level){
                        case LEVEL_DEBUG:
                            System.out.println("debug:"+ prefix + msg);
                            break;
                        case LEVEL_INFO:
                            System.out.println("info:" + prefix + msg);
                            break;
                        case LEVEL_ERROR:
                            System.out.println("error:" + prefix + msg);
                            break;
                        case LEVEL_WARNING:
                            System.out.println("warnings:" + prefix + msg);
                            break;
                        default:
                            System.out.println("other:" + prefix + msg);
                    }
                }
            });
  • Retrieve the SDK version number:

    ALog.i(TAG, "sdk version = " + LinkKit.getInstance().getSDKVersion());

Fast reconnection for Android devices

After the Android SDK is disconnected, the default fast reconnection interval is 65 seconds. In some scenarios, you may need to reconnect more quickly after a disconnection. Refer to the following code:

LinkKit.getInstance().reconnect();

Set a custom heartbeat and resolve inaccurate heartbeats on battery power and when the screen is off

  • You can use the MqttPingSender interface to set a custom heartbeat time. For more information, see TimerPingSender. `TimerPingSender` lets you set the time for the next heartbeat and stop sending heartbeats.

    Note

    The heartbeat interval must be less than the keepalive time.

    The following code provides an example:

    // PrivateMqttPingSender implements the MqttPingSender interface.
    MqttConfigure.pingSender = new PrivateMqttPingSender();
  • For some battery-powered Android devices running Android 6 or later, the device may go offline when the screen is off because heartbeats are not sent on time. In this case, you can refer to the AlarmMqttPingSender interface to set a custom heartbeat time.

    Important
    • This method periodically wakes up the Android system, which causes additional power consumption.

    • For devices with Android API level >= 31:

      • You must add the following permission to the AndroidManifest.xml file:

        android.permission.SCHEDULE_EXACT_ALARM.

      • When calling the PendingIntent.getBroadcast API on line 67, consider setting the flag field (the last parameter) to FLAG_IMMUTABLE or FLAG_MUTABLE as needed.

    • When a device is on battery power and the screen is off, the network connection on devices running Android 6 or later may be automatically disconnected because of the Doze mechanism. For example, the system log may contain Netd : Destroyed 3 sockets for UidRanges. In this case, even if the timer is accurate, heartbeat messages cannot be sent correctly. Contact your Android system provider to resolve this issue.

Demo instructions

  • If device initialization fails, re-initialize the SDK. If the device disconnects from IoT Platform because of network issues or other reasons, the SDK automatically attempts to reconnect.

  • To quickly connect the device, modify the parameters in the ./app/src/main/res/raw/deviceinfo file of the Android Link SDK demo. Fill in the parameters marked as required in the following table and keep the default settings for optional parameters.

  • Parameter

    Description

    One-device-one-secret

    One-product-one-secret (pre-registration)

    One-product-one-secret (no pre-registration)

    productKey

    The identifier that IoT Platform issues to a product.

    Required

    Required

    Required

    deviceName

    The identifier of the device within the product.

    Required

    Required

    Required

    productSecret

    Product key

    No

    Required

    Required

    deviceSecret

    Device secret.

    Required

    No

    No

    registerType

    The one-product-one-secret method: pre-registration or no pre-registration.

    No

    No

    Required. Set the value to the string regnwl.

    instanceId

    The instance ID. This applies to Enterprise instances and new public instances. For more information, see Instance overview.

    No

    Required

    Required

    mqttHost

    The MQTT endpoint.

    Required (except for the China (Shanghai) region). You must modify the domain name and port number of the endpoint.

    • If you use a new public instance or an Enterprise instance, the instance details page in the console provides the port number. You can copy it directly. The format is as follows: {instanceid}.mqtt.iothub.aliyuncs.com:8883

    • If you use an old instance, the instance details page in the console does not provide the port number. Copy the endpoint and add the port number yourself. The reference format is as follows:

      {YourProductKey}.iot-as-mqtt.{region}.aliyuncs.com:8883For more information, see View instance endpoints.

FAQ

Questions about the Android Link SDK