aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Android_Studio/Tango.Integration/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'Software/Android_Studio/Tango.Integration/src/main/java')
-rw-r--r--Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/MachineIdentityProvider.java15
-rw-r--r--Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/operators/IMachineOperator.java12
-rw-r--r--Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/operators/MachineOperator.java20
-rw-r--r--Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/providers/IMachineIdentityProvider.java (renamed from Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/IMachineIdentityProvider.java)2
-rw-r--r--Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/providers/MachineIdentityProvider.java19
-rw-r--r--Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/services/ExternalBridgeService.java281
-rw-r--r--Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/services/IExternalBridgeService.java53
-rw-r--r--Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/services/ISynchronizationService.java19
-rw-r--r--Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/services/SynchronizationService.java179
9 files changed, 584 insertions, 16 deletions
diff --git a/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/MachineIdentityProvider.java b/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/MachineIdentityProvider.java
deleted file mode 100644
index 29cb0c30c..000000000
--- a/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/MachineIdentityProvider.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.twine.tango.integration;
-
-
-/**
- * Represents the default machine identity provider implementation.
- */
-public class MachineIdentityProvider implements IMachineIdentityProvider
-{
-
- @Override
- public String getSerialNumber()
- {
- return "1234";
- }
-}
diff --git a/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/operators/IMachineOperator.java b/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/operators/IMachineOperator.java
new file mode 100644
index 000000000..9963f295b
--- /dev/null
+++ b/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/operators/IMachineOperator.java
@@ -0,0 +1,12 @@
+package com.twine.tango.integration.operators;
+
+import com.twine.tango.transport.ITransporter;
+
+/**
+ * Created by Roy on 12/23/2017.
+ */
+
+public interface IMachineOperator extends ITransporter
+{
+
+}
diff --git a/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/operators/MachineOperator.java b/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/operators/MachineOperator.java
new file mode 100644
index 000000000..f23c8c705
--- /dev/null
+++ b/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/operators/MachineOperator.java
@@ -0,0 +1,20 @@
+package com.twine.tango.integration.operators;
+
+import com.twine.tango.transport.ITransportAdapter;
+import com.twine.tango.transport.transporters.ProtoTransporter;
+
+/**
+ * Created by Roy on 12/23/2017.
+ */
+
+public class MachineOperator extends ProtoTransporter implements IMachineOperator
+{
+ public MachineOperator()
+ {
+ }
+
+ public MachineOperator(ITransportAdapter adapter)
+ {
+ super(adapter);
+ }
+}
diff --git a/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/IMachineIdentityProvider.java b/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/providers/IMachineIdentityProvider.java
index 3e35be9d0..547a17e50 100644
--- a/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/IMachineIdentityProvider.java
+++ b/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/providers/IMachineIdentityProvider.java
@@ -1,4 +1,4 @@
-package com.twine.tango.integration;
+package com.twine.tango.integration.providers;
/**
diff --git a/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/providers/MachineIdentityProvider.java b/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/providers/MachineIdentityProvider.java
new file mode 100644
index 000000000..c72b9cea4
--- /dev/null
+++ b/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/providers/MachineIdentityProvider.java
@@ -0,0 +1,19 @@
+package com.twine.tango.integration.providers;
+import com.twine.tango.dal.dao.TangoDAO;
+import com.twine.tango.dal.entities.Machine;
+
+import static br.com.zbra.androidlinq.Linq.stream;
+
+
+/**
+ * Represents the default machine identity provider implementation.
+ */
+public class MachineIdentityProvider implements IMachineIdentityProvider
+{
+ @Override
+ public String getSerialNumber()
+ {
+ Machine machine = stream(TangoDAO.getAllMachines()).firstOrNull();
+ return machine != null ? machine.getSerialNumber() : "0000";
+ }
+}
diff --git a/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/services/ExternalBridgeService.java b/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/services/ExternalBridgeService.java
new file mode 100644
index 000000000..24ae72eba
--- /dev/null
+++ b/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/services/ExternalBridgeService.java
@@ -0,0 +1,281 @@
+package com.twine.tango.integration.services;
+
+import android.content.Context;
+import android.net.DhcpInfo;
+import android.net.wifi.WifiManager;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.provider.SyncStateContract.Constants;
+import android.util.Log;
+
+import com.elvishew.xlog.XLog;
+import com.google.protobuf.GeneratedMessageV3;
+import com.twine.tango.core.ContextHelper;
+import com.twine.tango.core.IEventHandler;
+import com.twine.tango.integration.operators.IMachineOperator;
+import com.twine.tango.integration.providers.IMachineIdentityProvider;
+import com.twine.tango.pmr.MessageFactory;
+import com.twine.tango.pmr.TangoMessage;
+import com.twine.tango.pmr.common.MessageContainerOuterClass.MessageContainer;
+import com.twine.tango.pmr.common.MessageTypeOuterClass.MessageType;
+import com.twine.tango.pmr.integration.ExternalBridgeUdpDiscoveryPacketOuterClass.ExternalBridgeUdpDiscoveryPacket;
+import com.twine.tango.pmr.integration.ExternalClientLoginRequestOuterClass.ExternalClientLoginRequest;
+import com.twine.tango.pmr.integration.ExternalClientLoginResponseOuterClass.ExternalClientLoginResponse;
+import com.twine.tango.transport.ITransportAdapter;
+import com.twine.tango.transport.ResponseErrorException;
+import com.twine.tango.transport.adapters.TcpTransportAdapter;
+import com.twine.tango.transport.transporters.ProtoTransporter;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import static br.com.zbra.androidlinq.Linq.stream;
+
+/**
+ * Created by Roy on 12/22/2017.
+ */
+
+public class ExternalBridgeService extends ProtoTransporter implements IExternalBridgeService
+{
+ private ServerSocket listener;
+ private boolean isStarted;
+ private List<HandlerAndType> handlers;
+ private IMachineIdentityProvider identityProvider;
+ private IMachineOperator machineOperator;
+
+ @Inject
+ public ExternalBridgeService(IMachineIdentityProvider identityProvider, IMachineOperator machineOperator)
+ {
+ this.identityProvider = identityProvider;
+ this.machineOperator = machineOperator;
+ handlers = new ArrayList<>();
+ }
+
+ @Override
+ public boolean isStarted()
+ {
+ return isStarted;
+ }
+
+ @Override
+ public void start() throws IOException
+ {
+ listener = new ServerSocket(1984);
+ isStarted = true;
+ Thread listenerThread = new Thread(this::listenerThreadMethod);
+ Thread discoveryThread = new Thread(this::discoveryThreadMethod);
+ listenerThread.start();
+ discoveryThread.start();
+ }
+
+ private void discoveryThreadMethod()
+ {
+ // Hack Prevent crash (sending should be done using an async task)
+ StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
+ StrictMode.setThreadPolicy(policy);
+
+ byte[] packet = ExternalBridgeUdpDiscoveryPacket.newBuilder().setSerialNumber(identityProvider.getSerialNumber()).build().toByteArray();
+
+ while (isStarted)
+ {
+ try
+ {
+ DatagramSocket socket = new DatagramSocket();
+ socket.setBroadcast(true);
+
+ //First try to send to emulator host...
+ DatagramPacket sendPacket = new DatagramPacket(packet, packet.length, InetAddress.getByName("10.0.2.2"), 8888);
+ socket.send(sendPacket);
+
+ //This will not work on the emulator as far as I know.
+ //TODO: This needs to be tested on a real device!
+ sendPacket = new DatagramPacket(packet, packet.length, InetAddress.getByName("255.255.255.255"), 8888);
+ socket.send(sendPacket);
+
+ } catch (IOException e)
+ {
+ XLog.e(e);
+ }
+
+ SystemClock.sleep(5000);
+ }
+ }
+
+ private InetAddress getBroadcastAddress() throws IOException
+ {
+ WifiManager wifi = (WifiManager) ContextHelper.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
+ assert wifi != null;
+ DhcpInfo dhcp = wifi.getDhcpInfo();
+
+ if (dhcp != null)
+ {
+ int broadcast = (dhcp.ipAddress & dhcp.netmask) | ~dhcp.netmask;
+ byte[] quads = new byte[4];
+ for (int k = 0; k < 4; k++)
+ quads[k] = (byte) ((broadcast >> k * 8) & 0xFF);
+ return InetAddress.getByAddress(quads);
+ } else
+ {
+ return null;
+ }
+ }
+
+ @Override
+ public void stop()
+ {
+ isStarted = false;
+ try
+ {
+ listener.close();
+ } catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public <T> void registerMessageHandler(IEventHandler<MessageReceivedArgs<T>> handler, MessageType messageType)
+ {
+ handlers.add(new HandlerAndType(handler, messageType));
+ }
+
+ @Override
+ public <T> void unRegisterMessageHandler(IEventHandler<MessageReceivedArgs<T>> handler)
+ {
+ HandlerAndType h = stream(handlers).singleOrNull(x -> x.handler == handler);
+ if (h != null)
+ {
+ handlers.remove(h);
+ }
+ }
+
+ private void listenerThreadMethod()
+ {
+ while (isStarted)
+ {
+ try
+ {
+ Socket newSocket = listener.accept();
+
+ if (newSocket != null)
+ {
+ for (ITransportAdapter adapter : getAdapters())
+ {
+ adapter.disconnect().blockingAwait();
+ }
+ getAdapters().clear();
+ TcpTransportAdapter adapter = new TcpTransportAdapter(newSocket);
+ adapter.setAuthenticated(false);
+ getAdapters().add(adapter);
+ connect().blockingAwait();
+ }
+
+ } catch (IOException e)
+ {
+ XLog.e(e);
+ }
+
+ SystemClock.sleep(100);
+ }
+
+ disconnect().blockingAwait();
+ }
+
+ @Override
+ protected void onRequestReceived(MessageContainer container)
+ {
+ super.onRequestReceived(container);
+
+ try
+ {
+ if (container.getType() == MessageType.ExternalClientLoginRequest)
+ {
+ ExternalClientLoginRequest request = MessageFactory.parseMessageFromContainer(container);
+ if (request.getPassword().equals("Aa123456")) //TODO: Compare with global machine settings password...
+ {
+ getAdapters().get(0).setAuthenticated(true);
+ sendResponse(MessageFactory.createTangoMessage(
+ ExternalClientLoginResponse.class,
+ ExternalClientLoginResponse.newBuilder().setAuthenticated(true).build(),
+ container.getToken()));
+ } else
+ {
+ getAdapters().get(0).setAuthenticated(false);
+ sendResponse(MessageFactory.createTangoMessage(
+ ExternalClientLoginResponse.class,
+ ExternalClientLoginResponse.newBuilder().setAuthenticated(false).build(),
+ container.getToken()));
+ }
+
+ return;
+ }
+
+ if (getAdapters().get(0).isAuthenticated())
+ {
+ GeneratedMessageV3 message = MessageFactory.parseMessageFromContainerAgnostic(container);
+
+ List<HandlerAndType> receivers = stream(handlers).where((x) -> x.messageType == container.getType()).toList();
+
+ if (receivers.size() > 0)
+ {
+ for (HandlerAndType receiver : receivers)
+ {
+ //noinspection unchecked
+ receiver.handler.invoke(this, new MessageReceivedArgs(container, message));
+ }
+ } else
+ {
+ //Route to machine...
+ if (container.getContinuous())
+ {
+ //noinspection unchecked
+ machineOperator.sendContinuousRequest(MessageFactory.createTangoMessage(container, message)).subscribe((response) ->
+ {
+ sendResponse((TangoMessage<? extends GeneratedMessageV3>) response);
+ }, (ex) ->
+ {
+ sendResponse((TangoMessage<? extends GeneratedMessageV3>) ((ResponseErrorException)ex).getResponse());
+ });
+ } else
+ {
+ //noinspection unchecked
+ machineOperator.sendRequest(MessageFactory.createTangoMessage(container, message)).subscribe((response) ->
+ {
+ sendResponse((TangoMessage<? extends GeneratedMessageV3>) response);
+ }, (ex) ->
+ {
+ sendResponse((TangoMessage<? extends GeneratedMessageV3>) ((ResponseErrorException)ex).getResponse());
+ });
+ }
+ }
+ } else
+ {
+ XLog.w("Message received from an unauthorized adapter!");
+ }
+
+ } catch (Exception e)
+ {
+ XLog.e(e);
+ }
+ }
+
+ private class HandlerAndType
+ {
+ IEventHandler handler;
+ MessageType messageType;
+
+ public HandlerAndType(IEventHandler handler, MessageType messageType)
+ {
+ this.handler = handler;
+ this.messageType = messageType;
+ }
+ }
+}
diff --git a/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/services/IExternalBridgeService.java b/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/services/IExternalBridgeService.java
new file mode 100644
index 000000000..5a2aed9aa
--- /dev/null
+++ b/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/services/IExternalBridgeService.java
@@ -0,0 +1,53 @@
+package com.twine.tango.integration.services;
+
+import com.twine.tango.core.IEventHandler;
+import com.twine.tango.pmr.common.MessageContainerOuterClass.MessageContainer;
+import com.twine.tango.pmr.common.MessageTypeOuterClass.MessageType;
+import com.twine.tango.transport.ITransporter;
+
+import java.io.IOException;
+
+/**
+ * Created by Roy on 12/22/2017.
+ */
+
+public interface IExternalBridgeService extends ITransporter
+{
+ boolean isStarted();
+ void start() throws IOException;
+ void stop();
+ <T> void registerMessageHandler(IEventHandler<MessageReceivedArgs<T>> handler, MessageType messageType);
+ <T> void unRegisterMessageHandler(IEventHandler<MessageReceivedArgs<T>> handler);
+
+ public class MessageReceivedArgs<T>
+ {
+ private MessageContainer container;
+ private T message;
+
+ public MessageReceivedArgs(MessageContainer container, T message)
+ {
+ this.container = container;
+ this.message = message;
+ }
+
+ public MessageContainer getContainer()
+ {
+ return container;
+ }
+
+ public void setContainer(MessageContainer container)
+ {
+ this.container = container;
+ }
+
+ public T getMessage()
+ {
+ return message;
+ }
+
+ public void setMessage(T message)
+ {
+ this.message = message;
+ }
+ }
+}
diff --git a/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/services/ISynchronizationService.java b/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/services/ISynchronizationService.java
new file mode 100644
index 000000000..631522fd0
--- /dev/null
+++ b/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/services/ISynchronizationService.java
@@ -0,0 +1,19 @@
+package com.twine.tango.integration.services;
+
+import java.io.IOException;
+
+import io.reactivex.Completable;
+
+
+/**
+ * Represents the Tango application local to remote synchronization engine.
+ */
+public interface ISynchronizationService
+{
+ /**
+ * Synchronizes the local database with the remote database through the remote machine service.
+ *
+ * @return the completable.
+ */
+ Completable synchronizeDB();
+}
diff --git a/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/services/SynchronizationService.java b/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/services/SynchronizationService.java
new file mode 100644
index 000000000..53b4c8194
--- /dev/null
+++ b/Software/Android_Studio/Tango.Integration/src/main/java/com/twine/tango/integration/services/SynchronizationService.java
@@ -0,0 +1,179 @@
+package com.twine.tango.integration.services;
+
+import android.os.AsyncTask;
+
+import com.elvishew.xlog.XLog;
+import com.google.protobuf.ByteString;
+import com.snatik.storage.Storage;
+import com.twine.tango.core.ContextHelper;
+import com.twine.tango.dal.TangoDB;
+import com.twine.tango.integration.providers.IMachineIdentityProvider;
+import com.twine.tango.integration.services.IExternalBridgeService.MessageReceivedArgs;
+import com.twine.tango.pmr.MessageFactory;
+import com.twine.tango.pmr.TangoMessage;
+import com.twine.tango.pmr.common.MessageTypeOuterClass.MessageType;
+import com.twine.tango.pmr.integration.DirectSynchronizationRequestOuterClass.DirectSynchronizationRequest;
+import com.twine.tango.pmr.integration.DirectSynchronizationResponseOuterClass.DirectSynchronizationResponse;
+import com.twine.tango.pmr.integration.OverrideDataBaseRequestOuterClass.OverrideDataBaseRequest;
+import com.twine.tango.pmr.integration.OverrideDataBaseResponseOuterClass.OverrideDataBaseResponse;
+import com.twine.tango.pmr.stubs.CalculateRequestOuterClass.CalculateRequest;
+import com.twine.tango.pmr.stubs.CalculateResponseOuterClass.CalculateResponse;
+import com.twine.tango.pmr.synchronization.SynchronizeDBRequestOuterClass.SynchronizeDBRequest;
+import com.twine.tango.web.WebApiFactory;
+import com.twine.tango.web.ISynchronizationAPI;
+
+import java.io.IOException;
+
+import javax.inject.Inject;
+
+import io.reactivex.Completable;
+
+
+/**
+ * Represents the Tango application local to remote synchronization engine.
+ */
+public class SynchronizationService implements ISynchronizationService
+{
+ private IMachineIdentityProvider machineIdentityProvider;
+ private IExternalBridgeService bridgeService;
+
+ /**
+ * Instantiates a new Tango synchronizer.
+ *
+ * @param machineIdentityProvider the machine identity provider
+ */
+ @Inject
+ public SynchronizationService(IMachineIdentityProvider machineIdentityProvider, IExternalBridgeService bridgeService)
+ {
+ this.machineIdentityProvider = machineIdentityProvider;
+ this.bridgeService = bridgeService;
+ this.bridgeService.registerMessageHandler(this::handleDirectSynchronizationRequest, MessageType.DirectSynchronizationRequest);
+ this.bridgeService.<OverrideDataBaseRequest>registerMessageHandler(this::handleOverrideDataBaseRequest, MessageType.OverrideDataBaseRequest);
+
+ }
+
+ private void handleOverrideDataBaseRequest(Object o, MessageReceivedArgs<OverrideDataBaseRequest> request)
+ {
+ AsyncTask.execute(() ->
+ {
+ try
+ {
+ //Backup DataBase.
+ TangoDB.backup();
+
+ //Replace Database.
+ TangoDB.replace(request.getMessage().getRemoteDB().toByteArray());
+
+ //Create response.
+ OverrideDataBaseResponse response = OverrideDataBaseResponse.newBuilder()
+ .setSuccessful(true)
+ .build();
+
+ TangoMessage<OverrideDataBaseResponse> message = MessageFactory.createTangoMessage(
+ OverrideDataBaseResponse.class,
+ response);
+
+ bridgeService.sendResponse(message, request.getContainer().getToken()).subscribe(() ->
+ {
+
+ },(ex) ->
+ {
+ XLog.e(ex);
+ });
+
+ } catch (Exception e)
+ {
+ XLog.e(e);
+ //Create response.
+ OverrideDataBaseResponse response = OverrideDataBaseResponse.newBuilder()
+ .setSuccessful(false)
+ .build();
+
+ TangoMessage<OverrideDataBaseResponse> message = MessageFactory.createTangoMessage(
+ OverrideDataBaseResponse.class,
+ response);
+
+ bridgeService.sendResponse(message, request.getContainer().getToken()).subscribe(() ->
+ {
+
+ },(ex) ->
+ {
+ XLog.e(ex);
+ });
+ }
+ });
+ }
+
+ private void handleDirectSynchronizationRequest(Object o, MessageReceivedArgs<DirectSynchronizationRequest> request)
+ {
+ AsyncTask.execute(() ->
+ {
+ try
+ {
+ Storage storage = new Storage(ContextHelper.getApplicationContext());
+
+ //Read current DB file bytes.
+ byte[] data = storage.readFile(TangoDB.getDataBasePath());
+
+ //Create synchronization response.
+ DirectSynchronizationResponse response = DirectSynchronizationResponse.newBuilder()
+ .setLocalDB(ByteString.copyFrom(data))
+ .build();
+
+ TangoMessage<DirectSynchronizationResponse> message = MessageFactory.createTangoMessage(
+ DirectSynchronizationResponse.class,
+ response);
+
+ bridgeService.sendResponse(message, request.getContainer().getToken()).subscribe(() ->
+ {
+
+ },(ex) ->
+ {
+ XLog.e(ex);
+ });
+
+ } catch (Exception e)
+ {
+ XLog.e(e);
+ }
+ });
+ }
+
+ @Override
+ public Completable synchronizeDB()
+ {
+ return Completable.create((emitter) ->
+ {
+
+ //Backup DataBase.
+ TangoDB.backup();
+
+ Storage storage = new Storage(ContextHelper.getApplicationContext());
+
+ //Read current DB file bytes.
+ byte[] data = storage.readFile(TangoDB.getDataBasePath());
+
+ //Create synchronization web api.
+ ISynchronizationAPI synchAPI = WebApiFactory.getSynchronizationAPI();
+
+ //Create synchronization response.
+ SynchronizeDBRequest request = SynchronizeDBRequest.newBuilder()
+ .setSerialNumber(machineIdentityProvider.getSerialNumber())
+ .setLocalDB(ByteString.copyFrom(data))
+ .build();
+
+ synchAPI.synchronize(request).singleOrError().subscribe((response) ->
+ {
+
+ TangoDB.replace(response.getRemoteDB().toByteArray());
+ emitter.onComplete();
+
+ }, (ex) ->
+ {
+ XLog.e(ex);
+ emitter.onError(ex);
+ });
+
+ });
+ }
+}