feat: update file transfer api

Change-Id: I278cea3dad005d41457df4aab75ff1cc46b517e1
diff --git a/ring-android/.gitignore b/ring-android/.gitignore
index 828d430..0c457c6 100644
--- a/ring-android/.gitignore
+++ b/ring-android/.gitignore
@@ -41,15 +41,7 @@
 /app/build/
 /androidtv/build/
 /libringclient/build/
-/libringclient/src/main/java/cx/ring/daemon/Blob.java
-/libringclient/src/main/java/cx/ring/daemon/FloatVect.java
-/libringclient/src/main/java/cx/ring/daemon/Ringservice.java
-/libringclient/src/main/java/cx/ring/daemon/RingserviceJNI.java
-/libringclient/src/main/java/cx/ring/daemon/UintVect.java
-/libringclient/src/main/java/cx/ring/daemon/VideoCallback.java
-/libringclient/src/main/java/cx/ring/daemon/PresenceCallback.java
-/libringclient/src/main/java/cx/ring/daemon/DataTransferCallback.java
-/libringclient/src/main/java/cx/ring/daemon/DataTransferInfo.java
+/libringclient/src/main/java/cx/ring/daemon/*
 /build/
 /app/google-services.json
 /app/src/debug/google-services.json
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java
index 460b138..764bcc9 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java
@@ -39,6 +39,8 @@
 import android.view.ViewGroup;
 import android.view.inputmethod.EditorInfo;
 import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.ProgressBar;
 import android.widget.Spinner;
 import android.widget.Toast;
 
@@ -100,6 +102,12 @@
     @BindView(R.id.number_selector)
     protected Spinner mNumberSpinner;
 
+    @BindView(R.id.pb_data_transfer)
+    protected ProgressBar pbDataTransfer;
+
+    @BindView(R.id.send_data)
+    protected ImageButton sendData;
+
     private AlertDialog mDeleteDialog;
     private boolean mDeleteConversation = false;
 
@@ -193,7 +201,7 @@
         presenter.sendTextMessage(mMsgEditTxt.getText().toString());
     }
 
-    @OnClick(R.id.file_send)
+    @OnClick(R.id.send_data)
     public void selectFile() {
         presenter.selectFile();
     }
@@ -215,6 +223,34 @@
     }
 
     @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
+        super.onActivityResult(requestCode, resultCode, resultData);
+
+        if (requestCode == REQUEST_CODE_FILE_PICKER && resultCode == RESULT_OK) {
+            if (resultData != null) {
+                android.net.Uri uri = resultData.getData();
+                if (uri == null) {
+                    return;
+                }
+
+                new Thread(() -> {
+                    getActivity().runOnUiThread(() -> setLoading(true));
+
+                    try {
+                        File cacheFile = FileUtils.getCacheFile(getActivity(), uri);
+                        presenter.sendFile(cacheFile.toString());
+                    } catch (IOException e) {
+                        Log.e(TAG, "onActivityResult: not able to create cache file");
+                        getActivity().runOnUiThread(() -> displayErrorToast(RingError.INVALID_FILE));
+                    }
+
+                    getActivity().runOnUiThread(() -> setLoading(false));
+                }).start();
+            }
+        }
+    }
+
+    @Override
     public void writeCacheFile(String cacheFilename) {
         File cacheFile = new File(getActivity().getCacheDir(), cacheFilename);
         try {
@@ -230,28 +266,6 @@
         }
     }
 
-    @Override
-    public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
-        super.onActivityResult(requestCode, resultCode, resultData);
-
-        if (requestCode == REQUEST_CODE_FILE_PICKER && resultCode == RESULT_OK) {
-            if (resultData != null) {
-                android.net.Uri uri = resultData.getData();
-                if (uri == null) {
-                    return;
-                }
-
-                try {
-                    File cacheFile = FileUtils.getCacheFile(getActivity(), uri);
-                    presenter.sendFile(cacheFile.toString());
-                } catch (IOException e) {
-                    Log.e(TAG, "onActivityResult: not able to create cache file");
-                    displayErrorToast(RingError.INVALID_FILE);
-                }
-            }
-        }
-    }
-
     @OnEditorAction(R.id.msg_input_txt)
     public boolean actionSendMsgText(int actionId) {
         switch (actionId) {
@@ -500,4 +514,14 @@
     public void toggleButtonClicked() {
         presenter.clickOnGoingPane();
     }
+
+    private void setLoading(boolean isLoading) {
+        if (isLoading) {
+            sendData.setVisibility(View.GONE);
+            pbDataTransfer.setVisibility(View.VISIBLE);
+        } else {
+            sendData.setVisibility(View.VISIBLE);
+            pbDataTransfer.setVisibility(View.GONE);
+        }
+    }
 }
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/services/NotificationServiceImpl.java b/ring-android/app/src/main/java/cx/ring/services/NotificationServiceImpl.java
index 5eb4edc..8b988ee 100644
--- a/ring-android/app/src/main/java/cx/ring/services/NotificationServiceImpl.java
+++ b/ring-android/app/src/main/java/cx/ring/services/NotificationServiceImpl.java
@@ -501,22 +501,23 @@
                 case DATA_TRANSFER: {
                     Long transferId = event.getEventInput(ServiceEvent.EventInput.TRANSFER_ID, Long.class);
                     DataTransferEventCode transferEventCode = event.getEventInput(ServiceEvent.EventInput.TRANSFER_EVENT_CODE, DataTransferEventCode.class);
-                    DataTransferInfo dataTransferInfo = mCallService.dataTransferInfo(transferId);
+                    DataTransferInfo dataTransferInfo = new DataTransferInfo();
+                    mCallService.dataTransferInfo(transferId, dataTransferInfo);
 
                     if (transferEventCode == DataTransferEventCode.CREATED) {
 
                         HistoryFileTransfer historyFileTransfer = new HistoryFileTransfer(transferId, dataTransferInfo.getDisplayName(),
-                                dataTransferInfo.getIsOutgoing(), dataTransferInfo.getTotalSize(),
+                                dataTransferInfo.getFlags() == 0, dataTransferInfo.getTotalSize(),
                                 dataTransferInfo.getBytesProgress(), dataTransferInfo.getPeer(),
                                 dataTransferInfo.getAccountId());
                         mHistoryService.addFileTransfer(historyFileTransfer);
 
-                        if (!dataTransferInfo.getIsOutgoing()) {
+                        if (dataTransferInfo.getFlags() == 1){
                             showFileTransferNotification(transferId, dataTransferInfo.getPeer());
                         }
                     }
 
-                    if (!dataTransferInfo.getIsOutgoing()) {
+                    if (dataTransferInfo.getFlags() == 1) {
                         mHistoryService.updateFileTransferStatus(transferId, transferEventCode);
                     }
 
diff --git a/ring-android/app/src/main/res/layout/frag_conversation.xml b/ring-android/app/src/main/res/layout/frag_conversation.xml
index 5a2bbbc..bf07c4a 100644
--- a/ring-android/app/src/main/res/layout/frag_conversation.xml
+++ b/ring-android/app/src/main/res/layout/frag_conversation.xml
@@ -51,6 +51,7 @@
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="match_parent"
+            android:gravity="center"
             android:orientation="horizontal">
 
             <Spinner
@@ -73,16 +74,23 @@
                 android:padding="8dp" />
 
             <ImageButton
-                android:id="@+id/file_send"
+                android:id="@+id/send_data"
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
+                android:layout_marginEnd="5dp"
                 android:background="?selectableItemBackgroundBorderless"
                 android:contentDescription="@string/send_file"
-                android:layout_marginEnd="5dp"
                 android:padding="8dp"
                 android:tint="@android:color/darker_gray"
                 card_view:srcCompat="@drawable/ic_upload_black" />
 
+            <ProgressBar
+                android:id="@+id/pb_data_transfer"
+                android:layout_width="30dp"
+                android:layout_height="30dp"
+                android:indeterminate="true"
+                android:visibility="gone" />
+
             <ImageButton
                 android:id="@+id/msg_send"
                 android:layout_width="wrap_content"
diff --git a/ring-android/libringclient/src/main/java/cx/ring/conversation/ConversationPresenter.java b/ring-android/libringclient/src/main/java/cx/ring/conversation/ConversationPresenter.java
index 643e4d5..605dfe1 100644
--- a/ring-android/libringclient/src/main/java/cx/ring/conversation/ConversationPresenter.java
+++ b/ring-android/libringclient/src/main/java/cx/ring/conversation/ConversationPresenter.java
@@ -30,6 +30,7 @@
 import cx.ring.model.CallContact;
 import cx.ring.model.Conference;
 import cx.ring.model.Conversation;
+import cx.ring.model.DataTransferError;
 import cx.ring.model.DataTransferEventCode;
 import cx.ring.model.HistoryCall;
 import cx.ring.model.HistoryFileTransfer;
@@ -43,6 +44,7 @@
 import cx.ring.services.AccountService;
 import cx.ring.services.CallService;
 import cx.ring.services.ContactService;
+import cx.ring.services.DataTransferWrapper;
 import cx.ring.services.DeviceRuntimeService;
 import cx.ring.services.HardwareService;
 import cx.ring.services.HistoryService;
@@ -205,7 +207,12 @@
 
         // send file
         Uri uri = new Uri(mContactRingId);
-        mCallService.sendFile(mAccountId, uri.getHost(), filePath, file.getName());
+        DataTransferInfo dataTransferInfo = new DataTransferInfo();
+        dataTransferInfo.setAccountId(mAccountId);
+        dataTransferInfo.setPeer(uri.getHost());
+        dataTransferInfo.setPath(filePath);
+        dataTransferInfo.setDisplayName(file.getName());
+        mCallService.sendFile(0L, dataTransferInfo);
     }
 
     public void sendTrustRequest() {
@@ -375,31 +382,30 @@
         // find corresponding transfer
         mConversation.updateFileTransfer(transferId, transferEventCode);
 
-        DataTransferInfo dataTransferInfo = null;
+        DataTransferInfo dataTransferInfo = new DataTransferInfo();
+        DataTransferWrapper dataTransferWrapper = null;
         if (transferEventCode == DataTransferEventCode.CREATED || transferEventCode == DataTransferEventCode.FINISHED) {
-            dataTransferInfo = mCallService.dataTransferInfo(transferId);
+            dataTransferWrapper = mCallService.dataTransferInfo(transferId, dataTransferInfo);
+
+            if (dataTransferWrapper.getDataTransferError() != DataTransferError.SUCCESS) {
+                Log.e(TAG, "handleDataTransferEvent: an error occurred during data transfer " + dataTransferWrapper.getDataTransferError().name());
+                return;
+            }
         }
 
+        Log.d(TAG, "handleDataTransferEvent: " + transferEventCode.name());
         switch (transferEventCode) {
             case CREATED:
-                Log.i(TAG, "handleDataTransferEvent: CREATED");
-
-                if (dataTransferInfo != null) {
-                    mConversation.addFileTransfer(transferId, dataTransferInfo.getDisplayName(),
-                            dataTransferInfo.getIsOutgoing(), dataTransferInfo.getTotalSize(),
-                            dataTransferInfo.getBytesProgress(), dataTransferInfo.getPeer(),
-                            dataTransferInfo.getAccountId());
-                }
+                mConversation.addFileTransfer(transferId, dataTransferInfo.getDisplayName(),
+                        dataTransferWrapper.isOutgoing(), dataTransferInfo.getTotalSize(),
+                        dataTransferInfo.getBytesProgress(), dataTransferInfo.getPeer(),
+                        dataTransferInfo.getAccountId());
                 break;
             case FINISHED:
-                if (dataTransferInfo != null) {
-                    if (!dataTransferInfo.getIsOutgoing()) {
-                        getView().writeCacheFile(dataTransferInfo.getDisplayName());
-                    }
+                if (!dataTransferWrapper.isOutgoing()) {
+                    getView().writeCacheFile(dataTransferInfo.getDisplayName());
                 }
                 break;
-            default:
-                Log.d(TAG, "handleDataTransferEvent: " + transferEventCode.name());
         }
 
         Log.d(TAG, "handleDataTransferEvent: AggregateHistorySize=" + mConversation.getAggregateHistory().size() + ", transferEventCode=" + transferEventCode);
diff --git a/ring-android/libringclient/src/main/java/cx/ring/model/DataTransferError.java b/ring-android/libringclient/src/main/java/cx/ring/model/DataTransferError.java
new file mode 100644
index 0000000..5ac37ed
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/cx/ring/model/DataTransferError.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2004-2018 Savoir-faire Linux Inc.
+ *
+ * Author: Pierre Duchemin <pierre.duchemin@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package cx.ring.model;
+
+public enum DataTransferError {
+    SUCCESS,
+    UNKNOWN,
+    IO,
+    INVALID_ARGUMENT
+}
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/cx/ring/model/DataTransferEventCode.java b/ring-android/libringclient/src/main/java/cx/ring/model/DataTransferEventCode.java
index b1d14ed..35eda56 100644
--- a/ring-android/libringclient/src/main/java/cx/ring/model/DataTransferEventCode.java
+++ b/ring-android/libringclient/src/main/java/cx/ring/model/DataTransferEventCode.java
@@ -21,12 +21,13 @@
 package cx.ring.model;
 
 public enum DataTransferEventCode {
-    CREATED(false),
+    INVALID,
+    CREATED,
     UNSUPPORTED(true),
-    WAIT_PEER_ACCEPTANCE(false),
-    WAIT_HOST_ACCEPTANCE(false),
-    ONGOING(false),
-    FINISHED(false),
+    WAIT_PEER_ACCEPTANCE,
+    WAIT_HOST_ACCEPTANCE,
+    ONGOING,
+    FINISHED,
     CLOSED_BY_HOST(true),
     CLOSED_BY_PEER(true),
     INVALID_PATHNAME(true),
@@ -34,6 +35,10 @@
 
     private boolean isError;
 
+    DataTransferEventCode() {
+        isError = false;
+    }
+
     DataTransferEventCode(boolean isError) {
         this.isError = isError;
     }
diff --git a/ring-android/libringclient/src/main/java/cx/ring/services/CallService.java b/ring-android/libringclient/src/main/java/cx/ring/services/CallService.java
index ba9f29d..1e4647d 100644
--- a/ring-android/libringclient/src/main/java/cx/ring/services/CallService.java
+++ b/ring-android/libringclient/src/main/java/cx/ring/services/CallService.java
@@ -33,6 +33,7 @@
 import cx.ring.daemon.Ringservice;
 import cx.ring.daemon.StringMap;
 import cx.ring.model.CallContact;
+import cx.ring.model.DataTransferError;
 import cx.ring.model.DataTransferEventCode;
 import cx.ring.model.ServiceEvent;
 import cx.ring.model.SipCall;
@@ -426,63 +427,65 @@
         );
     }
 
-    public DataTransferInfo dataTransferInfo(final Long dataTransferId) {
-        return FutureUtils.executeDaemonThreadCallable(
-                mExecutor,
-                mDeviceRuntimeService.provideDaemonThreadId(),
-                true,
-                new Callable<DataTransferInfo>() {
-                    @Override
-                    public DataTransferInfo call() throws Exception {
-                        Log.i(TAG, "dataTransferInfo() thread running... dataTransferId=" + dataTransferId);
-                        return Ringservice.dataTransferInfo(dataTransferId);
-                    }
-                }
-        );
-    }
-
-    public Long sendFile(final String accountId, final String to, final String filePath, final String displayName) {
-        return FutureUtils.executeDaemonThreadCallable(
+    public DataTransferError sendFile(final Long dataTransferId, final DataTransferInfo dataTransferInfo) {
+        Long errorCode = FutureUtils.executeDaemonThreadCallable(
                 mExecutor,
                 mDeviceRuntimeService.provideDaemonThreadId(),
                 true,
                 new Callable<Long>() {
                     @Override
                     public Long call() throws Exception {
-                        Log.i(TAG, "sendFile() thread running... accountId=" + accountId + ", to=" + to + ", filePath=" + filePath);
-                        return Ringservice.sendFile(accountId, to, filePath, displayName);
+                        Log.i(TAG, "sendFile() thread running... accountId=" + dataTransferInfo.getAccountId() + ", peer=" + dataTransferInfo.getPeer() + ", filePath=" + dataTransferInfo.getPath());
+                        return Ringservice.sendFile(dataTransferInfo, dataTransferId);
                     }
                 }
         );
+        return getDataTransferError(errorCode);
     }
 
-    public void acceptFileTransfer(final Long dataTransferId, final String filePath, final long offset) {
-        FutureUtils.executeDaemonThreadCallable(
+    public DataTransferError acceptFileTransfer(final Long dataTransferId, final String filePath, final long offset) {
+        Long errorCode = FutureUtils.executeDaemonThreadCallable(
                 mExecutor,
                 mDeviceRuntimeService.provideDaemonThreadId(),
                 true,
-                new Callable<Boolean>() {
+                new Callable<Long>() {
                     @Override
-                    public Boolean call() throws Exception {
+                    public Long call() throws Exception {
                         Log.i(TAG, "acceptFileTransfer() thread running... dataTransferId=" + dataTransferId + ", filePath=" + filePath + ", offset=" + offset);
-                        Ringservice.acceptFileTransfer(dataTransferId, filePath, offset);
-                        return true;
+                        return Ringservice.acceptFileTransfer(dataTransferId, filePath, offset);
                     }
                 }
         );
+        return getDataTransferError(errorCode);
     }
 
-    public void cancelDataTransfer(final Long dataTransferId) {
-        FutureUtils.executeDaemonThreadCallable(
+    public DataTransferError cancelDataTransfer(final Long dataTransferId) {
+        Long errorCode = FutureUtils.executeDaemonThreadCallable(
                 mExecutor,
                 mDeviceRuntimeService.provideDaemonThreadId(),
                 true,
-                new Callable<Boolean>() {
+                new Callable<Long>() {
                     @Override
-                    public Boolean call() throws Exception {
+                    public Long call() throws Exception {
                         Log.i(TAG, "cancelDataTransfer() thread running... dataTransferId=" + dataTransferId);
-                        Ringservice.cancelDataTransfer(dataTransferId);
-                        return true;
+                        return Ringservice.cancelDataTransfer(dataTransferId);
+                    }
+                }
+        );
+        return getDataTransferError(errorCode);
+    }
+
+    public DataTransferWrapper dataTransferInfo(final Long dataTransferId, final DataTransferInfo dataTransferInfo) {
+        return FutureUtils.executeDaemonThreadCallable(
+                mExecutor,
+                mDeviceRuntimeService.provideDaemonThreadId(),
+                true,
+                new Callable<DataTransferWrapper>() {
+                    @Override
+                    public DataTransferWrapper call() throws Exception {
+                        Log.i(TAG, "dataTransferInfo() thread running... dataTransferId=" + dataTransferId);
+                        long errorCode = Ringservice.dataTransferInfo(dataTransferId, dataTransferInfo);
+                        return new DataTransferWrapper(dataTransferInfo, getDataTransferError(errorCode));
                     }
                 }
         );
@@ -596,12 +599,7 @@
     }
 
     public void dataTransferEvent(long transferId, int eventCode) {
-        DataTransferEventCode dataTransferEventCode = DataTransferEventCode.UNSUPPORTED;
-        try {
-            dataTransferEventCode = DataTransferEventCode.values()[eventCode];
-        } catch (ArrayIndexOutOfBoundsException ignored) {
-            Log.e(TAG, "dataTransferEvent: invalid data transfer status from daemon");
-        }
+        DataTransferEventCode dataTransferEventCode = getDataTransferEventCode(eventCode);
 
         setChanged();
         ServiceEvent event = new ServiceEvent(ServiceEvent.EventType.DATA_TRANSFER);
@@ -609,4 +607,28 @@
         event.addEventInput(ServiceEvent.EventInput.TRANSFER_EVENT_CODE, dataTransferEventCode);
         notifyObservers(event);
     }
+
+    private static DataTransferEventCode getDataTransferEventCode(int eventCode) {
+        DataTransferEventCode dataTransferEventCode = DataTransferEventCode.INVALID;
+        try {
+            dataTransferEventCode = DataTransferEventCode.values()[eventCode];
+        } catch (ArrayIndexOutOfBoundsException ignored) {
+            Log.e(TAG, "getDataTransferEventCode: invalid data transfer status from daemon");
+        }
+        return dataTransferEventCode;
+    }
+
+    private static DataTransferError getDataTransferError(Long errorCode) {
+        DataTransferError dataTransferError = DataTransferError.UNKNOWN;
+        if (errorCode == null) {
+            Log.e(TAG, "getDataTransferError: invalid error code");
+        } else {
+            try {
+                dataTransferError = DataTransferError.values()[errorCode.intValue()];
+            } catch (ArrayIndexOutOfBoundsException ignored) {
+                Log.e(TAG, "getDataTransferError: invalid data transfer error from daemon");
+            }
+        }
+        return dataTransferError;
+    }
 }
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/cx/ring/services/DaemonService.java b/ring-android/libringclient/src/main/java/cx/ring/services/DaemonService.java
index 6fced71..112db5d 100644
--- a/ring-android/libringclient/src/main/java/cx/ring/services/DaemonService.java
+++ b/ring-android/libringclient/src/main/java/cx/ring/services/DaemonService.java
@@ -42,7 +42,7 @@
 
 public class DaemonService {
 
-    private static final String TAG = DaemonService.class.getName();
+    private static final String TAG = DaemonService.class.getSimpleName();
 
     private static final int POLLING_TIMEOUT = 50;
 
diff --git a/ring-android/libringclient/src/main/java/cx/ring/services/DataTransferWrapper.java b/ring-android/libringclient/src/main/java/cx/ring/services/DataTransferWrapper.java
new file mode 100644
index 0000000..8556e14
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/cx/ring/services/DataTransferWrapper.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2004-2018 Savoir-faire Linux Inc.
+ *
+ * Author: Pierre Duchemin <pierre.duchemin@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package cx.ring.services;
+
+import cx.ring.daemon.DataTransferInfo;
+import cx.ring.model.DataTransferError;
+
+public class DataTransferWrapper {
+
+        private DataTransferInfo dataTransferInfo;
+        private DataTransferError dataTransferError;
+
+        public DataTransferWrapper(DataTransferInfo dataTransferInfo, DataTransferError dataTransferError) {
+            this.dataTransferInfo = dataTransferInfo;
+            this.dataTransferError = dataTransferError;
+        }
+
+        public DataTransferInfo getDataTransferInfo() {
+            return dataTransferInfo;
+        }
+
+        public DataTransferError getDataTransferError() {
+            return dataTransferError;
+        }
+
+        public boolean isOutgoing() {
+            return this.dataTransferInfo.getFlags() == 0;
+        }
+    }
\ No newline at end of file