diff --git a/app.js b/app.js
index d8e73ae53e0c..9207cf6caa35 100644
--- a/app.js
+++ b/app.js
@@ -22,8 +22,7 @@ import conference from './conference';
 import API from './modules/API/API';
 
 import translation from "./modules/translation/translation";
-// For remote control testing:
-// import remoteControlController from "./modules/remotecontrol/Controller";
+import remoteControl from "./modules/remotecontrol/remotecontrol";
 
 const APP = {
     // Used by do_external_connect.js if we receive the attach data after
@@ -61,7 +60,8 @@ const APP = {
      */
     ConferenceUrl : null,
     connection: null,
-    API
+    API,
+    remoteControl
 };
 
 // TODO The execution of the mobile app starts from react/index.native.js.
diff --git a/conference.js b/conference.js
index 73e762d3d99d..a51192de94f8 100644
--- a/conference.js
+++ b/conference.js
@@ -488,6 +488,7 @@ export default {
             }).then(([tracks, con]) => {
                 logger.log('initialized with %s local tracks', tracks.length);
                 APP.connection = connection = con;
+                APP.remoteControl.init();
                 this._bindConnectionFailedHandler(con);
                 this._createRoom(tracks);
                 this.isDesktopSharingEnabled =
diff --git a/modules/API/API.js b/modules/API/API.js
index b988c089f126..b9860d09b172 100644
--- a/modules/API/API.js
+++ b/modules/API/API.js
@@ -55,7 +55,9 @@ function initCommands() {
             APP.conference.toggleScreenSharing.bind(APP.conference),
         "video-hangup": () => APP.conference.hangup(),
         "email": APP.conference.changeLocalEmail,
-        "avatar-url": APP.conference.changeLocalAvatarUrl
+        "avatar-url": APP.conference.changeLocalAvatarUrl,
+        "remote-control-supported": isSupported =>
+            APP.remoteControl.onRemoteControlSupported(isSupported)
     };
     Object.keys(commands).forEach(function (key) {
         postis.listen(key, args => commands[key](...args));
@@ -94,7 +96,13 @@ function triggerEvent (name, object) {
     }
 }
 
-export default {
+class API {
+    /**
+     * Constructs new instance
+     * @constructor
+     */
+    constructor() { }
+
     /**
      * Initializes the APIConnector. Setups message event listeners that will
      * receive information from external applications that embed Jitsi Meet.
@@ -108,6 +116,13 @@ export default {
             return;
 
         enabled = true;
+    }
+
+    /**
+     * initializes postis library.
+     * @private
+     */
+    _initPostis() {
         let postisOptions = {
             window: target
         };
@@ -116,7 +131,7 @@ export default {
                 = "jitsi_meet_external_api_" + jitsi_meet_external_api_id;
         postis = postisInit(postisOptions);
         initCommands();
-    },
+    }
 
     /**
      * Notify external application (if API is enabled) that message was sent.
@@ -124,7 +139,7 @@ export default {
      */
     notifySendingChatMessage (body) {
         triggerEvent("outgoing-message", {"message": body});
-    },
+    }
 
     /**
      * Notify external application (if API is enabled) that
@@ -143,7 +158,7 @@ export default {
             "incoming-message",
             {"from": id, "nick": nick, "message": body, "stamp": ts}
         );
-    },
+    }
 
     /**
      * Notify external application (if API is enabled) that
@@ -152,7 +167,7 @@ export default {
      */
     notifyUserJoined (id) {
         triggerEvent("participant-joined", {id});
-    },
+    }
 
     /**
      * Notify external application (if API is enabled) that
@@ -161,7 +176,7 @@ export default {
      */
     notifyUserLeft (id) {
         triggerEvent("participant-left", {id});
-    },
+    }
 
     /**
      * Notify external application (if API is enabled) that
@@ -171,7 +186,7 @@ export default {
      */
     notifyDisplayNameChanged (id, displayName) {
         triggerEvent("display-name-change", {id, displayname: displayName});
-    },
+    }
 
     /**
      * Notify external application (if API is enabled) that
@@ -181,7 +196,7 @@ export default {
      */
     notifyConferenceJoined (room) {
         triggerEvent("video-conference-joined", {roomName: room});
-    },
+    }
 
     /**
      * Notify external application (if API is enabled) that
@@ -191,7 +206,7 @@ export default {
      */
     notifyConferenceLeft (room) {
         triggerEvent("video-conference-left", {roomName: room});
-    },
+    }
 
     /**
      * Notify external application (if API is enabled) that
@@ -199,7 +214,7 @@ export default {
      */
     notifyReadyToClose () {
         triggerEvent("video-ready-to-close", {});
-    },
+    }
 
     /**
      * Sends remote control event.
@@ -207,13 +222,15 @@ export default {
      */
     sendRemoteControlEvent(event) {
         sendMessage({method: "remote-control-event", params: event});
-    },
+    }
 
     /**
      * Removes the listeners.
      */
