Android app reading bluetooth socket can't keep up with data rate - android

I have a bluetooth GPS that outputs data at around 2000bytes/sec. When I first start my app it is able to keep up with this rate, but within about 5-10 seconds the rate falls all the way down to 500bytes/sec. From there it goes up and down (between 300bytes/sec and 700bytes/sec usually, but I've seen as high as 6000bytes/sec as a quick spike when it tries to catch up). The stream just falls further and further behind and data ends up getting dropped (the GPS is outputting 10 samples per second and it gets to the point where I will miss several seconds worth of data).
When I connect to this same device via bluetooth from my laptop I get all the data no matter how long it runs. So I know the device itself is able to transmit at this rate. But on android (HTC Droid DNA) it falls behind right away. I have tried bumping up the thread priority and that didn't help. The app stays in the foreground the entire time with the screen on. I have also tried it without the phone plugged into the debugger just in case that was slowing things down and it's still the same issue. I don't know if this is a bluetooth stack speed issue, or a thread priority issue or what. Any ideas?
UPDATE: I just tested the same code on my Galaxy Tab 10.1 and it is able to maintain around 2000 bytes/sec indefinitely. I then tested on an old Motorola Photon 4G and it also is able to maintain the data rate. On the Droid DNA I tested with WiFi disabled as well to see if that was hurting bluetooth performance but it didn't make a difference. And because the DNA is able to do the higher rate for 5-6 seconds I would think the hardware has the capability. For some reason it just falls off after that...
OutputStream mmOutputStream;
InputStream mmInputStream;
UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); //Standard SerialPortService ID
mmSocket = mmDevice.createRfcommSocketToServiceRecord(uuid);
mmSocket.connect();
mmOutputStream = mmSocket.getOutputStream();
mmInputStream = mmSocket.getInputStream();
dataReader();
...
...
void dataReader()
{
worker = new Thread(new Runnable()
{
public void run()
{
int priority = Process.getThreadPriority(Process.myTid());
Log.d("testApp", String.format("data thread priority %d", priority));
Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY);
priority = Process.getThreadPriority(Process.myTid());
Log.d("testApp", String.format("data thread priority %d", priority));
int bufferSize = 1024;
byte[] readBuffer = new byte[bufferSize];
long time1 = System.currentTimeMillis();
long time2 = 0;
long datacount = 0;
while(!Thread.currentThread().isInterrupted() && !stopWorker)
{
try
{
// read what we can
int bytesRead = mmInputStream.read(readBuffer);
datacount += bytesRead;
time2 = System.currentTimeMillis();
// every second output the data rate
if (time2 - time1 > 1000)
{
final float rate = ((float)datacount * 1000.0F) / (float)(time2 - time1);
handler.post(new Runnable() {
public void run()
{
String text = String.format("%.1f bytes/sec", rate);
myLabel.setText(text);
}
});
time1 = time2;
datacount = 0;
}
}
catch (IOException ex)
{
stopWorker = true;
}
}
}
}
}

Related

Android Slow Bluetooth RFCOMM Transfer Rate with RN4678

