Internet Speed in android programming - android

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.

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 - Fused Location Provider API issues with satellite information (count, signal, etc)

I am working on a project where we are trying to track the position of a device and keep the data for later use. Before I talk about the issue I would like to provide some background.
By searching around StackExchange and Google and everywhere else, I have come to the conclusion that it is virtually impossible to get information about the satellites using the Fused Location API (good job there Google).
The method that most people are using is to actually use a LocationManager along side the Fused location to get the GPS Status. My first question comes here:
How can we be 100% sure that the numbers provided by the LocationManager are in sync with what the Fused Location has given us? Does the Fused Location use the Manager internally?
And now the issue. The app is using an "always on" sticky service to pick up the positions no matter what. When there are no satellites everything works as intended. Placing the device to a position where it can see satellites it does not seem to have a lock. Using the debugger the GpsStatus.getSatellites() brings an empty list. Now, without moving the device I start the app Compass (by Catch.com as there are many) that has a GPS type compass scheme. That one locks the satellites, and quite fast, and from that moment on my app also reports the satellites. If the compass is closed then the app gets stuck on the last number the Compass was providing!!! The device I am personally using for testing is a Nexus 7 2013 with its latest official updates (Android 6.0.1).
Here is some code:
public class BackgroundLocationService extends Service implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
GpsStatus.Listener,
LocationListener {
// Constants here....
private GoogleApiClient mGoogleApiClient;
private LocationRequest mLocationRequest;
private LocationManager locationManager;
// Flag that indicates if a request is underway.
private boolean mInProgress;
private NotificationManagement myNotificationManager;
private Boolean servicesAvailable = false;
//And other variables here...
#Override
public void onCreate()
{
super.onCreate();
myNotificationManager = new NotificationManagement(getApplicationContext());
myNotificationManager.displayMainNotification();
mInProgress = false;
// Create the LocationRequest object
mLocationRequest = LocationRequest.create();
// Use high accuracy
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// Set the update interval
mLocationRequest.setInterval(PREFERRED_INTERVAL);
// Set the fastest update interval
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
servicesAvailable = servicesConnected();
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
locationManager.addGpsStatusListener(this);
setUpLocationClientIfNeeded();
}
/**
* Create a new location client, using the enclosing class to
* handle callbacks.
*/
protected synchronized void buildGoogleApiClient()
{
this.mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
private boolean servicesConnected()
{
// Check that Google Play services is available
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
// If Google Play services is available
if (ConnectionResult.SUCCESS == resultCode)
{
return true;
}
else
{
return false;
}
}
public int onStartCommand(Intent intent, int flags, int startId)
{
super.onStartCommand(intent, flags, startId);
if (!servicesAvailable || mGoogleApiClient.isConnected() || mInProgress)
return START_STICKY;
setUpLocationClientIfNeeded();
if (!mGoogleApiClient.isConnected() || !mGoogleApiClient.isConnecting() && !mInProgress)
{
mInProgress = true;
mGoogleApiClient.connect();
}
return START_STICKY;
}
private void setUpLocationClientIfNeeded()
{
if (mGoogleApiClient == null)
buildGoogleApiClient();
}
public void onGpsStatusChanged(int event)
{
}
// Define the callback method that receives location updates
#Override
public void onLocationChanged(Location location)
{
simpleGPSFilter(location);
}
// Other fancy and needed stuff here...
/**
* "Stupid" filter that utilizes experience data to filter out location noise.
* #param location Location object carrying all the needed information
*/
private void simpleGPSFilter(Location location)
{
//Loading all the required variables
int signalPower = 0;
satellites = 0;
// Getting the satellites
mGpsStatus = locationManager.getGpsStatus(mGpsStatus);
Iterable<GpsSatellite> sats = mGpsStatus.getSatellites();
if (sats != null)
{
for (GpsSatellite sat : sats)
{
if (sat.usedInFix())
{
satellites++;
signalPower += sat.getSnr();
}
}
}
if (satellites != 0)
signalPower = signalPower/satellites;
mySpeed = (location.getSpeed() * 3600) / 1000;
myAccuracy = location.getAccuracy();
myBearing = location.getBearing();
latitude = location.getLatitude();
longitude = location.getLongitude();
Log.i("START OF CYCLE", "START OF CYCLE");
Log.i("Sat Strength", Integer.toString(signalPower));
Log.i("Locked Sats", Integer.toString(satellites));
// Do the math for the coordinates distance
/*
* Earth's radius at given Latitude.
* Formula: Radius = sqrt( ((equatorR^2 * cos(latitude))^2 + (poleR^2 * sin(latitude))^2 ) / ((equatorR * cos(latitude))^2 + (poleR * sin(latitude))^2)
* IMPORTANT: Math lib uses radians for the trigonometry equations so do not forget to use toRadians()
*/
Log.i("Lat for Radius", Double.toString(latitude));
double earthRadius = Math.sqrt((Math.pow((EARTH_RADIUS_EQUATOR * EARTH_RADIUS_EQUATOR * Math.cos(Math.toRadians(latitude))), 2)
+ Math.pow((EARTH_RADIUS_POLES * EARTH_RADIUS_POLES * Math.cos(Math.toRadians(latitude))), 2))
/ (Math.pow((EARTH_RADIUS_EQUATOR * Math.cos(Math.toRadians(latitude))), 2)
+ Math.pow((EARTH_RADIUS_POLES * Math.cos(Math.toRadians(latitude))), 2)));
Log.i("Earth Radius", Double.toString(earthRadius));
/*
* Calculating distance between 2 points on map using the Haversine formula (arctangent writing) with the following algorithm
* latDifference = latitude - lastLatitude;
* lngDifference = longitude - lastLongitude;
* a = (sin(latDifference/2))^2 + cos(lastLatitude) * cos(latitude) * (sin(lngDifference/2))^2
* c = 2 * atan2( sqrt(a), sqrt(1-a) )
* distance = earthRadius * c
*/
double latDifference = latitude - lastLatitude;
double lngDifference = longitude - lastLongitude;
double a = Math.pow((Math.sin(Math.toRadians(latDifference / 2))), 2) + (Math.cos(Math.toRadians(lastLatitude))
* Math.cos(Math.toRadians(latitude))
* Math.pow((Math.sin(Math.toRadians(lngDifference / 2))), 2));
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
double distance = earthRadius * c;
Log.i("New point distance", Double.toString(distance));
// Filter logic
// Make an initial location log
if ((!isInit) && (myAccuracy < ACCEPTED_ACCURACY))
{
isInit = true;
lastLatitude = latitude;
lastLongitude = longitude;
logLocations(location);
}
else
{
// Satellite lock (use of GPS) on the higher level
if (satellites == 0)
{
// Accuracy filtering at the second level
if (myAccuracy < ACCEPTED_ACCURACY)
{
if ((distance > ACCEPTED_DISTANCE))
{
lastLatitude = latitude;
lastLongitude = longitude;
logLocations(location);
Log.i("Location Logged", "No Sats");
/*
// Calculate speed in correlation to perceived movement
double speed = distance / (PREFERRED_INTERVAL / 1000); // TODO: Need to make actual time dynamic as the fused location does not have fixed timing
if (speed < ACCEPTED_SPEED)
{
lastLatitude = latitude;
lastLongitude = longitude;
logLocations(location);
} */
}
}
}
else if ((satellites < 4) && (signalPower > ACCEPTED_SIGNAL))
{
if (myAccuracy < (ACCEPTED_ACCURACY + 50))
{
logLocations(location);
Log.i("Location Logged", "With Sats");
}
}
else
{
if (myAccuracy < (ACCEPTED_ACCURACY + 100))
{
lastSpeed = mySpeed;
lastBearing = myBearing;
lastLatitude = latitude;
lastLongitude = longitude;
logLocations(location);
Log.i("Location Logged", "With Good Sats");
}
}
}
Log.i("END OF CYCLE", "END OF CYCLE");
}
private void logLocations(Location location)
{
String myprovider = "false";
String temp = timestampFormat.format(location.getTime());
MySQLiteHelper dbHelper = new MySQLiteHelper(getApplicationContext());
try
{
dbHelper.createEntry(latitude, longitude, allschemes, temp, mySpeed, myAccuracy, myBearing, myprovider, satellites);
}
catch (Exception e)
{
e.printStackTrace();
}
CheckAutoArrive(String.valueOf(latitude), String.valueOf(longitude));
}
This is the part of the code I think might be needed. I am leaving all the filtering code there along with the math to compute Earth's radius given the latitude and the distance between 2 points on the map. Feel free to use that if you need it.
In connection to the fact that the Compass app can actually make the system get satellites while my app cannot. Is there a way to actually force read the location services? Is it possible that the Fused Location actually uses GPS but the Location Manager does not know it?
Finally I would like to mention that the application has been tested in other devices (phones, not tablets) with different versions of Android and seems to be working properly.
Any ideas would be more than welcome. And of course go ahead and ask anything I might have forgotten to mention.
EDIT : My actual questions were hidden in the text so to lay them out:
1) Are the Location data we get from Fused Location and the rest of the GPS data we can, seemingly, only get from the Location Manager in sync or is there the possibility to get a Location but wrong number of locked satellites for the particular point?
2) What could be the reason behind the weird behavior where the application cannot get a lock to satellites but if the lock comes from another application it seems to be used properly by the application? To make this even weirder this happens to a Nexus 7 (Android 6.0.1) but not to other devices tested with different Android versions.
From my understanding:
1)
The FusedLocationApi returns a new location every time there is a new reading from any relevant provider on the client device (WiFi, CellTower, GPS, Bluetooth). This reading gets fused with the previous location estimate (probably using an extended Kalman filter or similar). The resulting location update is a fused estimate of several sources, which is why there is no metadata from individual providers attached.
So the Location data you get from the API may coincide with a pure GPS reading obtained from the LocationManager (if GPS was the most recent and relevant location source), but it doesn't have to. Consequently, the number of satellites obtained from the last pure GPS reading may apply to the latest Location returned by the FusedLocationApi or it may not.
In short:
There is no guarantee that a location reading obtained from the LocationManager is in sync with a location from the FusedLocationApi.
2)
First off: To pinpoint the root cause of this issue you need to test with several devices at several locations. Since you asked
What could be the reason behind the weird behavior?
I'll throw one theory out there: Assuming that the LocationManager and the FusedLocationApi work completely separately, the LocationManager may be struggling to obtain a fix because you're relying on GPS only.
Try using the NETWORK_PROVIDER in addition to GPS to speed up the time-to-first-fix (thus enabling the LocationManager to make use of Assisted GPS). Other apps (like the Compass app) are almost certainly doing this, which would explain why they are getting a quicker fix.
Note: After you start to receive GPS data, you can of course unregister the network provider. Or you keep it on but simply ignore its updates.
That's one possible explanation for the weird behavior. You are probably aware that location behavior is dependent on the device, OS, GPS chipset, firmware and location you are in - so if you plan to go manual (i.e. not use the FusedLocationApi) you will have to experiment quite a bit.
In addition to the answers, let me provide an opinionated take on your problem (to be taken with a grain of salt ;-): I think you're trying to combine two things that were made for very different use cases and aren't meant to be combined.
Obtaining the number of satellites is something utterly technical. No end user will ever be interested in this kind of info, unless your application teaches them about GNSS. If you want to record it for internal analytics purposes that's fine, but then you must be able to deal with situations where this info is not available.
Scenario 1: For some reason you decide that you absolutely need the detailed (technical) specifics of GPS readings. In that case, build the logic yourself, the old school way. I.e. ask for GPS readings (and possibly speed up this process by using the network provider) via the LocationManager, then fuse this stuff on your own. However, in this scenario don't ever touch the FusedLocationApi.
Even though it may seem old-fashioned and arcane nowadays, using the LocationManager with DIY fusion logic still makes perfect sense for a small number of use cases. That's why the API is still around.
Scenario 2: You simply want to get quick and accurate updates on the client's location. In that case specify your desired update frequency and accuracy and let the FusedLocationApi do its job. The FusedLocationApi has come a long way in the past years and chances are that nowadays it will be quicker and better at figuring out how to obtain location info than any DIY logic. This is because obtaining location info is a very heterogenous problem that depends on the capabilities of the client device (chipset, firmware, OS, GPS, WiFi, GSM/LTE, Bluetooth etc.) as much as on the physical surroundings (WiFi/CellTower/Bluetooth signals nearby, inside or outside, clear sky or urban canyon etc.).
In this scenario, don't touch the manual providers. If you do, don't expect to make any meaningful inferences on the relation between the readings of individual providers and the fused results.
Two final remarks:
Android provides Location.distanceTo() and Location.distanceBetween(), so there is no need to implement the Haversine formula in your code.
If you simply need quick and reliable updates from the FusedLocationApi, I have written a small utility class, called the LocationAssistant, that simplifies the setup and does most of the heavy lifting for you.

