Adding Convergence Web API 50/69650/33
authorshulga <a.shulga@samsung.com>
Mon, 16 May 2016 08:47:13 +0000 (17:47 +0900)
committerHyunjin Park <hj.na.park@samsung.com>
Tue, 30 Aug 2016 09:46:34 +0000 (18:46 +0900)
1. Temporary removed all d2d-conv dependencies
2. Uploaded first working version
3. Quick fix: build config changed to support new d2d-conv-manager
4. Fixing device and service indexing and delivery params to C++ layer
5. Fixed failure, caused by using service handles after their lyfe cycle
6. Applied new internal D2D Conv Channel,Payload API; removed temporary used glib json
7. Supported local d2d services
8. Fixed warnings from Tomasz Marciniak, Jun 2 (3 warnings about payload
   dictionaries are remained)
9. Fixed warnings for patch set 15: only js file processed, other
   warnings to be fixed in next patch set
10. Added hidden members for the Service class, allowing handle
    remote/local services; removed useless helper-functions
11. Added type constants for two new experimental services
12. Applied Google coding style
13. Bug fixes
14. Implemented JavaScript API v0.2 (only Remote App Control Service)
15. Implemented new architecture for Native Layer
16. Implemented basics of App Communication services with new
    architecture
17. Implemented first working version of the whole Conv Web API plugin
    based on API v0.2

Change-Id: Ib930f46f6f31a85fba924376903505ebcad823cc
Signed-off-by: shulga <a.shulga@samsung.com>
26 files changed:
packaging/webapi-plugins.spec
src/convergence/convergence.gyp [new file with mode: 0644]
src/convergence/convergence_api.js [new file with mode: 0644]
src/convergence/convergence_app_communication_service.cc [new file with mode: 0644]
src/convergence/convergence_app_communication_service.h [new file with mode: 0644]
src/convergence/convergence_channel.cc [new file with mode: 0644]
src/convergence/convergence_channel.h [new file with mode: 0644]
src/convergence/convergence_client_info.cc [new file with mode: 0644]
src/convergence/convergence_client_info.h [new file with mode: 0644]
src/convergence/convergence_device.cc [new file with mode: 0644]
src/convergence/convergence_device.h [new file with mode: 0644]
src/convergence/convergence_extension.cc [new file with mode: 0644]
src/convergence/convergence_extension.h [new file with mode: 0644]
src/convergence/convergence_instance.cc [new file with mode: 0644]
src/convergence/convergence_instance.h [new file with mode: 0644]
src/convergence/convergence_manager.cc [new file with mode: 0644]
src/convergence/convergence_manager.h [new file with mode: 0644]
src/convergence/convergence_payload.cc [new file with mode: 0644]
src/convergence/convergence_payload.h [new file with mode: 0644]
src/convergence/convergence_remote_app_control_service.cc [new file with mode: 0644]
src/convergence/convergence_remote_app_control_service.h [new file with mode: 0644]
src/convergence/convergence_service.cc [new file with mode: 0644]
src/convergence/convergence_service.h [new file with mode: 0644]
src/convergence/convergence_utils.cc [new file with mode: 0644]
src/convergence/convergence_utils.h [new file with mode: 0644]
src/tizen-wrt.gyp

index a2fc356..eb925dd 100644 (file)
@@ -174,6 +174,12 @@ Source0:    %{name}-%{version}.tar.gz
 
 %define tizen_feature_tvinputdevice_support           0
 
+%if 0%{?tizen_is_emulator}
+%define tizen_feature_convergence_support             0
+%else
+%define tizen_feature_convergence_support             1
+%endif
+
 %endif # tizen_profile_mobile
 
 ####################################################################
@@ -270,6 +276,12 @@ Source0:    %{name}-%{version}.tar.gz
 %define    tizen_feature_nbs_support                  0
 %endif
 
+%if 0%{?tizen_is_emulator}
+%define tizen_feature_convergence_support             0
+%else
+%define tizen_feature_convergence_support             1
+%endif
+
 %endif # tizen_profile_wearable
 
 ####################################################################
@@ -325,6 +337,7 @@ Source0:    %{name}-%{version}.tar.gz
 %define tizen_feature_wi_fi_support                   1
 %define tizen_feature_inputdevice_support             0
 %define tizen_feature_tvinputdevice_support           1
+%define tizen_feature_convergence_support             1
 
 %endif # tizen_profile_tv
 
@@ -500,6 +513,10 @@ BuildRequires: pkgconfig(capi-system-sensor)
 BuildRequires:  pkgconfig(capi-system-media-key)
 %endif
 
+%if 0%{?tizen_feature_convergence_support}
+BuildRequires: pkgconfig(d2d-conv-manager)
+%endif
+
 %if 0%{?tizen_feature_widget_service_support}
 BuildRequires: pkgconfig(widget_service)
 %endif
@@ -575,6 +592,7 @@ GYP_OPTIONS="$GYP_OPTIONS -Dtizen_feature_web_setting_support=%{?tizen_feature_w
 GYP_OPTIONS="$GYP_OPTIONS -Dtizen_feature_widget_service_support=%{?tizen_feature_widget_service_support}"
 GYP_OPTIONS="$GYP_OPTIONS -Dtizen_feature_wi_fi_support=%{?tizen_feature_wi_fi_support}"
 GYP_OPTIONS="$GYP_OPTIONS -Dtizen_feature_tvinputdevice_support=%{?tizen_feature_tvinputdevice_support}"
+GYP_OPTIONS="$GYP_OPTIONS -Dtizen_feature_convergence_support=%{?tizen_feature_convergence_support}"
 
 ./tools/gyp/gyp $GYP_OPTIONS src/tizen-wrt.gyp
 
