I need to programmatically write data of say 1 to 100 MB in chunks of 1024 bytes to the remote Bluetooth device. Both are android devices. Here is a sample code snippet in my client program to transfer data –
bTSocket.connect(); //connect to remote BT device
DataOutputStream outStream = new DataOutputStream(bTSocket.getOutputStream());
byte[] buffer = new byte[1024];
int bytesToTransfer = 1000000;
while (bytesToTransfer > 0) {
outStream.write(buffer);
outStream.flush();
bytesToTransfer -= 1024;
}
outStream.close();
While running this piece of code on Android 2.2(Froyo), it works fine. However in case of Android 2.3.4 and 4.0.4, outStream.write(buffer) blocks infinitely after transfer of some data (say of 100 KB). Worth mentioning, the remote device is not configured for listening data. Is there any limitation on the amount of data that can be written?
The Bluetooth socket operates in blocking mode for both reads and writes.
If you fill up the send buffer, then the only thing that .write() can do to stop you trying to send any more data is to block. The alternative to it blocking would be to return an "operation would block!" error code, just like TCP sockets can do when placed in non-blocking mode. But the Bluetooth socket doesn't provide any such non-blocking mode.
You state that the remote Bluetooth device is not reading from its socket. With this being the case, the local sending buffer and remote receive buffer, with each only being of a certain finite size, will eventually fill up. At this point, your .write() operation is going to block until the remote end reads something from its socket. You can't just keep pumping in megabytes of data and expect it to just buffer it all somewhere.
The differences you experience between different Android platforms are probably down to the different amounts of buffer space available in the related Bluetooth stacks.
Related
I am working on an android app that has continuous communication between the phone and a local server on the computer through cable and Android Open Accessory connection(AOA), the server(the computer) keeps sending packets of bytes to the android, then android parse them, all kinds of data gets received successfully.
In the implementation I am using BufferedInputStream like this:
val bufferedInputStream = BufferedInputStream(inputStream, 8192) // 8192 is the def buffer size
As I said everything works fine UNTIL android received a packet that is larger than 8192 which is the buffer size, in this case I need to loop until I read the whole packet is read(I know the size of the packets because I send that in the beginning of the packet)
When this kind of packet gets received, the call
bufferedInputStream.read(anyByteArrayOfTheWriteSize)
fails with message "Invalid argument", the annoying thing is that I can read the beginning of the packet which has the size and parse it successfully, so for example it tells android that the size of this packet is for ex 10192 bytes, this means that it needs to read 8192 then will do another iteration and read the remaining 200 bytes.
but it does not, it just fails once it reaches the call of the method read.
I was trying to use inputStream instead of bufferedInputStream but it did not work at all, bufferedInputStream kinda worked like magic, not sure why?! but this is not the topic of the question, just adding this info if incase it is needed.
I started my adventure with AoT (I have basic toolkit).
1rst Idea that I try to implement is to communicate via USB with Card Reader (USB Card Reader HID Prox v3).
But I don't get it correctly.
This is "sample" code:
usbRequest = new UsbRequest();
usbConnection = mUsbManager.openDevice(device);
usbRequest.initialize(usbConnection, device.getInterface(0).getEndpoint(0));//communication from card scanner
//In handler:
byte[] byteArray = new byte[16];
int transfer = usbConnection.bulkTransfer(device.getInterface(0).getEndpoint(0), byteArray, 16, 300);
transfer result is = -1, did anyone has the same issue?
Assumption: Your USB device conforms to the USB CCID Device Class. If this is not true, you will need to provide the datasheet for your specific card reader.
Before you can execute any communications with USB endpoints, your app has to claim the interface first. In other words, there should be a call to claimInterface() in your code before any requests are sent.
Next, you are mixing two different forms of communication. Your app should EITHER use UsbRequest for asynchronous use OR bulkTransfer() for synchronous use. You don't need to initialize a UsbRequest if you are using bulkTransfer() instead.
You should verify what the max packet size of your UsbEndpoint is. Bulk CCID endpoints can support up to 512 byte packets, so a fixed length of 16 bytes may not be enough to read a full packet response (assuming this transaction was on the Bulk IN endpoint). Match your array size with your endpoint's packet size.
I'm trying to get a Xamarin app to receive around 10 kbps of SPP data through bluetooth from our custom PCB. The BC127 module on the PCB can send a maximum packet size of 255 bytes, so the app needs to receive a packet every ~25ms. I'm using the packets to separate the data, so they need to be received one at a time.
The test app is constantly trying to receive data, however sometimes when it arrives back at the start of the receive method, there are 2+ packets waiting in the receive stream. My question is, what is causing the huge delays in my bluetooth receive method? I've attached the method below. After connecting to the bluetooth module, the app does nothing but run this method in a while(true) loop.
public byte[] ReadBluetoothPacket()
{
int bytesAvailable = 0;
while (bytesAvailable == 0) // Wait for packet to arrive
{
bytesAvailable = stream.BaseInputStream.Available(); // Find out how big the packet is
}
byte[] result = new byte[bytesAvailable];
_socket.InputStream.Read(result, 0, bytesAvailable); // Add the bytes to the buffer
return result;
}
I believe the issue was due to the Android OS doing background tasks which were holding up my receive thread. As it turned out to only be an intermittent problem, rewriting the data receiver to handle multiple packets at once solved it.
I'm using sockets for data transfer from one android phone and PC, I am using DataInputStream.
But it takes a long time in data transfer, about 10 minutes to transfer a 4 MB file.
Could anyone suggest any better way to do that?
I did some changes in my code and now it is taking 15 seconds to read about 1 Mb of data. I want to improve its performance.
My Code is:
InputStream is= socket.getInputStream();
DataInputStream inChannel= new DataInputStream(new BufferedInputStream(in));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int oneByte;
while ((oneByte = inChannel.read()) != -1) {
if (oneByte == 0) {
break;
}
baos.write(oneByte);
byteCount++;
}
byte[] inData = baos.toByteArray();
baos.close();
Are you sure you are not sending empty packets in between there or packets doing something else? If you are using TCP if a packet doesn't reach its destination (a buffer is overfilled with other packets somewhere in between your connection on the router or on one of the devices) the packet will have to be resent. This might be because of your setup. Given the little information you have given us, I can give the the following advice:
Look into more advanced typologies, I am going to assume you are not using any of these:
A send a receive thread; the send thread pushes out packets which you put into a queue:
|P| - packet held inside a Que part of your program
send out thread <-| P | P | P | <-App/program
reveive -> | P | P | P| P -> App/Program deques and analyses data
because your send and receive queuing system works along side each other they are constantly buffering up the received and sent packets. What seems to be happening with your setup (as far as I assume) is that you have a loop which grabs the latest packets and analyses them. This means that the hardware (part of your network hardware) buffer is overfilling with packets which your program is doing other things and by the time your program comes around to collect them, some of them are missing. This causes one side to go "I didn't get those packets, send them again", in other words, your resending the packets you should of sucked up into a Que which are waiting to be dequeued and analysed. The queue can grow regardless of how quickly the program is back to grab that data and do stuff with it (of course you are confined to the RAM). That way you ensure that you have possession of packets that you are supposed to receive rather than rely on your network card/receiver to hold on to them for you, possibly overflowing its buffer.
Another approach is to do a handshake system were one of the programs waits until the last packet is trasmitted, recieved and the other side goes "Cool, send me the next one". This slows down your download/upload speed but is a little bit quicker than packets falling off the end of the end of one of the buffers (each node, such as your router buffers your packets in case more come in than it can handle at a cycle) in the network.
You should utilize a state machine if you can on one or both ends. When your app is downloading the file, lock it into a receive state so its not trying to send/receive other stuff at the same time. Once the download is complete, switch to any other state (say, open file state). If you don't know much about state machines, I recommend you look at the wikipedia article:
http://en.wikipedia.org/wiki/Finite-state_machine
I am writing software to communicate between tablet (Motorola Xoom with Android version 4.0.3 and Kernel version 2.6.39.4) and a peripheral device using USB Host API provided by Android. I use only two types of communication:
control: controlTransfer(int requestType, int request, int value, int index, byte[] buffer, int length, int timeout)
bulk: bulkTransfer(UsbEndpoint endpoint, byte[] buffer, int length, int timeout)
Control transfer works fine, but I have a problem with bulk transfer. I can use only 32768 as a size of the buffer for bulkTransfer function. It is not possible to use less or more. I know that I cannot use more because of the limit of the buffer pipe (size: 32769 bytes).
This peripheral device streams data which is not correctly read by bulkTranfer function. I suppose that some data is lost.
I find this: In Linux If a process attempts to read from an empty pipe (buffer), then read(2) will block until data is available. If a process attempts to write to a full pipe , then write(2) blocks until sufficient data has been read from the pipe to allow the write to complete.
And based on that, my explanation of the problem is that some data is not written to pipe (buffer) because of blocking flag made by write(2) function. Am I correct? If this is true I could change pipe buffer.
My first solution for this problem is greater buffer. For
kernel >= 2.6.35, you can change the size of a pipe with fcntl(fd, F_SETPIPE_SZ, size) but how can I find fd (file descriptor) for
USB pipes?
Second option is to use ulimit -p SIZE but parameter p for my kernel is not for pipe but process.
Has anyone faced the same problem, any solutions?
You should get a USB data analyzer, Im using this one: http://www.ellisys.com/products/usbex200/index.php
Using something like this really helped me when I was doing the same type of thing, what I found was that you had to do some type of while loop.
For my device I had 64 bytes of data coming in packets to me, the packet would be two control bytes and 62 for data, so for my transfer I had to do something like
StringBuilder sb = new StringBuilder();
while(bulkTransfer(UsbEndpoint endpoint, byte[] buffer, int length, int timeout) > 2){
for(int i = 2; i < buffer.length(); i++){
sb.append((char) buffer[i]);
}
}
something a long these lines worked good for me, i had exactly the same issue and this is how I fixed it. I have more information if you need it. Just comment :). I know this was really frustrating for me to. Im using an Acer Iconia A500 with Android 4.0.3 btw
Transferring larger amounts of data USB
Transferring data USB
According to the same .pdf gfour posted, I found this paragraph in there:
"The size of the packet will affect the performance and is dependent on the data rate. For very high speed, the largest packet size is needed. For 'real-time' applications that are transferring audio data at 115200 Baud for example, the smallest packet possible is desirable, otherwise the device will be holding up 4k of data at a time. This can give the effect of 'jerky' data transfer if the USB request size is too large and the data rate too low (relatively)."
I'm running into a situation similar to SmartLemon with a FTDI Serial device, so I've been recently looking up ways to alleviate it. This will require me to pretty much write my own functions from scratch while previously I was using a library.
However, it seems that in your case, you could try using bulkTransfer's lowest buffer size instead of shooting for the biggest. You may have tried this already, but maybe not. I see that you say 32768 is the only size, but maybe you only meant the max. It seems weird that it'd only allow one specific size.
The Android SDK's UsbEndpoint object provides a getMaxPacketSize() method, allowing you to check what is appropriate for your device. Generally, the maximum allowable packet size is 64 bytes for USB 'Full-speed' devices and 512 for 'High-speed' devices - far less than 32,768 you are attempting. Are you perhaps confusing the underlying USB packet size with that of some higher-level protocol?
According to AN232B-04_DataLatencyFlow.pdf, flow control is needed for high data rates:
It is strongly encouraged that flow control is used because it is
impossible to ensure that the FTDI driver will always be scheduled.
Have you tried using one of the flow control options (RTS/CTS, DTR/DSR, XON/XOFF) to synchronize your data?
You can try this for Bulk transfer issue
byte[] buffer = new byte[4096];
StringBuilder strIN = new StringBuilder();
if (conn.bulkTransfer(epIN, buffer, 4096, 500) >= 0) {
for (int i = 2; i < 4096; i++) {
if (buffer[i] != 0) {
strIN.append((char) buffer[i]);
textReceiveDataInfo.append(strIN + "\n");
} else {
l(strIN);
break;
}
}
}