-    dispose: function () {
+    dispose () {
         if(enabled)
             postis.destroy();
     }
-};
+}
+
+export default new API();
diff --git a/modules/remotecontrol/Controller.js b/modules/remotecontrol/Controller.js
index 5650b4c7f173..56b739f11490 100644
--- a/modules/remotecontrol/Controller.js
+++ b/modules/remotecontrol/Controller.js
@@ -1,5 +1,7 @@
 /* global $, APP */
 import * as KeyCodes from "../keycode/keycode";
+import {EVENT_TYPES, API_EVENT_TYPE}
+    from "../../service/remotecontrol/Constants";
 
 /**
  * Extract the keyboard key from the keyboard event.
@@ -42,11 +44,21 @@ function getModifiers(event) {
  * It listens for mouse and keyboard events and sends them to the receiver
  * party of the remote control session.
  */
-class Controller {
+export default class Controller {
     /**
      * Creates new instance.
      */
-    constructor() {}
+    constructor() {
+        this.enabled = false;
+    }
+
+    /**
+     * Enables / Disables the remote control
+     * @param {boolean} enabled the new state.
+     */
+    enable(enabled) {
+        this.enabled = enabled;
+    }
 
     /**
      * Starts processing the mouse and keyboard events.
@@ -54,29 +66,34 @@ class Controller {
      * attaching the listeners on.
      */
     start(area) {
+        if(!this.enabled)
+            return;
         this.area = area;
         this.area.mousemove(event => {
             const position = this.area.position();
-            this._sendEvent({
-                type: "mousemove",
+            this._sendRemoteControlEvent({
+                type: EVENT_TYPES.mousemove,
                 x: (event.pageX - position.left)/this.area.width(),
                 y: (event.pageY - position.top)/this.area.height()
             });
         });
-        this.area.mousedown(this._onMouseClickHandler.bind(this, "mousedown"));
-        this.area.mouseup(this._onMouseClickHandler.bind(this, "mouseup"));
+        this.area.mousedown(this._onMouseClickHandler.bind(this,
+            EVENT_TYPES.mousedown));
+        this.area.mouseup(this._onMouseClickHandler.bind(this,
+            EVENT_TYPES.mouseup));
         this.area.dblclick(
-            this._onMouseClickHandler.bind(this, "mousedblclick"));
+            this._onMouseClickHandler.bind(this, EVENT_TYPES.mousedblclick));
         this.area.contextmenu(() => false);
         this.area[0].onmousewheel = event => {
-            this._sendEvent({
-                type: "mousescroll",
+            this._sendRemoteControlEvent({
+                type: EVENT_TYPES.mousescroll,
                 x: event.deltaX,
                 y: event.deltaY
             });
         };
-        $(window).keydown(this._onKeyPessHandler.bind(this, "keydown"));
-        $(window).keyup(this._onKeyPessHandler.bind(this, "keyup"));
+        $(window).keydown(this._onKeyPessHandler.bind(this,
+            EVENT_TYPES.keydown));
+        $(window).keyup(this._onKeyPessHandler.bind(this, EVENT_TYPES.keyup));
     }
 
     /**
@@ -99,7 +116,7 @@ class Controller {
      * @param {Event} event the mouse event.
      */
     _onMouseClickHandler(type, event) {
-        this._sendEvent({
+        this._sendRemoteControlEvent({
             type: type,
             button: event.which
         });
@@ -111,7 +128,7 @@ class Controller {
      * @param {Event} event the key event.
      */
     _onKeyPessHandler(type, event) {
-        this._sendEvent({
+        this._sendRemoteControlEvent({
             type: type,
             key: getKey(event),
             modifiers: getModifiers(event),
@@ -123,14 +140,13 @@ class Controller {
      * @param {Object} event the remote control event.
      */
     _sendRemoteControlEvent(event) {
+        if(!this.enabled)
+            return;
         try{
             APP.conference.sendEndpointMessage("",
-                {type: "remote-control-event", event});
+                {type: API_EVENT_TYPE, event});
         } catch (e) {
             // failed to send the event.
         }
     }
 }
-
-
-export default new Controller();
diff --git a/modules/remotecontrol/Receiver.js b/modules/remotecontrol/Receiver.js
index 144d5b57edfb..7785d6324fd8 100644
--- a/modules/remotecontrol/Receiver.js
+++ b/modules/remotecontrol/Receiver.js
@@ -1,4 +1,7 @@
 /* global APP, JitsiMeetJS */
+import {DISCO_REMOTE_CONTROL_FEATURE, API_EVENT_TYPE}
+    from "../../service/remotecontrol/Constants";
+
 const ConferenceEvents = JitsiMeetJS.events.conference;
 
 /**
@@ -7,20 +10,36 @@ const ConferenceEvents = JitsiMeetJS.events.conference;
  * API module. From there the events can be received from wrapper application
  * and executed.
  */
-class Receiver {
+export default class Receiver {
     /**
      * Creates new instance.
      * @constructor
      */
-    constructor() {}
+    constructor() {
+        this.enabled = false;
+    }
+
+    /**
+     * Enables / Disables the remote control
+     * @param {boolean} enabled the new state.
+     */
+    enable(enabled) {
+        if(this.enabled !== enabled && enabled === true) {
+            this.enabled = enabled;
+            // Announce remote control support.
+            APP.connection.addFeature(DISCO_REMOTE_CONTROL_FEATURE, true);
+        }
+    }
 
     /**
      * Attaches listener for ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED events.
      */
     start() {
-        APP.conference.addConferenceListener(
-            ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
-            this._onRemoteControlEvent);
+        if(this.enabled) {
+            APP.conference.addConferenceListener(
+                ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
+                this._onRemoteControlEvent);
+        }
     }
 
     /**
@@ -39,9 +58,7 @@ class Receiver {
      * @param {Object} event the remote control event.
      */
     _onRemoteControlEvent(participant, event) {
-        if(event.type === "remote-control-event")
+        if(event.type === API_EVENT_TYPE && this.enabled)
             APP.API.sendRemoteControlEvent(event.event);
     }
 }
-
-export default new Receiver();
diff --git a/modules/remotecontrol/RemoteControl.js b/modules/remotecontrol/RemoteControl.js
new file mode 100644
index 000000000000..2da4680dd3cf
--- /dev/null
+++ b/modules/remotecontrol/RemoteControl.js
@@ -0,0 +1,51 @@
+/* global APP, config */
+import Controller from "./Controller";
+import Receiver from "./Receiver";
+
+/**
+ * Implements the remote control functionality.
+ */
+class RemoteControl {
+    /**
+     * Constructs new instance. Creates controller and receiver properties.
+     * @constructor
+     */
+    constructor() {
+        this.controller = new Controller();
+        this.receiver = new Receiver();
+        this.enabled = false;
+        this.initialized = false;
+    }
+
+    /**
+     * Initializes the remote control - checks if the remote control should be
+     * enabled or not, initializes the API module.
+     */
+    init() {
+        if(config.disableRemoteControl || this.initialized) {
+            return;
+        }
+        this.initialized = true;
+        APP.API.init({
+            forceEnable: true,
+        });
+        this.controller.enable(true);
+    }
+
+    /**
+     * Handles API event for support for executing remote control events into
+     * the wrapper application.
+     * @param {boolean} isSupported true if the receiver side is supported by
+     * the wrapper application.
+     */
+    onRemoteControlSupported(isSupported) {
+        if(isSupported && !config.disableRemoteControl) {
+            this.enabled = true;
+            if(this.initialized) {
+                this.receiver.enable(true);
+            }
+        }
+    }
+}
+
+export default new RemoteControl();
diff --git a/modules/tokendata/TokenData.js b/modules/tokendata/TokenData.js
index 6e1856ddf69e..86438937b1fa 100644
--- a/modules/tokendata/TokenData.js
+++ b/modules/tokendata/TokenData.js
@@ -73,10 +73,6 @@ class TokenData{
 
         this.jwt = jwt;
 
-        //External API settings
-        this.externalAPISettings = {
-            forceEnable: true
-        };
         this._decode();
         // Use JWT param as token if there is not other token set and if the
         // iss field is not anonymous. If you want to pass data with JWT token
diff --git a/react/features/app/functions.web.js b/react/features/app/functions.web.js
index ec4bbee3af4e..77319fde8929 100644
--- a/react/features/app/functions.web.js
+++ b/react/features/app/functions.web.js
@@ -23,7 +23,7 @@ export function init() {
 
     APP.keyboardshortcut = KeyboardShortcut;
     APP.tokenData = getTokenData();
-    APP.API.init(APP.tokenData.externalAPISettings);
+    APP.API.init(APP.tokenData.jwt ? {forceEnable: true} : undefined);
 
     APP.translation.init(settings.getLanguage());
 }
diff --git a/service/remotecontrol/Constants.js b/service/remotecontrol/Constants.js
new file mode 100644
index 000000000000..34e776ea995b
--- /dev/null
+++ b/service/remotecontrol/Constants.js
@@ -0,0 +1,23 @@
+/**
+ * The value for the "var" attribute of feature tag in disco-info packets.
+ */
+export const DISCO_REMOTE_CONTROL_FEATURE
+    = "http://jitsi.org/meet/remotecontrol";
+
+/**
+ * Types of remote-control-event events.
+ */
+export const EVENT_TYPES = {
+    mousemove: "mousemove",
+    mousedown: "mousedown",
+    mouseup: "mouseup",
+    mousedblclick: "mousedblclick",
+    mousescroll: "mousescroll",
+    keydown: "keydown",
+    keyup: "keyup"
+};
+
+/**
+ * The type of remote control events sent trough the API module.
+ */
+export const API_EVENT_TYPE = "remote-control-event";