We are experimenting with a bunch of new tablets, and every one we tried is having issues with slow transfer rates with the RN4678 board. We currently use the Lenovo M10 FHD Plus. We tried a few such as the Teclast M40S, Nokia T20, and Samsung Galaxy Tab A8. The first two had horrible transfer rates, while the latter was okay but not ideal. We cannot use the Lenovo M10 Plus 3rd Gen because the buttons are too close to the corner to use with our tablet holders.
Here is my code:
public void SendMessage(BluetoothSocket socket, String msg) {
OutputStream outStream;
try {
outStream = BluetoothConnectionService.outputStream;
outStream.write("S".getBytes());
Thread.sleep(4000);
processThread = true;
mApp.running = true;
BluetoothSocketListener bsl = new BluetoothSocketListener(socket,
CollectingDetail.this);
Thread messageListener = new Thread(bsl);
messageListener.start();
timer = new CounterClass(remaingTime, 1000);
timer.start();
bt_stop.setText("Stop");
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
registerReceiver(bluetoothReceiver, filter);
bluetoothReceiver.setRegistered(true);
} catch (IOException | InterruptedException e) {
Log.e("BLUETOOTH_COMMS", e.getMessage());
connectSocket();
}
}
public static class BluetoothSocketListener implements Runnable {
private final WeakReference<CollectingDetail> wrActivity;
private BluetoothSocket socket;
public BluetoothSocketListener(BluetoothSocket socket, CollectingDetail collectingDetail) {
this.socket = socket;
wrActivity = new WeakReference<CollectingDetail>(collectingDetail);
}
#Override
public void run() {
final CollectingDetail activity = wrActivity.get();
if (activity != null) {
activity.inStream = null;
if (!Thread.currentThread().isInterrupted()) {
int bufferSize = 512;
byte[] buffer = new byte[bufferSize];
Log.i("Bluetooth bytes", new String(buffer));
activity.inStream = BluetoothConnectionService.inputStream;
int availableBytes;
int bytesRead = -1;
String message = "";
while (activity.processThread) {
message = "";
try {
availableBytes = activity.inStream.available();
if (availableBytes > 0) {
bytesRead = activity.inStream.read(buffer);
if (bytesRead != -1 && bytesRead < bufferSize) {
message = new String(buffer, 0, bytesRead);
if (activity.mainHandler != null) {
activity.mainHandler.post(new MessagePoster(message, activity));
}
}
}
} catch (IOException e) {
Log.e("BLUETOOTH_COMMS", "Error reading bytes");
try {
socket.close();
} catch (IOException e1) {
Log.e("BLUETOOTH_COMMS", "Could not close socket");
}
activity.processThread = false;
}
}
}
}
}
}
public void seprateData(String message) {
try {
message = message.replaceAll("(\\r\\n|\\n|\\r)", ",");
String[] a = message.split(",");
boolean goodData = false;
for (int i = 0; i < a.length; i++) {
final String data = a[i];
if (data.length() > 0 && !data.equals(" ")) {
if (data.length() >= 10 && data.startsWith("5A")) {
al_sepratedMessageList.add(data);
goodData = true;
}
}
}
if (goodData) {
calculation();
if (ConnectThrough.equalsIgnoreCase("usb")) {
UsbConnectionSerivce.sendMessage("K");
} else {
BluetoothConnectionService.sendMessage(socket, "K");
}
}
} catch (Exception e) {
Log.e("BiSym", "Error Parsing BiSym Data");
}
}
Is there any way we can increase the transfer rate without changing the firmware? It appears others have faced similar problems, but none of the answers have a real solution. Could you please help me out. Thanks.
I fear this may not be software-solvable and may be an issue with BT hardware or firmware. How would I communicate with my boss about this?
I fear this may not be software-solvable and may be an issue with BT hardware or firmware. How would I communicate with my boss about this?
The difference is in the quality of the filtering of the signal, a better filter, narrower bandwidth, means lower Signal to Noise Ratio. Lower SNR means faster transfer.
Better analog filters, mean more components and slightly more cost and loss, however, due to the wide-band nature of Bluetooth, most analog filters can only filter out of band signals (nearby AM/FM/TV broadcasters).
In addition to the analog filters, digital filters are applied to the signal to narrow the bandwidth within the band, this technique incurs little loss, but requires more processing power to be included in the chip, more transistors, more costs.
The order of the filter and the type FIR or IIR determine the characteristics of the filer.
Most designers will minimize the cost to meet the minimum specifications, some will balance the cost versus performance and go further, you never know.
You tell your boss, the the better platforms perform digital filtering well beyond what the Bluetooth specification requires.
I just tested the Teclast M40 Plus which doesn't have this problem.
Something wants to make me believe it is an issue with the UNISOC Bluetooth stack. The Teclast M40 Plus has MediaTek which doesn't have this issue.
EDIT: Also tested on Lenovo M10 Plus 3rd Gen with MediaTek Helio G80, no issue. If we have to use it, we may need a new tablet holder.
Have you repeated the tests? Bluetooth uses the same frequencies as 2.4 GHz Wifi and microwave ovens. In a congested Wifi environment, and/or too many Bluetooth connections(mice, keyboards, headphones, speakers, watches ...), slowdowns are normal for high-speed transfers.
Wifi 6 MIMO utilizes all three distinct channels in the 2.4G band, (1,6,11) of the 12 overlapping. Higher bandwidth/speeds are available in 5.4G (and above), but wall penetration and signal propagation factors keep 2.4G as the primary band in the absence of other possibilities.
There is only so much space in the 2.4G band that Bluetooth operates.
Repeat your tests in an area without interference and see if you get the same results.
A cheap isolation method is to build a Faraday Cage out of .25 inch chicken wire fencing. Isolate the sender and receiver inside of the cage and measure the throughput.
In an uncontrolled environment, hundreds to thousands of tests for each device are required across different time spans to establish a true baseline measurement. You never know when you neighbor is going to start or stop a variable bit-rate video, or move a mouse.Every action contributes to the background noise in the band.

Android Bluetooth Increase Speed Performance

