title: What is causing my image to corrupt during transfer via Bluetooth RFCOMM using FTP?
body: <p>I'm developing two separate applications for data transfer over Bluetooth RFCOMM using the Obex File Transfer protocol. On one side, a Windows C# Console Application running on a PC listens for incoming bluetooth connections and sends an image whenever a client makes a request. On the other side, an Android application running on a mobile device scans the nearby bluetooth devices, looks for the server and receives the image.</p>
  <p>In most cases, everything works fine and the image is transmitted without problems. Sometimes - not very often, I still can't figure out how to reproduce the error - the image is corrupted during the transmission, as some of the received bytes from the Android app do not match the original buffer (I compute the CRC of the received buffer and compare it with the original one to check if the image has been sent successfully).</p>
  <p>Here's an example:</p>
  <p>Original</p>
  <p><img src="https://i.stack.imgur.com/1zVY2.jpg" alt="Original" />,</p>
  <p>Received</p>
  <p><img src="https://i.stack.imgur.com/nWYd4.jpg" alt="Received" /></p>
  <p>This kind of &quot;glitchy&quot; image is just an example, every time something goes wrong the received image has a different 'glitch effect'.</p>
  <p>Few things I tried to solve the problem:</p>
  <ul>
  <li>Changing UUID, but neither the OOP UUID nor a custom UUID seems to work, as the exact same problem arises.</li>
  <li>My smartphone (Xiaomi Redmi Note 8T) from which I am running the client app had almost zero free space of internal storage, so I got desperate and tried to free some memory to see if that was causing the error for some reason (yeah it doesn't make much sense but it's worth mentioning). At first it worked and I thought that solved the problem somehow, but then the error re-appeared just like before.</li>
  <li>Using an ACK system to control each sub array of data sent from the server to the client, something like: the PC sends the first sub array of data, then it waits until the smartphone sends an ACK to acknowledge the reception of the sub array, and ONLY after that it proceeds to send the next sub array of data, and so on until the end of the buffer. Needless to say that neither this option worked (again, same error and corrupted data).</li>
  <li>I also tried to see if other devices trying to connect to my smartphone could cause the problem, but it wasn't the case.</li>
  </ul>
  <p><strong>CODE</strong></p>
  <p><strong>Server side</strong></p>
  <p>Here's my implementation of the listener in the <strong>C# Console app</strong> running on Windows 10. I took <a href="https://docs.microsoft.com/en-us/windows/uwp/devices-sensors/send-or-receive-files-with-rfcomm" rel="nofollow noreferrer">this Server sample</a> as a reference.</p>
  <pre><code>// Initialize the provider for the hosted RFCOMM service
  _provider = await RfcommServiceProvider.CreateAsync(
          RfcommServiceId.ObexFileTransfer);       // Use Obex FTP protocol
          // UUID is 00001106-0000-1000-8000-00805F9B34FB
  
  
  // Create a listener for this service and start listening
  StreamSocketListener listener = new StreamSocketListener();
  listener.ConnectionReceived += OnConnectionReceivedAsync;
  await listener.BindServiceNameAsync(
      _provider.ServiceId.AsString(),
      SocketProtectionLevel
          .BluetoothEncryptionAllowNullAuthentication);
  
  // Set the SDP attributes and start advertising
  InitializeServiceSdpAttributes(_provider);
  _provider.StartAdvertising(listener);
  </code></pre>
  <p><code>InitializeServiceSdpAttributes</code> function:</p>
  <pre><code>const uint SERVICE_VERSION_ATTRIBUTE_ID = 0x0300;
  const byte SERVICE_VERSION_ATTRIBUTE_TYPE = 0x0A;   // UINT32
  const uint SERVICE_VERSION = 200;
  
  void InitializeServiceSdpAttributes(RfcommServiceProvider provider)
  {
      Windows.Storage.Streams.DataWriter writer = new Windows.Storage.Streams.DataWriter();
  
      // First write the attribute type
      writer.WriteByte(SERVICE_VERSION_ATTRIBUTE_TYPE);
      // Then write the data
      writer.WriteUInt32(MINIMUM_SERVICE_VERSION);
  
      IBuffer data = writer.DetachBuffer();
      provider.SdpRawAttributes.Add(SERVICE_VERSION_ATTRIBUTE_ID, data);
  }
  </code></pre>
  <p>Whenever a new connection attempt is detected, the <code>OnConnectionReceivedAsync</code> function stops the advertisement, disposes the listener and creates a new <code>StreamSocket</code> object. At this point I set the input and output streams, convert the image to an array of bytes and send the buffer length to the remote device through the socket. Once the Android app has received the length of the buffer, it sends an ACK which means that it is ready to recevie the actual data.</p>
  <pre><code>// Create input and output stream
  DataWriter writer = new DataWriter(_socket.OutputStream);
  
  // Convert image to array of bytes
  byte[] imageByteArray;
  using (var inputStream = await file.OpenSequentialReadAsync())
  {
      var readStream = inputStream.AsStreamForRead();
  
      imageByteArray = new byte[readStream.Length];
      await readStream.ReadAsync(imageByteArray, 0, imageByteArray.Length);
  }
  
  // Write length of data
  writer.WriteBytes(intToByteArray(imageByteArray.Length));
  await writer.StoreAsync();
  
  // Wait for ACK ...
  </code></pre>
  <p>Finally, I send the image:</p>
  <pre><code>// Write bytes and send
  writer.WriteBytes(imageByteArray);
  await writer.StoreAsync();
  
  // Wait for ACK ...
  </code></pre>
  <p>As soon as the image is sent, the app receives an ACK from the remote device when all the data has been received and then closes the connection.</p>
  <p><strong>Client Side</strong></p>
  <p>First of all, the <strong>Android app</strong> creates a <code>BluetoothSocket</code> object using the same UUID specified from the Server app:</p>
  <pre><code>// Scan devices and find the remote Server by specifying the target MAC address
  // ...
  
  // targetDevice is the Server device 
  BluetoothSocket socket = targetDevice.createInsecureRfcommSocketToServiceRecord(
         UUID.fromString(&quot;00001106-0000-1000-8000-00805F9B34FB&quot;)         // FTP
  );
  
  // Connect the server
  socket.connect();
  </code></pre>
  <p>Finally, it reads the incoming data from the socket <code>InputStream</code>. First it reads the length of the incoming buffer and sends an ACK to confirm that it's ready to receive the image. Then waits for each sub array until all the buffer is complete. At this point, it sends a final ACK and closes the connection.</p>
  <pre><code>// Get input stream
  InputStream inputStream = socket.getInputStream();
  
  // Buffer that contains the incoming data
  byte[] buffer = null;
  // The numOfBytes is the expected length of the buffer
  int numOfBytes = 0;
  // Index of the sub array within the complete buffer
  int index = 0;
  // flag is true if the receiver is computing the number of bytes that it has to receive
  // flag is false if the receiver is actually reading the image sub arrays from the stream
  int flag = true;
  
  while(true){
      // Estimate number of incoming bytes
      if(flag){
          try{
              // inputStream.available() estimates the number of bytes that can be read
              byte[] temp = new byte[inputStream.available()];
  
              // Read the incoming data and store it in byte array temp (returns &gt; 0 if successful)
              if(inputStream.read(temp) &gt; 0){
                  // Get length of expected data as array and parse it to an Integer
                  String lengthString = new String(temp, StandardCharsets.UTF_8);
                  numOfBytes = Integer.parseInt(lengthString);
                  // Create buffer
                  buffer = new byte[numOfBytes];
                  // Set the flag to false (turn on read image mode)
                  flag = false;
  
                  // Send ACK
              }
          }
          catch (IOException e){
              // ...
          }
      }
      // Read image sub arrays
      else {
          try{
              byte[] data = new byte[inputStream.available()];
              // Read sub array and store it
              int numbers = inputStream.read(data);
  
              if(numbers &lt;= 0 &amp;&amp; index &lt; numOfBytes)
                  continue;
  
              // Copy sub array into the full image byte array
              System.arraycopy(data, 0, buffer, index, numbers);
              // Update index
              index = index + numbers;
  
              // Reached the end of the buffer (received all the data)
              if(index == numOfBytes){
  
                  // Send ACK (Transfer success)
                  // ...
  
                  // Decode buffer and create image from byte array
                  Bitmap bmp = BitmapFactory.decodeByteArray(buffer, 0, numOfBytes);
                  // Store output image
                  outputImage = bmp;
  
                  // Dismiss the bluetooth manager (close socket, exit waiting loop...)
                  dismiss();
  
                  // Return the image
                  return bmp;
              }    
          }
          catch (IOException e){
              // ...
          }
      }
  }
  </code></pre>