The below example uses Android.Runtime.InputStreamInvoker and Android.Runtime.OutputStreamInvoker types obtain Java.IO.InputStream and Java.IO.OutputStream. Once we have a Java.IO.InputStream instance, we can use its .Available() method to get the number of available response bytes which we can use in .Read() method:
byte[] Talk2BTsocket(BluetoothSocket socket, byte[] cmd, Mutex _mx, int timeOut = 150)
{
    var buf = new byte[0x20];
    _mx.WaitOne();
    try
    {
        using (var ost = socket.OutputStream)
        {
            var _ost = (ost as OutputStreamInvoker).BaseOutputStream;
            _ost.Write(cmd, 0, cmd.Length);
        }
        // needed because when skipped, it can cause no or invalid data on input stream
        Thread.Sleep(timeOut);
        using (var ist = socket.InputStream)
        {
            var _ist = (ist as InputStreamInvoker).BaseInputStream;
            var aa = 0;
            if ((aa = _ist.Available()) > 0)
            {
                var nn = _ist.Read(buf, 0, aa);
                System.Array.Resize(ref buf, nn);
            }
        }
    }
    catch (System.Exception ex)
    {
        DisplayAlert(ex.Message);
    }
    finally
    {
        _mx.ReleaseMutex();     // must be called here !!!
    }
    return buf;
}
| Parameter | Details | 
|---|---|
| socket | An instance of BluetoothSocket object. Socket must be opened before call this method. | 
| cmd | Command as a byte array to send to BT device. | 
| _mx | Since this method uses a hardware resource, it is better to call it from a separate worker thread. This parameter is an instance of System.Threading.Mutex object and is used to synchronize the thread with other threads optionally calling this method. | 
| timeOut | Wait time in milliseconds between Write and Read operations. |