I am trying to increase the transfer speed on android bluetooth. I experimented transferring a 2.7MB buffer from one android device to another using RFComm socket of the Bluetooth API (see code below). It took ~70 secs to complete. I compared this method against the "Share" via bluetooth function that came with the phone. The "Share" function gave exceptionally better performance (~15 secs to transfer a 2.7MB file).
How does the "Share" function differ from using the Bluetooth API? How can I replicate the "Share" method to get optimized transfer speed?
Thanks,
Bluetooth API test code:
Server side - installed on one android device
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord("DeviceName", MY_UUID);
socket = mServerSocket.accept();
mInStream = socket.getInputStream();
int totalByte = 1;
while (totalByte < 2718720)
{
int bytesAvailable = mInStream.available();
if (bytesAvailable > 0) {
totalByte += bytesAvailable;
byte[] buffer = new byte[bytesAvailable];
mInStream.read(buffer);
}
}
Client side - installed on other android device
mClientSocket = device.createInsecureRfcommSocketToServiceRecord(
MY_UUID);
mClientSocket.connect();
mOutStream = mClientSocket.getOutputStream();
byte byteValue = 0;
for (int i=0; i<2718720; i++) {
byteValue++;
mOutStream.write(byteValue);
}

Internet Speed in android programming

I want to write an app that showing internet speed in notification bar, i tried this code but it only show the link speed not real speed at the moment
WifiInfo wifiInfo = wifiManger.getConnectionInfo();
int speedMbps = wifiInfo.getLinkSpeed();
How can i get the exact internet speed throw wifi or 3G ?
At first, I thought you meant you were interested in seeing maximum download/upload speed, such as the info that Speedtest.net provides. Now I believe you meant you are interested in obtaining the current network traffic usage. I've left the explanation relating to my original assumption at the bottom.
New answer (if you are interested in obtaining current upload/download traffic):
Look into the TrafficStats class. This has functions such as getMobileRxBytes() and getMobileTxBytes() which give the number of bytes received and transmitted since boot, respectively. You can get these values every second, then do a calculation to find the difference per second (or, "bytes per second").
// set this to true when you want it to stop
boolean mStopHandler = false;
Runnable runnable = new Runnable() {
#Override
public void run() {
// complete calculations
if (!mStopHandler) {
mHandler.postDelayed(this, 1000); //runs every second
}
}
};
// begin task
mHandler.post(runnable);
Original answer (assuming you are interested in maximum download/upload speed):
This is not possible in the way that you intend. The internet speed is the rate of data transfer between your device and a destination server that you specify. When you go to Speedtest.net, you are sending information to their servers and receiving from their servers as fast as possible, and it tells you the rate that it is detecting. To be able to see real-time speed statistics, you would need to be constantly communicating with a remote server. (Additionally, this server would need to be able to both download and upload faster than the client could, otherwise you end up testing the maximum transfer rate of the server's internet connection, and not the client's!).
Try this code, Iam using it with my app
public class MainActivity extends AppCompatActivity {
final double [] RXOld = new double [1];
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
////////////////////////Code to be executed every second////////////////////////////////////////
double overallTraffic = TrafficStats.getMobileRxBytes();
double currentDataRate = overallTraffic - RXOld [0];
TextView view1 = null;
view1 = (TextView) findViewById(R.id.view1);
view1.setText("Current Data Rate per second= " + currentDataRate);
RXOld [0] = overallTraffic;
handler.postDelayed(this, 1000);
}
}, 1000 );
You can get an answer nere:Getting data speed of wifi/mobile network programatically.
There's no method that can return directly the real speed of the network.

Android BLE API strange behavior