BLE Android, can't enable more than 1 notify on read characteristics

I'm developing an Android application which opens a BLE connection between the Android device and a BLE pheripheral (a simple transmitter).
The peripheral is programmed to have multiple reading characterics which I found.
The problem shows up when I try to enable the notification.
The first always returns true, and than it starts to trigger my notify callback, the others always return a false value.
List<BluetoothGattDescriptor> descrittoriDellaChar = getListaDescrittoriDaCharact(charact);
Boolean status = null;
for (int i = 0; i < descrittoriDellaChar.size(); i++) {
BluetoothGattDescriptor TargetDescriptor = descrittoriDellaChar.get(i);
byte[] valore = TargetDescriptor.getValue();
if (valore != BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE) {
getCharDiLettura().add(charact);
TargetDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
//TargetDescriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
boolean success=false;
while(success==false) {
success = gattGlobale.writeDescriptor(TargetDescriptor);
}
status = gattGlobale.setCharacteristicNotification(charact, true);
}
}
boolean prio= gattGlobale.requestConnectionPriority(gattGlobale.CONNECTION_PRIORITY_HIGH);
I was using the same method since I had just 1 characteristic to read, and now it doesn't work anymore.
Sending read and write requests one after one other synchronously does not work since android only allows one pending GATT operation at a time (that's the reason it returns false). You must somehow enqueue the work and continue sending the next request once the callback (onCharacteristicRead/onCharacteristicWrite/onDescriptorWrite) of the previous request arrives.

Android BLE read Gatt Characteristic

I am trying to read some Bluetooth Characteristics in my APP.
Now i have a Problem with what to do after the characteristic changed from my Gatt Server. At first i've tried to use a thread to retrigger the read for the characteristic again and again like this:
new Thread(new Runnable() {
#Override
public void run() {
int[] newData = new int[30];
while(true){
try{
for(int i=0;i<newData.length;i++){
newData[i] = 0;
}
BluetoothGatt tmpGatt = refExtDataClass.getRefBluetoothGatt();
tmpGatt.readCharacteristic(characteristic);
byte[] value = characteristic.getValue();
for(int i=0;i<newData.length;i++){
newData[i] = value[i];
}
refExtDataClass.setNmData(newData);
}catch(Exception e){
break;
}
}
}
}).start();
But the Problem is that it's seems like the data is corrupt at one point (like i've always writing the same data into the characteristic from my MCU side).
Is it allowed to read BLE data like this? Is there any suggested way to read BLE data all the time? Or update it on my App side?
If you Need any additional code, please let me know.
Reading GATT characteristics is an asynchronous operation. The result is not available until you receive the onCharacteristicRead callback.
Anyway, you should rather configure your GATT server to send notifications when it has new data to send rather than polling it all the time.

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

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;
}
}
}
}
}

Categories

Resources