2

I'm trying to implement file transfer using Nearby Connection with P2P_STAR strategy where the "spoke" sends a video file to the "hub".

Using the documentation example I send the filenameMessage as bytes payload and the file payload. The receiver uses the SimpleArrayMaps to store the filename, incoming and completed payloads to rename the file later on. Target is Q with android:requestLegacyExternalStorage="true", because Uri uri = filePayload.asFile().asUri(); is using a method not yet implemented I think.

Sending messages works fine but when it comes to file transfer only a part of it is transfered. For example I had a video around 24MB and the transfered file in the download/nearby folder was 17MB, and I waited many minutes. Other times the transferred file was a few KB.

My code, one activity app(device is either spoke or hub):

    public void sendMessage(String endpointId, String msg) {

        if (endpointId != null) {
            // Convert the message to Bytes
            byte[] bytes = msg.getBytes(StandardCharsets.UTF_8);
            // Generate payload and send it to destination
            Payload bytesPayload = Payload.fromBytes(bytes);
            Nearby.getConnectionsClient(context).sendPayload(endpointId, bytesPayload);
        }
    }

    private PayloadCallback payloadCallback = new PayloadCallback() {

        private SimpleArrayMap<Long, Payload> incomingFilePayloads = new SimpleArrayMap<>();
        private SimpleArrayMap<Long, Payload> completedFilePayloads = new SimpleArrayMap<>();
        private SimpleArrayMap<Long, String> filePayloadFilenames = new SimpleArrayMap<>();

        @Override
        public void onPayloadReceived(@NonNull String endpointId, @NonNull Payload payload) {

            if (payload.getType() == Payload.Type.BYTES) {

                // Convert the payload from Bytes to a String
                String msg = new String(payload.asBytes(), StandardCharsets.UTF_8);
                // Check if it's the filename bytes payload
                String[] parts = msg.split(":");
                if (parts[0].equals("filename")) {
                    msg = parts[0];
                }
                switch (msg) {

                    // OTHER CASES...

                    case "create_video":

                        //Video created here
                        sendRecording(endpointId);
                        break;

                    case "filename":

                        long payloadId = Long.parseLong(parts[1]);
                        String filename = "video_" + parts[2] + ".mp4";
                        filePayloadFilenames.put(payloadId, filename);
                        processFilePayload(payload.getId());
                        break;

                }

            } else {

                if (payload.getType() == Payload.Type.FILE)
                    incomingFilePayloads.put(payload.getId(), payload);
            }
        }

        @Override
        public void onPayloadTransferUpdate(@NonNull String endpointId, @NonNull PayloadTransferUpdate payloadTransferUpdate) {

            if (payloadTransferUpdate.getStatus() == PayloadTransferUpdate.Status.SUCCESS) {
                long payloadId = payloadTransferUpdate.getPayloadId();
                Payload payload = incomingFilePayloads.remove(payloadId);

                // ALWAYS NULL?
                if (payload != null) {
                    completedFilePayloads.put(payloadId, payload);
                    if (payload.getType() == Payload.Type.FILE) {
                        processFilePayload(payloadId);
                    }
                }
            }

        }

        private void processFilePayload(long payloadId) {
            Payload filePayload = completedFilePayloads.get(payloadId);
            String filename = filePayloadFilenames.get(payloadId);
            if (filePayload != null && filename != null) {
                completedFilePayloads.remove(payloadId);
                filePayloadFilenames.remove(payloadId);

                File payloadFile = filePayload.asFile().asJavaFile();
                payloadFile.renameTo(new File(payloadFile.getParentFile(), filename));
            }
        }

    };
    

    private void sendRecording(String endpointId) {
        // The uri is saved when I create the file in the first place
        Uri uri = Utils.getUriSavedVideo();
        Payload videoPayload = null;
        try {
            ParcelFileDescriptor video = getContentResolver().openFileDescriptor(uri, "r");
            videoPayload = Payload.fromFile(video);
            String filenameMsg = "filename" + ":" + videoPayload.getId() + ":" + Utils.getTimeStampString();

            sendMessage(endpointId, filenameMsg);
            Nearby.getConnectionsClient(context).sendPayload(endpointId, videoPayload);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

I've noticed that inside method onPayloadTransferUpdate the payload is always null when retrived from incomingFilePayload, but the payloadId get inserted in incomingFilePayload inside onPayloadReceived Payload.Type.FILE. However this part should only be relevant to renaming the file, manually renaming the file doesn't work because the video file is incomplete.

Since at least a part of the file is transferred could this be related to the devices themselves? The connection is still in place and I can still send other messages. I thought that the STAR strategy was fine to send small videos in a short period of time.

Razac
  • 21
  • 1
  • At a glance, the code looks correct. Can you attach the logcat logs for the tag 'NearbyConnections'? It should go into more detail about what's happening to this payload. – Xlythe Dec 29 '20 at 20:44
  • @Xlythe I tried this code again after a couple of weeks and now it seems to work, but it's very slow(one test did 10MB in 10 minutes). The devices disconnected only one time but I wasn't checking the logs unfortunately. I think there are still some anomalies if you want to have a look: https://gist.github.com/leomazzon/58c3d08dbbf793795fd55540ea2260bf – Razac Jan 06 '21 at 13:21
  • It's attempting, and failing, to upgrade to WiFi Hotspot. Can you also grab the log tag 'NearbyMediums'? That might tell why it's failing. Also, grab logs from both devices. If even that doesn't have enough information, you'll need to enable WiFi Verbose Logging (in developer options) and give me a raw logcat. – Xlythe Jan 09 '21 at 05:21
  • I updated the previous github gist; it's a bit long but if you search for "logcat" you will find all the separate files for sender/receiver with NearbyConnection, NearbyMediums and verbose. This time the receiver never got the full file even though the sender did succeed. – Razac Jan 11 '21 at 20:19
  • If the sender is succeeding, but the receiver isn't, I'd recommend adding an ack from Receiver -> Sender before disconnecting. There are a lot of buffers that can be filled up internally so that it looks like everything has been sent but in actuality it's only queued or in progress. Disconnecting often clears these buffers, resulting in a similar issue to the one you're mentioning. – Xlythe Jan 12 '21 at 22:15
  • Useful to know but I do not disconnect the devices after the transfer and the connections is still in place. Anyway I stopped working on this project, maybe in the future I will look back into it. Thanks for the help so far. – Razac Jan 14 '21 at 20:09

0 Answers0