I connect to my Bluetooth Low Energy device like it's described in : https://developer.android.com/guide/topics/connectivity/bluetooth-le.html
I get device:
#Override
public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
if(device.getName().startsWith("BLE device")){
mDevice = device;
mDevice.connectGatt(RGBLight.this, false, bgc);
}
}
Getting a gatt from Device:
public void onConnectionStateChange(final android.bluetooth.BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
//My app should keep connection to the BLE device as long as app lives.
RGBLight.this.gatt = gatt;
gatt.discoverServices();
}
}
3.When gatt is connected and services are discovered i'm trying to send a messages queue to the characteristic.
new Thread(new Runnable() {
#Override
public void run() {
if (mService == null)
mService = gatt.getService(UUID_SERVICE);
if (mCharacteristic == null)
mCharacteristic = mService.getCharacteristic(UUID_CHAR);
for(int i = 0; i < 100; i++){
int r = Color.red(colorParsed);
int g = Color.green(colorParsed);
int b = Color.blue(colorParsed);
int br = Color.blue(brParsed);
mCharacteristic.setValue(new byte[] { COMMAND_SET_RGBW, (byte) r, (byte) g, (byte) b, (byte) br, 0, 0, 0, 0 });
gatt.writeCharacteristic(mCharacteristic);
TimeUnit.MILLISECONDS.sleep(100);
}
}
}).start();
When i run this code, most of commands are missed. Execution speed is about 1 command per second. And there is an error in LogCat:
06-23 12:34:25.627: E/bt-btif(18002): already has a pending command!!
This low speed worsens user experience of my app.
I investigated a few days and found very interesting behavior. If I start the app and quickly send message queue, it works fast.(1 command per 100ms). But after 10-15 seconds after start it begin to slow down and error message occures again:
06-23 12:34:25.627: E/bt-btif(18002): already has a pending command!!
Maybe someone already faced with a such problem and there is a way to reset a message queue with Android API or something else.
You MUST wait for onCharacteristicWritten before you continue or you flood the buffers. That gatt.write characteristic returns merely means it is on its way through the ble stack and air.

Bluetooth on 2.0+

I'm doing bluetooth development for connecting with a PC. I've basicly used the BTChatExample and changed the UUID to the standard PC SPP-profile.
Trying to close a bluetooth application during a blocking read, by closing the BluetoothSocket will leave the Bluetooth stack in a unusable state. This can only be fixed by disabling and enabling bluetooth and restarting the application. Checking logcat, you can see that some of the internal methods are failing, leaving a open port. Any information on this?
First of all there seams to be differences on how bluetooth is implemented on N1 and HTC Legend/Desire both running 2.1, do you know anything about this?
Connecting isn't 100% reliable, sometimes I get a warning saying ~PortSystemContext init: FAILED. This leaves bluetooth unusable, and restarting is needed.
Am I right in assuming that SPP is the only profile supported for use with the APIs? That's what the docs on the BluetoothAdapter says.
I would love to discuss issues on bluetooth with a developer and iron out these bugs so that Android can have good proper Bluetooth support it deserves.
Closing a socket in one thread during a blocking read should definitely cause the read to return (by throwing IOException) and should not leave the stack in a 'bad state'. This is the behavior on Droid and Nexus.
I spoke directly to the original poster, and he had observed this problem on HTC Legend and HTC Desire. It appears like they are not implementing the API's correctly. I am raising the issue with them.
You are correct that SPP/RFCOMM is the only profile that is intended for use with the API's. SPP/RFCOMM gets you a streaming socket which is good enough for a lot of use cases.
For now I recommend BT development on Nexus One / Motorola Droid, which are considered 'reference' implementations of the Bluetooth API's.
May I suggest that you do not perform blocking read() calls unless you have first checked that there is data ready to be read by using inputstream.available() which returns an integer indicating how many bytes are waiting in the input buffer.
long timeouttime = gettimeinseconds() + 2;
String response = "";
while (gettimeinseconds() < timeouttime) {
if (inputstream.available() > 0)
response = response + inputstream.read();
} else {
Thread.sleep(100); // sleep to slow down the while() loop.
}
}
return response;
That's just pseudo code, and its oversimplified. The bottom line is that we're not performing any blocking calls (read()) unless we're sure they will return immediately without delay.
Also, I highly recommend using BufferedInputStream instead of the standard InputStream.
Anyone could solve this problem ?
I try the following code :
// Keep listening to the InputStream while connected
while (!isInterrupted)
{
try
{
//Clear buffer
buffer = new byte[1024];
// Read from the InputStream
if (mmInStream != null && mmInStream.available() > 0)
{
if (isInterrupted)
break;
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(Act_Main.MESSAGE_READ, bytes, -1, buffer).sendToTarget();
}
else
{
try
{
synchronized (this)
{
this.wait(100);
}
if (isInterrupted)
break;
}
catch(InterruptedException ex)
{
Log.e(TAG, "WAIT_EXCEPTION:"+ ex.getMessage());
}
}
}
catch(Exception ex)
{
Log.e(TAG, "disconnected", ex);
connectionLost();
break;
}
}
And I changed the isInterrupted boolean in the cancel() method. Here is my stop() method:
/**
* Stop all threads
*/
public synchronized void stop()
{
isStop = true ;
if (D)
Log.d(TAG, "stop");
if(mConnectThread != null)
{
mConnectThread.cancel();
mConnectThread = null;
}
if(mConnectedThread != null)
{
mConnectedThread.cancel();
mConnectedThread = null;
}
setState(STATE_NONE);
}

Categories

Resources