diff --git a/src/convergence/convergence.gyp b/src/convergence/convergence.gyp
new file mode 100644 (file)
index 0000000..1ab30fc
--- /dev/null
@@ -0,0 +1,48 @@
+{
+  'includes':[
+    '../common/common.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'tizen_convergence',
+      'type': 'loadable_module',
+      'dependencies': [
+        '../common/common.gyp:tizen_common',
+      ],
+      'sources': [
+        'convergence_api.js',
+        'convergence_extension.cc',
+        'convergence_extension.h',
+        'convergence_instance.cc',
+        'convergence_instance.h',
+        'convergence_utils.cc',
+        'convergence_utils.h',
+        'convergence_manager.cc',
+        'convergence_manager.h',
+        'convergence_device.cc',
+        'convergence_device.h',
+        'convergence_service.cc',
+        'convergence_service.h',
+        'convergence_remote_app_control_service.cc',
+        'convergence_remote_app_control_service.h',
+        'convergence_app_communication_service.cc',
+        'convergence_app_communication_service.h',
+        'convergence_channel.cc',
+        'convergence_channel.h',
+        'convergence_payload.cc',
+        'convergence_payload.h',
+        'convergence_client_info.cc',
+        'convergence_client_info.h',
+      ],
+      'conditions': [
+        ['tizen == 1', {
+          'variables': {
+            'packages': [
+              'd2d-conv-manager',
+            ]
+          },
+        }],
+      ],
+    },
+  ],
+}
diff --git a/src/convergence/convergence_api.js b/src/convergence/convergence_api.js
new file mode 100644 (file)
index 0000000..1d048f7
--- /dev/null
@@ -0,0 +1,916 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+
+var validator_ = xwalk.utils.validator;
+var types_ = validator_.Types;
+var utils_ = xwalk.utils;
+var native_ = new utils_.NativeManager(extension);
+var T_ = utils_.type;
+
+// Flag showing if the discovery procedure has started
+var discoveryStarted = false;
+
+// Currently available devices
+var convergenceDevices = [];
+
+// Issued service objects
+// We should stor it for proper calling user callbacks
+var nextServiceId = 0;  // Next service id (internally used on JS layer)
+var convergenceServices = {}; // Issued services
+
+var ConnectionState = {
+  CONNECTED: 'CONNECTED',
+  NOT_CONNECTED: 'NOT_CONNECTED',
+  CONNECTING: 'CONNECTING'
+};
+
+var DeviceProfile = {
+  MOBILE: 'MOBILE',
+  WEARABLE: 'WEARABLE',
+  TV: 'TV'
+};
+
+
+var PayloadType = {
+  STRING: 'STRING',
+  RAW_BYTES: 'RAW_BYTES'
+};
+
+function SetReadOnlyProperty(obj, n, v) {
+  if (arguments.length > 2)
+    Object.defineProperty(
+      obj, n, {
+        value: v,
+        writable: false,
+        enumerable: true,
+        configurable: true
+      });
+  else
+    Object.defineProperty(obj, n, {
+      writable: false,
+      enumerable: true,
+      configurable: true
+    });
+}
+
+
+function getServiceConnectionStateName(connectionStateNumber) {
+  switch(connectionStateNumber) {
+  case 0:
+    return ConnectionState.CONNECTED;
+  case 1:
+    return ConnectionState.NOT_CONNECTED;
+  case 2:
+    return ConnectionState.CONNECTING;
+  default:
+    console.log('ERROR: Unknown connection state');
+    return -1; // TODO throw exception
+  }
+}
+
+function Device(id, name, profile, services) {
+  validator_.isConstructorCall(this, Device);
+  this.id = id;
+  this.name = name;
+  this.profile = profile;
+  this.services = services;
+}
+
+function ConvergenceManager() {
+  // constructor of ConvergenceManager
+}
+
+ConvergenceManager.prototype.startDiscovery = function(successCallback,
+  errorCallback, timeout) {
+  console.log('Entered ConvergenceManager.startDiscovery()');
+  var args = validator_.validateArgs(arguments, [
+    {name: 'successCallback', type: types_.LISTENER, values: ['onfound', 'onfinished' ]},
+    {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true},
+    {name: 'timeout', type: types_.LONG, optional: true, nullable: true}
+  ]);
+
+  // Indicate, that discovery procedure is on
+  discoveryStarted = true;
+
+  // Reset currently available device list
+  convergenceDevices = [];
+
+  native_.addListener('CONVERGENCE_DISCOVERY_LISTENER', function(result) {
+    console.log('Entered discovery listener');
+
+    if (native_.isFailure(result)) {
+      native_.callIfPossible(errorCallback, native_.getErrorObject(result));
+    } else {
+      if (result.discovery_status == 'device_found') {
+
+        // Prepare service array
+        if (!result || !result.device || !result.device.services) {
+          console.log('INVALID device info obtained from Native API'); // TODO throw exception
+          return;
+        }
+
+        // Create an instance of the device
+        var d = new Device(result.device.id, result.device.name, result.device.type, []);
+
+        console.log('Service number: ' + result.device.services.length);
+        for (var i = 0; i < result.device.services.length; ++i) {
+          var service_data = result.device.services[i];
+          var s;
+          if (service_data.serviceType == 0) { // App Communication Client
+            s = new AppCommunicationClientService();
+          } else if (service_data.serviceType == 1) { // Remote App Control
+            s = new RemoteAppControlService();
+          } else {
+            // Error: unknown service type
+            // TODO throw exception
+            console.log('UNKNOWN SERVICE TYPE: ' + service_data.serviceType);
+            continue;
+          }
+
+          s.connectionState = getServiceConnectionStateName(
+            result.device.services[i].connectionState);
+          s._deviceId = d.id;
+          d.services.push(s);
+        }
+
+        // Store newly found device internally
+        convergenceDevices.push(d);
+
+        // Invoke user callback retrieving newly found device
+        native_.callIfPossible(successCallback.onfound, d);
+
+      } else if (result.discovery_status == 'discovery_finished') {
+        // Unregister discovery listener, because Convergence Manager is a
+        // singleton object and no one else can receive discovery results
+        native_.removeListener('CONVERGENCE_DISCOVERY_LISTENER');
+
+        // Notify the customer about discovery results
+        native_.callIfPossible(successCallback.onfinished, convergenceDevices.slice());
+        convergenceDevices = [];
+
+      } else {
+        console.log('UNKNOWN discovery state exception'); // TODO throw exception
+      }
+    }
+  });
+
+  // Start the discovery using Native API
+  var result = native_.call('ConvergenceManager_startDiscovery', {
+      timeout: (args.timeout) ? args.timeout : 0
+    }, function(result) {
+    if (native_.isFailure(result)) {
+      native_.callIfPossible(errorCallback, native_.getErrorObject(result));
+    }
+  });
+  if (native_.isFailure(result))
+    throw native_.getErrorObject(result);
+};
+
+ConvergenceManager.prototype.stopDiscovery = function() {
+  console.log('Entered ConvergenceManager.stopDiscovery()');
+
+  if (!discoveryStarted)
+    throw new WebAPIException('InvalidStateError', 'Discovery has not started yet.');
+
+  discoveryStarted = false;
+
+  var result = native_.callSync('ConvergenceManager_stopDiscovery', null);
+  if (native_.isFailure(result))
+    throw native_.getErrorObject(result);
+};
+
+function Service() {
+  console.log('Entered Service.constructor()');
+
+  //validator_.isConstructorCall(this, Service);
+  this.connectionState = ConnectionState.NOT_CONNECTED;
+}
+
+native_.addListener('REMOTE_APP_CONTROL_SERVICE_LISTENER', function(result) {
+  if (native_.isFailure(result)) {
+    //native_.callIfPossible(errorCallback, native_.getErrorObject(result));
+    console.log('Error in REMOTE_APP_CONTROL_SERVICE_LISTENER');
+  } else {
+
+      console.log('');
+      console.log('REMOTE_APP_CONTROL_SERVICE_LISTENER invoked with data');
+      console.log(JSON.stringify(result));
+      console.log('');
+
+    // Invoke corresponding callback
+    var lid = result.curListenerId;
+    if (!lid || !convergenceServices.hasOwnProperty(lid)) {
+      console.log('ERROR: Remote App Ctrl listener is not registered [' + lid + ']');
+      return; // Something is wrong: listener MUST be there
+    }
+    var s = convergenceServices[lid];
+
+    var result_type = result.result_type;
+
+    switch (result_type) {
+    case 'Connected':
+      if (s) { // Service MUST NOT be null here
+        s.connectionState = ConnectionState.CONNECTED;
+        convergenceServices[lid] = s;
+      }
+      native_.callIfPossible(s._connectCallback, s);
+      break;
+    case 'onPublish':
+      native_.callIfPossible(s._remoteAppControlCallback,
+        result.payload);
+      break;
+    default:
+      console.log('Ignoring result type: [' + result_type + ']');
+      break;
+    }
+  }
+});
+
+function RemoteAppControlService() {
+  console.log('Entered RemoteAppControlService.constructor()');
+
+  validator_.isConstructorCall(this, RemoteAppControlService);
+
+  // The device id is needed for getting the valid service handle on the
+  // native layer
+  // I have to implement this property here instead of base constructor in order
+  // to mask it from accessing
+  Object.defineProperties(this, {
+    _serviceId : {
+      value: ++nextServiceId,
+      writable: false,
+      enumerable: false
+    },
+    // The id of device of the service or 'localhost' for local service
+   _deviceId : {
+      value: 'localhost',
+      writable: true,
+      enumerable: false
+    },
+    // Remote App Control Service user-defined callbacks
+    _connectCallback : {
+      value: null,
+      writable: true,
+      enumerable: false
+    },
+    _remoteAppControlCallback : {
+      value: null,
+      writable: true,
+      enumerable: false
+    }
+  });
+
+  // Registering the service in the table of issued services
+  convergenceServices[this._serviceId] = this;
+}
+
+RemoteAppControlService.prototype = new Service();
+
+RemoteAppControlService.prototype.connect = function(successCallback, errorCallback) {
+  console.log('Entered RemoteAppControlService.connect()');
+
+  var args = validator_.validateArgs(arguments, [
+    {
+      name: 'successCallback',
+      type: types_.FUNCTION,
+      //values: ConnectSuccessCallback,
+      optional: false,
+      nullable: false
+    },
+    {
+      name: 'errorCallback',
+      type: types_.FUNCTION,
+      //values: ErrorCallback,
+      optional: true,
+      nullable: true
+    }
+  ]);
+
+  if (this.serviceState == ConnectionState.CONNECTED)
+    throw new WebAPIException('InvalidStateError', 'Service is connected already.');
+
+  var lid = this._serviceId;
+  this._connectCallback = successCallback;
+  convergenceServices[lid] = this;
+
+  var result = native_.call('RemoteAppControlService_connect', {
+      deviceId: this._deviceId,
+      curListenerId: lid
+    }, function(result) {
+    if (native_.isFailure(result))
+      native_.callIfPossible(errorCallback, native_.getErrorObject(result));
+  });
+  if (native_.isFailure(result))
+    throw native_.getErrorObject(result);
+};
+
+RemoteAppControlService.prototype.disconnect = function(successCallback, errorCallback) {
+  console.log('Entered RemoteAppControlService.disconnect()');
+
+  var args = validator_.validateArgs(arguments, [
+    {
+      name: 'successCallback',
+      type: types_.FUNCTION,
+      //values: ConnectSuccessCallback,
+      optional: true,
+      nullable: true
+    },
+    {
+      name: 'errorCallback',
+      type: types_.FUNCTION,
+      //values: ErrorCallback,
+      optional: true,
+      nullable: true
+    }
+  ]);
+
+  if (this.serviceState != ConnectionState.CONNECTED) {
+    throw new WebAPIException('InvalidStateError', 'Service is not connected yet.');
+  }
+
+  var result = native_.callSync('RemoteAppControlService_disconnect', {
+      deviceId: this._deviceId
+    });
+
+  if (native_.isFailure(result))
+    throw native_.getErrorObject(result);
+  else
+    connectionState = ConnectionState.DISCONNECTED;
+
+  native_.callIfPossible(successCallback, this);
+};
+
+RemoteAppControlService.prototype.launch = function(appId, successCallback, errorCallback) {
+  console.log('Entered RemoteAppControlService.launch()');
+  var args = validator_.validateArgs(arguments, [
+    {
+      name: 'appId',
+      //type: types_.PLATFORM_OBJECT,
+      type: types_.STRING,
+      values: tizen.ApplicationId,
+      optional: false,
+      nullable:false
+    },
+    {
+      name: 'successCallback',
+      type: types_.FUNCTION,
+      //values: RemoteAppControlCallback,
+      optional: true,
+      nullable: true
+    },
+    {
+      name: 'errorCallback',
+      type: types_.FUNCTION,
+      //values: ErrorCallback,
+      optional: true,
+      nullable: true
+    }
+  ]);
+
+  if (this.serviceState == ConnectionState.CONNECTED)
+    throw new WebAPIException('InvalidStateError', 'Service is connected already.');
+
+  var lid = this._serviceId;
+  // TODO In fact it must be a list of callbacks
+  // But untill D2D FW suppurts transaction management, it is meaningless to
+  // have more than one callback, because all payload is delivered to
+  // a single point without identification of initial request
+  this._remoteAppControlCallback = successCallback;
+  convergenceServices[lid] = this;
+
+  var needReply = !(successCallback == null);
+
+  var result = native_.call('RemoteAppControlService_launch', {
+      appId: args.appId,
+      reply: needReply,
+      deviceId: this._deviceId,
+      curListenerId: lid
+    }, function(result) {
+    if (native_.isFailure(result))
+      native_.callIfPossible(errorCallback, native_.getErrorObject(result));
+  });
+  if (native_.isFailure(result))
+    throw native_.getErrorObject(result);
+};
+
+RemoteAppControlService.prototype.launchAppControl = function(
+    appControl, appId, successCallback, errorCallback) {
+  console.log('Entered RemoteAppControlService.launchAppControl()');
+  var args = validator_.validateArgs(arguments, [
+    {
+      name: 'appControl',
+      type: types_.PLATFORM_OBJECT,
+      values: tizen.ApplicationControl,
+      optional: false,
+      nullable:false
+    },
+    {
+      name: 'appId',
+      type: types_.PLATFORM_OBJECT,
+      values: tizen.ApplicationId,
+      optional: true,
+      nullable: true
+    },
+    {
+      name: 'successCallback',
+      type: types_.FUNCTION,
+      //values: RemoteAppControlCallback,
+      optional: true,
+      nullable: true
+    },
+    {
+      name: 'errorCallback',
+      type: types_.FUNCTION,
+      //values: ErrorCallback,
+      optional: true,
+      nullable: true
+    }
+  ]);
+
+  // TODO Implement pls
+
+  return; // TODO remove when native layer is implemented
+};
+
+function AppCommunicationService() {
+  console.log('Entered AppCommunicationService.constructor()');
+
+  // The device id is needed for getting the valid service handle on the
+  // native layer
+  // I have to implement this property here instead of base constructor in order
+  // to mask it from accessing
+  Object.defineProperties(this, {
+    _serviceId : {
+      value: ++nextServiceId,
+      writable: false,
+      enumerable: false
+    },
+    // The id of device of the service or 'localhost' for local service
+    _deviceId : {
+      value: 'localhost',
+      writable: true,
+      enumerable: false
+    },
+    // App Communication Service basic callbacks
+    _connectCallback : {
+      value: null,
+      writable: true,
+      enumerable: false
+    },
+    _startCallback : {
+      value: null,
+      writable: true,
+      enumerable: false
+    },
+    _sendCallback : {
+      value: null,
+      writable: true,
+      enumerable: false
+    },
+    _stopCallback : {
+      value: null,
+      writable: true,
+      enumerable: false
+    },
+    _listenerCallback : {
+      value: null,
+      writable: true,
+      enumerable: false
+    },
+    _getClientListCallback : {
+      value: null,
+      writable: true,
+      enumerable: false
+    }
+  });
+
+  // TODO add callbacks in runtime instead of constructor
+
+  // Registering the service in the table of issued services
+  convergenceServices[this._serviceId] = this;
+}
+
+AppCommunicationService.prototype = new Service();
+
+native_.addListener('APP_COMMUNICATION_SERVICE_LISTENER', function(result) {
+
+  console.log('On App Communication Service Success Callback');
+
+  if (native_.isFailure(result)) {
+    console.log('ERROR in APP_COMMUNICATION_SERVICE_LISTENER');
+    //native_.callIfPossible(errorCallback, native_.getErrorObject(result));
+  } else {
+
+      console.log('');
+      console.log('APP_COMMUNICATION_SERVICE_LISTENER invoked with data');
+      console.log(JSON.stringify(result));
+      console.log('');
+
+    // Invoke corresponding callback
+    var lid = result.curListenerId;
+    if (!lid || !convergenceServices.hasOwnProperty(lid)) {
+      console.log('ERROR: App Comm listener is not registered [' + lid + ']');
+      return; // Something is wrong: listener MUST be there
+    }
+    var s = convergenceServices[lid];
+
+    var result_type = result.result_type;
+
+    switch (result_type) {
+    case 'Connected':
+      if (s) { // Service MUST NOT be null here
+        s.connectionState = ConnectionState.CONNECTED;
+        convergenceServices[lid] = s;
+      }
+      native_.callIfPossible(s._connectCallback, s);
+      break;
+    case 'onStart':
+      native_.callIfPossible(s._startCallback,
+        new Channel(result.channel.uri, result.channel.id), null);
+      break;
+    case 'onPublish':
+      native_.callIfPossible(s._sendCallback,
+        new Channel(result.channel.uri, result.channel.id), null);
+      break;
+    case 'onStop':
+      native_.callIfPossible(s._stopCallback,
+        new Channel(result.channel.uri, result.channel.id), null);
+      break;
+    case 'onMessage': {
+        var payload = [];
+        for(var i = 0; i < result.payload.length; i++) {
+          var curPl = result.payload[i];
+          switch(curPl.type) {
+          case PayloadType.STRING:
+            payload.push(new PayloadString(curPl.key, curPl.value));
+            break;
+          case PayloadType.RAW_BYTES:
+            payload.push(new PayloadRawBytes(curPl.key, curPl.value));
+            break;
+          default:
+            console.log('ERROR: Unknown payload type');
+            break;
+          }
+        }
+
+        native_.callIfPossible(s._listenerCallback,
+          new Channel(result.channel.uri, result.channel.id),
+          payload, result.senderId);
+      }
+      break;
+    default:
+      console.log('Ignoring result type: [' + result_type + ']');
+      break;
+    }
+
+    /*
+    // TODO uncomment when implemented (or remove thid data from the protocol)
+    *var clientInfo = new ClientInfo(
+      result.clientInfo.isHost,
+      result.clientInfo.lientId,
+      result.clientInfo.connectionTime);*
+    var clientInfo = null;
+    */
+  }
+});
+
+AppCommunicationService.prototype.start = function(channel, successCallback, errorCallback) {
+  console.log('Entered AppCommunicationService.start()');
+
+  var args = validator_.validateArgs(arguments, [
+    {
+      name: 'channel',
+      type: types_.PLATFORM_OBJECT,
+      values: tizen.Channel,
+      optional: false,
+      nullable: false
+    },
+    {
+      name: 'successCallback',
+      type: types_.FUNCTION,
+      //values: ConnectSuccessCallback,
+      optional: false,
+      nullable: false
+    },
+    {
+      name: 'errorCallback',
+      type: types_.FUNCTION,
+      //values: ErrorCallback,
+      optional: true,
+      nullable: true
+    }
+  ]);
+
+  // TODO check if the service is connected and started
+  // Raise the exception instead
+
+  var lid = this._serviceId;
+  this._startCallback = successCallback;
+  convergenceServices[lid] = this;
+
+  var result = native_.call('AppCommunicationService_start', {
+      deviceId: this._deviceId,
+      curListenerId: lid,
+      channel_data: channel
+    }, function(result) {
+    if (native_.isFailure(result))
+      native_.callIfPossible(errorCallback, native_.getErrorObject(result));
+  });
+  if (native_.isFailure(result))
+    throw native_.getErrorObject(result);
+};
+
+AppCommunicationService.prototype.stop = function(channel, successCallback, errorCallback) {
+  console.log('Entered AppCommunicationService.stop()');
+
+  var args = validator_.validateArgs(arguments, [
+    {
+      name: 'channel',
+      type: types_.PLATFORM_OBJECT,
+      values: tizen.Channel,
+      optional: false,
+      nullable: false
+    },
+    {
+      name: 'successCallback',
+      type: types_.FUNCTION,
+      //values: ConnectSuccessCallback,
+      optional: true,
+      nullable: true
+    },
+    {
+      name: 'errorCallback',
+      type: types_.FUNCTION,
+      //values: ErrorCallback,
+      optional: true,
+      nullable: true
+    }
+  ]);
+
+  // TODO check if the service is connected and started
+  // Raise the exception instead
+
+  var lid = -1;
+  if (successCallback) {
+    lid = this._serviceId;
+    this._stopCallback = successCallback;
+    convergenceServices[lid] = this;
+  }
+
+  var result = native_.call('AppCommunicationService_stop', {
+      deviceId: this._deviceId,
+      curListenerId: lid,
+      channel_data: channel
+    }, function(result) {
+    if (native_.isFailure(result))
+      native_.callIfPossible(errorCallback, native_.getErrorObject(result));
+  });
+  if (native_.isFailure(result))
+    throw native_.getErrorObject(result);
+};
+
+AppCommunicationService.prototype.send = function(channel, payload, successCallback, errorCallback) {
+  console.log('Entered AppCommunicationService.send()');
+
+  var args = validator_.validateArgs(arguments, [
+    {
+      name: 'channel',
+      type: types_.PLATFORM_OBJECT,
+      values: tizen.Channel,
+      optional: false,
+      nullable: false
+    },
+    {
+      name: 'payload',
+      type: types_.PLATFORM_OBJECT,
+      values: [tizen.PayloadString, tizen.PayloadRawBytes],
+      optional: false,
+      nullable: false
+    },
+    {
+      name: 'successCallback',
+      type: types_.FUNCTION,
+      //values: ConnectSuccessCallback,
+      optional: false,
+      nullable: false
+    },
+    {
+      name: 'errorCallback',
+      type: types_.FUNCTION,
+      //values: ErrorCallback,
+      optional: true,
+      nullable: true
+    }
+  ]);
+
+  // TODO check if the service is connected and started
+  // Raise the exception instead
+
+  var lid = this._serviceId;
+  this._sendCallback = successCallback;
+  convergenceServices[lid] = this;
+
+  var result = native_.call('AppCommunicationService_send', {
+      deviceId: this._deviceId,
+      curListenerId: lid,
+      channel_data: channel,
+      payload: payload
+    }, function(result) {
+    if (native_.isFailure(result))
+      native_.callIfPossible(errorCallback, native_.getErrorObject(result));
+  });
+  if (native_.isFailure(result))
+    throw native_.getErrorObject(result);
+};
+
+AppCommunicationService.prototype.addListener = function(listenerCallback) {
+  console.log('Entered AppCommunicationService.addListener()');
+  var args = validator_.validateArgs(arguments, [
+    {name: 'listenerCallback', type: types_.FUNCTION}
+  ]);
+
+  var lid = this._serviceId;
+  // TODO in fact, it should be even the list of callbacks instead of
+  // the single callback
+  this._listenerCallback = listenerCallback;
+  convergenceServices[lid] = this;
+
+      console.log('');
+      console.log('AppCommunicationServiceCommandListeners ADDED [' + lid + ']');
+      console.log('');
+
+  var result = native_.callSync('AppCommunicationService_addListener', {
+      deviceId: this._deviceId,
+      curListenerId: lid
+    });
+  if (native_.isFailure(result))
+    throw native_.getErrorObject(result);
+
+  return args.curListenerId; // TODO return proper index of listener
+};
+
+AppCommunicationService.prototype.removeListener = function(id) {
+  console.log('Entered AppCommunicationService.removeListener()');
+  var args = validator_.validateArgs(arguments, [
+    {name: 'id', type: types_.LONG, optional: false}
+  ]);
+
+  var result = native_.callSync('AppCommunicationService_removeListener', {
+      deviceId: this._deviceId
+      //curListenerId: id // not needed in below layers
+    });
+  if (native_.isFailure(result))
+    throw native_.getErrorObject(result);
+
+  var lid = this._serviceId;
+  if (this._listenerCallback) {// TODO extend this to manage the list of callbacks
+    //delete this._listenerCallback;
+    this._listenerCallback = null;
+  }
+  convergenceServices[lid] = this;
+};
+
+function AppCommunicationServerService() {
+  console.log('Entered AppCommunicationServerService.constructor()');
+
+  validator_.isConstructorCall(this, AppCommunicationServerService);
+
+  this.connectionState = ConnectionState.NOT_CONNECTED;
+
+  native_.callSync('AppCommunicationServerService_constructLocal', {
+      deviceId: this._deviceId
+    });
+}
+
+AppCommunicationServerService.prototype = new AppCommunicationService();
+
+function AppCommunicationClientService() {
+  console.log('Entered AppCommunicationClientService.constructor()');
+
+  validator_.isConstructorCall(this, AppCommunicationClientService);
+
+  this.connectionState = ConnectionState.NOT_CONNECTED;
+}
+
+AppCommunicationClientService.prototype = new AppCommunicationService();
+
+AppCommunicationClientService.prototype.connect = function(successCallback, errorCallback) {
+  console.log('Entered AppCommunicationClientService.connect()');
+
+  var args = validator_.validateArgs(arguments, [
+    {
+      name: 'successCallback',
+      type: types_.FUNCTION,
+      //values: ConnectSuccessCallback,
+      optional: false,
+      nullable: false
+    },
+    {
+      name: 'errorCallback',
+      type: types_.FUNCTION,
+      //values: ErrorCallback,
+      optional: true,
+      nullable: true
+    }
+  ]);
+
+  if (this.serviceState == ConnectionState.CONNECTED)
+    throw new WebAPIException('InvalidStateError', 'Service is connected already.');
+
+  var lid = this._serviceId;
+  this._connectCallback = successCallback;
+  convergenceServices[lid] = this;
+
+  var result = native_.call('AppCommunicationClientService_connect', {
+      deviceId: this._deviceId,
+      curListenerId: lid
+    }, function(result) {
+    if (native_.isFailure(result))
+      native_.callIfPossible(errorCallback, native_.getErrorObject(result));
+  });
+  if (native_.isFailure(result))
+    throw native_.getErrorObject(result);
+};
+
+AppCommunicationClientService.prototype.disconnect = function(successCallback, errorCallback) {
+  console.log('Entered AppCommunicationClientService.disconnect()');
+
+  var args = validator_.validateArgs(arguments, [
+    {
+      name: 'successCallback',
+      type: types_.FUNCTION,
+      //values: ConnectSuccessCallback,
+      optional: true,
+      nullable: true
+    },
+    {
+      name: 'errorCallback',
+      type: types_.FUNCTION,
+      //values: ErrorCallback,
+      optional: true,
+      nullable: true
+    }
+  ]);
+  if (this.serviceState != ConnectionState.CONNECTED)
+    throw new WebAPIException('InvalidStateError', 'Service is not connected yet.');
+
+  var result = native_.callSync('AppCommunicationClientService_disconnect', {
+      deviceId: this._deviceId
+    });
+
+  if (native_.isFailure(result))
+    throw native_.getErrorObject(result);
+  else
+    connectionState = ConnectionState.DISCONNECTED;
+
+  native_.callIfPossible(successCallback, this);
+};
+
+function Channel(uri, id) {
+  validator_.isConstructorCall(this, Channel);
+  this.uri = uri;
+  this.id = id;
+}
+
+function PayloadString(key, value) {
+  validator_.isConstructorCall(this, PayloadString);
+  this.key = key;
+  this.value = value;
+  SetReadOnlyProperty(this, 'type', PayloadType.STRING); // read only property
+}
+
+function PayloadRawBytes(key, value) {
+  validator_.isConstructorCall(this, PayloadRawBytes);
+  this.key = key;
+  this.value = value;
+  SetReadOnlyProperty(this, 'type', PayloadType.RAW_BYTES); // read only property
+}
+
+function ClientInfo(isHost, clientId, connectionTime) {
+  validator_.isConstructorCall(this, ClientInfo);
+  this.isHost = isHost;
+  this.clientId = clientId;
+  this.connectionTime = connectionTime;
+}
+
+exports = new ConvergenceManager();
+tizen.RemoteAppControlService = RemoteAppControlService;
+tizen.AppCommunicationServerService = AppCommunicationServerService;
+tizen.AppCommunicationClientService = AppCommunicationClientService;
+tizen.Channel = Channel;
+tizen.PayloadString = PayloadString;
+tizen.PayloadRawBytes = PayloadRawBytes;
diff --git a/src/convergence/convergence_app_communication_service.cc b/src/convergence/convergence_app_communication_service.cc
new file mode 100644 (file)
index 0000000..0cc7bd6
--- /dev/null
@@ -0,0 +1,550 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+// TODO check includes
+#include "convergence/convergence_app_communication_service.h"
+
+#include <glib.h>
+#include <d2d_conv_internal.h>
+#include <stdlib.h>
+
+#include "convergence/convergence_instance.h"
+#include "convergence/convergence_channel.h"
+#include "convergence/convergence_payload.h"
+#include "convergence/convergence_client_info.h"
+#include "common/logger.h"
+
+namespace extension {
+namespace convergence {
+
+using common::TizenResult;
+using common::TizenSuccess;
+
+namespace {
+// Payload data keys and some values
+static const std::string kPayload = "payload"; // ---
+
+// Service connection status keys
+static const std::string kServiceConnectionStatus = "connect_status"; // ---
+static const std::string kServiceConnectionStatusConnected = "service_connected";
+static const std::string kServiceConnectionStatusNotConnected = "service_not_connected";
+
+// Service listener status keys
+static const std::string kServiceListenerStatus = "listener_status"; //---
+static const std::string kServiceListenerStatusOk = "listener_ok"; // ---
+static const std::string kServiceListenerStatusError = "listener_error"; //---
+static const std::string kServiceListenerError = "listener_error"; //---
+} // namespace
+
+ConvergenceAppCommunicationService::ConvergenceAppCommunicationService()
+ : ConvergenceService() {
+  ScopeLogger();
+}
+
+ConvergenceAppCommunicationService::ConvergenceAppCommunicationService(conv_device_h device, ConvergenceInstance *convergence_plugin)
+ : ConvergenceService(device, CONV_SERVICE_APP_TO_APP_COMMUNICATION, convergence_plugin) {
+  ScopeLogger();
+}
+
+ConvergenceAppCommunicationService::~ConvergenceAppCommunicationService() {
+  ScopeLogger();
+
+  // Release all memory, used by callback paramerers
+  for (size_t i = 0; i < callback_param_gc_.size(); i++) {
+    delete callback_param_gc_[i];
+  }
+  callback_param_gc_.clear();
+}
+
+common::TizenResult ConvergenceAppCommunicationService::Start(
+  ConvergenceChannel *channel,
+  const int cur_listener_id) {
+  ScopeLogger();
+
+  LoggerI("Starting service [0x%x] with handle [0x%x]", this, service_handle_);
+
+  conv_service_h service_handle = FindServiceHandle();
+  if (!service_handle) {
+    LoggerE("AAAAAA!!! Service not found");
+    return LogAndCreateTizenError(NotFoundError,
+      "Service with specified type does not exist");
+  }
+
+LoggerI("1");
+
+  UpdateListener(cur_listener_id);
+
+LoggerI("2");
+
+
+  { // DBG
+    conv_channel_h ch = channel->GetHandle();
+    char *id = NULL;
+    conv_channel_get_string(ch, "channel_id", &id);
+    char *uri = NULL;
+    conv_channel_get_string(ch, "uri", &uri);
+    LoggerI("===== CHANNEL ID [%s] URI [%s]", id, uri);
+  }
+
+  const int error = conv_service_start(service_handle,
+    channel->GetHandle(), nullptr);
+  if (CONV_ERROR_NONE != error) {
+    // TODO: Handle error
+    trace_conv_error(error, __LINE__, "conv_service_start");
+  }
+
+LoggerI("3");
+  delete channel;
+LoggerI("4");
+  return TizenSuccess();
+}
+
+common::TizenResult ConvergenceAppCommunicationService::Stop(
+  ConvergenceChannel *channel,
+  const int cur_listener_id) {
+  ScopeLogger();
+
+  conv_service_h service_handle = FindServiceHandle();
+  if (!service_handle) {
+    return LogAndCreateTizenError(NotFoundError,
+      "Service with specified type does not exist");
+  }
+
+  UpdateListener(cur_listener_id);
+
+  const int error = conv_service_stop(service_handle,
+    channel->GetHandle(), nullptr);
+  if (CONV_ERROR_NONE != error) {
+    // TODO: Handle error
+    trace_conv_error(error, __LINE__, "conv_service_stop");
+  }
+
+  delete channel;
+  return TizenSuccess();
+}
+
+common::TizenResult ConvergenceAppCommunicationService::Send(
+  ConvergenceChannel *channel,
+  ConvergencePayloadArray *payload,
+  const int cur_listener_id) {
+  ScopeLogger();
+
+  conv_service_h service_handle = FindServiceHandle();
+  if (!service_handle) {
+    return LogAndCreateTizenError(NotFoundError,
+      "Service with specified type does not exist");
+  }
+
+  UpdateListener(cur_listener_id);
+
+  { // DBG
+    LoggerI("...PUBLISHING for service handle [0x0%x]", service_handle);
+
+    conv_service_e t = CONV_SERVICE_NONE;
+    int e = conv_service_get_type(service_handle, &t);
+    if (CONV_ERROR_NONE != e) {
+      trace_conv_error(e, __LINE__, "get service type");
+    }
+    LoggerI("....type [%d]", t);
+
+    char *sid = nullptr;
+    e = conv_service_get_property_string(service_handle, CONV_SERVICE_ID, &sid);
+    if (CONV_ERROR_NONE != e) {
+      trace_conv_error(e, __LINE__, "get service id");
+    }
+    LoggerI("....id [%s]", sid);
+    free(sid);
+
+    conv_channel_h ch = channel->GetHandle();
+    char *id = NULL;
+    conv_channel_get_string(ch, "channel_id", &id);
+    char *uri = NULL;
+    conv_channel_get_string(ch, "uri", &uri);
+    LoggerI("===== CHANNEL ID [%s] URI [%s]", id, uri);
+  }
+
+  const int error = conv_service_publish(service_handle,
+    channel->GetHandle(), payload->GetHandle());
+  if (CONV_ERROR_NONE != error) {
+    // TODO: Handle error
+    trace_conv_error(error, __LINE__, "conv_service_publish");
+  } else {
+    LoggerI("PUBLISHED SUCCESSFULY listener is [%d]", cur_listener_id);
+  }
+
+  delete channel;
+  delete payload;
+  return TizenSuccess();
+}
+
+
+// TODO move to Payload class
+
+
+void ConvergenceAppCommunicationService::ServiceListenerCb(conv_service_h service_handle,
+  conv_channel_h channel_handle,
+  conv_error_e error, conv_payload_h result, void* user_data) {
+  ScopeLogger();
+
+  CallbackParam *callbackParam = static_cast<CallbackParam *>(user_data);
+  if (!callbackParam) {
+    LoggerE("ERROR! NULL user data in Service Listener Callback");
+    return;
+  }
+
+  // TODO Send all payload and channel to the JS layer and parse it there
+
+  // TODO parse the payload and fill the param
+  LoggerI("YAY! Got the Callback (TODO implement it)");
+
+  if (CONV_ERROR_NONE != error) { // Error occured during connection
+    picojson::object param;
+    param[kServiceListenerStatus] = picojson::value(kServiceListenerStatusError);
+    param[kServiceListenerError] = picojson::value(static_cast<double>(error));
+    callbackParam->plugin_->ReplyAsync(kAppCommunicationListenerCallback,
+      callbackParam->callback_id_, false, param);
+    return;
+  }
+
+  // Parse the payload
+  const std::string result_type = ConvergencePayloadArray::ExtractPayloadString(result, "result_type"); // TODO extract to constant kResultType = "result_type";
+  LoggerI("Callback type [%s]", result_type.c_str());
+
+  picojson::object param;
+  param[kServiceListenerStatus] = picojson::value(kServiceListenerStatusOk);
+  param["channel"] = ConvergenceChannel::ToJson(channel_handle);
+  param["result_type"] = picojson::value(result_type);
+
+  if (("onStart" == result_type)
+   || ("onPublish" == result_type)
+   || ("onStop" == result_type)) {
+    // The service doesn't send any callback with thie response
+  } else if ("onMessage" == result_type) {
+    param["senderId"] = picojson::value(
+      ConvergencePayloadArray::ExtractPayloadString(result, "from")); // Define the string as a constant
+    param["payload"] = ConvergencePayloadArray::ToJson(result);
+  } else if ("onConnect" == result_type) {
+    //param["clientInfo"] = ConvergenceClientInfo(result).ToJson();
+    //callbackParam->plugin_->ReplyAsync(kAppCommunicationClientServiceConnectCallback,
+    //  callbackParam->callback_id_, true, param);
+    return;
+  } else if ("onClientConnect" == result_type) {
+    // TODO the service doesn't send any callback with thie response
+    return; // TODO
+  } else if ("onDisconnect" == result_type) {
+    return; // TODO
+  } else if ("onClientDisconnect" == result_type) {
+    return; // TODO
+  } else if ("onRead" == result_type) {
+    // TODO
+  } else {
+    // Unsupported payload type, ignoring it
+    LoggerE("ERROR: Unsupported payload type; ignored");
+    return;
+  }
+
+  callbackParam->plugin_->ReplyAsync(kAppCommunicationListenerCallback,
+    callbackParam->callback_id_, true, param);
+
+  // TODO Remove callbackParam from GC
+  // TODO delete callbackParam
+}
+
+void ConvergenceAppCommunicationService::UpdateListener(const int cur_listener_id) {
+  ScopeLogger();
+
+  // TODO make sure that callback is not called twice for the same listener
+
+  // TODO: make a garbage collection and release this memory when callback unset
+  // and when whole manager is destructed
+  CallbackParam *param = new CallbackParam(convergence_plugin_, cur_listener_id);
+  callback_param_gc_.push_back(param);
+
+  conv_service_h service_handle = FindServiceHandle();
+  if (!service_handle) {
+    LoggerE("AAAAAA!!! Service not found");
+    return;
+  }
+
+  { // dbg
+    LoggerI("...found service handle [%x0x]", service_handle);
+
+    conv_service_e t = CONV_SERVICE_NONE;
+    int e = conv_service_get_type(service_handle, &t);
+    if (CONV_ERROR_NONE != e) {
+      trace_conv_error(e, __LINE__, "get service type");
+    }
+    LoggerI("....type [%d]", t);
+
+    char *sid = nullptr;
+    e = conv_service_get_property_string(service_handle, CONV_SERVICE_ID, &sid);
+    if (CONV_ERROR_NONE != e) {
+      trace_conv_error(e, __LINE__, "get service id");
+    }
+    LoggerI("....id [%s]", sid);
+    free(sid);
+
+    char *sver = nullptr;
+    e = conv_service_get_property_string(service_handle, CONV_SERVICE_VERSION, &sver);
+    if (CONV_ERROR_NONE != e) {
+      trace_conv_error(e, __LINE__, "get service version");
+    }
+    LoggerI("....ver [%s]", sver);
+    free(sver);
+  }
+
+  const int error = conv_service_set_listener_cb(service_handle, ServiceListenerCb, param);
+  if (CONV_ERROR_NONE != error) {
+    // TODO: Handle error
+    trace_conv_error(error, __LINE__, "conv_service_set_listener_cb");
+  } else {
+    LoggerI("APP COMMUNICATION SERVICE LISTENER SET SUCCESSFULY [%d]", cur_listener_id);
+  }
+}
+
+common::TizenResult ConvergenceAppCommunicationService::SetListener(const int cur_listener_id) {
+  ScopeLogger();
+  conv_service_h service_handle = FindServiceHandle();
+  if (!service_handle)
+    return LogAndCreateTizenError(NotFoundError,
+      "Service with specified type does not exist");
+
+  UpdateListener(cur_listener_id);
+
+  return TizenSuccess();
+}
+
+common::TizenResult ConvergenceAppCommunicationService::RemoveListener() {
+  ScopeLogger();
+
+  conv_service_h service_handle = FindServiceHandle();
+  if (!service_handle) {
+    LoggerE("AAAAAA!!! Service not found");
+    return LogAndCreateTizenError(NotFoundError,
+      "Service with specified type does not exist");
+  }
+
+  const int error = conv_service_unset_listener_cb(service_handle);
+  if (CONV_ERROR_NONE != error) {
+    // TODO: Handle error
+    trace_conv_error(error, __LINE__, "conv_service_set_listener_cb");
+    return LogAndCreateTizenError(NotFoundError,
+      "Unset Listener Failed");
+  }
+
+  return TizenSuccess();
+}
+
+picojson::value ConvergenceAppCommunicationService::PayloadToJson(conv_payload_h payload) {
+  ScopeLogger();
+  picojson::value payload_json;
+
+  // TODO convert payload to array of ApplicationControlData
+
+  return payload_json;
+}
+
+
+
+ConvergenceAppCommunicationServerService::ConvergenceAppCommunicationServerService()
+ : ConvergenceAppCommunicationService() {
+  ScopeLogger();
+}
+
+ConvergenceAppCommunicationServerService::ConvergenceAppCommunicationServerService(
+  conv_device_h device, ConvergenceInstance *convergence_plugin)
+ : ConvergenceAppCommunicationService(device, convergence_plugin) {
+  ScopeLogger();
+
+  // Creating the handle of local service instance
+  int error = conv_service_create(&service_handle_);
+  if (CONV_ERROR_NONE != error) {
+    // TODO: Handle error
+    trace_conv_error(error, __LINE__, "conv_service_create App Comm Service");
+  }
+
+  error = conv_service_set_type(service_handle_, CONV_SERVICE_APP_TO_APP_COMMUNICATION);
+  if (CONV_ERROR_NONE != error) {
+    // TODO: Handle error
+    trace_conv_error(error, __LINE__, "conv_service_set_type App Comm Service");
+  }
+
+  /*
+   * Consider a constructor of App Communication Server Service with parameters:
+   *  - Id
+   *  - Version
+   */
+
+  error = conv_service_set_property_string(service_handle_, CONV_SERVICE_ID, "test_app"); // TODO set a proper service ID
+  if (CONV_ERROR_NONE != error) {
+    // TODO: Handle error
+    trace_conv_error(error, __LINE__, "conv_service_set_property Id");
+  }
+
+  error = conv_service_set_property_string(service_handle_, CONV_SERVICE_VERSION, "1.0");
+  if (CONV_ERROR_NONE != error) {
+    // TODO: Handle error
+    trace_conv_error(error, __LINE__, "conv_service_set_property Version");
+  }
+}
+
+ConvergenceAppCommunicationServerService::~ConvergenceAppCommunicationServerService() {
+  ScopeLogger();
+}
+
+common::TizenResult ConvergenceAppCommunicationServerService::Start(
+  ConvergenceChannel *channel,
+  const int cur_listener_id) {
+  ScopeLogger();
+
+
+  /* By implementation, the local App Communication Service doesn't send
+   * the callback confirming that the Start is successful.
+   * So we are sending this callback manually
+   */
+  picojson::object param;
+  param[kServiceListenerStatus] = picojson::value(kServiceListenerStatusOk);
+
+  // The object 'channel' will be deleted in the super::Start() so we should
+  // add its json before
+  param["channel"] = ConvergenceChannel::ToJson(channel->GetHandle()); // Define string as constant
+  param["result_type"] = picojson::value("onStart");
+
+  common::TizenResult result = ConvergenceAppCommunicationService::Start(
+    channel, cur_listener_id);
+
+  // TODO check if result value is OK
+
+  convergence_plugin_->ReplyAsync(kAppCommunicationListenerCallback,
+    cur_listener_id, true, param);
+  return result;
+}
+
+common::TizenResult ConvergenceAppCommunicationServerService::Stop(
+  ConvergenceChannel *channel,
+  const int cur_listener_id) {
+  ScopeLogger();
+
+  /* By implementation, the local App Communication Service doesn't send
+   * the callback confirming that the Start is successful.
+   * So we are sending this callback manually
+   */
+  picojson::object param;
+  param[kServiceListenerStatus] = picojson::value(kServiceListenerStatusOk);
+
+  // The object 'channel' will be deleted in the super::Start() so we should
+  // add its json before
+  param["channel"] = ConvergenceChannel::ToJson(channel->GetHandle());
+  param["result_type"] = picojson::value("onStop");
+
+  common::TizenResult result = ConvergenceAppCommunicationService::Stop(
+    channel, cur_listener_id);
+
+  // TODO check if result value is OK
+
+  convergence_plugin_->ReplyAsync(kAppCommunicationListenerCallback,
+    cur_listener_id, true, param);
+  return result;
+}
+
+ConvergenceAppCommunicationClientService::ConvergenceAppCommunicationClientService()
+ : ConvergenceAppCommunicationService() {
+  ScopeLogger();
+}
+
+ConvergenceAppCommunicationClientService::ConvergenceAppCommunicationClientService(
+  conv_device_h device, ConvergenceInstance *convergence_plugin)
+ : ConvergenceAppCommunicationService(device, convergence_plugin) {
+  ScopeLogger();
+}
+
+ConvergenceAppCommunicationClientService::~ConvergenceAppCommunicationClientService() {
+  ScopeLogger();
+}
+
+void ConvergenceAppCommunicationClientService::ServiceConnectedCb(conv_service_h service_handle,
+  conv_error_e error, conv_payload_h result, void* user_data) {
+  ScopeLogger();
+
+  CallbackParam *callbackParam = static_cast<CallbackParam *>(user_data);
+  if (!callbackParam) {
+    LoggerE("ERROR! NULL user data in Service Connect Callback");
+    return;
+  }
+
+  picojson::object param;
+  param[kPayload] = ConvergenceAppCommunicationService::PayloadToJson(result);
+  param["result_type"] = picojson::value("Connected");
+
+  if (CONV_ERROR_NONE == error) {
+    param[kServiceConnectionStatus] = picojson::value(kServiceConnectionStatusConnected);
+    callbackParam->plugin_->ReplyAsync(kAppCommunicationListenerCallback,
+      callbackParam->callback_id_,
+      true,
+      param);
+  } else {
+    // Error occured during connection
+    param[kServiceConnectionStatus] = picojson::value(kServiceConnectionStatusNotConnected);
+    callbackParam->plugin_->ReplyAsync(kAppCommunicationListenerCallback,
+      callbackParam->callback_id_,
+      false,
+      param);
+  }
+}
+
+common::TizenResult ConvergenceAppCommunicationClientService::Connect(const int cur_listener_id) {
+  ScopeLogger();
+
+  conv_service_h service = FindServiceHandle();
+  if (!service) {
+    return LogAndCreateTizenError(NotFoundError,
+      "Service with specified type does not exist");
+  }
+
+  // TODO: make a garbage collection and release this memory when service is disconnected
+  // and when whole manager is destructed
+  CallbackParam *param = new CallbackParam(convergence_plugin_, cur_listener_id);
+  callback_param_gc_.push_back(param);
+
+  const int error = conv_service_connect(service, ServiceConnectedCb, param);
+  if (CONV_ERROR_NONE != error) {
+    // TODO: Handle error
+    trace_conv_error(error, __LINE__, "conv_service_connect");
+  }
+
+  return TizenSuccess();
+}
+
+common::TizenResult ConvergenceAppCommunicationClientService::Disconnect() {
+  ScopeLogger();
+
+  conv_service_h service = FindServiceHandle();
+  if (!service)
+    return LogAndCreateTizenError(NotFoundError,
+      "Service with specified type does not exist");
+
+  const int error = conv_service_disconnect(service);
+  if (CONV_ERROR_NONE != error) {
+    // TODO: Handle error
+    trace_conv_error(error, __LINE__, "conv_service_disconnect");
+  }
+
+  return TizenSuccess();
+}
+
+
+} // namespace convergence
+}  // namespace extension
diff --git a/src/convergence/convergence_app_communication_service.h b/src/convergence/convergence_app_communication_service.h
new file mode 100644 (file)
index 0000000..49ad8e6
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef CONVERGENCE_CONVERGENCE_APP_COMMUNICATION_SERVICE_H__
+#define CONVERGENCE_CONVERGENCE_APP_COMMUNICATION_SERVICE_H__
+
+// TODO check includes
+#include <d2d_conv_manager.h>
+
+#include <string>
+#include <unordered_map>
+
+#include "convergence/convergence_service.h"
+#include "convergence/convergence_utils.h"
+#include "common/tizen_result.h"
+
+namespace extension {
+namespace convergence {
+
+class ConvergenceChannel;
+class ConvergencePayloadArray;
+
+class ConvergenceAppCommunicationService : public ConvergenceService {
+ public:
+  ConvergenceAppCommunicationService();
+  ConvergenceAppCommunicationService(conv_device_h device, ConvergenceInstance *convergence_plugin);
+  virtual ~ConvergenceAppCommunicationService();
+  ConvergenceAppCommunicationService(const ConvergenceAppCommunicationService&) = delete;
+  ConvergenceAppCommunicationService(ConvergenceAppCommunicationService&&) = delete;
+  ConvergenceAppCommunicationService& operator=(const ConvergenceAppCommunicationService&) = delete;
+  ConvergenceAppCommunicationService& operator=(ConvergenceAppCommunicationService&&) = delete;
+ public:
+  virtual common::TizenResult Start(ConvergenceChannel *channel,
+   const int cur_listener_id);
+  virtual common::TizenResult Stop(ConvergenceChannel *channel,
+   const int cur_listener_id);
+  virtual common::TizenResult Send(ConvergenceChannel *channel,
+   ConvergencePayloadArray *payload,
+   const int cur_listener_id);
+  common::TizenResult SetListener(const int cur_listener_id);
+  common::TizenResult RemoveListener();
+ protected:
+  static picojson::value PayloadToJson(conv_payload_h payload);
+ private:
+  void UpdateListener(const int cur_listener_id);
+  static void ServiceListenerCb(conv_service_h service_handle,
+   conv_channel_h channel_handle,
+   conv_error_e error, conv_payload_h result, void* user_data);
+
+ protected:
+  std::vector<CallbackParam *> callback_param_gc_;
+};
+
+class ConvergenceAppCommunicationServerService : public ConvergenceAppCommunicationService {
+ public:
+  ConvergenceAppCommunicationServerService();
+  ConvergenceAppCommunicationServerService(conv_device_h device, ConvergenceInstance *convergence_plugin);
+  virtual ~ConvergenceAppCommunicationServerService();
+  ConvergenceAppCommunicationServerService(const ConvergenceAppCommunicationServerService&) = delete;
+  ConvergenceAppCommunicationServerService(ConvergenceAppCommunicationServerService&&) = delete;
+  ConvergenceAppCommunicationServerService& operator=(const ConvergenceAppCommunicationServerService&) = delete;
+  ConvergenceAppCommunicationServerService& operator=(ConvergenceAppCommunicationServerService&&) = delete;
+
+ public:
+  virtual common::TizenResult Start(ConvergenceChannel *channel,
+   const int cur_listener_id);
+  virtual common::TizenResult Stop(ConvergenceChannel *channel,
+   const int cur_listener_id);
+};
+
+class ConvergenceAppCommunicationClientService : public ConvergenceAppCommunicationService {
+ public:
+  ConvergenceAppCommunicationClientService();
+  ConvergenceAppCommunicationClientService(conv_device_h device, ConvergenceInstance *convergence_plugin);
+  virtual ~ConvergenceAppCommunicationClientService();
+  ConvergenceAppCommunicationClientService(const ConvergenceAppCommunicationClientService&) = delete;
+  ConvergenceAppCommunicationClientService(ConvergenceAppCommunicationClientService&&) = delete;
+  ConvergenceAppCommunicationClientService& operator=(const ConvergenceAppCommunicationClientService&) = delete;
+  ConvergenceAppCommunicationClientService& operator=(ConvergenceAppCommunicationClientService&&) = delete;
+ public:
+  common::TizenResult Connect(const int cur_listener_id);
+  common::TizenResult Disconnect();
+ private:
+  static void ServiceConnectedCb(conv_service_h service_handle,
+   conv_error_e error, conv_payload_h result, void* user_data);
+};
+
+} // namespace convergence
+} // namespace extension
+
+#endif // CONVERGENCE_CONVERGENCE_APP_COMMUNICATION_SERVICE_H__
diff --git a/src/convergence/convergence_channel.cc b/src/convergence/convergence_channel.cc
new file mode 100644 (file)
index 0000000..45e77c8
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+// TODO check includes
+#include "convergence/convergence_channel.h"
+
+#include <glib.h>
+#include <d2d_conv_internal.h>
+
+#include "convergence/convergence_instance.h"
+#include "convergence/convergence_utils.h"
+#include "common/logger.h"
+
+namespace extension {
+namespace convergence {
+
+namespace {
+// Channel keys
+static const std::string kId = "id"; // This id is used in arguments (comes from JS layer)
+static const std::string kChannelId = "channel_id"; // This id is used in the App Comm Service engine
+static const std::string kUri = "uri";
+} // namespace
+
+ConvergenceChannel::ConvergenceChannel()
+  : channel_handle_(nullptr) {
+  ScopeLogger();
+}
+
+ConvergenceChannel::ConvergenceChannel(const picojson::value &channel_json)
+  : channel_handle_(nullptr) {
+  ScopeLogger();
+  FromJson(channel_json);
+}
+
+ConvergenceChannel::~ConvergenceChannel() {
+  ScopeLogger();
+
+  if (channel_handle_) {
+    conv_channel_destroy(channel_handle_);
+    channel_handle_ = NULL;
+  }
+}
+
+conv_channel_h ConvergenceChannel::GetHandle() const {
+  ScopeLogger();
+  return channel_handle_;
+}
+
+
+void ConvergenceChannel::FromJson(const picojson::value &channel_json) {
+  ScopeLogger();
+  if (channel_json.is<picojson::null>()) {
+    LoggerE("ERROR: Channel json value is NULL");
+    return;
+  }
+
+  if (channel_handle_) {
+    conv_channel_destroy(channel_handle_);
+    channel_handle_ = NULL;
+  }
+
+  int error = conv_channel_create(&channel_handle_);
+  if ((CONV_ERROR_NONE != error) || !channel_handle_) {
+    trace_conv_error(error, __LINE__, "creating channel handle");
+    return;
+  }
+
+  const std::string id = channel_json.get(kId).to_str();
+  error = conv_channel_set_string(channel_handle_, kChannelId.c_str(), id.c_str());
+  if (CONV_ERROR_NONE != error) {
+    trace_conv_error(error, __LINE__, "setting channel string Id");
+  }
+
+  const std::string uri = channel_json.get(kUri).to_str();
+  error = conv_channel_set_string(channel_handle_, kUri.c_str(), uri.c_str());
+  if (CONV_ERROR_NONE != error) {
+    trace_conv_error(error, __LINE__, "setting channel string URI");
+  }
+}
+
+picojson::value ConvergenceChannel::ToJson(conv_channel_h channel_handle) {
+  ScopeLogger();
+
+  picojson::object channel_object;
+  if (!channel_handle) {
+    LoggerE("Error: trying to convert NULL channel handle to json");
+    return picojson::value(channel_object);
+  }
+
+  { // Extracting channel ID
+    char *id = nullptr;
+    const int error = conv_channel_get_string(channel_handle, kChannelId.c_str(), &id);
+    if (CONV_ERROR_NONE != error) {
+      trace_conv_error(error, __LINE__, "getting channel string Id");
+    } else {
+      channel_object[kId] = picojson::value(id);
+      free(id);
+    }
+  }
+
+  { // Extracting channel URI
+    char *uri = nullptr;
+    const int error = conv_channel_get_string(channel_handle, kUri.c_str(), &uri);
+    if (CONV_ERROR_NONE != error) {
+      trace_conv_error(error, __LINE__, "getting channel string URI");
+    } else {
+      channel_object[kUri] = picojson::value(uri);
+      free(uri);
+    }
+  };
+
+  return picojson::value(channel_object);
+}
+
+
+} // namespace convergence
+}  // namespace extension
diff --git a/src/convergence/convergence_channel.h b/src/convergence/convergence_channel.h
new file mode 100644 (file)
index 0000000..952838c
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef CONVERGENCE_CONVERGENCE_CHANNEL_H__
+#define CONVERGENCE_CONVERGENCE_CHANNEL_H__
+
+// TODO check includes
+#include <d2d_conv_manager.h>
+
+#include <string>
+#include <unordered_map>
+
+#include "common/tizen_result.h"
+
+namespace extension {
+namespace convergence {
+
+class ConvergenceChannel {
+
+ public:
+  ConvergenceChannel();
+  ConvergenceChannel(const picojson::value &channel_json);
+  virtual ~ConvergenceChannel();
+  ConvergenceChannel(const ConvergenceChannel&) = delete;
+  ConvergenceChannel(ConvergenceChannel&&) = delete;
+  ConvergenceChannel& operator=(const ConvergenceChannel&) = delete;
+  ConvergenceChannel& operator=(ConvergenceChannel&&) = delete;
+
+ public:
+  conv_channel_h GetHandle() const;
+  void FromJson(const picojson::value &channel_json);
+
+ public:
+  static picojson::value ToJson(conv_channel_h channel_handle);
+
+ private:
+  conv_channel_h channel_handle_;
+};
+
+} // namespace convergence
+} // namespace extension
+
+#endif // CONVERGENCE_CONVERGENCE_CHANNEL_H__
diff --git a/src/convergence/convergence_client_info.cc b/src/convergence/convergence_client_info.cc
new file mode 100644 (file)
index 0000000..6dece11
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+// TODO check includes
+#include "convergence/convergence_client_info.h"
+
+#include <glib.h>
+#include <d2d_conv_internal.h>
+#include <stdlib.h>
+
+#include "convergence/convergence_instance.h"
+#include "convergence/convergence_utils.h"
+#include "common/logger.h"
+
+namespace extension {
+namespace convergence {
+
+namespace {
+// Service keys
+static const std::string kIsHost = "client_is_host";
+static const std::string kConnectionTime = "client_connect_time"; //--
+static const std::string kClientId = "client_id";
+} // namespace
+
+ConvergenceClientInfo::ConvergenceClientInfo()
+ : isHost_(false)
+ , connectionTime_(0) {
+  ScopeLogger();
+}
+
+ConvergenceClientInfo::ConvergenceClientInfo(conv_payload_h payload)
+ : isHost_(false)
+ , connectionTime_(0) {
+  ScopeLogger();
+
+  isHost_ = ExtractPayloadString(payload, "client_is_host") == "1";
+  connectionTime_ = atol(ExtractPayloadString(payload, "client_connect_time").c_str());
+  clientId_ = ExtractPayloadString(payload, "client_id").c_str();
+}
+
+ConvergenceClientInfo::~ConvergenceClientInfo() {
+  ScopeLogger();
+}
+
+std::string ConvergenceClientInfo::ExtractPayloadString(conv_payload_h payload, const char *key) {
+  ScopeLogger();
+  char *value = nullptr;
+  const int error = conv_payload_get_string(payload, key, &value);
+  if (CONV_ERROR_NONE != error) {
+    trace_conv_error(error, __LINE__, "conv_payload_get_string");
+    return "";
+  }
+
+  if (value) {
+    const std::string result = std::string(value);
+    free(value);
+    return result;
+  }
+  return "";
+}
+
+picojson::value ConvergenceClientInfo::ToJson() const {
+  ScopeLogger();
+  picojson::object clientInfo;
+  clientInfo["isHost"] = picojson::value(static_cast<double>(isHost_));
+  clientInfo["connectionTime"] = picojson::value(static_cast<double>(connectionTime_));
+  clientInfo["clientId"] = picojson::value(clientId_);
+  return picojson::value(clientInfo);
+}
+
+
+} // namespace convergence
+}  // namespace extension
diff --git a/src/convergence/convergence_client_info.h b/src/convergence/convergence_client_info.h
new file mode 100644 (file)
index 0000000..665200c
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef CONVERGENCE_CONVERGENCE_CLIENT_INFO_H__
+#define CONVERGENCE_CONVERGENCE_CLIENT_INFO_H__
+
+// TODO check includes
+#include <d2d_conv_manager.h>
+
+#include <string>
+#include <unordered_map>
+
+#include "common/tizen_result.h"
+
+namespace extension {
+namespace convergence {
+
+class ConvergenceClientInfo {
+ public:
+  ConvergenceClientInfo();
+  ConvergenceClientInfo(conv_payload_h payload);
+  virtual ~ConvergenceClientInfo();
+  ConvergenceClientInfo(const ConvergenceClientInfo&) = delete;
+  ConvergenceClientInfo(ConvergenceClientInfo&&) = delete;
+  ConvergenceClientInfo& operator=(const ConvergenceClientInfo&) = delete;
+  ConvergenceClientInfo& operator=(ConvergenceClientInfo&&) = delete;
+
+ public:
+  picojson::value ToJson() const;
+
+ private:
+  std::string ExtractPayloadString(conv_payload_h payload, const char *key);
+
+ private:
+  bool isHost_;
+  std::string clientId_;
+  long connectionTime_;
+};
+
+} // namespace convergence
+} // namespace extension
+
+#endif // CONVERGENCE_CONVERGENCE_CLIENT_INFO_H__
diff --git a/src/convergence/convergence_device.cc b/src/convergence/convergence_device.cc
new file mode 100644 (file)
index 0000000..6e82033
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+// TODO check includes
+#include "convergence/convergence_device.h"
+
+#include <glib.h>
+#include <d2d_conv_internal.h>
+
+#include "convergence/convergence_instance.h"
+#include "convergence/convergence_service.h"
+#include "convergence/convergence_remote_app_control_service.h"
+#include "convergence/convergence_app_communication_service.h"
+#include "convergence/convergence_utils.h"
+#include "common/logger.h"
+
+namespace extension {
+namespace convergence {
+
+namespace {
+// Service keys
+static const std::string kId = "id";
+static const std::string kName = "name"; //--
+static const std::string kDeviceType = "profile";
+static const std::string kDeviceServices = "services";
+} // namespace
+
+ConvergenceDevice::ConvergenceDevice()
+ : device_handle_(nullptr)
+ , convergence_plugin_(NULL) {
+  ScopeLogger();
+}
+
+ConvergenceDevice::ConvergenceDevice(conv_device_h device, ConvergenceInstance *convergence_plugin)
+ : device_handle_(device)
+ , convergence_plugin_(convergence_plugin) {
+  ScopeLogger();
+}
+
+ConvergenceDevice::~ConvergenceDevice() {
+  ScopeLogger();
+
+  // Releasing all registered services
+  for (auto it = services_.begin(); it != services_.end(); ++it) {
+    delete it->second;
+  }
+  services_.clear();
+
+  conv_device_destroy(device_handle_);
+}
+
+std::string ConvergenceDevice::ExtractDevicePropery(const char *property) {
+  ScopeLogger();
+  char *value = nullptr;
+  const int error = conv_device_get_property_string(device_handle_, property, &value);
+  if (CONV_ERROR_NONE != error) {
+    trace_conv_error(error, __LINE__, "conv_device_get_property_string");
+    return "";
+  }
+
+  if (value) {
+    const std::string result = std::string(value);
+    free(value);
+    return result;
+  }
+  return "";
+}
+
+ConvergenceService *ConvergenceDevice::SwapService(const int service_type) {
+  ScopeLogger();
+  LoggerI("Swapping service of type [%d]", service_type);
+  if (services_.count(service_type) <= 0) {
+    switch(service_type) {
+      case CONV_SERVICE_APP_TO_APP_COMMUNICATION: {
+        services_[service_type] =
+          new ConvergenceAppCommunicationService(device_handle_,
+            convergence_plugin_);
+        break;
+      }
+      case CONV_SERVICE_REMOTE_APP_CONTROL: {
+        LoggerI("Adding Remote App Control service");
+        services_[service_type]  =
+          new ConvergenceRemoteAppControlService(device_handle_,
+            convergence_plugin_);
+        break;
+      }
+      default: {
+        LoggerE("ERROR! UNKNOWN SERVICE TYPE [%d]", service_type);
+        return nullptr;
+      }
+    }
+  }
+  return services_[service_type];
+}
+
+ConvergenceService *ConvergenceDevice::RegisterLocalService(const int service_type) {
+  ScopeLogger();
+  LoggerI("Registering the local service of type [%d]", service_type);
+  if (services_.count(service_type) <= 0) {
+    switch(service_type) {
+      case CONV_SERVICE_APP_TO_APP_COMMUNICATION: {
+        services_[service_type] =
+          new ConvergenceAppCommunicationServerService(device_handle_,
+            convergence_plugin_);
+        LoggerI("Registered for local device [%s] the service [0x%0x] of type [%d]",
+          id_.c_str(), services_[service_type], CONV_SERVICE_APP_TO_APP_COMMUNICATION);
+        break;
+      }
+      default: {
+        LoggerE("ERROR! UNKNOWN SERVICE TYPE [%d]", service_type);
+        return nullptr;
+      }
+    }
+  }
+  return services_[service_type];
+}
+
+void ConvergenceDevice::ForEachServiceCb(conv_service_h service_handle,
+  void* user_data) {
+  ScopeLogger();
+  if (!service_handle || !user_data) {
+    LoggerE("ERROR! Invalid parameters of D2D API Callback");
+    return;
+  }
+  ConvergenceDevice *owner = static_cast<ConvergenceDevice *>(user_data);
+
+  // Extracting service type
+  conv_service_e type = CONV_SERVICE_NONE;
+  int error = conv_service_get_type(service_handle, &type);
+  if (CONV_ERROR_NONE != error) {
+    LoggerE("ERROR! D2D API Get Service Type error [%d]", error);
+    return;
+  }
+
+  ConvergenceService *s = owner->SwapService(type);
+  if (!s) {
+    LoggerE("ERROR! Cannot add NULL service");
+    return;
+  }
+
+  s->Refresh();
+}
+
+void ConvergenceDevice::Refresh() {
+  ScopeLogger();
+  // Getting device properties from handle
+  id_ = ExtractDevicePropery(CONV_DEVICE_ID);
+  name_ = ExtractDevicePropery(CONV_DEVICE_NAME);
+  type_ = ExtractDevicePropery(CONV_DEVICE_TYPE);
+
+  LoggerE("Refreshed device id [%s] name [%s] type [%s]",
+    id_.c_str(), name_.c_str(), type_.c_str());
+
+  // Extracting services
+  const int error = conv_device_foreach_service(device_handle_, ForEachServiceCb, this);
+  if (CONV_ERROR_NONE != error) {
+    trace_conv_error(error, __LINE__, "conv_device_foreach_service");
+  }
+}
+
+picojson::value ConvergenceDevice::ToJson() const {
+  ScopeLogger();
+  picojson::object device_json;
+
+  // Convert device props and services into JSON
+  device_json[kId] = picojson::value(id_);
+  device_json[kName] = picojson::value(name_);
+  device_json[kDeviceType] = picojson::value(type_);
+
+  picojson::array services;
+  for (auto it = services_.begin(); it != services_.end(); ++it) {
+    ConvergenceService *s = it->second;
+    services.push_back(s->ToJson());
+  }
+  device_json[kDeviceServices] = picojson::value(services);
+
+  return picojson::value(device_json);
+}
+
+ConvergenceService *ConvergenceDevice::GetService(const int service_type) const {
+  ScopeLogger();
+  if (services_.count(service_type) <= 0)
+    return nullptr;
+  else
+    return services_[service_type];
+}
+
+
+} // namespace convergence
+}  // namespace extension
diff --git a/src/convergence/convergence_device.h b/src/convergence/convergence_device.h
new file mode 100644 (file)
index 0000000..e16e410
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef CONVERGENCE_CONVERGENCE_DEVICE_H__
+#define CONVERGENCE_CONVERGENCE_DEVICE_H__
+
+// TODO check includes
+#include <d2d_conv_manager.h>
+
+#include <string>
+#include <unordered_map>
+
+#include "common/tizen_result.h"
+
+namespace extension {
+namespace convergence {
+
+class ConvergenceInstance;
+class ConvergenceService;
+
+class ConvergenceDevice {
+
+ public:
+  ConvergenceDevice();
+  ConvergenceDevice(conv_device_h device_handle, ConvergenceInstance *convergence_plugin);
+  virtual ~ConvergenceDevice();
+  ConvergenceDevice(const ConvergenceDevice&) = delete;
+  ConvergenceDevice(ConvergenceDevice&&) = delete;
+  ConvergenceDevice& operator=(const ConvergenceDevice&) = delete;
+  ConvergenceDevice& operator=(ConvergenceDevice&&) = delete;
+
+
+ public:
+  void Refresh();
+  ConvergenceService *GetService(const int service_type) const;
+  //picojson::object ToJson() const;
+  picojson::value ToJson() const;
+  //std::string get_device() const {return device_handle_; }
+
+ public:
+  ConvergenceService *RegisterLocalService(const int service_type);
+  void SetId(const std::string &id) { id_ = id; }
+
+ private:
+  ConvergenceService *SwapService(const int service_type);
+  std::string ExtractDevicePropery(const char *property);
+  static void ForEachServiceCb(conv_service_h service_handle, void* user_data);
+
+ private:
+  conv_device_h device_handle_;
+  ConvergenceInstance *convergence_plugin_;
+  mutable std::unordered_map<int, ConvergenceService *> services_;
+
+ private:
+  std::string id_;
+  std::string name_;
+  std::string type_; // Device profile: mobile, wearable, TV
+};
+
+} // namespace convergence
+} // namespace extension
+
+#endif // CONVERGENCE_CONVERGENCE_DEVICE_H__
diff --git a/src/convergence/convergence_extension.cc b/src/convergence/convergence_extension.cc
new file mode 100644 (file)
index 0000000..6ddea57
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#include "convergence/convergence_extension.h"
+
+#include "convergence/convergence_instance.h"
+
+// This will be generated from convergence_api.js
+extern const char kSource_convergence_api[];
+
+common::Extension* CreateExtension() {
+  return new ConvergenceExtension;
+}
+
+ConvergenceExtension::ConvergenceExtension() {
+  SetExtensionName("tizen.convergence");
+  SetJavaScriptAPI(kSource_convergence_api);
+
+  const char* entry_points[] = {
+      "tizen.RemoteAppControlService",
+      "tizen.AppCommunicationServerService",
+      "tizen.AppCommunicationClientService",
+      "tizen.Channel",
+      "tizen.PayloadString",
+      "tizen.PayloadRawBytes",
+      nullptr
+    };
+  SetExtraJSEntryPoints(entry_points);
+}
+
+ConvergenceExtension::~ConvergenceExtension() {}
+
+common::Instance* ConvergenceExtension::CreateInstance() {
+  return new extension::convergence::ConvergenceInstance;
+}
diff --git a/src/convergence/convergence_extension.h b/src/convergence/convergence_extension.h
new file mode 100644 (file)
index 0000000..1a043d2
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef CONVERGENCE_CONVERGENCE_EXTENSION_H_
+#define CONVERGENCE_CONVERGENCE_EXTENSION_H_
+
+#include "common/extension.h"
+
+class ConvergenceExtension : public common::Extension {
+ public:
+  ConvergenceExtension();
+  virtual ~ConvergenceExtension();
+
+ private:
+  virtual common::Instance* CreateInstance();
+};
+
+#endif // CONVERGENCE_CONVERGENCE_EXTENSION_H_
diff --git a/src/convergence/convergence_instance.cc b/src/convergence/convergence_instance.cc
new file mode 100644 (file)
index 0000000..22bd337
--- /dev/null
@@ -0,0 +1,683 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#include "convergence/convergence_instance.h"
+
+#include <functional>
+#include <string>
+
+#include "convergence/convergence_manager.h"
+#include "convergence/convergence_remote_app_control_service.h"
+#include "convergence/convergence_app_communication_service.h"
+#include "convergence/convergence_channel.h"
+#include "convergence/convergence_payload.h"
+#include "common/logger.h"
+#include "common/picojson.h"
+#include "common/task-queue.h"
+#include "common/tools.h"
+
+
+namespace extension {
+namespace convergence {
+
+namespace {
+// The privileges that required in Convergence API
+const std::string kPrivilegeInternet = "http://tizen.org/privilege/internet";
+const std::string kPrivilegeBluetooth = "http://tizen.org/privilege/bluetooth";
+const std::string kPrivilegeWifiDirect = "http://tizen.org/privilege/wifidirect";
+
+// JS listener keys
+static const std::string kJSListenerStatus = "status";
+static const std::string kJSCurrentListenerId = "curListenerId";
+static const std::string kJSTargetListenerId = "listenerId";
+static const std::string kSuccess = "success";
+static const std::string kError = "error";
+
+// Arguments, passed from JS
+static const std::string kJSCallbackId = "callbackId";
+static const std::string kJSArgumentDeviceId = "deviceId";
+static const std::string kJSArgumentServiceTypeNumber = "serviceTypeNumber";
+static const std::string kJSArgumentChannel = "channel_data";
+static const std::string kJSArgumentPayload = "payload";
+static const std::string kJSArgumentAppId = "appId";
+static const std::string kJSArgumentReply = "reply";
+static const std::string kJSArgumentTimeout = "timeout";
+static const std::string kJSArgumentService = "service";
+} // namespace
+
+using namespace common;
+
+ConvergenceInstance::ConvergenceInstance() {
+  using namespace std::placeholders;
+  #define REGISTER_SYNC(c,x) \
+    RegisterSyncHandler(c, std::bind(&ConvergenceInstance::x, this, _1, _2));
+  REGISTER_SYNC("ConvergenceManager_stopDiscovery",
+    ConvergenceManagerStopDiscovery);
+
+  REGISTER_SYNC("AppCommunicationService_addListener", AppCommunicationServiceAddListener);
+  REGISTER_SYNC("AppCommunicationService_removeListener", AppCommunicationServiceRemoveListener);
+  //REGISTER_SYNC("Service_createLocalService", ServiceCreateLocal);
+  #undef REGISTER_SYNC
+  #define REGISTER_ASYNC(c,x) \
+    RegisterSyncHandler(c, std::bind(&ConvergenceInstance::x, this, _1, _2));
+  REGISTER_ASYNC("ConvergenceManager_startDiscovery",
+    ConvergenceManagerStartDiscovery);
+
+  REGISTER_ASYNC("RemoteAppControlService_connect", RemoteAppControlServiceConnect);
+  REGISTER_ASYNC("RemoteAppControlService_disconnect", RemoteAppControlServiceDisconnect);
+  REGISTER_ASYNC("RemoteAppControlService_launch", RemoteAppControlServiceLaunch);
+
+  REGISTER_ASYNC("AppCommunicationService_start", AppCommunicationServiceStart);
+  REGISTER_ASYNC("AppCommunicationService_stop", AppCommunicationServiceStop);
+  REGISTER_ASYNC("AppCommunicationService_send", AppCommunicationServiceSend);
+
+  REGISTER_ASYNC("AppCommunicationServerService_constructLocal", AppCommunicationServerServiceConstructLocal);
+
+  REGISTER_ASYNC("AppCommunicationClientService_connect", AppCommunicationClientServiceConnect);
+  REGISTER_ASYNC("AppCommunicationClientService_disconnect", AppCommunicationClientServiceDisconnect);
+  #undef REGISTER_ASYNC
+}
+
+ConvergenceInstance::~ConvergenceInstance() {
+}
+
+void ConvergenceInstance::ReplyAsync(ConvergenceCallbacks callback_function_type,
+  int curListenerId, bool isSuccess, picojson::object& param) {
+  ScopeLogger();
+
+  param[kJSListenerStatus] = picojson::value(isSuccess ? kSuccess : kError);
+  param[kJSCurrentListenerId] = picojson::value(static_cast<double>(curListenerId));
+
+  switch(callback_function_type) {
+    case kConvergenceManagerDiscoveryCallback: {
+      param[kJSTargetListenerId] = picojson::value("CONVERGENCE_DISCOVERY_LISTENER");
+      break;
+    }
+    /*case kRemoteAppControlServiceConnectCallback: {
+      param[kJSTargetListenerId] =
+        picojson::value("REMOTE_APP_CONTROL_SERVICE_CONNECT_LISTENER");
+      break;
+    }*/
+    case kRemoteAppControlListenerCallback: {
+      param[kJSTargetListenerId] =
+        picojson::value("REMOTE_APP_CONTROL_SERVICE_LISTENER");
+      break;
+    }
+    /*case kAppCommunicationSuccessCallback: {
+      param[kJSTargetListenerId] =
+        picojson::value("APP_COMMUNICATION_SERVICE_SUCCESS_LISTENER");
+      break;
+    }*/
+    case kAppCommunicationListenerCallback: {
+      param[kJSTargetListenerId] =
+        picojson::value("APP_COMMUNICATION_SERVICE_LISTENER");
+      break;
+    }
+    /*case kAppCommunicationClientServiceConnectCallback: {
+      param[kJSTargetListenerId] =
+        picojson::value("APP_COMMUNICATON_CLIENT_SERVICE_CONNECT_LISTENER");
+      break;
+    }*/
+    default: {
+      LoggerE("Invalid Callback Type");
+      return;
+    }
+  }
+
+  picojson::value result = picojson::value(param);
+  LoggerD("---> %s", result.serialize().c_str()); // TODO remove
+
+  PostMessage(result.serialize().c_str());
+}
+
+#define CHECK_EXIST(args, name, out) \
+    if (!args.contains(name)) {\
+      ReportError(TypeMismatchException(name" is required argument"), out);\
+      return;\
+    }
+
+void ConvergenceInstance::ConvergenceManagerStartDiscovery(
+  const picojson::value& args, picojson::object& out) {
+  ScopeLogger();
+  CHECK_EXIST(args, "callbackId", out)
+  CHECK_EXIST(args, "timeout", out)
+
+  /*CHECK_PRIVILEGE_ACCESS(kPrivilegeInternet, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeBluetooth, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeWifiDirect, &out)*/
+
+  LoggerI("ARGS: %s", args.serialize().c_str());
+
+  auto start_discovery =
+    [this, args](const std::shared_ptr<picojson::value>& result) {
+    ScopeLogger("start_discovery");
+
+    // Start the discovery procedure
+    ConvergenceManager::GetInstance(this).StartDiscovery(
+      static_cast<long>(args.get(kJSArgumentTimeout).get<double>()));
+
+    picojson::object& object = result->get<picojson::object>();
+    object[kJSCallbackId] = args.get(kJSCallbackId);
+    ReportSuccess(object);
+  };
+
+  auto start_discovery_result =
+    [this, args](const std::shared_ptr<picojson::value>& result) {
+    ScopeLogger("start_discovery_result");
+    result->get<picojson::object>()[kJSCallbackId] = args.get(kJSCallbackId);
+    Instance::PostMessage(this, result->serialize().c_str());
+  };
+
+  auto data =
+    std::shared_ptr<picojson::value>{new picojson::value{picojson::object()}};
+
+  TaskQueue::GetInstance().Queue<picojson::value>(
+    start_discovery,
+    start_discovery_result,
+    data);
+
+  ReportSuccess(out);
+}
+
+void ConvergenceInstance::ConvergenceManagerStopDiscovery(
+  const picojson::value& args, picojson::object& out) {
+  ScopeLogger();
+
+  /*CHECK_PRIVILEGE_ACCESS(kPrivilegeInternet, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeBluetooth, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeWifiDirect, &out)*/
+
+  // Running the discovery stop procedure
+  ConvergenceManager::GetInstance(this).StopDiscovery();
+  //out[kJSCallbackId] = args.get(kJSCallbackId);
+  ReportSuccess(out);
+}
+
+void ConvergenceInstance::RemoteAppControlServiceConnect(
+  const picojson::value& args, picojson::object& out) {
+  ScopeLogger();
+  CHECK_EXIST(args, "callbackId", out)
+
+  /*CHECK_PRIVILEGE_ACCESS(kPrivilegeInternet, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeBluetooth, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeWifiDirect, &out)*/
+
+  LoggerI("ARGS: %s", args.serialize().c_str());
+
+  auto connect =
+    [this, args](const std::shared_ptr<picojson::value>& result) {
+    ScopeLogger("connect");
+
+    // Finding the service
+    ConvergenceRemoteAppControlService *service =
+      static_cast<ConvergenceRemoteAppControlService *>(
+        ConvergenceManager::GetInstance(this).GetService(args.get(kJSArgumentDeviceId).to_str().c_str(),
+          CONV_SERVICE_REMOTE_APP_CONTROL));
+    if (!service) {
+      LoggerE("Can not find the service type = 1, device_id = ",
+        args.get(kJSArgumentDeviceId).to_str().c_str());
+      //ReportSuccess(object); // TODO ReportError
+      return;
+    }
+
+    // Running the service connect procedure
+    service->Connect(static_cast<int>(args.get(kJSCurrentListenerId).get<double>()));
+
+    picojson::object& object = result->get<picojson::object>();
+    object[kJSCallbackId] = args.get(kJSCallbackId);
+    ReportSuccess(object);
+  };
+
+  auto connect_result =
+    [this, args](const std::shared_ptr<picojson::value>& result) {
+    ScopeLogger("connect_result");
+
+    result->get<picojson::object>()[kJSCallbackId] = args.get(kJSCallbackId);
+    Instance::PostMessage(this, result->serialize().c_str());
+  };
+
+  auto data =
+    std::shared_ptr<picojson::value>{new picojson::value{picojson::object()}};
+
+  TaskQueue::GetInstance().Queue<picojson::value>(
+    connect,
+    connect_result,
+    data);
+
+  ReportSuccess(out);
+}
+
+void ConvergenceInstance::RemoteAppControlServiceDisconnect(
+  const picojson::value& args, picojson::object& out) {
+  ScopeLogger();
+
+  /*CHECK_PRIVILEGE_ACCESS(kPrivilegeInternet, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeBluetooth, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeWifiDirect, &out)*/
+
+  //LoggerI("ARGS: %s", args.serialize().c_str());
+
+  auto disconnect =
+    [this, args](const std::shared_ptr<picojson::value>& result) {
+    ScopeLogger("disconnect");
+
+    // Finding the service
+    ConvergenceRemoteAppControlService *service =
+      static_cast<ConvergenceRemoteAppControlService *>(
+        ConvergenceManager::GetInstance(this).GetService(args.get(kJSArgumentDeviceId).to_str().c_str(),
+          CONV_SERVICE_REMOTE_APP_CONTROL));
+    if (!service) {
+      //ReportSuccess(object); // TODO ReportError
+      LoggerE("Can not find the service type = 1, device_id = ",
+        args.get(kJSArgumentDeviceId).to_str().c_str());
+      return;
+    }
+
+    // Running the service disconnect procedure
+    service->Disconnect();
+
+    picojson::object& object = result->get<picojson::object>();
+    ReportSuccess(object);
+  };
+
+  auto data =
+    std::shared_ptr<picojson::value>{new picojson::value{picojson::object()}};
+
+  TaskQueue::GetInstance().Async<picojson::value>(
+      disconnect,
+      data);
+
+  ReportSuccess(out);
+}
+
+void ConvergenceInstance::RemoteAppControlServiceLaunch(
+  const picojson::value& args, picojson::object& out) {
+  ScopeLogger();
+  CHECK_EXIST(args, "callbackId", out)
+
+  /*CHECK_PRIVILEGE_ACCESS(kPrivilegeInternet, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeBluetooth, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeWifiDirect, &out)*/
+
+  auto launch = [this, args](const std::shared_ptr<picojson::value>& result) {
+    ScopeLogger("send");
+
+    // Finding the service
+    ConvergenceRemoteAppControlService *service =
+      static_cast<ConvergenceRemoteAppControlService *>(
+        ConvergenceManager::GetInstance(this).GetService(args.get(kJSArgumentDeviceId).to_str().c_str(),
+          CONV_SERVICE_REMOTE_APP_CONTROL));
+    if (!service) {
+      //ReportSuccess(object); // TODO ReportError
+      LoggerE("Can not find the service type = 1, device_id = ",
+        args.get(kJSArgumentDeviceId).to_str().c_str());
+      return;
+    }
+
+    // Running the service app control procedure
+    service->Launch(
+      args.get(kJSArgumentAppId).to_str().c_str(),
+      static_cast<bool>(args.get(kJSArgumentReply).get<bool>()),
+      static_cast<int>(args.get(kJSCurrentListenerId).get<double>()));
+
+    picojson::object& object = result->get<picojson::object>();
+    object[kJSCallbackId] = args.get(kJSCallbackId);
+    ReportSuccess(object);
+  };
+
+  auto launch_result =
+    [this, args](const std::shared_ptr<picojson::value>& result) {
+    ScopeLogger("send_result");
+    result->get<picojson::object>()[kJSCallbackId] = args.get(kJSCallbackId);
+    Instance::PostMessage(this, result->serialize().c_str());
+  };
+
+  auto data =
+    std::shared_ptr<picojson::value>{new picojson::value{picojson::object()}};
+
+  TaskQueue::GetInstance().Queue<picojson::value>(
+    launch,
+    launch_result,
+    data);
+
+  ReportSuccess(out);
+}
+
+void ConvergenceInstance::AppCommunicationServiceStart(
+  const picojson::value& args, picojson::object& out) {
+  ScopeLogger();
+  CHECK_EXIST(args, "callbackId", out)
+
+  /*CHECK_PRIVILEGE_ACCESS(kPrivilegeInternet, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeBluetooth, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeWifiDirect, &out)*/
+
+  auto start = [this, args](const std::shared_ptr<picojson::value>& result) {
+    ScopeLogger("start");
+
+    // Finding the service
+    ConvergenceAppCommunicationService *service =
+      static_cast<ConvergenceAppCommunicationService *>(
+        ConvergenceManager::GetInstance(this).GetService(args.get(kJSArgumentDeviceId).to_str().c_str(),
+          CONV_SERVICE_APP_TO_APP_COMMUNICATION));
+    if (!service) {
+      //ReportSuccess(object); // TODO ReportError
+      LoggerE("Can not find the service type = 0, device_id = ",
+        args.get(kJSArgumentDeviceId).to_str().c_str());
+      return;
+    }
+
+    // Running the service start procedure
+    service->Start(new ConvergenceChannel(args.get(kJSArgumentChannel)),
+      static_cast<int>(args.get(kJSCurrentListenerId).get<double>()));
+
+    picojson::object& object = result->get<picojson::object>();
+    object[kJSCallbackId] = args.get(kJSCallbackId);
+    ReportSuccess(object);
+  };
+
+  auto start_result =
+    [this, args](const std::shared_ptr<picojson::value>& result) {
+    ScopeLogger("start_result");
+    result->get<picojson::object>()[kJSCallbackId] = args.get(kJSCallbackId);
+    Instance::PostMessage(this, result->serialize().c_str());
+  };
+
+  auto data = std::shared_ptr<picojson::value>{new picojson::value{picojson::object()}};
+
+  TaskQueue::GetInstance().Queue<picojson::value>(
+    start,
+    start_result,
+    data);
+
+  ReportSuccess(out);
+}
+
+void ConvergenceInstance::AppCommunicationServiceSend(
+  const picojson::value& args, picojson::object& out) {
+  ScopeLogger();
+  CHECK_EXIST(args, "callbackId", out)
+
+  /*CHECK_PRIVILEGE_ACCESS(kPrivilegeInternet, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeBluetooth, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeWifiDirect, &out)*/
+
+  auto send = [this, args](const std::shared_ptr<picojson::value>& result) {
+    ScopeLogger("send");
+
+    // Finding the service
+    ConvergenceAppCommunicationService *service =
+      static_cast<ConvergenceAppCommunicationService *>(
+        ConvergenceManager::GetInstance(this).GetService(args.get(kJSArgumentDeviceId).to_str().c_str(),
+          CONV_SERVICE_APP_TO_APP_COMMUNICATION));
+    if (!service) {
+      //ReportSuccess(object); // TODO ReportError
+      LoggerE("Can not find the service type = 0, device_id = ",
+        args.get(kJSArgumentDeviceId).to_str().c_str());
+      return;
+    }
+
+    // Running the service send procedure
+    service->Send(new ConvergenceChannel(args.get(kJSArgumentChannel)),
+      new ConvergencePayloadArray(args.get(kJSArgumentPayload)),
+      static_cast<int>(args.get(kJSCurrentListenerId).get<double>()));
+
+    picojson::object& object = result->get<picojson::object>();
+    object[kJSCallbackId] = args.get(kJSCallbackId);
+    ReportSuccess(object);
+  };
+
+  auto send_result =
+    [this, args](const std::shared_ptr<picojson::value>& result) {
+    ScopeLogger("send_result");
+    result->get<picojson::object>()[kJSCallbackId] = args.get(kJSCallbackId);
+    Instance::PostMessage(this, result->serialize().c_str());
+  };
+
+  auto data =
+    std::shared_ptr<picojson::value>{new picojson::value{picojson::object()}};
+
+  TaskQueue::GetInstance().Queue<picojson::value>(
+    send,
+    send_result,
+    data);
+
+  ReportSuccess(out);
+}
+
+void ConvergenceInstance::AppCommunicationServiceStop(
+  const picojson::value& args, picojson::object& out) {
+  ScopeLogger();
+  CHECK_EXIST(args, "callbackId", out)
+
+  /*CHECK_PRIVILEGE_ACCESS(kPrivilegeInternet, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeBluetooth, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeWifiDirect, &out)*/
+
+  auto stop = [this, args](const std::shared_ptr<picojson::value>& result) {
+    ScopeLogger("stop");
+
+    // Finding the service
+    ConvergenceAppCommunicationService *service =
+      static_cast<ConvergenceAppCommunicationService *>(
+        ConvergenceManager::GetInstance(this).GetService(args.get(kJSArgumentDeviceId).to_str().c_str(),
+          CONV_SERVICE_APP_TO_APP_COMMUNICATION));
+    if (!service) {
+      //ReportSuccess(object); // TODO ReportError
+      LoggerE("Can not find the service type = 0, device_id = ",
+        args.get(kJSArgumentDeviceId).to_str().c_str());
+      return;
+    }
+
+    // Running the service stop procedure
+    service->Stop(new ConvergenceChannel(args.get(kJSArgumentChannel)),
+      static_cast<int>(args.get(kJSCurrentListenerId).get<double>()));
+
+    picojson::object& object = result->get<picojson::object>();
+    object[kJSCallbackId] = args.get(kJSCallbackId);
+    ReportSuccess(object);
+  };
+
+  auto stop_result =
+    [this, args](const std::shared_ptr<picojson::value>& result) {
+    ScopeLogger("stop_result");
+    result->get<picojson::object>()[kJSCallbackId] = args.get(kJSCallbackId);
+    Instance::PostMessage(this, result->serialize().c_str());
+  };
+
+  auto data =
+    std::shared_ptr<picojson::value>{new picojson::value{picojson::object()}};
+
+  TaskQueue::GetInstance().Queue<picojson::value>(
+    stop,
+    stop_result,
+    data);
+
+  ReportSuccess(out);
+}
+
+void ConvergenceInstance::AppCommunicationServiceAddListener(
+  const picojson::value& args, picojson::object& out) {
+  ScopeLogger();
+
+  /*CHECK_PRIVILEGE_ACCESS(kPrivilegeInternet, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeBluetooth, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeWifiDirect, &out)*/
+
+  LoggerI("ARGS: %s", args.serialize().c_str());
+
+  // Finding the service
+  ConvergenceAppCommunicationService *service =
+    static_cast<ConvergenceAppCommunicationService *>(
+      ConvergenceManager::GetInstance(this).GetService(args.get(kJSArgumentDeviceId).to_str().c_str(),
+        CONV_SERVICE_APP_TO_APP_COMMUNICATION));
+  if (!service) {
+    //ReportSuccess(object); // TODO ReportError
+    LoggerE("Can not find the service type = 0, device_id = ",
+      args.get(kJSArgumentDeviceId).to_str().c_str());
+    return;
+  }
+
+  // Running the service stop procedure
+  service->SetListener(static_cast<int>(args.get(kJSCurrentListenerId).get<double>()));
+
+  ReportSuccess(out);
+}
+
+void ConvergenceInstance::AppCommunicationServiceRemoveListener(
+  const picojson::value& args, picojson::object& out) {
+  ScopeLogger();
+
+  /*CHECK_PRIVILEGE_ACCESS(kPrivilegeInternet, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeBluetooth, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeWifiDirect, &out)*/
+
+  // Finding the service
+  ConvergenceAppCommunicationService *service =
+    static_cast<ConvergenceAppCommunicationService *>(
+      ConvergenceManager::GetInstance(this).GetService(args.get(kJSArgumentDeviceId).to_str().c_str(),
+        CONV_SERVICE_APP_TO_APP_COMMUNICATION));
+  if (!service) {
+    //ReportSuccess(object); // TODO ReportError
+    LoggerE("Can not find the service type = 0, device_id = ",
+      args.get(kJSArgumentDeviceId).to_str().c_str());
+    return;
+  }
+
+  // Running the service stop procedure
+  service->RemoveListener();
+
+  ReportSuccess(out);
+}
+
+void ConvergenceInstance::AppCommunicationServerServiceConstructLocal(
+  const picojson::value& args, picojson::object& out) {
+  ScopeLogger();
+
+  // Finding the service
+  ConvergenceAppCommunicationClientService *service =
+    static_cast<ConvergenceAppCommunicationClientService *>(
+      ConvergenceManager::GetInstance(this).RegisterLocalService(args.get(kJSArgumentDeviceId).to_str().c_str(),
+        CONV_SERVICE_APP_TO_APP_COMMUNICATION));
+  if (!service) {
+    //ReportSuccess(object); // TODO ReportError
+    LoggerE("Can not find the service type = 1, device_id = ",
+      args.get(kJSArgumentDeviceId).to_str().c_str());
+    return;
+  }
+
+  ReportSuccess(out);
+}
+
+void ConvergenceInstance::AppCommunicationClientServiceConnect(
+  const picojson::value& args, picojson::object& out) {
+  ScopeLogger();
+  CHECK_EXIST(args, "callbackId", out)
+
+  /*CHECK_PRIVILEGE_ACCESS(kPrivilegeInternet, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeBluetooth, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeWifiDirect, &out)*/
+
+  LoggerI("ARGS: %s", args.serialize().c_str());
+
+  auto connect =
+    [this, args](const std::shared_ptr<picojson::value>& result) {
+    ScopeLogger("connect");
+
+    // Finding the service
+    ConvergenceAppCommunicationClientService *service =
+      static_cast<ConvergenceAppCommunicationClientService *>(
+        ConvergenceManager::GetInstance(this).GetService(args.get(kJSArgumentDeviceId).to_str().c_str(),
+          CONV_SERVICE_APP_TO_APP_COMMUNICATION));
+    if (!service) {
+      LoggerE("Can not find the service type = 1, device_id = ",
+        args.get(kJSArgumentDeviceId).to_str().c_str());
+      //ReportSuccess(object); // TODO ReportError
+      return;
+    }
+
+    // Running the service connect procedure
+    service->Connect(static_cast<int>(args.get(kJSCurrentListenerId).get<double>()));
+
+    picojson::object& object = result->get<picojson::object>();
+    object[kJSCallbackId] = args.get(kJSCallbackId);
+    ReportSuccess(object);
+  };
+
+  auto connect_result =
+    [this, args](const std::shared_ptr<picojson::value>& result) {
+    ScopeLogger("connect_result");
+
+    result->get<picojson::object>()[kJSCallbackId] = args.get(kJSCallbackId);
+    Instance::PostMessage(this, result->serialize().c_str());
+  };
+
+  auto data =
+    std::shared_ptr<picojson::value>{new picojson::value{picojson::object()}};
+
+  TaskQueue::GetInstance().Queue<picojson::value>(
+    connect,
+    connect_result,
+    data);
+  ReportSuccess(out);
+}
+
+void ConvergenceInstance::AppCommunicationClientServiceDisconnect(
+  const picojson::value& args, picojson::object& out) {
+  ScopeLogger();
+  /*CHECK_PRIVILEGE_ACCESS(kPrivilegeInternet, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeBluetooth, &out)
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeWifiDirect, &out)*/
+
+  //LoggerI("ARGS: %s", args.serialize().c_str());
+
+  auto disconnect =
+    [this, args](const std::shared_ptr<picojson::value>& result) {
+    ScopeLogger("disconnect");
+
+    // Finding the service
+    ConvergenceAppCommunicationClientService *service =
+      static_cast<ConvergenceAppCommunicationClientService *>(
+        ConvergenceManager::GetInstance(this).GetService(args.get(kJSArgumentDeviceId).to_str().c_str(),
+          CONV_SERVICE_APP_TO_APP_COMMUNICATION));
+    if (!service) {
+      //ReportSuccess(object); // TODO ReportError
+      LoggerE("Can not find the service type = 1, device_id = ",
+        args.get(kJSArgumentDeviceId).to_str().c_str());
+      return;
+    }
+
+    // Running the service disconnect procedure
+    service->Disconnect();
+
+    picojson::object& object = result->get<picojson::object>();
+    ReportSuccess(object);
+  };
+
+  auto data =
+    std::shared_ptr<picojson::value>{new picojson::value{picojson::object()}};
+
+  TaskQueue::GetInstance().Async<picojson::value>(
+      disconnect,
+      data);
+  ReportSuccess(out);
+}
+
+
+#undef CHECK_EXIST
+
+} // namespace convergence
+} // namespace extension
diff --git a/src/convergence/convergence_instance.h b/src/convergence/convergence_instance.h
new file mode 100644 (file)
index 0000000..b38f631
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef CONVERGENCE_CONVERGENCE_INSTANCE_H_
+#define CONVERGENCE_CONVERGENCE_INSTANCE_H_
+
+//#include "common/tizen_instance.h"
+#include "common/extension.h"
+
+namespace extension {
+namespace convergence {
+
+enum ConvergenceCallbacks {
+  // Convergence Manager Discovery
+  kConvergenceManagerDiscoveryCallback,
+
+  // Remote App Control
+  //kRemoteAppControlServiceConnectCallback,
+  kRemoteAppControlListenerCallback,
+
+  // App Communication
+  //kAppCommunicationSuccessCallback,
+  kAppCommunicationListenerCallback,
+
+
+  // App Communication Client
+  //kAppCommunicationClientServiceConnectCallback
+};
+
+
+// TODO:  inherit it by common::TizenInstance class
+//class ConvergenceInstance : public common::TizenInstance {
+
+class ConvergenceInstance : public common::ParsedInstance {
+ public:
+  ConvergenceInstance();
+  virtual ~ConvergenceInstance();
+ public:
+  void ReplyAsync(ConvergenceCallbacks cbfunc,
+    int curListenerId, bool isSuccess, picojson::object& param);
+
+ private:
+  // TODO: make all API functions return common::TizenResult
+
+  // Convergence Manager
+  void ConvergenceManagerStartDiscovery(const picojson::value& args,
+   picojson::object& out);
+  void ConvergenceManagerStopDiscovery(const picojson::value& args,
+   picojson::object& out);
+
+  // Remote App Control Service
+  void RemoteAppControlServiceConnect(const picojson::value& args, picojson::object& out);
+  void RemoteAppControlServiceDisconnect(const picojson::value& args, picojson::object& out);
+  void RemoteAppControlServiceLaunch(const picojson::value& args, picojson::object& out);
+
+  // App Communication Service
+  void AppCommunicationServiceStart(const picojson::value& args, picojson::object& out);
+  void AppCommunicationServiceStop(const picojson::value& args, picojson::object& out);
+  void AppCommunicationServiceSend(const picojson::value& args, picojson::object& out);
+  void AppCommunicationServiceAddListener(const picojson::value& args, picojson::object& out);
+  void AppCommunicationServiceRemoveListener(const picojson::value& args, picojson::object& out);
+
+  // App Communication Server Service
+  void AppCommunicationServerServiceConstructLocal(const picojson::value& args, picojson::object& out);
+
+  // App Communication Client Service
+  void AppCommunicationClientServiceConnect(const picojson::value& args, picojson::object& out);
+  void AppCommunicationClientServiceDisconnect(const picojson::value& args, picojson::object& out);
+};
+
+} // namespace convergence
+} // namespace extension
+
+#endif // CONVERGENCE_CONVERGENCE_INSTANCE_H_
diff --git a/src/convergence/convergence_manager.cc b/src/convergence/convergence_manager.cc
new file mode 100644 (file)
index 0000000..07b05b9
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#include "convergence/convergence_manager.h"
+
+#include <glib.h>
+#include <d2d_conv_internal.h>
+
+#include "convergence/convergence_instance.h"
+#include "convergence/convergence_device.h"
+#include "convergence/convergence_service.h"
+#include "convergence/convergence_utils.h"
+#include "common/logger.h"
+
+namespace extension {
+namespace convergence {
+
+using common::TizenResult;
+using common::TizenSuccess;
+
+
+// TODO check which fields are needed
+// TODO remove dublications, marked with //---
+namespace {
+// Commonly used key words
+static const std::string kId = "id"; //---
+static const std::string kName = "name"; //--
+static const std::string kKey = "key";
+static const std::string kValue = "value";
+
+// Channel data keys
+static const std::string kChannel = "channel";
+static const std::string kChannelDataChannelId = "channel_id";
+static const std::string kChannelDataChannelOption = "option";
+static const std::string kChannelDataChannelOptions = "options";
+
+// Payload data keys and some values
+static const std::string kPayload = "payload"; // ---
+static const std::string kPayloadDataType = "type"; // String, bytes or app control
+static const std::string kTypeString = "STRING";
+static const std::string kTypeRawBytes = "RAW_BYTES";
+static const std::string kTypeAppControl = "APP_CONTROL";
+static const std::string kPayloadDataAppControlAppId = "appI";
+static const std::string kPayloadDataAppControlOperation = "operation";
+static const std::string kPayloadDataAppControlUri = "uri";
+static const std::string kPayloadDataAppControlMime = "mime";
+static const std::string kPayloadDataAppControlLaunchMode = "launch_mode";
+static const std::string kPayloadDataAppControlCategory = "category";
+
+// Service keys
+static const std::string kServiceType = "serviceType"; //---
+static const std::string kServiceConnectionState = "connectionState"; //---
+static const std::string kServiceProperties = "properties";
+
+// Device keys
+static const std::string kDevice = "device";
+static const std::string kDeviceType = "profile"; //---
+static const std::string kDeviceServices = "services"; //---
+
+// Discovery keys
+static const std::string kDiscoveryStatus = "discovery_status";
+static const std::string kDiscoveryStatusDeviceFound = "device_found";
+static const std::string kDiscoveryStatusFinished = "discovery_finished";
+static const std::string kDiscoveryStatusError = "discovery_error";
+static const std::string kDiscoveryError = "discovery_error";
+
+// Service connection status keys
+static const std::string kServiceConnectionStatus = "connect_status"; //---
+static const std::string kServiceConnectionStatusConnected = "service_connected"; //---
+static const std::string kServiceConnectionStatusNotConnected = "service_not_connected"; //---
+
+// Service listener status keys
+static const std::string kServiceListenerStatus = "listener_status"; //---
+static const std::string kServiceListenerStatusOk = "listener_ok"; // ---
+static const std::string kServiceListenerStatusError = "listener_error"; //---
+static const std::string kServiceListenerError = "listener_error"; //---
+} // namespace
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+ConvergenceManager &ConvergenceManager::GetInstance(
+  ConvergenceInstance *owner) {
+  static ConvergenceManager instance;
+  instance.convergence_plugin_ = owner;
+  return instance;
+}
+
+ConvergenceManager::ConvergenceManager()
+ : convergence_plugin_(nullptr)
+ , convergence_manager_(nullptr) {
+  ScopeLogger();
+  const int error = conv_create(&convergence_manager_);
+  if (CONV_ERROR_NONE != error) {
+    // Handle error
+    trace_conv_error(error, __LINE__, "conv_create");
+  }
+}
+
+ConvergenceManager::~ConvergenceManager() {
+  ScopeLogger();
+
+  // Releasing all registered devices
+  for (auto it = registered_devices_.begin(); it != registered_devices_.end(); ++it) {
+      delete it->second;
+  }
+  registered_devices_.clear();
+
+  int error = conv_destroy(convergence_manager_);
+  if (CONV_ERROR_NONE != error) {
+    // Handle error
+    trace_conv_error(error, __LINE__, "conv_destroy");
+  }
+  convergence_manager_ = nullptr;
+}
+
+ConvergenceDevice *ConvergenceManager::SwapDevice(const char *device_id,
+  conv_device_h device_handle) {
+  ScopeLogger();
+  if (registered_devices_.count(device_id) <= 0) {
+    ConvergenceDevice *d = new ConvergenceDevice(device_handle, convergence_plugin_);
+    d->SetId(device_id);
+    registered_devices_[device_id] = d;
+    LoggerI("...registering the device [%s, %x]", device_id, device_handle);
+  }
+  return registered_devices_[device_id];
+}
+
+void ConvergenceManager::DiscoveryCb(conv_device_h device_handle,
+  conv_discovery_result_e result, void* user_data) {
+  ScopeLogger();
+
+  if (!user_data) {
+    LoggerE("ERROR! NULL user data in discovery callback");
+    return;
+  }
+
+  ConvergenceManager *owner = static_cast<ConvergenceManager *>(user_data);
+
+  switch(result) {
+  case CONV_DISCOVERY_RESULT_SUCCESS: {
+    LoggerI("...found a device");
+
+    char *id = nullptr;
+    int error = conv_device_get_property_string(device_handle, CONV_DEVICE_ID, &id);
+    if (CONV_ERROR_NONE != error) {
+      trace_conv_error(error, __LINE__, "conv_device_get_property_string ID");
+    }
+    if (!id) {
+      LoggerE("BAD ERROR: Device ID is NULL");
+      return; // TODO report error
+    }
+
+    conv_device_h clone_device = nullptr;
+    error = conv_device_clone(device_handle, &clone_device);
+    if (CONV_ERROR_NONE != error) {
+      trace_conv_error(error, __LINE__, "conv_device_clone");
+      break;
+    }
+    ConvergenceDevice *d = owner->SwapDevice(id, clone_device);
+    free(id);
+
+    if (!d) {
+      // Handle bad error
+      LoggerE("BAD ERROR: Cannot swap the device");
+      break;
+    }
+
+    d->Refresh();
+
+    picojson::object param;
+    param[kDiscoveryStatus] = picojson::value(kDiscoveryStatusDeviceFound);
+    param[kDevice] = d->ToJson();
+    owner->convergence_plugin_->ReplyAsync(kConvergenceManagerDiscoveryCallback,
+      -1,
+      true,
+      param);
+
+    break;
+  }
+  case CONV_DISCOVERY_RESULT_FINISHED: {
+    LoggerE("...discovery finished");
+
+    picojson::object param;
+    param[kDiscoveryStatus] = picojson::value(kDiscoveryStatusFinished);
+    owner->convergence_plugin_->ReplyAsync(kConvergenceManagerDiscoveryCallback,
+      -1,
+      true,
+      param);
+    break;
+  }
+  case CONV_DISCOVERY_RESULT_ERROR: {
+    LoggerE("Discovery Error");
+    picojson::object param;
+    param[kDiscoveryStatus] = picojson::value(kDiscoveryStatusError);
+    param[kDiscoveryError] = picojson::value(static_cast<double>(result));
+    owner->convergence_plugin_->ReplyAsync(kConvergenceManagerDiscoveryCallback,
+      -1,
+      false,
+      param);
+    break;
+  }
+  default: {
+    LoggerE("Unknown discovery result");
+    break;
+  }
+  }
+}
+
+TizenResult ConvergenceManager::StartDiscovery(long timeout) {
+  ScopeLogger();
+  const int error = conv_discovery_start(convergence_manager_,
+    (const int)timeout, DiscoveryCb, this);
+  if (CONV_ERROR_NONE != error) {
+    // TODO: Handle error
+    trace_conv_error(error, __LINE__, "conv_discovery_start");
+  }
+  return TizenSuccess();
+}
+
+TizenResult ConvergenceManager::StopDiscovery() {
+  ScopeLogger();
+  const int error = conv_discovery_stop(convergence_manager_);
+  if (CONV_ERROR_NONE != error) {
+    // TODO: Handle error
+    trace_conv_error(error, __LINE__, "conv_discovery_stop");
+  }
+  return TizenSuccess();
+}
+
+//-------------
+// TODO Implement the NULL Object pattern for Service,
+// so this function would return always  valid pointers
+//-------------
+ConvergenceService *ConvergenceManager::GetService(const char *device_id,
+  const int service_type) const {
+  ScopeLogger();
+
+  LoggerI("Getting the service object for id [%s], type [%d]", device_id, service_type);
+  if (registered_devices_.count(device_id) <= 0) {
+    LoggerE("Device ID not found");
+    return nullptr;
+  }
+
+  ConvergenceDevice *d = registered_devices_[device_id];
+  if (!d) {
+    LoggerE("Device object not found");
+    return nullptr;
+  }
+
+  return d->GetService(service_type);
+}
+
+ConvergenceService *ConvergenceManager::RegisterLocalService(const char *device_id,
+  const int service_type) {
+  ScopeLogger();
+
+  LoggerI("Registering local service [%d] for device id [%s]", service_type, device_id);
+
+  ConvergenceDevice *d = SwapDevice(device_id, nullptr);
+  if (!d) {
+    LoggerE("Device object not found");
+    return nullptr;
+  }
+
+  return d->RegisterLocalService(service_type);
+}
+
+} // namespace convergence
+}  // namespace extension
diff --git a/src/convergence/convergence_manager.h b/src/convergence/convergence_manager.h
new file mode 100644 (file)
index 0000000..4b61407
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef CONVERGENCE_CONVERGENCE_MANAGER_H__
+#define CONVERGENCE_CONVERGENCE_MANAGER_H__
+
+#include <d2d_conv_manager.h>
+
+#include <string>
+#include <unordered_map>
+
+#include "common/tizen_result.h"
+
+namespace extension {
+namespace convergence {
+
+
+class ConvergenceDevice;
+class ConvergenceService;
+
+// TODO rename to Discovery Manager
+// TODO extract service start, stop, read, send to Service Manager class
+class ConvergenceManager {
+
+ public:
+   static ConvergenceManager &GetInstance(class ConvergenceInstance *owner);
+
+ public:
+  ConvergenceManager();
+  ~ConvergenceManager();
+  ConvergenceManager(const ConvergenceManager&) = delete;
+  ConvergenceManager(ConvergenceManager&&) = delete;
+  ConvergenceManager& operator=(const ConvergenceManager&) = delete;
+  ConvergenceManager& operator=(ConvergenceManager&&) = delete;
+
+ public:
+  common::TizenResult StartDiscovery(long timeout);
+  common::TizenResult StopDiscovery();
+
+ public:
+  ConvergenceService *GetService(const char *device_id,
+   const int service_type) const;
+  ConvergenceService *RegisterLocalService(const char *device_id,
+   const int service_type);
+
+ private:
+  static void DiscoveryCb(conv_device_h device_handle,
+   conv_discovery_result_e result, void* user_data);
+  ConvergenceDevice *SwapDevice(const char *device_id,
+   conv_device_h device_handle);
+
+ private:
+  class ConvergenceInstance *convergence_plugin_;
+  conv_h convergence_manager_;
+  mutable std::unordered_map<std::string, ConvergenceDevice *> registered_devices_; // TODO rename to discovered_devices_ or simply devices_
+};
+
+} // namespace convergence
+} // namespace extension
+
+#endif // CONVERGENCE_CONVERGENCE_MANAGER_H__
diff --git a/src/convergence/convergence_payload.cc b/src/convergence/convergence_payload.cc
new file mode 100644 (file)
index 0000000..aaefef3
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+// TODO check includes
+#include "convergence/convergence_payload.h"
+
+#include <glib.h>
+#include <d2d_conv_internal.h>
+
+#include "convergence/convergence_instance.h"
+#include "convergence/convergence_utils.h"
+#include "common/logger.h"
+
+namespace extension {
+namespace convergence {
+
+namespace {
+// Payload data keys and some values
+static const std::string kKey = "key";
+static const std::string kValue = "value";
+static const std::string kPayloadDataType = "type"; // String, bytes
+static const std::string kTypeString = "STRING";
+static const std::string kTypeRawBytes = "RAW_BYTES";
+} // namespace
+
+ConvergencePayloadArray::ConvergencePayloadArray()
+  : payload_handle_(nullptr) {
+  ScopeLogger();
+}
+
+ConvergencePayloadArray::ConvergencePayloadArray(const picojson::value &payload_json)
+  : payload_handle_(nullptr) {
+  ScopeLogger();
+  FromJson(payload_json);
+}
+
+ConvergencePayloadArray::~ConvergencePayloadArray() {
+  ScopeLogger();
+
+  if (payload_handle_) {
+    conv_payload_destroy(payload_handle_);
+    payload_handle_ = nullptr;
+  }
+}
+
+conv_payload_h ConvergencePayloadArray::GetHandle() const {
+  ScopeLogger();
+  return payload_handle_;
+}
+
+
+void ConvergencePayloadArray::FromJson(const picojson::value &payload_json) {
+  ScopeLogger();
+  if (payload_json.is<picojson::null>()) {
+    LoggerE("ERROR: trying to convert NULL payload json value");
+    return;
+  }
+
+  if (payload_handle_) {
+    conv_payload_destroy(payload_handle_);
+    payload_handle_ = nullptr;
+  }
+
+  int error = conv_payload_create(&payload_handle_);
+  if ((CONV_ERROR_NONE != error) || !payload_handle_) {
+    trace_conv_error(error, __LINE__, "creating payload handle");
+    return;
+  }
+
+  if (!payload_json.is<picojson::array>()) {
+    LoggerE("ERROR: Payload json is not an array");
+    return;
+  }
+
+  const picojson::array &payload_items = payload_json.get<picojson::array>();
+  for (size_t i = 0; i < payload_items.size(); i++) {
+    picojson::value item = payload_items[i];
+    const std::string type = item.get(kPayloadDataType).to_str();
+    if (kTypeString == type) {
+      const std::string key = item.get(kKey).to_str();
+      const std::string value = item.get(kValue).to_str(); // TODO: check if it is an object
+      int error = conv_payload_set_string(payload_handle_, key.c_str(), value.c_str());
+      if (CONV_ERROR_NONE != error) {
+        trace_conv_error(error, __LINE__, "setting payload string");
+      }
+    } else if (kTypeRawBytes == type) {
+      // TODO
+      LoggerW("IMPLEMENT BYTE PAYLOAD!!!");
+    } else {
+      LoggerE("ERROR! Unknown type of payload [%s]", type.c_str());
+    }
+  }
+}
+
+picojson::value ConvergencePayloadArray::ToJson(conv_payload_h payload_handle) {
+  ScopeLogger();
+
+  picojson::array payloads; // Array of payloads to deliver to JS layer
+
+  do {
+    if (!payload_handle) {
+      LoggerE("ERROR: trying to convert NULL payload handle to json");
+      break;
+    }
+
+    picojson::value parsed_payload;
+    {
+      char *payload_json_str = nullptr;
+      int error = conv_payload_internal_export_to_string(payload_handle, &payload_json_str);
+      if ((CONV_ERROR_NONE != error) || !payload_json_str) {
+        trace_conv_error(error, __LINE__, "converting payload handle to json");
+        break;
+      }
+
+      std::string err;
+      picojson::parse(parsed_payload, payload_json_str,
+        payload_json_str + strlen(payload_json_str), &err);
+      free(payload_json_str);
+      if (!err.empty()) {
+        LoggerE("Error parsing payload json: %s", err.c_str());
+        break;
+      }
+    }
+
+    if (!parsed_payload.is<picojson::object>()) {
+      LoggerE("ERROR: Payload is not an object");
+      break;
+    }
+
+    const picojson::object &payload_items = parsed_payload.get<picojson::object>();
+    for (auto it = std::begin(payload_items); it != std::end(payload_items); ++it) {
+      const std::string item_name = it->first;
+      const picojson::value item_value = it->second;
+
+      picojson::object payload_object;
+      payload_object[kKey] = picojson::value(item_name);
+
+      // Recognizing type of the payload
+      if (item_value.is<picojson::array>()) { // Byte array payload
+        payload_object[kPayloadDataType] = picojson::value(kTypeRawBytes);
+        payload_object[kValue] = item_value;
+      } else { // String payload
+        payload_object[kPayloadDataType] = picojson::value(kTypeString);
+        payload_object[kValue] = item_value;
+      }
+
+      payloads.push_back(picojson::value(payload_object));
+    }
+  } while(false);
+
+  return picojson::value(payloads);
+}
+
+std::string ConvergencePayloadArray::ExtractPayloadString(conv_payload_h payload, const char *key) {
+  ScopeLogger();
+  char *value = nullptr;
+  const int error = conv_payload_get_string(payload, key, &value);
+  if (CONV_ERROR_NONE != error) {
+    trace_conv_error(error, __LINE__, "conv_payload_get_string");
+    return "";
+  }
+
+  if (value) {
+    const std::string result = std::string(value);
+    free(value);
+    return result;
+  }
+  return "";
+}
+} // namespace convergence
+}  // namespace extension
diff --git a/src/convergence/convergence_payload.h b/src/convergence/convergence_payload.h
new file mode 100644 (file)
index 0000000..fc439c7
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef CONVERGENCE_CONVERGENCE_PAYLOAD_H__
+#define CONVERGENCE_CONVERGENCE_PAYLOAD_H__
+
+// TODO check includes
+#include <d2d_conv_manager.h>
+
+#include <string>
+#include <unordered_map>
+
+#include "common/tizen_result.h"
+
+namespace extension {
+namespace convergence {
+
+
+/*enum ConvergencePayloadType {
+  kUnknownPayload = -1,
+  kStringPayload,
+  kRawBytesPayload
+};*/
+
+
+// TODO rename to simply Payload
+class ConvergencePayloadArray {
+ public:
+  ConvergencePayloadArray();
+  ConvergencePayloadArray(const picojson::value &payload_json);
+  virtual ~ConvergencePayloadArray();
+  ConvergencePayloadArray(const ConvergencePayloadArray&) = delete;
+  ConvergencePayloadArray(ConvergencePayloadArray&&) = delete;
+  ConvergencePayloadArray& operator=(const ConvergencePayloadArray&) = delete;
+  ConvergencePayloadArray& operator=(ConvergencePayloadArray&&) = delete;
+
+ public:
+  conv_payload_h GetHandle() const;
+  void FromJson(const picojson::value &payload_json);
+
+ public:
+  static picojson::value ToJson(conv_payload_h payload_handle);
+  static std::string ExtractPayloadString(conv_payload_h payload, const char *key);
+
+ private:
+  conv_payload_h payload_handle_;
+};
+
+/*
+class ConvergencePayload {
+ public:
+  ConvergencePayload();
+  virtual ~ConvergencePayload();
+  ConvergencePayload(const ConvergencePayload&) = delete;
+  ConvergencePayload(ConvergencePayload&&) = delete;
+  ConvergencePayload& operator=(const ConvergencePayload&) = delete;
+  ConvergencePayload& operator=(ConvergencePayload&&) = delete;
+
+ public:
+  void FromJson(const picojson::value &payload_json);
+
+ protected:
+  std::string id_;
+  ConvergencePayloadType type_;
+};
+
+class ConvergencePayloadString : public ConvergencePayload {
+ public:
+  ConvergencePayloadString();
+  virtual ~ConvergencePayloadString();
+  ConvergencePayloadString(const ConvergencePayloadString&) = delete;
+  ConvergencePayloadString(ConvergencePayloadString&&) = delete;
+  ConvergencePayloadString& operator=(const ConvergencePayloadString&) = delete;
+  ConvergencePayloadString& operator=(ConvergencePayloadString&&) = delete;
+
+ public:
+  void FromJson(const picojson::value &payload_json);
+
+ private:
+  std::string string_value_;
+};
+
+class ConvergencePayloadRawBytes : public ConvergencePayload {
+ public:
+  ConvergencePayloadRawBytes();
+  virtual ~ConvergencePayloadRawBytes();
+  ConvergencePayloadRawBytes(const ConvergencePayloadRawBytes&) = delete;
+  ConvergencePayloadRawBytes(ConvergencePayloadRawBytes&&) = delete;
+  ConvergencePayloadRawBytes& operator=(const ConvergencePayloadRawBytes&) = delete;
+  ConvergencePayloadRawBytes& operator=(ConvergencePayloadRawBytes&&) = delete;
+
+ public:
+  void FromJson(const picojson::value &payload_json);
+
+ private:
+  std::vector<unsigned char> raw_bytes_value_;
+};
+*/
+
+} // namespace convergence
+} // namespace extension
+
+#endif // CONVERGENCE_CONVERGENCE_PAYLOAD_H__
diff --git a/src/convergence/convergence_remote_app_control_service.cc b/src/convergence/convergence_remote_app_control_service.cc
new file mode 100644 (file)
index 0000000..de3380b
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+// TODO check includes
+#include "convergence/convergence_remote_app_control_service.h"
+
+#include <glib.h>
+#include <d2d_conv_internal.h>
+
+#include "convergence/convergence_instance.h"
+#include "convergence/convergence_payload.h"
+#include "common/logger.h"
+
+namespace extension {
+namespace convergence {
+
+using common::TizenResult;
+using common::TizenSuccess;
+
+namespace {
+// Payload data keys and some values
+static const std::string kPayload = "payload"; // ---
+
+// Service connection status keys
+static const std::string kServiceConnectionStatus = "connect_status"; // ---
+static const std::string kServiceConnectionStatusConnected = "service_connected";
+static const std::string kServiceConnectionStatusNotConnected = "service_not_connected";
+
+// Service listener status keys
+static const std::string kServiceListenerStatus = "listener_status"; //---
+static const std::string kServiceListenerStatusOk = "listener_ok"; // ---
+static const std::string kServiceListenerStatusError = "listener_error"; //---
+static const std::string kServiceListenerError = "listener_error"; //---
+} // namespace
+
+ConvergenceRemoteAppControlService::ConvergenceRemoteAppControlService()
+ : ConvergenceService()
+ , started_(false) {
+  ScopeLogger();
+}
+
+ConvergenceRemoteAppControlService::ConvergenceRemoteAppControlService(conv_device_h device, ConvergenceInstance *convergence_plugin)
+ : ConvergenceService(device, CONV_SERVICE_REMOTE_APP_CONTROL, convergence_plugin)
+ , started_(false) {
+  ScopeLogger();
+}
+
+ConvergenceRemoteAppControlService::~ConvergenceRemoteAppControlService() {
+  ScopeLogger();
+
+  // Release all memory, used by callback paramerers
+  for (size_t i = 0; i < callback_param_gc_.size(); i++) {
+    delete callback_param_gc_[i];
+  }
+  callback_param_gc_.clear();
+}
+
+void ConvergenceRemoteAppControlService::ServiceConnectedCb(conv_service_h service_handle,
+  conv_error_e error, conv_payload_h result, void* user_data) {
+  ScopeLogger();
+
+  CallbackParam *callbackParam = static_cast<CallbackParam *>(user_data);
+  if (!callbackParam) {
+    LoggerE("ERROR! NULL user data in Service Connect Callback");
+    return;
+  }
+
+  picojson::object param;
+  param["result_type"] = picojson::value("Connected");
+  //param[kPayload] = ConvergenceRemoteAppControlService::PayloadToJson(result);
+
+  if (CONV_ERROR_NONE == error) {
+    param[kServiceConnectionStatus] = picojson::value(kServiceConnectionStatusConnected);
+    callbackParam->plugin_->ReplyAsync(kRemoteAppControlListenerCallback,
+      callbackParam->callback_id_,
+      true,
+      param);
+  } else {
+    // Error occured during connection
+    param[kServiceConnectionStatus] = picojson::value(kServiceConnectionStatusNotConnected);
+    callbackParam->plugin_->ReplyAsync(kRemoteAppControlListenerCallback,
+      callbackParam->callback_id_,
+      false,
+      param);
+  }
+}
+
+common::TizenResult ConvergenceRemoteAppControlService::Connect(const int cur_listener_id) {
+  ScopeLogger();
+
+  conv_service_h service = FindServiceHandle();
+  if (!service) {
+    return LogAndCreateTizenError(NotFoundError,
+      "Service with specified type does not exist");
+  }
+
+  // TODO: make a garbage collection and release this memory when service is disconnected
+  // and when whole manager is destructed
+  CallbackParam *param = new CallbackParam(convergence_plugin_, cur_listener_id);
+  callback_param_gc_.push_back(param);
+
+  const int error = conv_service_connect(service, ServiceConnectedCb, param);
+  if (CONV_ERROR_NONE != error) {
+    // TODO: Handle error
+    trace_conv_error(error, __LINE__, "conv_service_connect");
+  }
+
+  return TizenSuccess();
+}
+
+common::TizenResult ConvergenceRemoteAppControlService::Disconnect() {
+  ScopeLogger();
+
+  conv_service_h service = FindServiceHandle();
+  if (!service)
+    return LogAndCreateTizenError(NotFoundError,
+      "Service with specified type does not exist");
+
+  const int error = conv_service_disconnect(service);
+  if (CONV_ERROR_NONE != error) {
+    // TODO: Handle error
+    trace_conv_error(error, __LINE__, "conv_service_disconnect");
+  }
+
+  return TizenSuccess();
+}
+
+void ConvergenceRemoteAppControlService::ServiceListenerCb(conv_service_h service_handle,
+  conv_channel_h channel_handle,
+  conv_error_e error, conv_payload_h result, void* user_data) {
+  ScopeLogger();
+
+  CallbackParam *callbackParam = static_cast<CallbackParam *>(user_data);
+  if (!callbackParam) {
+    LoggerE("ERROR! NULL user data in Service Listener Callback");
+    return;
+  }
+
+  picojson::object param;
+  param["payload"] = picojson::value("TODO");
+
+  // TODO parse the payload and fill the param
+
+  const std::string result_type = ConvergencePayloadArray::ExtractPayloadString(result, "result_type"); // TODO extract to constant kResultType = "result_type";
+  param["result_type"] = picojson::value(result_type);
+
+
+  if (CONV_ERROR_NONE == error) {
+    param[kServiceListenerStatus] = picojson::value(kServiceListenerStatusOk);
+    callbackParam->plugin_->ReplyAsync(kRemoteAppControlListenerCallback,
+      callbackParam->callback_id_,
+      true,
+      param);
+  } else {
+    // Error occured during connection
+    param[kServiceListenerStatus] = picojson::value(kServiceListenerStatusError);
+    param[kServiceListenerError] = picojson::value(static_cast<double>(error));
+    callbackParam->plugin_->ReplyAsync(kRemoteAppControlListenerCallback,
+      callbackParam->callback_id_,
+      false,
+      param);
+  }
+}
+
+void ConvergenceRemoteAppControlService::UpdateListener(const int cur_listener_id) {
+  ScopeLogger();
+  // TODO: make a garbage collection and release this memory when callback unset
+  // and when whole manager is destructed
+  CallbackParam *param = new CallbackParam(convergence_plugin_, cur_listener_id);
+  callback_param_gc_.push_back(param);
+
+  conv_service_h service_handle = FindServiceHandle();
+  if (!service_handle) {
+    LoggerE("AAAAAA!!! Service not found");
+    return;
+  }
+  const int error = conv_service_set_listener_cb(service_handle, ServiceListenerCb, param);
+  if (CONV_ERROR_NONE != error) {
+    // TODO: Handle error
+    trace_conv_error(error, __LINE__, "conv_service_set_listener_cb");
+  } else {
+    LoggerI("Listener is set correctly");
+  }
+}
+
+void ConvergenceRemoteAppControlService::EnsureStarted() {
+  ScopeLogger();
+  if (started_)
+    return;
+
+  conv_service_h service_handle = FindServiceHandle();
+  if (!service_handle) {
+    LoggerE("AAAAAA!!! Service not found");
+    return; // TODO handle error
+  }
+
+  const int error = conv_service_start(service_handle, nullptr, nullptr);
+  if (CONV_ERROR_NONE != error) {
+    // TODO: Handle error
+    trace_conv_error(error, __LINE__, "conv_service_publish START");
+  } else {
+    LoggerI("APP CONTROL SERVICE IS STARTED");
+    started_ = true;
+  }
+}
+
+common::TizenResult ConvergenceRemoteAppControlService::Launch(const char *appId, bool reply, const int cur_listener_id) {
+  ScopeLogger();
+
+  conv_service_h service_handle = FindServiceHandle();
+  if (!service_handle) {
+    LoggerE("AAAAAA!!! Service not found");
+    return LogAndCreateTizenError(NotFoundError,
+     "Service with specified type does not exist");
+  }
+
+  // Create app control payload with passed appId
+  conv_payload_h payload = CreateAppIdPayload(appId, reply);
+  if (!payload) {
+    // Handle error
+    return TizenSuccess(); // TODO TizenError
+  }
+
+  // Update listener: assign it if it is not yet assigned
+  if (reply)
+    UpdateListener(cur_listener_id);
+
+  // Ensure the service was started
+  EnsureStarted();
+
+  // Sending app control on the remote device
+  const int error = conv_service_publish(service_handle, nullptr, payload);
+  if (CONV_ERROR_NONE != error) {
+    // TODO: Handle error
+    trace_conv_error(error, __LINE__, "conv_service_publish LAUNCH");
+  } else {
+    LoggerI("---- SEEMS APP CONTROL WAS LAUNCHED ----");
+  }
+
+/*
+       conv_service_start(service_handle, NULL, NULL);
+       conv_payload_h payload_handle;
+       conv_payload_create(&payload_handle);
+       app_control_h app_control = NULL;
+       app_control_create(&app_control);
+       app_control_set_app_id(app_control, "org.example.d2d_test");
+       app_control_set_operation(app_control, APP_CONTROL_OPERATION_MAIN);
+       conv_payload_set_app_control(payload_handle, "app_control", app_control);
+       conv_payload_set_string(payload_handle, "reply", "0");
+       conv_service_publish(service_handle, NULL, payload_handle);
+*/
+
+  conv_payload_destroy(payload);
+  return TizenSuccess();
+}
+
+conv_payload_h ConvergenceRemoteAppControlService::CreateAppIdPayload(const char *appId, bool reply) const {
+  ScopeLogger();
+
+  conv_payload_h payload = nullptr;
+  int error = conv_payload_create(&payload);
+  if (CONV_ERROR_NONE != error) {
+    // Handle error
+  }
+
+  app_control_h app_control = nullptr;
+  error = app_control_create(&app_control);
+  if (APP_CONTROL_ERROR_NONE != error) {
+    LoggerE("ERROR! AppControl API error [%d]", error);
+  }
+
+  error = app_control_set_app_id(app_control, appId);
+  if (APP_CONTROL_ERROR_NONE != error) {
+    LoggerE("ERROR! AppControl API error [%d]", error);
+  }
+
+  error = app_control_set_operation(app_control, APP_CONTROL_OPERATION_MAIN);
+  if (CONV_ERROR_NONE != error) {
+    // Handle error
+  }
+
+  error = conv_payload_set_app_control(payload, "app_control", app_control);
+  if (APP_CONTROL_ERROR_NONE != error) {
+    // Handle error
+  }
+
+  if (reply) {
+    error = conv_payload_set_string(payload, "reply", "1");
+    if (CONV_ERROR_NONE != error) {
+      // Handle error
+    }
+  }
+
+  return payload;
+}
+
+picojson::value ConvergenceRemoteAppControlService::PayloadToJson(conv_payload_h payload) {
+  ScopeLogger();
+  picojson::value payload_json;
+
+  // TODO convert payload to array of ApplicationControlData
+
+  return payload_json;
+}
+
+} // namespace convergence
+}  // namespace extension
diff --git a/src/convergence/convergence_remote_app_control_service.h b/src/convergence/convergence_remote_app_control_service.h
new file mode 100644 (file)
index 0000000..2f416ae
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef CONVERGENCE_CONVERGENCE_REMOTE_APP_CONTROL_SERVICE_H__
+#define CONVERGENCE_CONVERGENCE_REMOTE_APP_CONTROL_SERVICE_H__
+
+// TODO check includes
+#include <d2d_conv_manager.h>
+
+#include <string>
+#include <unordered_map>
+
+#include "convergence/convergence_service.h"
+#include "convergence/convergence_utils.h"
+#include "common/tizen_result.h"
+
+namespace extension {
+namespace convergence {
+
+class ConvergenceRemoteAppControlService : public ConvergenceService {
+
+ public:
+  ConvergenceRemoteAppControlService();
+  ConvergenceRemoteAppControlService(conv_device_h device, ConvergenceInstance *convergence_plugin);
+  virtual ~ConvergenceRemoteAppControlService();
+  ConvergenceRemoteAppControlService(const ConvergenceRemoteAppControlService&) = delete;
+  ConvergenceRemoteAppControlService(ConvergenceRemoteAppControlService&&) = delete;
+  ConvergenceRemoteAppControlService& operator=(const ConvergenceRemoteAppControlService&) = delete;
+  ConvergenceRemoteAppControlService& operator=(ConvergenceRemoteAppControlService&&) = delete;
+ public:
+  common::TizenResult Connect(const int cur_listener_id);
+  common::TizenResult Disconnect();
+  common::TizenResult Launch(const char *appId, bool reply, const int cur_listener_id);
+ private:
+  conv_payload_h CreateAppIdPayload(const char *appId, bool reply) const;
+  static picojson::value PayloadToJson(conv_payload_h payload);
+  void UpdateListener(const int cur_listener_id);
+  void EnsureStarted();
+  static void ServiceConnectedCb(conv_service_h service_handle,
+   conv_error_e error, conv_payload_h result, void* user_data);
+  static void ServiceListenerCb(conv_service_h service_handle,
+   conv_channel_h channel_handle,
+   conv_error_e error, conv_payload_h result, void* user_data);
+
+ private:
+  std::vector<CallbackParam *> callback_param_gc_;
+  bool started_;
+};
+
+} // namespace convergence
+} // namespace extension
+
+#endif // CONVERGENCE_CONVERGENCE_REMOTE_APP_CONTROL_SERVICE_H__
diff --git a/src/convergence/convergence_service.cc b/src/convergence/convergence_service.cc
new file mode 100644 (file)
index 0000000..035b51f
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+// TODO check includes
+#include "convergence/convergence_service.h"
+
+#include <glib.h>
+#include <d2d_conv_internal.h>
+
+#include "convergence/convergence_instance.h"
+#include "convergence/convergence_utils.h"
+#include "common/logger.h"
+
+namespace extension {
+namespace convergence {
+
+namespace {
+// Service keys
+static const std::string kServiceType = "serviceType";
+static const std::string kServiceConnectionState = "connectionState";
+static const std::string kId = "id";
+static const std::string kVersion = "version";
+} // namespace
+
+ConvergenceService::ConvergenceService()
+ : convergence_plugin_(nullptr)
+ , device_(nullptr)
+ , type_(CONV_SERVICE_NONE)
+ , service_handle_(nullptr) {
+  ScopeLogger();
+}
+
+ConvergenceService::ConvergenceService(conv_device_h device, conv_service_e type, ConvergenceInstance *convergence_plugin)
+ : convergence_plugin_(convergence_plugin)
+ , device_(device)
+ , type_(type)
+ , service_handle_(nullptr) {
+  ScopeLogger();
+}
+
+ConvergenceService::~ConvergenceService() {
+  ScopeLogger();
+  LoggerI("DESTROYING SERVICE HANDLE [0x0%x] in ConvergenceService destr", service_handle_);
+  conv_service_destroy(service_handle_);
+}
+
+struct ServiceSearchQuery {
+  conv_service_h handle;
+  conv_service_e type;
+};
+
+void ConvergenceService::ForEachServiceCb(conv_service_h service_handle, void* user_data) {
+  ScopeLogger();
+  ServiceSearchQuery *query = static_cast<ServiceSearchQuery *>(user_data);
+  if (!query)
+    return;
+  if (query->handle)
+    return; // Already found a required service handle (no need to analyze something else)
+
+  conv_service_e type = CONV_SERVICE_NONE;
+  int error = conv_service_get_type(service_handle, &type);
+  if (error != CONV_ERROR_NONE) {
+    // TODO Handle error
+  }
+
+  if (type != query->type) {
+    return;
+  }
+  conv_service_h clone = nullptr;
+  error = conv_service_clone(service_handle, &clone);
+  if (error != CONV_ERROR_NONE) {
+    // TODO Handle error
+  }
+  query->handle = clone;
+}
+
+conv_service_h ConvergenceService::FindServiceHandle() const {
+  ScopeLogger();
+  if (!service_handle_) {
+
+    if (!device_) {
+      LoggerE("ERROR: Device handle is NULL, can not get device services");
+      return nullptr;
+    }
+
+    ServiceSearchQuery query = {nullptr, type_};
+    const int error = conv_device_foreach_service(device_, ForEachServiceCb, &query);
+    if (CONV_ERROR_NONE != error) {
+      // TODO Handle error
+      trace_conv_error(error, __LINE__, "conv_device_foreach_service");
+    }
+    service_handle_ = query.handle;
+  }
+  return service_handle_;
+}
+
+std::string ConvergenceService::ExtractServicePropery(conv_service_h service_handle,
+  const char *property) {
+  ScopeLogger();
+  char *value = nullptr;
+  const int error = conv_service_get_property_string(service_handle, property, &value);
+  if (CONV_ERROR_NONE != error) {
+    trace_conv_error(error, __LINE__, "conv_service_get_property_string");
+    return "";
+  }
+
+  if (value) {
+    const std::string result = std::string(value);
+    free(value);
+    return result;
+  }
+  return "";
+}
+
+void ConvergenceService::Refresh() {
+  ScopeLogger();
+  conv_service_h service_handle = FindServiceHandle();
+  if (!service_handle) {
+    LoggerE("AAAAAA!!! Service not found");
+    return;
+  }
+
+  conv_service_connection_state_e state = CONV_SERVICE_CONNECTION_STATE_NONE;
+  const int error = conv_service_get_connection_state(service_handle, &state);
+  if (CONV_ERROR_NONE != error) {
+    LoggerE("ERROR! D2D API Get Service Connection State error [%d]", error);
+    trace_conv_error(error, __LINE__, "conv_service_get_connection_state");
+  } else {
+    connection_state_ = state;
+  }
+
+  id_ = ExtractServicePropery(service_handle, CONV_SERVICE_ID);
+  version_ = ExtractServicePropery(service_handle, CONV_SERVICE_VERSION);
+
+  LoggerE("Refreshed service id [%s] version [%s] connection state [%d]",
+    id_.c_str(), version_.c_str(), connection_state_);
+
+  //conv_service_destroy(service_handle);
+  //LoggerI("DESTROYING SERVICE HANDLE [0x0%x] in ConvergenceService Refresh", service_handle_);
+}
+
+picojson::value ConvergenceService::ToJson() const {
+  ScopeLogger();
+  picojson::object service_json;
+
+  service_json[kServiceType] = picojson::value(static_cast<double>(type_));
+  service_json[kServiceConnectionState] = picojson::value(static_cast<double>(connection_state_));
+
+  // Following fields are not used in Web API layer now, but I parse it for
+  // future extensions
+  service_json[kId] = picojson::value(id_);
+  service_json[kVersion] = picojson::value(version_);
+
+  return picojson::value(service_json);
+}
+
+} // namespace convergence
+}  // namespace extension
diff --git a/src/convergence/convergence_service.h b/src/convergence/convergence_service.h
new file mode 100644 (file)
index 0000000..55fe741
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef CONVERGENCE_CONVERGENCE_SERVICE_H__
+#define CONVERGENCE_CONVERGENCE_SERVICE_H__
+
+// TODO check includes
+#include <d2d_conv_manager.h>
+
+#include <string>
+#include <unordered_map>
+
+#include "common/tizen_result.h"
+
+namespace extension {
+namespace convergence {
+
+class ConvergenceInstance;
+
+class ConvergenceService {
+
+ public:
+  ConvergenceService();
+  ConvergenceService(conv_device_h device, conv_service_e type, ConvergenceInstance *convergence_plugin);
+  virtual ~ConvergenceService();
+  ConvergenceService(const ConvergenceService&) = delete;
+  ConvergenceService(ConvergenceService&&) = delete;
+  ConvergenceService& operator=(const ConvergenceService&) = delete;
+  ConvergenceService& operator=(ConvergenceService&&) = delete;
+
+ public:
+  void Refresh();
+
+ public:
+  //conv_service_e get_type() const { return type_; }
+  //conv_device_h get_device() const {return device_; }
+  picojson::value ToJson() const;
+
+ protected:
+  conv_service_h FindServiceHandle() const;
+  static void ForEachServiceCb(conv_service_h service_handle, void* user_data);
+  std::string ExtractServicePropery(conv_service_h service_handle,
+   const char *property);
+
+ protected:
+  ConvergenceInstance *convergence_plugin_;
+
+ //private:
+ protected: // TODO switch back to private:
+  conv_device_h device_; // TODO rename to device_handle_
+  conv_service_e type_;
+  mutable conv_service_h service_handle_;
+  friend class ConvergenceAppCommunicationServerService; // It is needed to register the local service
+ private:
+  conv_service_connection_state_e connection_state_;
+  std::string id_;
+  std::string version_;
+};
+
+} // namespace convergence
+} // namespace extension
+
+#endif // CONVERGENCE_CONVERGENCE_SERVICE_H__
diff --git a/src/convergence/convergence_utils.cc b/src/convergence/convergence_utils.cc
new file mode 100644 (file)
index 0000000..86e81c0
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+// TODO check includes
+#include "convergence/convergence_service.h"
+
+#include <glib.h>
+#include <d2d_conv_internal.h>
+
+#include "convergence/convergence_instance.h"
+#include "common/logger.h"
+
+namespace extension {
+namespace convergence {
+
+
+
+
+void trace_conv_error(const int error, int line_number, const char *extra_text) {
+  std::string error_text;
+  switch (error) {
+  case CONV_ERROR_NONE:
+    error_text = "CONV_ERROR_NONE";
+    break;
+  case CONV_ERROR_INVALID_PARAMETER:
+    error_text = "CONV_ERROR_INVALID_PARAMETER";
+    break;
+  case CONV_ERROR_INVALID_OPERATION:
+    error_text = "CONV_ERROR_INVALID_OPERATION";
+    break;
+  case CONV_ERROR_OUT_OF_MEMORY:
+    error_text = "CONV_ERROR_OUT_OF_MEMORY";
+    break;
+  case CONV_ERROR_PERMISSION_DENIED:
+    error_text = "CONV_ERROR_PERMISSION_DENIED";
+    break;
+  case CONV_ERROR_NOT_SUPPORTED:
+    error_text = "CONV_ERROR_NOT_SUPPORTED";
+    break;
+  case CONV_ERROR_NO_DATA:
+    error_text = "CONV_ERROR_NO_DATA";
+    break;
+  default:
+    error_text = "UNSUPPORTED D2D ERROR CODE";
+    break;
+  }
+
+  if (extra_text) {
+    LoggerE("ERROR! D2D API error [%s], line %d: %s", error_text.c_str(), line_number, extra_text);
+  } else {
+    LoggerE("ERROR! D2D API error [%s], line %d", error_text.c_str(), line_number);
+  }
+}
+
+
+
+} // namespace convergence
+}  // namespace extension
diff --git a/src/convergence/convergence_utils.h b/src/convergence/convergence_utils.h
new file mode 100644 (file)
index 0000000..1afde74
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef CONVERGENCE_CONVERGENCE_UTILS_H__
+#define CONVERGENCE_CONVERGENCE_UTILS_H__
+
+// TODO check includes
+#include <d2d_conv_manager.h>
+
+#include <string>
+#include <unordered_map>
+
+#include "common/tizen_result.h"
+
+namespace extension {
+namespace convergence {
+
+class ConvergenceInstance;
+
+struct CallbackParam {
+ CallbackParam(ConvergenceInstance *plg,  int cbId)
+   : plugin_(plg), callback_id_(cbId) {}
+ ConvergenceInstance *plugin_;
+ int callback_id_;
+};
+
+
+void trace_conv_error(const int error, int line_number, const char *extra_text);
+
+} // namespace convergence
+} // namespace extension
+
+#endif // CONVERGENCE_CONVERGENCE_UTILS_H__
index 939e691..7cc8a10 100755 (executable)
             ],
           },
         ],
+        [
+          'tizen_feature_convergence_support==1', {
+            'dependencies': [
+              'convergence/convergence.gyp:*',
+            ],
+          },
+        ],
       ], # end conditions
     },
   ], # end targets