Repository: eclipse/leshan
Pull request #908 (2020-10-12 16:28:17)

Update BindingMode to be LWM2M 1.1 compliant and support Q parameter. 
There is several changes in LWM2M 1.1 about binding mode : 
- new mode was added (T,N)
- QueueMode is not part of binding mode anymore but has it own parameter.

This PR aims to support new format, trying to keep compatible with LWM2M 1.0.

[Here](https://github.com/OpenMobileAlliance/OMA_LwM2M_for_Developers/issues/490#issuecomment-706995813) some discussion about LWM2M changes about Binding Mode.

(I will maybe add  test about that to this PR)

sbernard31: I read more about binding mode. I think this PR is not OK I need to change it.
sbernard31: This time I think it's OK. 
More changes about binding in LWM2M 1.1 than what I thought :sweat_smile:.

See : 
- https://github.com/OpenMobileAlliance/OMA_LwM2M_for_Developers/issues/505
- https://github.com/OpenMobileAlliance/OMA_LwM2M_for_Developers/issues/490
sbernard31: Need more work about bootstrap part ... :confounded: 

Patch:
diff --git a/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/JSONFileBootstrapStore.java b/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/JSONFileBootstrapStore.java
index c70492b3..91588644 100644
--- a/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/JSONFileBootstrapStore.java
+++ b/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/JSONFileBootstrapStore.java
@@ -22,16 +22,19 @@ import java.io.FileOutputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.lang.reflect.Type;
+import java.util.EnumSet;
 import java.util.Map;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
+import org.eclipse.leshan.core.request.BindingMode;
 import org.eclipse.leshan.core.util.Validate;
 import org.eclipse.leshan.server.bootstrap.BootstrapConfig;
 import org.eclipse.leshan.server.bootstrap.EditableBootstrapConfigStore;
 import org.eclipse.leshan.server.bootstrap.InMemoryBootstrapConfigStore;
 import org.eclipse.leshan.server.bootstrap.InvalidConfigurationException;
+import org.eclipse.leshan.server.bootstrap.demo.json.BindingModeTypeAdapter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -69,6 +72,9 @@ public class JSONFileBootstrapStore extends InMemoryBootstrapConfigStore {
         Validate.notEmpty(filename);
         GsonBuilder builder = new GsonBuilder();
         builder.setPrettyPrinting();
+        builder.registerTypeAdapter(new TypeToken<EnumSet<BindingMode>>() {
+        }.getType(), new BindingModeTypeAdapter());
+
         this.gson = builder.create();
         this.gsonType = new TypeToken<Map<String, BootstrapConfig>>() {
         }.getType();
diff --git a/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/json/BindingModeTypeAdapter.java b/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/json/BindingModeTypeAdapter.java
new file mode 100644
index 00000000..82f6aa8e
--- /dev/null
+++ b/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/json/BindingModeTypeAdapter.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Sierra Wireless and others.
+ * 
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ * 
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v20.html
+ * and the Eclipse Distribution License is available at
+ *    http://www.eclipse.org/org/documents/edl-v10.html.
+ * 
+ * Contributors:
+ *     Sierra Wireless - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.leshan.server.bootstrap.demo.json;
+
+import java.io.IOException;
+import java.util.EnumSet;
+
+import org.eclipse.leshan.core.request.BindingMode;
+
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+public class BindingModeTypeAdapter extends TypeAdapter<EnumSet<BindingMode>> {
+
+    @Override
+    public void write(JsonWriter out, EnumSet<BindingMode> value) throws IOException {
+        out.value(BindingMode.toString(value));
+    }
+
+    @Override
+    public EnumSet<BindingMode> read(JsonReader in) throws IOException {
+        return BindingMode.parse(in.nextString());
+    }
+}
diff --git a/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/servlet/BootstrapServlet.java b/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/servlet/BootstrapServlet.java
index e48c2f9a..a717d032 100644
--- a/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/servlet/BootstrapServlet.java
+++ b/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/servlet/BootstrapServlet.java
@@ -20,6 +20,7 @@ import java.io.IOException;
 import java.io.InputStreamReader;
 import java.lang.reflect.Type;
 import java.nio.charset.StandardCharsets;
+import java.util.EnumSet;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
@@ -27,9 +28,11 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.commons.lang.StringUtils;
+import org.eclipse.leshan.core.request.BindingMode;
 import org.eclipse.leshan.server.bootstrap.BootstrapConfig;
 import org.eclipse.leshan.server.bootstrap.EditableBootstrapConfigStore;
 import org.eclipse.leshan.server.bootstrap.InvalidConfigurationException;
+import org.eclipse.leshan.server.bootstrap.demo.json.BindingModeTypeAdapter;
 
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
@@ -41,6 +44,7 @@ import com.google.gson.JsonPrimitive;
 import com.google.gson.JsonSerializationContext;
 import com.google.gson.JsonSerializer;
 import com.google.gson.JsonSyntaxException;
+import com.google.gson.reflect.TypeToken;
 
 /**
  * Servlet for REST API in charge of adding bootstrap information to the bootstrap server.
@@ -70,8 +74,10 @@ public class BootstrapServlet extends HttpServlet {
     public BootstrapServlet(EditableBootstrapConfigStore bsStore) {
         this.bsStore = bsStore;
 
-        this.gson = new GsonBuilder().registerTypeHierarchyAdapter(Byte.class, new SignedByteUnsignedByteAdapter())
-                .create();
+        this.gson = new GsonBuilder()//
+                .registerTypeAdapter(new TypeToken<EnumSet<BindingMode>>() {
+                }.getType(), new BindingModeTypeAdapter()) //
+                .registerTypeHierarchyAdapter(Byte.class, new SignedByteUnsignedByteAdapter()).create();
     }
 
     @Override
diff --git a/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/LeshanClientBuilder.java b/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/LeshanClientBuilder.java
index 94860e8f..be06ab02 100644
--- a/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/LeshanClientBuilder.java
+++ b/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/LeshanClientBuilder.java
@@ -17,6 +17,7 @@ package org.eclipse.leshan.client.californium;
 
 import java.net.InetSocketAddress;
 import java.security.cert.Certificate;
+import java.util.EnumSet;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ScheduledExecutorService;
@@ -160,6 +161,7 @@ public class LeshanClientBuilder {
 
     /**
      * Set optional trust store for verifying X.509 server certificates.
+     * 
      * @param trustStore List of trusted CA certificates
      */
     public LeshanClientBuilder setTrustStore(List<Certificate> trustStore) {
@@ -207,6 +209,7 @@ public class LeshanClientBuilder {
 
     /**
      * Set the additionalAttributes for {@link BootstrapRequest}
+     * 
      * @since 1.1
      */
     public LeshanClientBuilder setBootstrapAdditionalAttributes(Map<String, String> additionalAttributes) {
@@ -253,8 +256,9 @@ public class LeshanClientBuilder {
             ObjectsInitializer initializer = new ObjectsInitializer();
             initializer.setInstancesForObject(LwM2mId.SECURITY,
                     Security.noSec("coap://leshan.eclipseprojects.io:5683", 12345));
-            initializer.setInstancesForObject(LwM2mId.SERVER, new Server(12345, 5 * 60, BindingMode.U, false));
-            initializer.setInstancesForObject(LwM2mId.DEVICE, new Device("Eclipse Leshan", "model12345", "12345", "U"));
+            initializer.setInstancesForObject(LwM2mId.SERVER, new Server(12345, 5 * 60));
+            initializer.setInstancesForObject(LwM2mId.DEVICE,
+                    new Device("Eclipse Leshan", "model12345", "12345", EnumSet.of(BindingMode.U)));
             objectEnablers = initializer.createAll();
         }
         if (encoder == null)
diff --git a/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/request/CoapRequestBuilder.java b/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/request/CoapRequestBuilder.java
index c1108de5..46ce6bdf 100644
--- a/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/request/CoapRequestBuilder.java
+++ b/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/request/CoapRequestBuilder.java
@@ -15,6 +15,7 @@
  *******************************************************************************/
 package org.eclipse.leshan.client.californium.request;
 
+import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.Map.Entry;
 
@@ -85,12 +86,20 @@ public class CoapRequestBuilder implements UplinkRequestVisitor {
         if (lwVersion != null)
             attributes.put("lwm2m", lwVersion);
 
-        BindingMode bindingMode = request.getBindingMode();
+        EnumSet<BindingMode> bindingMode = request.getBindingMode();
         if (bindingMode != null)
-            attributes.put("b", bindingMode.toString());
+            attributes.put("b", BindingMode.toString(bindingMode));
+
+        Boolean queueMode = request.getQueueMode();
+        if (queueMode != null && queueMode)
+            attributes.put("Q", null);
 
         for (Entry<String, String> attr : attributes.entrySet()) {
-            coapRequest.getOptions().addUriQuery(attr.getKey() + "=" + attr.getValue());
+            if (attr.getValue() != null) {
+                coapRequest.getOptions().addUriQuery(attr.getKey() + "=" + attr.getValue());
+            } else {
+                coapRequest.getOptions().addUriQuery(attr.getKey());
+            }
         }
 
         Link[] objectLinks = request.getObjectLinks();
@@ -113,9 +122,9 @@ public class CoapRequestBuilder implements UplinkRequestVisitor {
         if (smsNumber != null)
             coapRequest.getOptions().addUriQuery("sms=" + smsNumber);
 
-        BindingMode bindingMode = request.getBindingMode();
+        EnumSet<BindingMode> bindingMode = request.getBindingMode();
         if (bindingMode != null)
-            coapRequest.getOptions().addUriQuery("b=" + bindingMode.toString());
+            coapRequest.getOptions().addUriQuery("b=" + BindingMode.toString(bindingMode));
 
         Link[] linkObjects = request.getObjectLinks();
         if (linkObjects != null) {
diff --git a/leshan-client-core/src/main/java/org/eclipse/leshan/client/RegistrationUpdate.java b/leshan-client-core/src/main/java/org/eclipse/leshan/client/RegistrationUpdate.java
index b2f11ef6..bc39503f 100644
--- a/leshan-client-core/src/main/java/org/eclipse/leshan/client/RegistrationUpdate.java
+++ b/leshan-client-core/src/main/java/org/eclipse/leshan/client/RegistrationUpdate.java
@@ -15,6 +15,7 @@
  *******************************************************************************/
 package org.eclipse.leshan.client;
 
+import java.util.EnumSet;
 import java.util.Map;
 
 import org.eclipse.leshan.core.Link;
@@ -27,12 +28,12 @@ public class RegistrationUpdate {
 
     private final Long lifeTimeInSec;
     private final String smsNumber;
-    private final BindingMode bindingMode;
+    private final EnumSet<BindingMode> bindingMode;
     private final Link[] objectLinks;
     private final Map<String, String> additionalAttributes;
 
-    public RegistrationUpdate(Long lifeTimeInSec, String smsNumber, BindingMode bindingMode, Link[] objectLinks,
-            Map<String, String> additionalAttributes) {
+    public RegistrationUpdate(Long lifeTimeInSec, String smsNumber, EnumSet<BindingMode> bindingMode,
+            Link[] objectLinks, Map<String, String> additionalAttributes) {
         this.lifeTimeInSec = lifeTimeInSec;
         this.smsNumber = smsNumber;
         this.bindingMode = bindingMode;
@@ -52,7 +53,7 @@ public class RegistrationUpdate {
         this(null, smsNumber, null, null, null);
     }
 
-    public RegistrationUpdate(BindingMode bindingMode) {
+    public RegistrationUpdate(EnumSet<BindingMode> bindingMode) {
         this(null, null, bindingMode, null, null);
     }
 
@@ -72,7 +73,7 @@ public class RegistrationUpdate {
         return smsNumber;
     }
 
-    public BindingMode getBindingMode() {
+    public EnumSet<BindingMode> getBindingMode() {
         return bindingMode;
     }
 
diff --git a/leshan-client-core/src/main/java/org/eclipse/leshan/client/RegistrationUpdateHandler.java b/leshan-client-core/src/main/java/org/eclipse/leshan/client/RegistrationUpdateHandler.java
index 3338f216..91e0d30b 100644
--- a/leshan-client-core/src/main/java/org/eclipse/leshan/client/RegistrationUpdateHandler.java
+++ b/leshan-client-core/src/main/java/org/eclipse/leshan/client/RegistrationUpdateHandler.java
@@ -15,6 +15,8 @@
  *******************************************************************************/
 package org.eclipse.leshan.client;
 
+import java.util.EnumSet;
+
 import org.eclipse.leshan.client.bootstrap.BootstrapHandler;
 import org.eclipse.leshan.client.engine.RegistrationEngine;
 import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
@@ -74,27 +76,35 @@ public class RegistrationUpdateHandler {
             public void resourceChanged(LwM2mObjectEnabler object, int instanceId, int... resourceIds) {
                 if (!bsHandler.isBootstrapping())
                     if (object.getId() == LwM2mId.SERVER) {
-                        Long lifetime = null;
-                        BindingMode bindingMode = null;
+                        // handle lifetime changes
                         for (int i = 0; i < resourceIds.length; i++) {
                             if (resourceIds[i] == LwM2mId.SRV_LIFETIME) {
-                                lifetime = ServersInfoExtractor.getLifeTime(object, instanceId);
-                            } else if (resourceIds[i] == LwM2mId.SRV_BINDING) {
-                                bindingMode = ServersInfoExtractor.getBindingMode(object, instanceId);
+                                Long lifetime = ServersInfoExtractor.getLifeTime(object, instanceId);
+                                Long serverId = ServersInfoExtractor.getServerId(object, instanceId);
+                                if (lifetime != null && serverId != null) {
+                                    ServerIdentity server = engine.getRegisteredServer(serverId);
+                                    if (server != null) {
+                                        engine.triggerRegistrationUpdate(server,
+                                                new RegistrationUpdate(lifetime, null, null, null, null));
+                                        return;
+                                    }
+                                }
                             }
                         }
-
-                        if (bindingMode != null || lifetime != null) {
-                            Long serverId = null;
-                            serverId = ServersInfoExtractor.getServerId(object, instanceId);
-                            if (serverId != null) {
-                                ServerIdentity server = engine.getRegisteredServer(serverId);
-                                if (server != null)
-                                    engine.triggerRegistrationUpdate(server,
-                                            new RegistrationUpdate(lifetime, null, bindingMode, null, null));
+                    } else if (object.getId() == LwM2mId.DEVICE) {
+                        // handle supported binding changes
+                        EnumSet<BindingMode> bindingMode = null;
+                        for (int i = 0; i < resourceIds.length; i++) {
+                            if (resourceIds[i] == LwM2mId.DVC_SUPPORTED_BINDING) {
+                                bindingMode = ServersInfoExtractor.getDeviceSupportedBindingMode(object, instanceId);
                             }
                         }
 
+                        if (bindingMode != null) {
+                            engine.triggerRegistrationUpdate(
+                                    new RegistrationUpdate(null, null, bindingMode, null, null));
+                            return;
+                        }
                     }
             }
         });
diff --git a/leshan-client-core/src/main/java/org/eclipse/leshan/client/engine/DefaultRegistrationEngine.java b/leshan-client-core/src/main/java/org/eclipse/leshan/client/engine/DefaultRegistrationEngine.java
index a83110cf..4c2bd3d2 100644
--- a/leshan-client-core/src/main/java/org/eclipse/leshan/client/engine/DefaultRegistrationEngine.java
+++ b/leshan-client-core/src/main/java/org/eclipse/leshan/client/engine/DefaultRegistrationEngine.java
@@ -16,6 +16,7 @@
 package org.eclipse.leshan.client.engine;
 
 import java.util.Collections;
+import java.util.EnumSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -41,7 +42,9 @@ import org.eclipse.leshan.client.servers.ServerInfo;
 import org.eclipse.leshan.client.servers.ServersInfoExtractor;
 import org.eclipse.leshan.client.util.LinkFormatHelper;
 import org.eclipse.leshan.core.LwM2m.Version;
+import org.eclipse.leshan.core.LwM2mId;
 import org.eclipse.leshan.core.ResponseCode;
+import org.eclipse.leshan.core.request.BindingMode;
 import org.eclipse.leshan.core.request.BootstrapRequest;
 import org.eclipse.leshan.core.request.DeregisterRequest;
 import org.eclipse.leshan.core.request.Identity;
@@ -88,6 +91,8 @@ public class DefaultRegistrationEngine implements RegistrationEngine {
     private boolean reconnectOnUpdate;
     // True if client should try to resume connection if possible.
     private boolean resumeOnConnect;
+    // True if client use queueMode : for now this just add Q parameter on register request.
+    private final boolean queueMode;
 
     private static enum Status {
         SUCCESS, FAILURE, TIMEOUT
@@ -124,7 +129,7 @@ public class DefaultRegistrationEngine implements RegistrationEngine {
             Integer communicationPeriodInMs, boolean reconnectOnUpdate, boolean resumeOnConnect) {
         this(endpoint, objectTree, endpointsManager, requestSender, bootstrapState, observer, additionalAttributes,
                 null, executor, requestTimeoutInMs, deregistrationTimeoutInMs, bootstrapSessionTimeoutInSec,
-                retryWaitingTimeInMs, communicationPeriodInMs, reconnectOnUpdate, resumeOnConnect);
+                retryWaitingTimeInMs, communicationPeriodInMs, reconnectOnUpdate, resumeOnConnect, false);
     }
 
     /** @since 1.1 */
@@ -133,7 +138,7 @@ public class DefaultRegistrationEngine implements RegistrationEngine {
             Map<String, String> additionalAttributes, Map<String, String> bsAdditionalAttributes,
             ScheduledExecutorService executor, long requestTimeoutInMs, long deregistrationTimeoutInMs,
             int bootstrapSessionTimeoutInSec, int retryWaitingTimeInMs, Integer communicationPeriodInMs,
-            boolean reconnectOnUpdate, boolean resumeOnConnect) {
+            boolean reconnectOnUpdate, boolean resumeOnConnect, boolean useQueueMode) {
         this.endpoint = endpoint;
         this.objectEnablers = objectTree.getObjectEnablers();
         this.bootstrapHandler = bootstrapState;
@@ -151,6 +156,7 @@ public class DefaultRegistrationEngine implements RegistrationEngine {
         this.communicationPeriodInMs = communicationPeriodInMs;
         this.reconnectOnUpdate = reconnectOnUpdate;
         this.resumeOnConnect = resumeOnConnect;
+        this.queueMode = useQueueMode;
 
         if (executor == null) {
             schedExecutor = createScheduledExecutor();
@@ -298,8 +304,11 @@ public class DefaultRegistrationEngine implements RegistrationEngine {
         LOG.info("Trying to register to {} ...", server.getUri());
         RegisterRequest request = null;
         try {
-            request = new RegisterRequest(endpoint, dmInfo.lifetime, Version.lastSupported().toString(), dmInfo.binding,
-                    null, LinkFormatHelper.getClientDescription(objectEnablers.values(), null), additionalAttributes);
+            EnumSet<BindingMode> supportedBindingMode = ServersInfoExtractor
+                    .getDeviceSupportedBindingMode(objectEnablers.get(LwM2mId.DEVICE), 0);
+            request = new RegisterRequest(endpoint, dmInfo.lifetime, Version.lastSupported().toString(),
+                    supportedBindingMode, queueMode, null,
+                    LinkFormatHelper.getClientDescription(objectEnablers.values(), null), additionalAttributes);
             if (observer != null) {
                 observer.onRegistrationStarted(server, request);
             }
diff --git a/leshan-client-core/src/main/java/org/eclipse/leshan/client/engine/DefaultRegistrationEngineFactory.java b/leshan-client-core/src/main/java/org/eclipse/leshan/client/engine/DefaultRegistrationEngineFactory.java
index 2b888b96..5bc84833 100644
--- a/leshan-client-core/src/main/java/org/eclipse/leshan/client/engine/DefaultRegistrationEngineFactory.java
+++ b/leshan-client-core/src/main/java/org/eclipse/leshan/client/engine/DefaultRegistrationEngineFactory.java
@@ -38,6 +38,7 @@ public class DefaultRegistrationEngineFactory implements RegistrationEngineFacto
     private Integer communicationPeriodInMs = null;
     private boolean reconnectOnUpdate = false;
     private boolean resumeOnConnect = true;
+    private boolean queueMode = false;
 
     public DefaultRegistrationEngineFactory() {
     }
@@ -50,7 +51,7 @@ public class DefaultRegistrationEngineFactory implements RegistrationEngineFacto
         return new DefaultRegistrationEngine(endpoint, objectTree, endpointsManager, requestSender, bootstrapState,
                 observer, additionalAttributes, bsAdditionalAttributes, sharedExecutor, requestTimeoutInMs,
                 deregistrationTimeoutInMs, bootstrapSessionTimeoutInSec, retryWaitingTimeInMs, communicationPeriodInMs,
-                reconnectOnUpdate, resumeOnConnect);
+                reconnectOnUpdate, resumeOnConnect, queueMode);
     }
 
     /**
@@ -150,4 +151,17 @@ public class DefaultRegistrationEngineFactory implements RegistrationEngineFacto
         this.resumeOnConnect = resumeOnConnect;
         return this;
     }
+
+    /**
+     * Configure client to use queueMode.
+     * <p>
+     * Default value is false
+     * 
+     * @param enable True if client must use queueMode
+     * @return this for fluent API
+     */
+    public DefaultRegistrationEngineFactory setQueueMode(boolean enable) {
+        this.queueMode = enable;
+        return this;
+    }
 }
diff --git a/leshan-client-core/src/main/java/org/eclipse/leshan/client/object/Device.java b/leshan-client-core/src/main/java/org/eclipse/leshan/client/object/Device.java
index 60666100..f91c53c3 100644
--- a/leshan-client-core/src/main/java/org/eclipse/leshan/client/object/Device.java
+++ b/leshan-client-core/src/main/java/org/eclipse/leshan/client/object/Device.java
@@ -20,6 +20,7 @@ package org.eclipse.leshan.client.object;
 import java.text.SimpleDateFormat;
 import java.util.Arrays;
 import java.util.Calendar;
+import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.List;
 import java.util.TimeZone;
@@ -30,6 +31,7 @@ import org.eclipse.leshan.client.servers.ServerIdentity;
 import org.eclipse.leshan.core.model.ObjectModel;
 import org.eclipse.leshan.core.model.ResourceModel.Type;
 import org.eclipse.leshan.core.node.LwM2mResource;
+import org.eclipse.leshan.core.request.BindingMode;
 import org.eclipse.leshan.core.response.ExecuteResponse;
 import org.eclipse.leshan.core.response.ReadResponse;
 import org.eclipse.leshan.core.response.WriteResponse;
@@ -44,7 +46,7 @@ public class Device extends BaseInstanceEnabler {
     private String manufacturer;
     private String modelNumber;
     private String serialNumber;
-    private String supportedBinding;
+    private EnumSet<BindingMode> supportedBinding;
 
     private String timezone = TimeZone.getDefault().getID();
     private String utcOffset = new SimpleDateFormat("X").format(Calendar.getInstance().getTime());
@@ -53,7 +55,14 @@ public class Device extends BaseInstanceEnabler {
         // should never be used
     }
 
-    public Device(String manufacturer, String modelNumber, String serialNumber, String supportedBinding) {
+    public Device(String manufacturer, String modelNumber, String serialNumber) {
+        this.manufacturer = manufacturer;
+        this.modelNumber = modelNumber;
+        this.serialNumber = serialNumber;
+        this.supportedBinding = EnumSet.of(BindingMode.U);
+    }
+
+    public Device(String manufacturer, String modelNumber, String serialNumber, EnumSet<BindingMode> supportedBinding) {
         this.manufacturer = manufacturer;
         this.modelNumber = modelNumber;
         this.serialNumber = serialNumber;
@@ -83,7 +92,7 @@ public class Device extends BaseInstanceEnabler {
             return ReadResponse.success(resourceid, timezone);
 
         case 16: // supported binding and modes
-            return ReadResponse.success(resourceid, supportedBinding);
+            return ReadResponse.success(resourceid, BindingMode.toString(supportedBinding));
 
         default:
             return super.read(identity, resourceid);
diff --git a/leshan-client-core/src/main/java/org/eclipse/leshan/client/object/Server.java b/leshan-client-core/src/main/java/org/eclipse/leshan/client/object/Server.java
index d184b732..ad7e9681 100644
--- a/leshan-client-core/src/main/java/org/eclipse/leshan/client/object/Server.java
+++ b/leshan-client-core/src/main/java/org/eclipse/leshan/client/object/Server.java
@@ -18,6 +18,7 @@
 package org.eclipse.leshan.client.object;
 
 import java.util.Arrays;
+import java.util.EnumSet;
 import java.util.List;
 import java.util.Objects;
 
@@ -41,24 +42,31 @@ public class Server extends BaseInstanceEnabler {
 
     private static final Logger LOG = LoggerFactory.getLogger(Server.class);
 
-    private final static List<Integer> supportedResources = Arrays.asList(0, 1, 2, 3, 6, 7, 8);
+    private final static List<Integer> supportedResources = Arrays.asList(0, 1, 2, 3, 6, 7, 8, 22);
 
     private int shortServerId;
     private long lifetime;
     private Long defaultMinPeriod;
     private Long defaultMaxPeriod;
-    private BindingMode binding;
+    private EnumSet<BindingMode> binding;
+    private BindingMode preferredTransport;
     private boolean notifyWhenDisable;
 
     public Server() {
         // should only be used at bootstrap time
     }
 
-    public Server(int shortServerId, long lifetime, BindingMode binding, boolean notifyWhenDisable) {
+    public Server(int shortServerId, long lifetime, EnumSet<BindingMode> binding, boolean notifyWhenDisable,
+            BindingMode preferredTransport) {
         this.shortServerId = shortServerId;
         this.lifetime = lifetime;
         this.binding = binding;
         this.notifyWhenDisable = notifyWhenDisable;
+        this.preferredTransport = preferredTransport;
+    }
+
+    public Server(int shortServerId, long lifetime) {
+        this(shortServerId, lifetime, EnumSet.of(BindingMode.U), false, BindingMode.U);
     }
 
     @Override
@@ -87,7 +95,12 @@ public class Server extends BaseInstanceEnabler {
             return ReadResponse.success(resourceid, notifyWhenDisable);
 
         case 7: // binding
-            return ReadResponse.success(resourceid, binding.toString());
+            return ReadResponse.success(resourceid, BindingMode.toString(binding));
+
+        case 22: // preferred transport
+            if (preferredTransport == null)
+                return ReadResponse.notFound();
+            return ReadResponse.success(resourceid, preferredTransport.toString());
 
         default:
             return super.read(identity, resourceid);
@@ -155,14 +168,27 @@ public class Server extends BaseInstanceEnabler {
                 return WriteResponse.badRequest("invalid type");
             }
             try {
-                BindingMode previousBinding = binding;
-                binding = BindingMode.valueOf((String) value.getValue());
+                EnumSet<BindingMode> previousBinding = binding;
+                binding = BindingMode.parse((String) value.getValue());
                 if (!Objects.equals(previousBinding, binding))
                     fireResourcesChange(resourceid);
                 return WriteResponse.success();
             } catch (IllegalArgumentException e) {
                 return WriteResponse.badRequest("invalid value");
             }
+        case 22: // preferredTransport
+            if (value.getType() != Type.STRING) {
+                return WriteResponse.badRequest("invalid type");
+            }
+            try {
+                BindingMode previousPreferedTransport = preferredTransport;
+                preferredTransport = BindingMode.valueOf((String) value.getValue());
+                if (!Objects.equals(previousPreferedTransport, preferredTransport))
+                    fireResourcesChange(resourceid);
+                return WriteResponse.success();
+            } catch (IllegalArgumentException e) {
+                return WriteResponse.badRequest("invalid value");
+            }
 
         default:
             return super.write(identity, resourceid, value);
diff --git a/leshan-client-core/src/main/java/org/eclipse/leshan/client/servers/DmServerInfo.java b/leshan-client-core/src/main/java/org/eclipse/leshan/client/servers/DmServerInfo.java
index e72de8c0..5e939f03 100644
--- a/leshan-client-core/src/main/java/org/eclipse/leshan/client/servers/DmServerInfo.java
+++ b/leshan-client-core/src/main/java/org/eclipse/leshan/client/servers/DmServerInfo.java
@@ -15,6 +15,8 @@
  *******************************************************************************/
 package org.eclipse.leshan.client.servers;
 
+import java.util.EnumSet;
+
 import org.eclipse.leshan.core.request.BindingMode;
 
 /**
@@ -26,11 +28,12 @@ import org.eclipse.leshan.core.request.BindingMode;
 public class DmServerInfo extends ServerInfo {
 
     public long lifetime;
-    public BindingMode binding;
+    public EnumSet<BindingMode> binding;
     // TODO add missing information like SMS number
 
     @Override
     public String toString() {
-        return String.format("DM Server [uri=%s, lifetime=%s, binding=%s]", serverUri, lifetime, binding);
+        return String.format("DM Server [uri=%s, lifetime=%s, binding=%s]", serverUri, lifetime,
+                BindingMode.toString(binding));
     }
 }
diff --git a/leshan-client-core/src/main/java/org/eclipse/leshan/client/servers/ServersInfoExtractor.java b/leshan-client-core/src/main/java/org/eclipse/leshan/client/servers/ServersInfoExtractor.java
index dca257d2..6383f835 100644
--- a/leshan-client-core/src/main/java/org/eclipse/leshan/client/servers/ServersInfoExtractor.java
+++ b/leshan-client-core/src/main/java/org/eclipse/leshan/client/servers/ServersInfoExtractor.java
@@ -33,6 +33,7 @@ import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.X509EncodedKeySpec;
+import java.util.EnumSet;
 import java.util.Map;
 
 import org.eclipse.leshan.client.resource.LwM2mInstanceEnabler;
@@ -119,7 +120,7 @@ public class ServersInfoExtractor {
                     for (LwM2mObjectInstance server : servers.getInstances().values()) {
                         if (info.serverId == (Long) server.getResource(SRV_SERVER_ID).getValue()) {
                             info.lifetime = (long) server.getResource(SRV_LIFETIME).getValue();
-                            info.binding = BindingMode.valueOf((String) server.getResource(SRV_BINDING).getValue());
+                            info.binding = BindingMode.parse((String) server.getResource(SRV_BINDING).getValue());
 
                             infos.deviceManagements.put(info.serverId, info);
                             break;
@@ -159,11 +160,21 @@ public class ServersInfoExtractor {
         }
     }
 
-    public static BindingMode getBindingMode(LwM2mObjectEnabler serverEnabler, int instanceId) {
+    public static EnumSet<BindingMode> getServerBindingMode(LwM2mObjectEnabler serverEnabler, int instanceId) {
         ReadResponse response = serverEnabler.read(ServerIdentity.SYSTEM,
                 new ReadRequest(SERVER, instanceId, SRV_BINDING));
         if (response.isSuccess()) {
-            return BindingMode.valueOf((String) ((LwM2mResource) response.getContent()).getValue());
+            return BindingMode.parse((String) ((LwM2mResource) response.getContent()).getValue());
+        } else {
+            return null;
+        }
+    }
+
+    public static EnumSet<BindingMode> getDeviceSupportedBindingMode(LwM2mObjectEnabler serverEnabler, int instanceId) {
+        ReadResponse response = serverEnabler.read(ServerIdentity.SYSTEM,
+                new ReadRequest(DEVICE, instanceId, DVC_SUPPORTED_BINDING));
+        if (response.isSuccess()) {
+            return BindingMode.parse((String) ((LwM2mResource) response.getContent()).getValue());
         } else {
             return null;
         }
diff --git a/leshan-client-demo/src/main/java/org/eclipse/leshan/client/demo/LeshanClientDemo.java b/leshan-client-demo/src/main/java/org/eclipse/leshan/client/demo/LeshanClientDemo.java
index 1521a5f3..a3c20612 100644
--- a/leshan-client-demo/src/main/java/org/eclipse/leshan/client/demo/LeshanClientDemo.java
+++ b/leshan-client-demo/src/main/java/org/eclipse/leshan/client/demo/LeshanClientDemo.java
@@ -70,7 +70,6 @@ import org.eclipse.leshan.core.model.ObjectModel;
 import org.eclipse.leshan.core.model.StaticModel;
 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder;
-import org.eclipse.leshan.core.request.BindingMode;
 import org.eclipse.leshan.core.util.Hex;
 import org.eclipse.leshan.core.util.SecurityUtil;
 import org.slf4j.Logger;
@@ -610,18 +609,18 @@ public class LeshanClientDemo {
         } else {
             if (pskIdentity != null) {
                 initializer.setInstancesForObject(SECURITY, psk(serverURI, 123, pskIdentity, pskKey));
-                initializer.setInstancesForObject(SERVER, new Server(123, lifetime, BindingMode.U, false));
+                initializer.setInstancesForObject(SERVER, new Server(123, lifetime));
             } else if (clientPublicKey != null) {
                 initializer.setInstancesForObject(SECURITY, rpk(serverURI, 123, clientPublicKey.getEncoded(),
                         clientPrivateKey.getEncoded(), serverPublicKey.getEncoded()));
-                initializer.setInstancesForObject(SERVER, new Server(123, lifetime, BindingMode.U, false));
+                initializer.setInstancesForObject(SERVER, new Server(123, lifetime));
             } else if (clientCertificate != null) {
                 initializer.setInstancesForObject(SECURITY, x509(serverURI, 123, clientCertificate.getEncoded(),
                         clientPrivateKey.getEncoded(), serverCertificate.getEncoded()));
-                initializer.setInstancesForObject(SERVER, new Server(123, lifetime, BindingMode.U, false));
+                initializer.setInstancesForObject(SERVER, new Server(123, lifetime));
             } else {
                 initializer.setInstancesForObject(SECURITY, noSec(serverURI, 123));
-                initializer.setInstancesForObject(SERVER, new Server(123, lifetime, BindingMode.U, false));
+                initializer.setInstancesForObject(SERVER, new Server(123, lifetime));
             }
         }
         initializer.setInstancesForObject(DEVICE, new MyDevice());
diff --git a/leshan-client-demo/src/main/java/org/eclipse/leshan/client/demo/MyDevice.java b/leshan-client-demo/src/main/java/org/eclipse/leshan/client/demo/MyDevice.java
index 5453fa05..207649d7 100644
--- a/leshan-client-demo/src/main/java/org/eclipse/leshan/client/demo/MyDevice.java
+++ b/leshan-client-demo/src/main/java/org/eclipse/leshan/client/demo/MyDevice.java
@@ -44,7 +44,8 @@ public class MyDevice extends BaseInstanceEnabler {
 
     @Override
     public ReadResponse read(ServerIdentity identity, int resourceid) {
-        LOG.info("Read on Device resource /{}/{}/{}", getModel().id, getId(), resourceid);
+        if (!identity.isSystem())
+            LOG.info("Read on Device resource /{}/{}/{}", getModel().id, getId(), resourceid);
         switch (resourceid) {
         case 0:
             return ReadResponse.success(resourceid, getManufacturer());
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/LwM2m.java b/leshan-core/src/main/java/org/eclipse/leshan/core/LwM2m.java
index ecce03a5..3ad78c12 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/LwM2m.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/LwM2m.java
@@ -60,7 +60,7 @@ public interface LwM2m {
                 return "version  MUST NOT be null or empty";
             String[] versionPart = version.split("\\.");
             if (versionPart.length != 2) {
-                throw new IllegalArgumentException(String.format("version (%s) MUST be composed of 2 parts", version));
+                return String.format("version (%s) MUST be composed of 2 parts", version);
             }
             for (int i = 0; i < 2; i++) {
                 try {
@@ -130,6 +130,10 @@ public interface LwM2m {
             return this.compareTo(version) > 0;
         }
 
+        public boolean olderThan(Version version) {
+            return this.compareTo(version) < 0;
+        }
+
         public boolean newerThan(String version) {
             return newerThan(Version.get(version));
         }
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/LwM2mId.java b/leshan-core/src/main/java/org/eclipse/leshan/core/LwM2mId.java
index 24a71737..392df7d8 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/LwM2mId.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/LwM2mId.java
@@ -47,4 +47,8 @@ public interface LwM2mId {
     public static final int SRV_SERVER_ID = 0;
     public static final int SRV_LIFETIME = 1;
     public static final int SRV_BINDING = 7;
+
+    /* DEVICE RESOURCES */
+
+    public static final int DVC_SUPPORTED_BINDING = 16;
 }
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/request/BindingMode.java b/leshan-core/src/main/java/org/eclipse/leshan/core/request/BindingMode.java
index dba777c0..273636e3 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/request/BindingMode.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/request/BindingMode.java
@@ -15,6 +15,10 @@
  *******************************************************************************/
 package org.eclipse.leshan.core.request;
 
+import java.util.EnumSet;
+
+import org.eclipse.leshan.core.LwM2m.Version;
+
 /**
  * Transport binding and Queue Mode
  */
@@ -23,30 +27,85 @@ public enum BindingMode {
     /** UDP */
     U,
 
-    /** UDP with Queue Mode */
-    UQ,
+    /** TCP */
+    T,
 
     /** SMS */
     S,
 
-    /** SMS with Queue Mode */
-    SQ,
+    /** Non-Ip */
+    N,
+
+    /** Queue Mode : removed since LWM2M 1.1 */
+    Q;
 
-    /** UDP and SMS */
-    US,
+    /**
+     * @param targetVersion the target LWM2M version
+     * @return null if the BindingMode value is compatible with the given LWM2M version, else return an error message.
+     */
+    public String isValidFor(Version targetVersion) {
+        switch (this) {
+        case T:
+        case N:
+            if (targetVersion.olderThan(Version.V1_1)) {
+                return String.format("%s is supported since LWM2M 1.1", this);
+            }
+            break;
+        case Q:
+            if (targetVersion.newerThan(Version.V1_0)) {
+                return String.format("%s is not supported since LWM2M 1.1", this);
+            }
+            break;
+        default:
+        }
+        return null;
+    }
+
+    private static BindingMode valueOf(char c) {
+        switch (c) {
+        case 'U':
+            return U;
+        case 'T':
+            return T;
+        case 'S':
+            return S;
+        case 'N':
+            return N;
+        case 'Q':
+            return Q;
+        default:
+            throw new IllegalArgumentException("No enum constant " + c + ".");
 
-    /** UDP with Queue Mode and SMS */
-    UQS;
+        }
+    }
 
-    public boolean useSMS() {
-        return equals(S) || equals(SQ) || equals(UQS);
+    /**
+     * @param bindings bindings to check
+     * @param targetVersion the target LWM2M version
+     * @return null if the bindings are compatible with the given LWM2M version, else return an error message.
+     */
+    public static String isValidFor(EnumSet<BindingMode> bindings, Version targetVersion) {
+        for (BindingMode binding : bindings) {
+            String err = binding.isValidFor(targetVersion);
+            if (err != null)
+                return err;
+        }
+        return null;
     }
 
-    public boolean useQueueMode() {
-        return equals(UQ) || equals(SQ) || equals(UQS);
+    public static String toString(EnumSet<BindingMode> bindings) {
+        StringBuilder b = new StringBuilder();
+        for (BindingMode binding : bindings) {
+            b.append(binding);
+        }
+        return b.toString();
     }
 
-    public boolean useUDP() {
-        return equals(U) || equals(UQ) || equals(UQS);
+    public static EnumSet<BindingMode> parse(String bindings) {
+        EnumSet<BindingMode> res = EnumSet.noneOf(BindingMode.class);
+        for (int i = 0; i < bindings.length(); i++) {
+            res.add(BindingMode.valueOf(bindings.charAt(i)));
+        }
+        return res;
     }
 }
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/request/RegisterRequest.java b/leshan-core/src/main/java/org/eclipse/leshan/core/request/RegisterRequest.java
index cf980d98..c4aaa77b 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/request/RegisterRequest.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/request/RegisterRequest.java
@@ -17,10 +17,12 @@ package org.eclipse.leshan.core.request;
 
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.Map;
 
 import org.eclipse.leshan.core.Link;
+import org.eclipse.leshan.core.LwM2m.Version;
 import org.eclipse.leshan.core.request.exception.InvalidRequestException;
 import org.eclipse.leshan.core.response.RegisterResponse;
 
@@ -33,7 +35,8 @@ public class RegisterRequest implements UplinkRequest<RegisterResponse> {
     private final String endpointName;
     private final Long lifetime;
     private final String lwVersion;
-    private final BindingMode bindingMode;
+    private final EnumSet<BindingMode> bindingMode;
+    private final Boolean queueMode; // since LWM2M 1.1
     private final String smsNumber;
     private final Link[] objectLinks;
     private final Map<String, String> additionalAttributes;
@@ -44,14 +47,15 @@ public class RegisterRequest implements UplinkRequest<RegisterResponse> {
      * @param endpointName is the LWM2M client identifier.
      * @param lifetime specifies the lifetime of the registration in seconds.
      * @param lwVersion indicates the version of the LWM2M Enabler that the LWM2M Client supports.
-     * @param bindingMode indicates current {@link BindingMode} of the LWM2M Client.
+     * @param bindingMode Indicates the supported {@link BindingMode}s in the LwM2M Client.
+     * @param queueMode Indicates whether Queue Mode is supported.
      * @param smsNumber is the MSISDN where the LWM2M Client can be reached for use with the SMS binding.
      * @param objectLinks is the list of Objects supported and Object Instances available on the LWM2M Client.
      * @param additionalAttributes are any attributes/parameters which is out of the LWM2M specification.
      * @exception InvalidRequestException if endpoint name or objectlinks is empty.
      */
-    public RegisterRequest(String endpointName, Long lifetime, String lwVersion, BindingMode bindingMode,
-            String smsNumber, Link[] objectLinks, Map<String, String> additionalAttributes)
+    public RegisterRequest(String endpointName, Long lifetime, String lwVersion, EnumSet<BindingMode> bindingMode,
+            Boolean queueMode, String smsNumber, Link[] objectLinks, Map<String, String> additionalAttributes)
             throws InvalidRequestException {
 
         if (endpointName == null || endpointName.isEmpty())
@@ -62,6 +66,29 @@ public class RegisterRequest implements UplinkRequest<RegisterResponse> {
                     "supported object list is mandatory and mandatory objects should be present for endpoint %s",
                     endpointName);
 
+        String err = Version.validate(lwVersion);
+        if (err != null) {
+            throw new InvalidRequestException("Invalid LWM2M version: %s", err);
+        }
+
+        if (bindingMode != null) {
+            err = BindingMode.isValidFor(bindingMode, Version.get(lwVersion));
+            if (err != null) {
+                throw new InvalidRequestException("Invalid Binding mode: %s", err);
+            }
+        }
+
+        // handle queue mode param
+        Version version = Version.get(lwVersion);
+        if (version.equals(Version.V1_0)) {
+            if (queueMode != null)
+                throw new InvalidRequestException("QueueMode is not defined in LWM2M v1.0");
+            else
+                this.queueMode = null;
+        } else {
+            this.queueMode = queueMode == null ? false : queueMode;
+        }
+
         this.endpointName = endpointName;
         this.lifetime = lifetime;
         this.lwVersion = lwVersion;
@@ -86,7 +113,7 @@ public class RegisterRequest implements UplinkRequest<RegisterResponse> {
         return lwVersion;
     }
 
-    public BindingMode getBindingMode() {
+    public EnumSet<BindingMode> getBindingMode() {
         return bindingMode;
     }
 
@@ -98,6 +125,10 @@ public class RegisterRequest implements UplinkRequest<RegisterResponse> {
         return objectLinks;
     }
 
+    public Boolean getQueueMode() {
+        return queueMode;
+    }
+
     public Map<String, String> getAdditionalAttributes() {
         return additionalAttributes;
     }
@@ -110,8 +141,8 @@ public class RegisterRequest implements UplinkRequest<RegisterResponse> {
     @Override
     public String toString() {
         return String.format(
-                "RegisterRequest [endpointName=%s, lifetime=%s, lwVersion=%s, bindingMode=%s, smsNumber=%s, objectLinks=%s, additionalAttributes=%s]",
-                endpointName, lifetime, lwVersion, bindingMode, smsNumber, Arrays.toString(objectLinks),
+                "RegisterRequest [endpointName=%s, lifetime=%s, lwVersion=%s, bindingMode=%s, queueMode=%s, smsNumber=%s, objectLinks=%s, additionalAttributes=%s]",
+                endpointName, lifetime, lwVersion, bindingMode, queueMode, smsNumber, Arrays.toString(objectLinks),
                 additionalAttributes);
     }
 
@@ -125,6 +156,7 @@ public class RegisterRequest implements UplinkRequest<RegisterResponse> {
         result = prime * result + ((lifetime == null) ? 0 : lifetime.hashCode());
         result = prime * result + ((lwVersion == null) ? 0 : lwVersion.hashCode());
         result = prime * result + Arrays.hashCode(objectLinks);
+        result = prime * result + ((queueMode == null) ? 0 : queueMode.hashCode());
         result = prime * result + ((smsNumber == null) ? 0 : smsNumber.hashCode());
         return result;
     }
@@ -162,6 +194,11 @@ public class RegisterRequest implements UplinkRequest<RegisterResponse> {
             return false;
         if (!Arrays.equals(objectLinks, other.objectLinks))
             return false;
+        if (queueMode == null) {
+            if (other.queueMode != null)
+                return false;
+        } else if (!queueMode.equals(other.queueMode))
+            return false;
         if (smsNumber == null) {
             if (other.smsNumber != null)
                 return false;
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/request/UpdateRequest.java b/leshan-core/src/main/java/org/eclipse/leshan/core/request/UpdateRequest.java
index ade346a1..4a6a7719 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/request/UpdateRequest.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/request/UpdateRequest.java
@@ -17,10 +17,12 @@ package org.eclipse.leshan.core.request;
 
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.Map;
 
 import org.eclipse.leshan.core.Link;
+import org.eclipse.leshan.core.LwM2m.Version;
 import org.eclipse.leshan.core.request.exception.InvalidRequestException;
 import org.eclipse.leshan.core.response.UpdateResponse;
 
@@ -32,7 +34,7 @@ public class UpdateRequest implements UplinkRequest<UpdateResponse> {
 
     private final Long lifeTimeInSec;
     private final String smsNumber;
-    private final BindingMode bindingMode;
+    private final EnumSet<BindingMode> bindingMode;
     private final String registrationId;
     private final Link[] objectLinks;
     private final Map<String, String> additionalAttributes;
@@ -47,7 +49,7 @@ public class UpdateRequest implements UplinkRequest<UpdateResponse> {
      * @param objectLinks the objects and object instances the client hosts/supports
      * @exception InvalidRequestException if the registrationId is empty.
      */
-    public UpdateRequest(String registrationId, Long lifetime, String smsNumber, BindingMode binding,
+    public UpdateRequest(String registrationId, Long lifetime, String smsNumber, EnumSet<BindingMode> binding,
             Link[] objectLinks, Map<String, String> additionalAttributes) throws InvalidRequestException {
 
         if (registrationId == null || registrationId.isEmpty())
@@ -80,7 +82,7 @@ public class UpdateRequest implements UplinkRequest<UpdateResponse> {
         return smsNumber;
     }
 
-    public BindingMode getBindingMode() {
+    public EnumSet<BindingMode> getBindingMode() {
         return bindingMode;
     }
 
@@ -88,6 +90,15 @@ public class UpdateRequest implements UplinkRequest<UpdateResponse> {
         return additionalAttributes;
     }
 
+    public void validate(String targetedVersion) {
+        if (bindingMode != null) {
+            String err = BindingMode.isValidFor(bindingMode, Version.get(targetedVersion));
+            if (err != null) {
+                throw new InvalidRequestException("Invalid Binding mode: %s", err);
+            }
+        }
+    }
+
     @Override
     public void accept(UplinkRequestVisitor visitor) {
         visitor.visit(this);
diff --git a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/registration/RegisterResource.java b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/registration/RegisterResource.java
index 818c2b87..d89b80e8 100644
--- a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/registration/RegisterResource.java
+++ b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/registration/RegisterResource.java
@@ -17,6 +17,7 @@ package org.eclipse.leshan.server.californium.registration;
 
 import static org.eclipse.leshan.core.californium.ResponseCodeUtil.toCoapResponseCode;
 
+import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -61,6 +62,8 @@ public class RegisterResource extends LwM2mCoapResource {
 
     private static final String QUERY_PARAM_LIFETIME = "lt=";
 
+    private static final String QUERY_PARAM_QUEUEMMODE = "Q"; // since LWM2M 1.1
+
     private static final Logger LOG = LoggerFactory.getLogger(RegisterResource.class);
 
     public static final String RESOURCE_NAME = "rd";
@@ -128,7 +131,8 @@ public class RegisterResource extends LwM2mCoapResource {
         Long lifetime = null;
         String smsNumber = null;
         String lwVersion = null;
-        BindingMode binding = null;
+        EnumSet<BindingMode> binding = null;
+        Boolean queueMode = null;
 
         // Get object Links
         Link[] objectLinks = Link.parse(request.getPayload());
@@ -146,7 +150,9 @@ public class RegisterResource extends LwM2mCoapResource {
             } else if (param.startsWith(QUERY_PARAM_LWM2M_VERSION)) {
                 lwVersion = param.substring(6);
             } else if (param.startsWith(QUERY_PARAM_BINDING_MODE)) {
-                binding = BindingMode.valueOf(param.substring(2));
+                binding = BindingMode.parse(param.substring(2));
+            } else if (param.equals(QUERY_PARAM_QUEUEMMODE)) {
+                queueMode = true;
             } else {
                 String[] tokens = param.split("\\=");
                 if (tokens != null && tokens.length == 2) {
@@ -156,8 +162,8 @@ public class RegisterResource extends LwM2mCoapResource {
         }
 
         // Create request
-        RegisterRequest registerRequest = new RegisterRequest(endpoint, lifetime, lwVersion, binding, smsNumber,
-                objectLinks, additionalParams);
+        RegisterRequest registerRequest = new RegisterRequest(endpoint, lifetime, lwVersion, binding, queueMode,
+                smsNumber, objectLinks, additionalParams);
 
         // Handle request
         // -------------------------------
@@ -183,7 +189,7 @@ public class RegisterResource extends LwM2mCoapResource {
         // Create LwM2m request from CoAP request
         Long lifetime = null;
         String smsNumber = null;
-        BindingMode binding = null;
+        EnumSet<BindingMode> binding = null;
         Link[] objectLinks = null;
         Map<String, String> additionalParams = new HashMap<>();
 
@@ -193,7 +199,7 @@ public class RegisterResource extends LwM2mCoapResource {
             } else if (param.startsWith(QUERY_PARAM_SMS)) {
                 smsNumber = param.substring(4);
             } else if (param.startsWith(QUERY_PARAM_BINDING_MODE)) {
-                binding = BindingMode.valueOf(param.substring(2));
+                binding = BindingMode.parse(param.substring(2));
             } else {
                 String[] tokens = param.split("\\=");
                 if (tokens != null && tokens.length == 2) {
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapConfig.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapConfig.java
index bcb60dd4..64e101d9 100644
--- a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapConfig.java
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapConfig.java
@@ -18,6 +18,7 @@ package org.eclipse.leshan.server.bootstrap;
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -103,7 +104,7 @@ public class BootstrapConfig implements Serializable {
          * This Resource defines the transport binding configured for the LwM2M Client. If the LwM2M Client supports the
          * binding specified in this Resource, the LwM2M Client MUST use that transport for the Current Binding Mode.
          */
-        public BindingMode binding = BindingMode.U;
+        public EnumSet<BindingMode> binding = EnumSet.of(BindingMode.U);
 
         @Override
         public String toString() {
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapUtil.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapUtil.java
index 275dca2c..f4488860 100644
--- a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapUtil.java
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/BootstrapUtil.java
@@ -27,6 +27,7 @@ import org.eclipse.leshan.core.node.LwM2mObjectInstance;
 import org.eclipse.leshan.core.node.LwM2mPath;
 import org.eclipse.leshan.core.node.LwM2mResource;
 import org.eclipse.leshan.core.node.LwM2mSingleResource;
+import org.eclipse.leshan.core.request.BindingMode;
 import org.eclipse.leshan.core.request.BootstrapDeleteRequest;
 import org.eclipse.leshan.core.request.BootstrapDownlinkRequest;
 import org.eclipse.leshan.core.request.BootstrapWriteRequest;
@@ -89,7 +90,7 @@ public class BootstrapUtil {
             resources.add(LwM2mSingleResource.newIntegerResource(5, serverConfig.disableTimeout));
         resources.add(LwM2mSingleResource.newBooleanResource(6, serverConfig.notifIfDisabled));
         if (serverConfig.binding != null)
-            resources.add(LwM2mSingleResource.newStringResource(7, serverConfig.binding.name()));
+            resources.add(LwM2mSingleResource.newStringResource(7, BindingMode.toString(serverConfig.binding)));
 
         return new LwM2mObjectInstance(instanceId, resources);
     }
@@ -121,13 +122,12 @@ public class BootstrapUtil {
         return new BootstrapWriteRequest(path, securityInstance, contentFormat);
     }
 
-    public static List<BootstrapDownlinkRequest<? extends LwM2mResponse>> toRequests(
-            BootstrapConfig bootstrapConfig) {
+    public static List<BootstrapDownlinkRequest<? extends LwM2mResponse>> toRequests(BootstrapConfig bootstrapConfig) {
         return toRequests(bootstrapConfig, ContentFormat.TLV);
     }
 
-    public static List<BootstrapDownlinkRequest<? extends LwM2mResponse>> toRequests(
-            BootstrapConfig bootstrapConfig, ContentFormat contentFormat) {
+    public static List<BootstrapDownlinkRequest<? extends LwM2mResponse>> toRequests(BootstrapConfig bootstrapConfig,
+            ContentFormat contentFormat) {
         List<BootstrapDownlinkRequest<? extends LwM2mResponse>> requests = new ArrayList<>();
         // handle delete
         for (String path : bootstrapConfig.toDelete) {
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/registration/Registration.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/registration/Registration.java
index 9c3687b5..c172a7c0 100644
--- a/leshan-server-core/src/main/java/org/eclipse/leshan/server/registration/Registration.java
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/registration/Registration.java
@@ -23,6 +23,7 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Date;
+import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicReference;
@@ -57,7 +58,9 @@ public class Registration implements Serializable {
 
     private final String lwM2mVersion;
 
-    private final BindingMode bindingMode;
+    private final EnumSet<BindingMode> bindingMode;
+
+    private final Boolean queueMode; // since LWM2M 1.1
 
     /**
      * The LWM2M Client's unique end point name.
@@ -80,8 +83,9 @@ public class Registration implements Serializable {
     private final Date lastUpdate;
 
     protected Registration(String id, String endpoint, Identity identity, String lwM2mVersion, Long lifetimeInSec,
-            String smsNumber, BindingMode bindingMode, Link[] objectLinks, Date registrationDate, Date lastUpdate,
-            Map<String, String> additionalRegistrationAttributes, Map<Integer, String> supportedObjects) {
+            String smsNumber, EnumSet<BindingMode> bindingMode, Boolean queueMode, Link[] objectLinks,
+            Date registrationDate, Date lastUpdate, Map<String, String> additionalRegistrationAttributes,
+            Map<Integer, String> supportedObjects) {
 
         Validate.notNull(id);
         Validate.notEmpty(endpoint);
@@ -109,7 +113,9 @@ public class Registration implements Serializable {
         this.supportedObjects = new AtomicReference<Map<Integer, String>>(supportedObjects);
         this.lifeTimeInSec = lifetimeInSec == null ? DEFAULT_LIFETIME_IN_SEC : lifetimeInSec;
         this.lwM2mVersion = lwM2mVersion == null ? Version.getDefault().toString() : lwM2mVersion;
-        this.bindingMode = bindingMode == null ? BindingMode.U : bindingMode;
+        this.bindingMode = bindingMode == null ? EnumSet.of(BindingMode.U) : bindingMode;
+        this.queueMode = queueMode == null && Version.get(this.lwM2mVersion).newerThan(Version.V1_0) ? Boolean.FALSE
+                : queueMode;
         this.registrationDate = registrationDate == null ? new Date() : registrationDate;
         this.lastUpdate = lastUpdate == null ? new Date() : lastUpdate;
         if (additionalRegistrationAttributes == null || additionalRegistrationAttributes.isEmpty()) {
@@ -233,10 +239,14 @@ public class Registration implements Serializable {
         return lwM2mVersion;
     }
 
-    public BindingMode getBindingMode() {
+    public EnumSet<BindingMode> getBindingMode() {
         return bindingMode;
     }
 
+    public Boolean getQueueMode() {
+        return queueMode;
+    }
+
     /**
      * @return the path where the objects are hosted on the device
      */
@@ -271,7 +281,7 @@ public class Registration implements Serializable {
     public boolean canInitiateConnection() {
         // We consider that initiates a connection (acting as DTLS client to initiate a handshake) does not make sense
         // for QueueMode as if we lost connection device is probably absent.
-        return !bindingMode.useQueueMode() && bindingMode.useUDP();
+        return !usesQueueMode();
     }
 
     /**
@@ -296,7 +306,10 @@ public class Registration implements Serializable {
     }
 
     public boolean usesQueueMode() {
-        return bindingMode.useQueueMode() && bindingMode.useUDP();
+        if (Version.get(lwM2mVersion).olderThan(Version.V1_1))
+            return bindingMode.contains(BindingMode.Q);
+        else
+            return queueMode;
     }
 
     /**
@@ -409,7 +422,8 @@ public class Registration implements Serializable {
         private Date lastUpdate;
         private Long lifeTimeInSec;
         private String smsNumber;
-        private BindingMode bindingMode;
+        private EnumSet<BindingMode> bindingMode;
+        private Boolean queueMode;
         private String lwM2mVersion;
         private Link[] objectLinks;
         private Map<Integer, String> supportedObjects;
@@ -445,11 +459,16 @@ public class Registration implements Serializable {
             return this;
         }
 
-        public Builder bindingMode(BindingMode bindingMode) {
+        public Builder bindingMode(EnumSet<BindingMode> bindingMode) {
             this.bindingMode = bindingMode;
             return this;
         }
 
+        public Builder queueMode(Boolean queueMode) {
+            this.queueMode = queueMode;
+            return this;
+        }
+
         public Builder lwM2mVersion(String lwM2mVersion) {
             this.lwM2mVersion = lwM2mVersion;
             return this;
@@ -473,8 +492,8 @@ public class Registration implements Serializable {
         public Registration build() {
             return new Registration(Builder.this.registrationId, Builder.this.endpoint, Builder.this.identity,
                     Builder.this.lwM2mVersion, Builder.this.lifeTimeInSec, Builder.this.smsNumber, this.bindingMode,
-                    this.objectLinks, this.registrationDate, this.lastUpdate, this.additionalRegistrationAttributes,
-                    this.supportedObjects);
+                    this.queueMode, this.objectLinks, this.registrationDate, this.lastUpdate,
+                    this.additionalRegistrationAttributes, this.supportedObjects);
         }
 
     }
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/registration/RegistrationHandler.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/registration/RegistrationHandler.java
index 674183c5..25393653 100644
--- a/leshan-server-core/src/main/java/org/eclipse/leshan/server/registration/RegistrationHandler.java
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/registration/RegistrationHandler.java
@@ -56,8 +56,9 @@ public class RegistrationHandler {
                 registrationIdProvider.getRegistrationId(registerRequest), registerRequest.getEndpointName(), sender);
 
         builder.lwM2mVersion(registerRequest.getLwVersion()).lifeTimeInSec(registerRequest.getLifetime())
-                .bindingMode(registerRequest.getBindingMode()).objectLinks(registerRequest.getObjectLinks())
-                .smsNumber(registerRequest.getSmsNumber()).registrationDate(new Date()).lastUpdate(new Date())
+                .bindingMode(registerRequest.getBindingMode()).queueMode(registerRequest.getQueueMode())
+                .objectLinks(registerRequest.getObjectLinks()).smsNumber(registerRequest.getSmsNumber())
+                .registrationDate(new Date()).lastUpdate(new Date())
                 .additionalRegistrationAttributes(registerRequest.getAdditionalAttributes());
 
         // We must check if the client is using the right identity.
@@ -100,6 +101,9 @@ public class RegistrationHandler {
             return new SendableResponse<>(UpdateResponse.badRequest("forbidden"));
         }
 
+        // validate request
+        updateRequest.validate(registration.getLwM2mVersion());
+
         // Create update
         final RegistrationUpdate update = new RegistrationUpdate(updateRequest.getRegistrationId(), sender,
                 updateRequest.getLifeTimeInSec(), updateRequest.getSmsNumber(), updateRequest.getBindingMode(),
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/registration/RegistrationUpdate.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/registration/RegistrationUpdate.java
index acc24c81..431b45c5 100644
--- a/leshan-server-core/src/main/java/org/eclipse/leshan/server/registration/RegistrationUpdate.java
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/registration/RegistrationUpdate.java
@@ -20,6 +20,7 @@ import java.net.InetAddress;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Date;
+import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -38,12 +39,12 @@ public class RegistrationUpdate {
     private final Identity identity;
     private final Long lifeTimeInSec;
     private final String smsNumber;
-    private final BindingMode bindingMode;
+    private final EnumSet<BindingMode> bindingMode;
     private final Link[] objectLinks;
     private final Map<String, String> additionalAttributes;
 
     public RegistrationUpdate(String registrationId, Identity identity, Long lifeTimeInSec, String smsNumber,
-            BindingMode bindingMode, Link[] objectLinks, Map<String, String> additionalAttributes) {
+            EnumSet<BindingMode> bindingMode, Link[] objectLinks, Map<String, String> additionalAttributes) {
         Validate.notNull(registrationId);
         Validate.notNull(identity);
         this.registrationId = registrationId;
@@ -68,7 +69,7 @@ public class RegistrationUpdate {
         Identity identity = this.identity != null ? this.identity : registration.getIdentity();
         Link[] linkObject = this.objectLinks != null ? this.objectLinks : registration.getObjectLinks();
         long lifeTimeInSec = this.lifeTimeInSec != null ? this.lifeTimeInSec : registration.getLifeTimeInSec();
-        BindingMode bindingMode = this.bindingMode != null ? this.bindingMode : registration.getBindingMode();
+        EnumSet<BindingMode> bindingMode = this.bindingMode != null ? this.bindingMode : registration.getBindingMode();
         String smsNumber = this.smsNumber != null ? this.smsNumber : registration.getSmsNumber();
 
         Map<String, String> additionalAttributes = this.additionalAttributes.isEmpty()
@@ -83,8 +84,9 @@ public class RegistrationUpdate {
                 identity);
 
         builder.lwM2mVersion(registration.getLwM2mVersion()).lifeTimeInSec(lifeTimeInSec).smsNumber(smsNumber)
-                .bindingMode(bindingMode).objectLinks(linkObject).registrationDate(registration.getRegistrationDate())
-                .lastUpdate(lastUpdate).additionalRegistrationAttributes(additionalAttributes);
+                .bindingMode(bindingMode).queueMode(registration.getQueueMode()).objectLinks(linkObject)
+                .registrationDate(registration.getRegistrationDate()).lastUpdate(lastUpdate)
+                .additionalRegistrationAttributes(additionalAttributes);
 
         return builder.build();
 
@@ -114,7 +116,7 @@ public class RegistrationUpdate {
         return smsNumber;
     }
 
-    public BindingMode getBindingMode() {
+    public EnumSet<BindingMode> getBindingMode() {
         return bindingMode;
     }
 
diff --git a/leshan-server-demo/src/main/java/org/eclipse/leshan/server/demo/servlet/json/RegistrationSerializer.java b/leshan-server-demo/src/main/java/org/eclipse/leshan/server/demo/servlet/json/RegistrationSerializer.java
index dce951a5..deca7ed0 100644
--- a/leshan-server-demo/src/main/java/org/eclipse/leshan/server/demo/servlet/json/RegistrationSerializer.java
+++ b/leshan-server-demo/src/main/java/org/eclipse/leshan/server/demo/servlet/json/RegistrationSerializer.java
@@ -17,6 +17,7 @@ package org.eclipse.leshan.server.demo.servlet.json;
 
 import java.lang.reflect.Type;
 
+import org.eclipse.leshan.core.request.BindingMode;
 import org.eclipse.leshan.server.queue.PresenceService;
 import org.eclipse.leshan.server.registration.Registration;
 
@@ -45,7 +46,7 @@ public class RegistrationSerializer implements JsonSerializer<Registration> {
         element.addProperty("smsNumber", src.getSmsNumber());
         element.addProperty("lwM2mVersion", src.getLwM2mVersion());
         element.addProperty("lifetime", src.getLifeTimeInSec());
-        element.addProperty("bindingMode", src.getBindingMode().toString());
+        element.addProperty("bindingMode", BindingMode.toString(src.getBindingMode()));
         element.add("rootPath", context.serialize(src.getRootPath()));
         element.add("objectLinks", context.serialize(src.getSortedObjectLinks()));
         element.add("secure", context.serialize(src.getIdentity().isSecure()));
diff --git a/leshan-server-redis/src/main/java/org/eclipse/leshan/server/redis/serialization/RegistrationSerDes.java b/leshan-server-redis/src/main/java/org/eclipse/leshan/server/redis/serialization/RegistrationSerDes.java
index 88e0219e..2967f0bf 100644
--- a/leshan-server-redis/src/main/java/org/eclipse/leshan/server/redis/serialization/RegistrationSerDes.java
+++ b/leshan-server-redis/src/main/java/org/eclipse/leshan/server/redis/serialization/RegistrationSerDes.java
@@ -43,7 +43,9 @@ public class RegistrationSerDes {
             o.add("sms", r.getSmsNumber());
         }
         o.add("ver", r.getLwM2mVersion());
-        o.add("bnd", r.getBindingMode().name());
+        o.add("bnd", BindingMode.toString(r.getBindingMode()));
+        if (r.getQueueMode() != null)
+            o.add("qm", r.getQueueMode());
         o.add("ep", r.getEndpoint());
         o.add("regId", r.getId());
 
@@ -84,7 +86,9 @@ public class RegistrationSerDes {
     public static Registration deserialize(JsonObject jObj) {
         Registration.Builder b = new Registration.Builder(jObj.getString("regId", null), jObj.getString("ep", null),
                 IdentitySerDes.deserialize(jObj.get("identity").asObject()));
-        b.bindingMode(BindingMode.valueOf(jObj.getString("bnd", null)));
+        b.bindingMode(BindingMode.parse(jObj.getString("bnd", null)));
+        if (jObj.get("qm") != null)
+            b.queueMode(jObj.getBoolean("qm", false));
         b.lastUpdate(new Date(jObj.getLong("lastUp", 0)));
         b.lifeTimeInSec(jObj.getLong("lt", 0));
         b.lwM2mVersion(jObj.getString("ver", Version.getDefault().toString()));


Test patch:
diff --git a/leshan-client-core/src/test/java/org/eclipse/leshan/client/util/LinkFormatHelperTest.java b/leshan-client-core/src/test/java/org/eclipse/leshan/client/util/LinkFormatHelperTest.java
index 6d52a1af..2f36a430 100644
--- a/leshan-client-core/src/test/java/org/eclipse/leshan/client/util/LinkFormatHelperTest.java
+++ b/leshan-client-core/src/test/java/org/eclipse/leshan/client/util/LinkFormatHelperTest.java
@@ -34,7 +34,6 @@ import org.eclipse.leshan.core.Link;
 import org.eclipse.leshan.core.LwM2m.Version;
 import org.eclipse.leshan.core.model.ObjectLoader;
 import org.eclipse.leshan.core.model.ObjectModel;
-import org.eclipse.leshan.core.request.BindingMode;
 import org.eclipse.leshan.core.request.ContentFormat;
 import org.junit.Test;
 
@@ -189,7 +188,7 @@ public class LinkFormatHelperTest {
     @Test
     public void encode_bootstrap_server_object() {
         Map<Integer, LwM2mInstanceEnabler> instancesMap = new HashMap<>();
-        instancesMap.put(0, new Server(333, 120, BindingMode.UQ, false));
+        instancesMap.put(0, new Server(333, 120));
         ObjectEnabler objectEnabler = new ObjectEnabler(1, getObjectModel(1), instancesMap, null,
                 ContentFormat.DEFAULT);
 
@@ -202,7 +201,7 @@ public class LinkFormatHelperTest {
     @Test
     public void encode_bootstrap_server_object_with_version() {
         Map<Integer, LwM2mInstanceEnabler> instancesMap = new HashMap<>();
-        instancesMap.put(0, new Server(333, 120, BindingMode.UQ, false));
+        instancesMap.put(0, new Server(333, 120));
         ObjectEnabler objectEnabler = new ObjectEnabler(1, getVersionedObjectModel(1, "2.0"), instancesMap, null,
                 ContentFormat.DEFAULT);
 
@@ -245,7 +244,7 @@ public class LinkFormatHelperTest {
 
         // object 1
         Map<Integer, LwM2mInstanceEnabler> serverInstances = new HashMap<>();
-        serverInstances.put(0, new Server(333, 120, BindingMode.UQ, false));
+        serverInstances.put(0, new Server(333, 120));
         ObjectEnabler serverObjectEnabler = new ObjectEnabler(1, getVersionedObjectModel(1, "2.0"), serverInstances,
                 null, ContentFormat.DEFAULT);
         objectEnablers.add(serverObjectEnabler);
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/BootstrapIntegrationTestHelper.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/BootstrapIntegrationTestHelper.java
index bbe1f175..0069c7c2 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/BootstrapIntegrationTestHelper.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/BootstrapIntegrationTestHelper.java
@@ -234,7 +234,7 @@ public class BootstrapIntegrationTestHelper extends SecureIntegrationTestHelper
         // Initialize LWM2M Object Tree
         initializer.setInstancesForObject(LwM2mId.SECURITY, security);
         initializer.setInstancesForObject(LwM2mId.DEVICE,
-                new Device("Eclipse Leshan", IntegrationTestHelper.MODEL_NUMBER, "12345", "U"));
+                new Device("Eclipse Leshan", IntegrationTestHelper.MODEL_NUMBER, "12345"));
         initializer.setClassForObject(LwM2mId.SERVER, DummyInstanceEnabler.class);
         createClient(initializer, additionalAttributes);
     }
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/IntegrationTestHelper.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/IntegrationTestHelper.java
index abdc2881..8def4b29 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/IntegrationTestHelper.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/IntegrationTestHelper.java
@@ -32,6 +32,7 @@ import org.eclipse.leshan.client.californium.LeshanClient;
 import org.eclipse.leshan.client.californium.LeshanClientBuilder;
 import org.eclipse.leshan.client.object.Device;
 import org.eclipse.leshan.client.object.Security;
+import org.eclipse.leshan.client.object.Server;
 import org.eclipse.leshan.client.resource.DummyInstanceEnabler;
 import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
 import org.eclipse.leshan.client.resource.ObjectsInitializer;
@@ -46,7 +47,6 @@ import org.eclipse.leshan.core.model.ResourceModel.Type;
 import org.eclipse.leshan.core.model.StaticModel;
 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder;
-import org.eclipse.leshan.core.request.BindingMode;
 import org.eclipse.leshan.core.response.ExecuteResponse;
 import org.eclipse.leshan.server.californium.LeshanServer;
 import org.eclipse.leshan.server.californium.LeshanServerBuilder;
@@ -151,8 +151,8 @@ public class IntegrationTestHelper {
             super();
         }
 
-        public TestDevice(String manufacturer, String modelNumber, String serialNumber, String supportedBinding) {
-            super(manufacturer, modelNumber, serialNumber, supportedBinding);
+        public TestDevice(String manufacturer, String modelNumber, String serialNumber) {
+            super(manufacturer, modelNumber, serialNumber);
         }
 
         @Override
@@ -171,9 +171,8 @@ public class IntegrationTestHelper {
         initializer.setInstancesForObject(LwM2mId.SECURITY, Security.noSec(
                 "coap://" + server.getUnsecuredAddress().getHostString() + ":" + server.getUnsecuredAddress().getPort(),
                 12345));
-        initializer.setInstancesForObject(LwM2mId.SERVER,
-                new org.eclipse.leshan.client.object.Server(12345, LIFETIME, BindingMode.U, false));
-        initializer.setInstancesForObject(LwM2mId.DEVICE, new TestDevice("Eclipse Leshan", MODEL_NUMBER, "12345", "U"));
+        initializer.setInstancesForObject(LwM2mId.SERVER, new Server(12345, LIFETIME));
+        initializer.setInstancesForObject(LwM2mId.DEVICE, new TestDevice("Eclipse Leshan", MODEL_NUMBER, "12345"));
         initializer.setClassForObject(LwM2mId.ACCESS_CONTROL, DummyInstanceEnabler.class);
         initializer.setInstancesForObject(TEST_OBJECT_ID, new DummyInstanceEnabler(0),
                 new SimpleInstanceEnabler(1, OPAQUE_RESOURCE_ID, new byte[0]));
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/LockStepLwM2mClient.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/LockStepLwM2mClient.java
index 28afe580..3fedf1c5 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/LockStepLwM2mClient.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/LockStepLwM2mClient.java
@@ -18,7 +18,9 @@ package org.eclipse.leshan.integration.tests;
 import java.net.InetSocketAddress;
 import java.util.Random;
 
+import org.eclipse.californium.core.coap.Message;
 import org.eclipse.californium.core.coap.Request;
+import org.eclipse.californium.core.coap.Token;
 import org.eclipse.californium.core.network.serialization.UdpDataSerializer;
 import org.eclipse.californium.core.test.lockstep.LockstepEndpoint;
 import org.eclipse.californium.elements.RawData;
@@ -37,7 +39,7 @@ public class LockStepLwM2mClient extends LockstepEndpoint {
         this.destination = destination;
     }
 
-    public void sendLwM2mRequest(UplinkRequest<? extends LwM2mResponse> lwm2mReq) {
+    public Request createCoapRequest(UplinkRequest<? extends LwM2mResponse> lwm2mReq) {
         // create CoAP request
         CoapRequestBuilder coapRequestBuilder = new CoapRequestBuilder(Identity.unsecure(destination));
         lwm2mReq.accept(coapRequestBuilder);
@@ -45,12 +47,21 @@ public class LockStepLwM2mClient extends LockstepEndpoint {
         byte[] token = new byte[8];
         r.nextBytes(token);
         coapReq.setToken(token);
+        coapReq.setMID(r.nextInt(Message.MAX_MID));
+        return coapReq;
+    }
+
+    public Token sendLwM2mRequest(UplinkRequest<? extends LwM2mResponse> lwm2mReq) {
+        return sendCoapRequest(createCoapRequest(lwm2mReq));
+    }
 
+    public Token sendCoapRequest(Request coapReq) {
         // serialize request
         UdpDataSerializer serializer = new UdpDataSerializer();
         RawData raw = serializer.serializeRequest(coapReq);
 
         // send it
         super.send(raw);
+        return coapReq.getToken();
     }
 }
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/FailingTest.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/LockStepTest.java
similarity index 51%
rename from leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/FailingTest.java
rename to leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/LockStepTest.java
index e91ac85a..397cffe8 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/FailingTest.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/LockStepTest.java
@@ -17,17 +17,22 @@ package org.eclipse.leshan.integration.tests;
 
 import static org.junit.Assert.assertEquals;
 
+import java.util.EnumSet;
 import java.util.concurrent.Callable;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 
+import org.eclipse.californium.core.coap.CoAP.ResponseCode;
 import org.eclipse.californium.core.coap.CoAP.Type;
+import org.eclipse.californium.core.coap.Request;
+import org.eclipse.californium.core.coap.Token;
 import org.eclipse.californium.core.network.config.NetworkConfig;
 import org.eclipse.leshan.core.Link;
 import org.eclipse.leshan.core.request.BindingMode;
 import org.eclipse.leshan.core.request.ReadRequest;
 import org.eclipse.leshan.core.request.RegisterRequest;
+import org.eclipse.leshan.core.request.UpdateRequest;
 import org.eclipse.leshan.core.request.exception.TimeoutException;
 import org.eclipse.leshan.core.response.ReadResponse;
 import org.eclipse.leshan.server.californium.LeshanServerBuilder;
@@ -36,7 +41,7 @@ import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
-public class FailingTest {
+public class LockStepTest {
 
     public IntegrationTestHelper helper = new IntegrationTestHelper() {
         @Override
@@ -67,13 +72,97 @@ public class FailingTest {
         helper.dispose();
     }
 
+    @Test
+    public void register_with_uq_binding_in_lw_1_0() throws Exception {
+        // Register client
+        LockStepLwM2mClient client = new LockStepLwM2mClient(helper.server.getUnsecuredAddress());
+        Token token = client.sendLwM2mRequest(new RegisterRequest(helper.getCurrentEndpoint(), 60l, "1.0",
+                EnumSet.of(BindingMode.U, BindingMode.Q), null, null, Link.parse("</1>,</2>,</3>".getBytes()), null));
+        client.expectResponse().token(token).code(ResponseCode.CREATED).go();
+        helper.waitForRegistrationAtServerSide(1);
+    }
+
+    @Test
+    public void register_with_ut_binding_in_lw_1_1() throws Exception {
+        // Register client
+        LockStepLwM2mClient client = new LockStepLwM2mClient(helper.server.getUnsecuredAddress());
+        Token token = client.sendLwM2mRequest(new RegisterRequest(helper.getCurrentEndpoint(), 60l, "1.1",
+                EnumSet.of(BindingMode.U, BindingMode.T), null, null, Link.parse("</1>,</2>,</3>".getBytes()), null));
+        client.expectResponse().token(token).code(ResponseCode.CREATED).go();
+        helper.waitForRegistrationAtServerSide(1);
+    }
+
+    @Test
+    public void register_update_with_invalid_binding_for_lw_1_1() throws Exception {
+        LockStepLwM2mClient client = new LockStepLwM2mClient(helper.server.getUnsecuredAddress());
+
+        // register with valid binding for 1.1
+        RegisterRequest validRegisterRequest = new RegisterRequest(helper.getCurrentEndpoint(), 60l, "1.1",
+                EnumSet.of(BindingMode.U), null, null, Link.parse("</1>,</2>,</3>".getBytes()), null);
+        Token token = client.sendLwM2mRequest(validRegisterRequest);
+        client.expectResponse().token(token).code(ResponseCode.CREATED).go();
+        helper.waitForRegistrationAtServerSide(1);
+
+        // update with valid binding for 1.1
+        UpdateRequest validUpdateRequest = new UpdateRequest("/rd/" + helper.getLastRegistration().getId(), 60l, null,
+                EnumSet.of(BindingMode.U), null, null);
+        token = client.sendLwM2mRequest(validUpdateRequest);
+        client.expectResponse().token(token).code(ResponseCode.CHANGED).go();
+
+        // register with invalid binding for 1.1
+        Request invalidRegisterRequest = client.createCoapRequest(validRegisterRequest);
+        invalidRegisterRequest.getOptions().removeUriQuery("b=U");
+        invalidRegisterRequest.getOptions().addUriQuery("b=UQ");
+        token = client.sendCoapRequest(invalidRegisterRequest);
+        client.expectResponse().token(token).code(ResponseCode.BAD_REQUEST).go();
+
+        // update with invalid binding for 1.1
+        Request invalidUpdateRequest = client.createCoapRequest(validRegisterRequest);
+        invalidUpdateRequest.getOptions().removeUriQuery("b=U");
+        invalidUpdateRequest.getOptions().addUriQuery("b=UQ");
+        token = client.sendCoapRequest(invalidUpdateRequest);
+        client.expectResponse().token(token).code(ResponseCode.BAD_REQUEST).go();
+    }
+
+    @Test
+    public void register_update_with_invalid_binding_for_lw_1_0() throws Exception {
+        LockStepLwM2mClient client = new LockStepLwM2mClient(helper.server.getUnsecuredAddress());
+
+        // register with valid binding for 1.0
+        RegisterRequest validRegisterRequest = new RegisterRequest(helper.getCurrentEndpoint(), 60l, "1.0",
+                EnumSet.of(BindingMode.U), null, null, Link.parse("</1>,</2>,</3>".getBytes()), null);
+        Token token = client.sendLwM2mRequest(validRegisterRequest);
+        client.expectResponse().token(token).code(ResponseCode.CREATED).go();
+        helper.waitForRegistrationAtServerSide(1);
+
+        // update with valid binding for 1.0
+        UpdateRequest validUpdateRequest = new UpdateRequest("/rd/" + helper.getLastRegistration().getId(), 60l, null,
+                EnumSet.of(BindingMode.U), null, null);
+        token = client.sendLwM2mRequest(validUpdateRequest);
+        client.expectResponse().token(token).code(ResponseCode.CHANGED).go();
+
+        // register with invalid binding for 1.0
+        Request invalidRegisterRequest = client.createCoapRequest(validRegisterRequest);
+        invalidRegisterRequest.getOptions().removeUriQuery("b=U");
+        invalidRegisterRequest.getOptions().addUriQuery("b=UT");
+        token = client.sendCoapRequest(invalidRegisterRequest);
+        client.expectResponse().token(token).code(ResponseCode.BAD_REQUEST).go();
+
+        // update with invalid binding for 1.0
+        Request invalidUpdateRequest = client.createCoapRequest(validRegisterRequest);
+        invalidUpdateRequest.getOptions().removeUriQuery("b=U");
+        invalidUpdateRequest.getOptions().addUriQuery("b=UT");
+        token = client.sendCoapRequest(invalidUpdateRequest);
+        client.expectResponse().token(token).code(ResponseCode.BAD_REQUEST).go();
+    }
+
     @Test
     public void sync_send_without_acknowleged() throws Exception {
         // Register client
         LockStepLwM2mClient client = new LockStepLwM2mClient(helper.server.getUnsecuredAddress());
-        client.sendLwM2mRequest(new RegisterRequest(helper.getCurrentEndpoint(), 60l, null, BindingMode.U, null,
-                Link.parse("</1>,</2>,</3>".getBytes()), null));
-        client.expectResponse().go();
+        Token token = client.sendLwM2mRequest(new RegisterRequest(helper.getCurrentEndpoint(), 60l, "1.1",
+                EnumSet.of(BindingMode.U), null, null, Link.parse("</1>,</2>,</3>".getBytes()), null));
+        client.expectResponse().token(token).go();
         helper.waitForRegistrationAtServerSide(1);
 
         // Send read
@@ -93,9 +182,9 @@ public class FailingTest {
     public void sync_send_with_acknowleged_request_without_response() throws Exception {
         // Register client
         LockStepLwM2mClient client = new LockStepLwM2mClient(helper.server.getUnsecuredAddress());
-        client.sendLwM2mRequest(new RegisterRequest(helper.getCurrentEndpoint(), 60l, null, BindingMode.U, null,
-                Link.parse("</1>,</2>,</3>".getBytes()), null));
-        client.expectResponse().go();
+        Token token = client.sendLwM2mRequest(new RegisterRequest(helper.getCurrentEndpoint(), 60l, "1.1",
+                EnumSet.of(BindingMode.U), null, null, Link.parse("</1>,</2>,</3>".getBytes()), null));
+        client.expectResponse().token(token).go();
         helper.waitForRegistrationAtServerSide(1);
 
         // Send read
@@ -122,9 +211,9 @@ public class FailingTest {
     public void async_send_without_acknowleged() throws Exception {
         // register client
         LockStepLwM2mClient client = new LockStepLwM2mClient(helper.server.getUnsecuredAddress());
-        client.sendLwM2mRequest(new RegisterRequest(helper.getCurrentEndpoint(), 60l, null, BindingMode.U, null,
-                Link.parse("</1>,</2>,</3>".getBytes()), null));
-        client.expectResponse().go();
+        Token token = client.sendLwM2mRequest(new RegisterRequest(helper.getCurrentEndpoint(), 60l, "1.1",
+                EnumSet.of(BindingMode.U), null, null, Link.parse("</1>,</2>,</3>".getBytes()), null));
+        client.expectResponse().token(token).go();
         helper.waitForRegistrationAtServerSide(1);
 
         // send read
@@ -141,9 +230,9 @@ public class FailingTest {
     public void async_send_with_acknowleged_request_without_response() throws Exception {
         // register client
         LockStepLwM2mClient client = new LockStepLwM2mClient(helper.server.getUnsecuredAddress());
-        client.sendLwM2mRequest(new RegisterRequest(helper.getCurrentEndpoint(), 60l, null, BindingMode.U, null,
-                Link.parse("</1>,</2>,</3>".getBytes()), null));
-        client.expectResponse().go();
+        Token token = client.sendLwM2mRequest(new RegisterRequest(helper.getCurrentEndpoint(), 60l, "1.1",
+                EnumSet.of(BindingMode.U), null, null, Link.parse("</1>,</2>,</3>".getBytes()), null));
+        client.expectResponse().token(token).go();
         helper.waitForRegistrationAtServerSide(1);
 
         // send read
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/QueueModeIntegrationTestHelper.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/QueueModeIntegrationTestHelper.java
index e3bf3b3f..08133ab9 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/QueueModeIntegrationTestHelper.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/QueueModeIntegrationTestHelper.java
@@ -23,6 +23,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
 import org.eclipse.leshan.client.californium.LeshanClientBuilder;
+import org.eclipse.leshan.client.engine.DefaultRegistrationEngineFactory;
 import org.eclipse.leshan.client.object.Security;
 import org.eclipse.leshan.client.object.Server;
 import org.eclipse.leshan.client.resource.DummyInstanceEnabler;
@@ -30,7 +31,6 @@ import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
 import org.eclipse.leshan.client.resource.ObjectsInitializer;
 import org.eclipse.leshan.core.LwM2mId;
 import org.eclipse.leshan.core.model.StaticModel;
-import org.eclipse.leshan.core.request.BindingMode;
 import org.eclipse.leshan.core.response.LwM2mResponse;
 import org.eclipse.leshan.server.californium.LeshanServerBuilder;
 import org.eclipse.leshan.server.queue.StaticClientAwakeTimeProvider;
@@ -65,15 +65,15 @@ public class QueueModeIntegrationTestHelper extends IntegrationTestHelper {
         initializer.setInstancesForObject(LwM2mId.SECURITY, Security.noSec(
                 "coap://" + server.getUnsecuredAddress().getHostString() + ":" + server.getUnsecuredAddress().getPort(),
                 12345));
-        initializer.setInstancesForObject(LwM2mId.SERVER, new Server(12345, LIFETIME, BindingMode.UQ, false));
-        initializer.setInstancesForObject(LwM2mId.DEVICE,
-                new TestDevice("Eclipse Leshan", MODEL_NUMBER, "12345", "UQ"));
+        initializer.setInstancesForObject(LwM2mId.SERVER, new Server(12345, LIFETIME));
+        initializer.setInstancesForObject(LwM2mId.DEVICE, new TestDevice("Eclipse Leshan", MODEL_NUMBER, "12345"));
         initializer.setClassForObject(LwM2mId.ACCESS_CONTROL, DummyInstanceEnabler.class);
         initializer.setDummyInstancesForObject(2000);
         List<LwM2mObjectEnabler> objects = initializer.createAll();
 
         // Build Client
         LeshanClientBuilder builder = new LeshanClientBuilder(currentEndpointIdentifier.get());
+        builder.setRegistrationEngineFactory(new DefaultRegistrationEngineFactory().setQueueMode(true));
         builder.setObjects(objects);
         client = builder.build();
         setupClientMonitoring();
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/SecureIntegrationTestHelper.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/SecureIntegrationTestHelper.java
index 23ef0113..a316560e 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/SecureIntegrationTestHelper.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/SecureIntegrationTestHelper.java
@@ -52,6 +52,7 @@ import org.eclipse.californium.scandium.dtls.PskPublicInformation;
 import org.eclipse.californium.scandium.dtls.cipher.CipherSuite;
 import org.eclipse.californium.scandium.dtls.pskstore.PskStore;
 import org.eclipse.leshan.client.californium.LeshanClientBuilder;
+import org.eclipse.leshan.client.engine.DefaultRegistrationEngineFactory;
 import org.eclipse.leshan.client.object.Device;
 import org.eclipse.leshan.client.object.Security;
 import org.eclipse.leshan.client.object.Server;
@@ -60,7 +61,6 @@ import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
 import org.eclipse.leshan.client.resource.ObjectsInitializer;
 import org.eclipse.leshan.core.LwM2mId;
 import org.eclipse.leshan.core.californium.EndpointFactory;
-import org.eclipse.leshan.core.request.BindingMode;
 import org.eclipse.leshan.core.util.Hex;
 import org.eclipse.leshan.core.util.X509CertUtil;
 import org.eclipse.leshan.server.californium.LeshanServerBuilder;
@@ -205,14 +205,14 @@ public class SecureIntegrationTestHelper extends IntegrationTestHelper {
                         "coaps://" + server.getSecuredAddress().getHostString() + ":"
                                 + server.getSecuredAddress().getPort(),
                         12345, GOOD_PSK_ID.getBytes(StandardCharsets.UTF_8), GOOD_PSK_KEY));
-        initializer.setInstancesForObject(LwM2mId.SERVER,
-                new Server(12345, LIFETIME, queueMode ? BindingMode.UQ : BindingMode.U, false));
-        initializer.setInstancesForObject(LwM2mId.DEVICE, new Device("Eclipse Leshan", MODEL_NUMBER, "12345", "U"));
+        initializer.setInstancesForObject(LwM2mId.SERVER, new Server(12345, LIFETIME));
+        initializer.setInstancesForObject(LwM2mId.DEVICE, new Device("Eclipse Leshan", MODEL_NUMBER, "12345"));
         initializer.setDummyInstancesForObject(LwM2mId.ACCESS_CONTROL);
         List<LwM2mObjectEnabler> objects = initializer.createAll();
 
         InetSocketAddress clientAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
         LeshanClientBuilder builder = new LeshanClientBuilder(getCurrentEndpoint());
+        builder.setRegistrationEngineFactory(new DefaultRegistrationEngineFactory().setQueueMode(queueMode));
         builder.setLocalAddress(clientAddress.getHostString(), clientAddress.getPort());
         builder.setObjects(objects);
         builder.setDtlsConfig(
@@ -269,8 +269,8 @@ public class SecureIntegrationTestHelper extends IntegrationTestHelper {
                 "coaps://" + server.getSecuredAddress().getHostString() + ":" + server.getSecuredAddress().getPort(),
                 12345, clientPublicKey.getEncoded(), clientPrivateKey.getEncoded(),
                 useServerCertificate ? serverX509Cert.getPublicKey().getEncoded() : serverPublicKey.getEncoded()));
-        initializer.setInstancesForObject(LwM2mId.SERVER, new Server(12345, LIFETIME, BindingMode.U, false));
-        initializer.setInstancesForObject(LwM2mId.DEVICE, new Device("Eclipse Leshan", MODEL_NUMBER, "12345", "U"));
+        initializer.setInstancesForObject(LwM2mId.SERVER, new Server(12345, LIFETIME));
+        initializer.setInstancesForObject(LwM2mId.DEVICE, new Device("Eclipse Leshan", MODEL_NUMBER, "12345"));
         initializer.setClassForObject(LwM2mId.ACCESS_CONTROL, DummyInstanceEnabler.class);
         List<LwM2mObjectEnabler> objects = initializer.createAll();
 
@@ -314,8 +314,8 @@ public class SecureIntegrationTestHelper extends IntegrationTestHelper {
                                 + server.getSecuredAddress().getPort(),
                         12345, clientCertificate.getEncoded(), privatekey.getEncoded(),
                         serverCertificate.getEncoded()));
-        initializer.setInstancesForObject(LwM2mId.SERVER, new Server(12345, LIFETIME, BindingMode.U, false));
-        initializer.setInstancesForObject(LwM2mId.DEVICE, new Device("Eclipse Leshan", MODEL_NUMBER, "12345", "U"));
+        initializer.setInstancesForObject(LwM2mId.SERVER, new Server(12345, LIFETIME));
+        initializer.setInstancesForObject(LwM2mId.DEVICE, new Device("Eclipse Leshan", MODEL_NUMBER, "12345"));
         initializer.setClassForObject(LwM2mId.ACCESS_CONTROL, DummyInstanceEnabler.class);
         List<LwM2mObjectEnabler> objects = initializer.createAll();
 
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/SynchronousRegistrationListener.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/SynchronousRegistrationListener.java
index 5db6304c..16df2b64 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/SynchronousRegistrationListener.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/SynchronousRegistrationListener.java
@@ -34,6 +34,8 @@ public class SynchronousRegistrationListener implements RegistrationListener {
 
     @Override
     public void registered(Registration reg, Registration previousReg, Collection<Observation> previousObsersations) {
+        System.out.println(reg);
+
         if (accept(reg)) {
             lastRegistration = reg;
             registerLatch.countDown();
@@ -42,6 +44,7 @@ public class SynchronousRegistrationListener implements RegistrationListener {
 
     @Override
     public void updated(RegistrationUpdate update, Registration updatedReg, Registration previousReg) {
+        System.out.println(updatedReg);
         if (accept(updatedReg))
             updateLatch.countDown();
     }
@@ -49,6 +52,8 @@ public class SynchronousRegistrationListener implements RegistrationListener {
     @Override
     public void unregistered(Registration reg, Collection<Observation> observations, boolean expired,
             Registration newReg) {
+        System.out.println(reg);
+
         if (accept(reg))
             deregisterLatch.countDown();
     }
diff --git a/leshan-server-cf/src/test/java/org/eclipse/leshan/server/californium/LeshanServerTest.java b/leshan-server-cf/src/test/java/org/eclipse/leshan/server/californium/LeshanServerTest.java
index c0740d18..3ca66b62 100644
--- a/leshan-server-cf/src/test/java/org/eclipse/leshan/server/californium/LeshanServerTest.java
+++ b/leshan-server-cf/src/test/java/org/eclipse/leshan/server/californium/LeshanServerTest.java
@@ -18,6 +18,7 @@ package org.eclipse.leshan.server.californium;
 import static org.junit.Assert.assertEquals;
 
 import java.net.InetSocketAddress;
+import java.util.EnumSet;
 
 import org.eclipse.leshan.core.request.BindingMode;
 import org.eclipse.leshan.core.request.Identity;
@@ -82,7 +83,7 @@ public class LeshanServerTest {
 
     private void forceThreadsCreation(LeshanServer server) {
         Registration reg = new Registration.Builder("id", "endpoint", Identity.unsecure(new InetSocketAddress(5555)))
-                .bindingMode(BindingMode.UQ).build();
+                .bindingMode(EnumSet.of(BindingMode.U, BindingMode.Q)).build();
         // Force timer thread creation of preference service.
         ((PresenceServiceImpl) server.getPresenceService()).setAwake(reg);
         // Force time thread creation of CoapAsyncRequestObserver
diff --git a/leshan-server-cf/src/test/java/org/eclipse/leshan/server/californium/registration/InMemoryRegistrationStoreTest.java b/leshan-server-cf/src/test/java/org/eclipse/leshan/server/californium/registration/InMemoryRegistrationStoreTest.java
index aa1ca6e7..91ca39bd 100644
--- a/leshan-server-cf/src/test/java/org/eclipse/leshan/server/californium/registration/InMemoryRegistrationStoreTest.java
+++ b/leshan-server-cf/src/test/java/org/eclipse/leshan/server/californium/registration/InMemoryRegistrationStoreTest.java
@@ -17,11 +17,11 @@ package org.eclipse.leshan.server.californium.registration;
 
 import java.net.InetAddress;
 import java.nio.charset.StandardCharsets;
+import java.util.EnumSet;
 
 import org.eclipse.leshan.core.Link;
 import org.eclipse.leshan.core.request.BindingMode;
 import org.eclipse.leshan.core.request.Identity;
-import org.eclipse.leshan.server.californium.registration.InMemoryRegistrationStore;
 import org.eclipse.leshan.server.registration.Registration;
 import org.eclipse.leshan.server.registration.RegistrationStore;
 import org.eclipse.leshan.server.registration.RegistrationUpdate;
@@ -38,7 +38,7 @@ public class InMemoryRegistrationStoreTest {
     int port = 23452;
     Long lifetime = 10000L;
     String sms = "0171-32423545";
-    BindingMode binding = BindingMode.UQS;
+    EnumSet<BindingMode> binding = EnumSet.of(BindingMode.U, BindingMode.Q, BindingMode.S);
     Link[] objectLinks = Link.parse("</3>".getBytes(StandardCharsets.UTF_8));
     String registrationId = "4711";
     Registration registration;
diff --git a/leshan-server-core/src/test/java/org/eclipse/leshan/server/SerializationUtil.java b/leshan-server-core/src/test/java/org/eclipse/leshan/server/SerializationUtil.java
index 0dc3d008..8469f31a 100644
--- a/leshan-server-core/src/test/java/org/eclipse/leshan/server/SerializationUtil.java
+++ b/leshan-server-core/src/test/java/org/eclipse/leshan/server/SerializationUtil.java
@@ -57,7 +57,18 @@ public class SerializationUtil {
                 return true;
             }
 
-            if (Serializable.class.isAssignableFrom(clazz)) {
+            // For Collection, Map interface as most implementation is Serializable, checking if parameterized type is
+            // serializable should enough.
+            if (Collection.class.isAssignableFrom(clazz) || Map.class.isAssignableFrom(clazz)) {
+                for (Type t : ptype.getActualTypeArguments()) {
+                    if (!isSerializable(t, notSerializableObject, excludes)) {
+                        notSerializableObject.put(clazz, String
+                                .format("[%s] is parameterized with not serializable type [%s].", clazz.getName(), t));
+                        return false;
+                    }
+                }
+                return true;
+            } else if (Serializable.class.isAssignableFrom(clazz)) {
                 boolean isSerializable = true;
                 for (Field field : clazz.getDeclaredFields()) {
                     // check if this field is Serializable
@@ -68,25 +79,11 @@ public class SerializationUtil {
                     }
                 }
                 return isSerializable;
-            }
-            // we accept Collection, Map interface because most implementation is Serializable and checking if
-            // parameterized type is serializable should enough.
-            else if (clazz.isInterface()) {
-                if (Collection.class.isAssignableFrom(clazz) || Map.class.isAssignableFrom(clazz)) {
-                    for (Type t : ptype.getActualTypeArguments()) {
-                        if (!isSerializable(t, notSerializableObject, excludes)) {
-                            notSerializableObject.put(clazz, String.format(
-                                    "[%s] is parameterized with not serializable type [%s].", clazz.getName(), t));
-                            return false;
-                        }
-                    }
-                    return true;
-                } else {
-                    notSerializableObject.put(clazz, String.format(
-                            "[%s] interface is maybe serializable but we only support Collection and Map as parameterized interface class.",
-                            clazz));
-                    return false;
-                }
+            } else if (clazz.isInterface()) {
+                notSerializableObject.put(clazz, String.format(
+                        "[%s] interface is maybe serializable but we only support Collection and Map as parameterized interface class.",
+                        clazz));
+                return false;
             } else {
                 notSerializableObject.put(clazz,
                         String.format("[%s] is not primitive or does not implement Serializable.", clazz.getName()));
diff --git a/leshan-server-core/src/test/java/org/eclipse/leshan/server/queue/PresenceServiceTest.java b/leshan-server-core/src/test/java/org/eclipse/leshan/server/queue/PresenceServiceTest.java
index 2858f643..432ecee8 100644
--- a/leshan-server-core/src/test/java/org/eclipse/leshan/server/queue/PresenceServiceTest.java
+++ b/leshan-server-core/src/test/java/org/eclipse/leshan/server/queue/PresenceServiceTest.java
@@ -19,6 +19,7 @@ import static org.junit.Assert.*;
 
 import java.net.Inet4Address;
 import java.net.UnknownHostException;
+import java.util.EnumSet;
 
 import org.eclipse.leshan.core.request.BindingMode;
 import org.eclipse.leshan.core.request.Identity;
@@ -74,7 +75,7 @@ public class PresenceServiceTest {
         Registration.Builder builder = new Registration.Builder("ID", "urn:client",
                 Identity.unsecure(Inet4Address.getLoopbackAddress(), 12354));
 
-        Registration reg = builder.bindingMode(BindingMode.UQ).build();
+        Registration reg = builder.bindingMode(EnumSet.of(BindingMode.U, BindingMode.Q)).build();
         presenceService.setAwake(reg);
         return reg;
     }