Android TrafficStats - Oversized value is a bug? - android

I am investigating about traffic measuring in android. I am developing on a Galaxy S4 and I programmed a service that catches TrafficStats API one time per minute, saves in SharedPreference the accumulated traffic (AKA BaseTraffic) and saves in database the difference between the current traffic minus BaseTraffic.
The problem is that in short periods (15 min) TrafficStats return an oversized value (1.6 GB per minute) and ever the same value. Someone know if this is a bug or other issue.
My code is the next for get the traffic:
public class TrafficTracker {
public static long getCurrentTraffic() {
long traff = 0;
traff = (TrafficStats.getTotalRxBytes() + TrafficStats.getTotalTxBytes());
if (traff > 0) {
return traff;
} else {
throw new UnsupportedOperationException("TrafficStats not supported");
}
}
public static long getTrafficWithOutBase(long baseTraffic) {
return TrafficStats.getTotalTxBytes() + TrafficStats.getTotalRxBytes() - baseTraffic;
}
}
And call this code here:
if (preferences.getBaseTraffic() != null) {
if (TrafficTracker.getCurrentTraffic() > preferences.getBaseTraffic().getByteTraffic()) {
TrafficObject trafficObject = new TrafficObject(new Date(calendar.getTimeInMillis()), TrafficTracker.getTrafficWithOutBase(preferences.getBaseTraffic().getByteTraffic()));
daoTraffic.create(trafficObject);
preferences.setBaseTraffic(new TrafficObject(new Date(System.currentTimeMillis()), preferences.getBaseTraffic().getByteTraffic() + trafficObject.getByteTraffic()));
} else {//when stats are reseted
TrafficObject trafficObject = new TrafficObject(new Date(calendar.getTimeInMillis()), TrafficTracker.getCurrentTraffic());
daoTraffic.create(trafficObject);
preferences.setBaseTraffic(trafficObject);
}
}
** UPDATE **
I found my error :). I replace >= instead of >. Now works properly when it is disconnected from data or wifi.
if (TrafficTracker.getCurrentTraffic() >= preferences.getBaseTraffic().getByteTraffic())

I found my error :). I replace >= instead of >. Now works properly when it is disconnected from data or wifi.
if (TrafficTracker.getCurrentTraffic() >= preferences.getBaseTraffic().getByteTraffic())

Related

How to scan IBeacon constantly on Android (Xamarin.Android)?

I'm working on a cross-platform (iOS/Android) Xamarin app, in which I need to scan IBeacon devices to assert the distance between them and the phone device.
On iOS, I use the native iOS iBeacon API, which works flawlessly and as expected.
On Android, since Android does not natively support iBeacon, I use a mix of my own code and a library "UniversalBeacon". This approach works, but when it comes to scanning (or "ranging") for Beacons over a period of time, in order to constantly assess the distance of the phone device, the experince proves very unreliable.
I am experiencing that incoming BLE packets come in as expected, but only in intervals.
Roughly summarized: Packets will come in, in a steady stream, for a seemingly random amount of time, before the packets eventually stop coming in entirely. Then, after another seemingly random amount time, packets will start coming in again. This process repeats indefinitely.
So my question is: What is causing this issue? Is it an Android quirk that I somehow have to work around?
Initiating the scan:
_ScanCallback.OnAdvertisementPacketReceived += ScanCallback_OnAdvertisementPacketReceived;
var settings = new ScanSettings.Builder()
.SetScanMode(ScanMode.LowLatency)
.Build();
_Adapter.BluetoothLeScanner.StartScan(null, settings, _ScanCallback);
Callback implementation:
internal class BLEScanCallback : ScanCallback
{
public event EventHandler<BLEAdvertisementPacketArgs> OnAdvertisementPacketReceived;
public override void OnScanFailed([GeneratedEnum] ScanFailure errorCode)
{
base.OnScanFailed(errorCode);
}
public override void OnScanResult([GeneratedEnum] ScanCallbackType callbackType, ScanResult result)
{
base.OnScanResult(callbackType, result);
switch (result.Device.Type)
{
case BluetoothDeviceType.Le:
case BluetoothDeviceType.Unknown:
try
{
var p = new BLEAdvertisementPacket
{
BluetoothAddress = result.Device.Address.ToNumericAddress(),
RawSignalStrengthInDBm = (short)result.Rssi,
Timestamp = DateTimeOffset.FromUnixTimeMilliseconds(result.TimestampNanos / 1000000),
AdvertisementType = (BLEAdvertisementType)result.ScanRecord.AdvertiseFlags,
Advertisement = new BLEAdvertisement
{
LocalName = result.ScanRecord.DeviceName
}
};
if (result.ScanRecord.ServiceUuids != null)
{
foreach (var svc in result.ScanRecord.ServiceUuids)
{
var guid = new Guid(svc.Uuid.ToString());
var data = result.ScanRecord.GetServiceData(svc);
p.Advertisement.ServiceUuids.Add(guid);
}
}
var recordData = result.ScanRecord.GetBytes();
var rec = RecordParser.Parse(recordData);
foreach (var curRec in rec)
{
if (curRec is BLEManufacturerData md)
{
p.Advertisement.ManufacturerData.Add(md);
}
if (curRec is BLEAdvertisementDataSection sd)
{
p.Advertisement.DataSections.Add(sd);
}
}
OnAdvertisementPacketReceived?.Invoke(this, new BLEAdvertisementPacketArgs(p));
}
catch (Exception ex)
{
Debugger.Break();
}
break;
default:
break;
}
}
}
I've read in various articles that this could be caused by Android automatically suspending the scan in order to save power. Whether this is the case is not obvious to me, as there does not seem to be much support on the subject.
I've already tried the following:
Changing ScanMode to LowPower/Balanced - no change
Scanning with a filter set to the specific Beacon I was testing with - filter worked, but no change in regards to the issue
Implementing logic that restarts the scan in set intervals to work around potentional limits for scan duration imposed by Android - did not affect the issue
Using other, more broadly used libraries, such as Shiny.Beacons - same experience
The issue is not caused by the Beacon device itself not advertising correctly - I've made sure of this by scanning it on another device, at the same time as my app. It is the app itself that stops scanning and/or receiving its advertisement packets.
Thanks for your time :)

Digital persona SDK - native problems

I am using in my app Digital Persona SDK for fingerprint identification.
When i use the identify function on less then 250 fmds it works fine.
Engine.Candidate candidates[] = m_engine.Identify(searchedFmd, 0, fmdArray, DEFAULT_THRESHOLD, 1); //fmdArray < 250
But with fmdArray > 250 it gives me a native runtime error:
A/art: art/runtime/indirect_reference_table.cc:132] JNI ERROR (app bug): local reference table overflow (max=512)
Now i runned this app on couple of android devices and came to conclusion that my app crushes with fmdArray > 250 when its running on android 7. But android 8 works fine. In 8 i can preform a check on even 4000 fmds and it works fine.
But i need to run this code in a specific device, that running android 7.
I tried to run it in couple of threads of 250 fmds only. But after single run there is another problem with the SDK. On the second run it doesnt works.
This is what i do:
First i get a fingerprint capture that i want to identify:
Reader.CaptureResult capture = m_reader.Capture(fidFormat, UrUSDK.DefaultImageProcessing, m_DPI, timeout);
// In second run, code after this line is not executed.
// My guees its not coming back from native. No exeptions. No errors.
...
Fmd scannedFmd = m_engine.CreateFmd(capture.image, fmdFormat);
...
int index = identifyFinger(fmds, scannedFmd);
...
private int identifyFinger(List<Fmd> fmdSearchArray, Fmd scannedFmd) {
List<List<Fmd>> lists = splitToChunks(fmdSearchArray);
AtomicInteger index = new AtomicInteger(-1);
List<Callable<Void>> threads = new ArrayList<>(lists.size());
AtomicInteger iteratorIndex = new AtomicInteger(0);
for (int i = 0; i < lists.size(); i++) {
int currentChunk = i;
Callable<Void> thread = () -> {
System.out.println(Thread.currentThread().getName() + " with chunk: " + iteratorIndex.getAndIncrement());
Fmd[] fmds = lists.get(currentChunk).toArray(new Fmd[IDENTIFY_BOUNDARY]);
try {
Engine.Candidate[] candidates = m_engine.Identify(scannedFmd, 0, fmds, threshold, 1);
if (candidates.length > 0) {
index.set(candidates[0].fmd_index + (currentChunk * IDENTIFY_BOUNDARY));
}
} catch (UareUException e) {
}
System.out.println(Thread.currentThread().getName() + " with chunk: " + currentChunk + " finished!");
return null;
};
threads.add(thread);
}
try {
List<Future<Void>> futures = executorService.invokeAll(threads);
System.out.println("All threads finished: " + index.get());
return index.get();
} catch (InterruptedException e) {
e.printStackTrace();
return -1;
}
}
...
private List<List<Fmd>> splitToChunks(List<Fmd> fmdSearchArray) {
int size = fmdSearchArray.size();
List<List<Fmd>> lists;
if (size > IDENTIFY_BOUNDARY) {
int chunks = size / IDENTIFY_BOUNDARY;
if (size % IDENTIFY_BOUNDARY > 0) {
chunks++;
}
lists = new ArrayList<>(chunks);
for (int i = 0; i < chunks; i++) {
if (i + 1 == chunks) {
lists.add(new ArrayList<>(fmdSearchArray.subList(i * IDENTIFY_BOUNDARY, size)));
break;
}
lists.add(new ArrayList<>(fmdSearchArray.subList(i * IDENTIFY_BOUNDARY, (i + 1) * IDENTIFY_BOUNDARY)));
}
} else {
lists = new ArrayList<>(1);
lists.add(fmdSearchArray);
}
return lists;
}
The problem with this code is that it runs once. But at another try it doesnt come back from the native code of Caprture call.
So my question is:
How i can overcome this and make it work from my java code?
Or at least what is the direction of the solution?
The root cause is that this Identify function holds on to at least two references per returned Candidate after pushing it to the result array. It should instead release the references after pushing, so its use of the (limited) local reference table remain constant. You should file a bug about that.
The simplest workaround for now is to cut your fmdArray into 250-sized chunks and call Identify for each chunk.

System.currentTimeMillis() returns incorrect timestamp on Huawei

The issue is that System.currentTimeMillis() returns the wrong milliseconds with different ranges of time mainly within the future sometimes up to 6 months, but it varies from a few seconds to months.
The device that this is occurring on is a Tablet model Huawei M2-A201W on android 5.1.1 the kernel version is: **3.10.74-gdbd9055**
My first assumption was that the NTP was somehow messing up with the time but I have thousands of those tablets and some of them have no network connection, no SIM card so no GSM/3G/4G.
Im using the System.currentTimeMillis() to save in a column for a table for when was a row created in the local sqlite database.
This anomally happens very often(30% of each System.currentTimeMillis() call) on the tablets that I use.
As a workaround for using System.currentTimeMillis(), maybe you can let sqlite handle the timestamp creation and see if that solves your problem?
Define/Alter your "created"-column with timestamp default current_timestamp or with default(strftime('%Y-%m-%d %H:%M:%f', 'now')) if you need milliseconds as well, like this:
sqlite> create table my_table(id integer primary key autoincrement not null, name text, created timestamp default(strftime('%Y-%m-%d %H:%M:%f', 'now')) not null);
sqlite> insert into my_table(name) values ('MyTestRow1');
sqlite> insert into my_table(name) values ('MyTestRow2');
sqlite> select * from my_table;
1|MyTestRow1|2017-08-07 10:08:50.898
2|MyTestRow2|2017-08-07 10:08:54.701
When you don't have SIM card so no GSM/3G/4G, your phone can't update correct time based on the network provided time/zone.
So, devices with the network show the correct time, while other devices without the network can show incorrect time -- you have to manually set the correct time. System.currentTimeMilis() reads the time from your system. g But on power turn, the clock works.
Check if NTP (UDP port 123) is blocked by apps using Socket or DatagramSocket. Note: NTP applies to the scenario where clocks of all hosts or routers on the network must be the same. If your device switch to two (or more) different network and gets time updated from different sources, it can fluctuate time.
Ultimately, your system time is being changed, that's why it is fluctuating. If you manually System.currentTimeMilis() after you manually disable automatic date and time, I believe, it doesn't fluctuate (no anomaly). If this is the case then your Huewai tablet doesn't have bugs.
The java API call System.currentTimeMillis() in Android platform use the POSIX api gettimeofday to get the time in milli seconds. See here.
static jlong System_currentTimeMillis(JNIEnv*, jclass) {
timeval now;
gettimeofday(&now, NULL);
jlong when = now.tv_sec * 1000LL + now.tv_usec / 1000;
return when;
}
It assume every call to gettimeofday will be successful. I guess your problem might happen here.
It's better to check the return value of every API call and determine what to do next if error happen.
So I suggest a more reliable method in JNI with your own implementation like below. Call these POSIX APIs in order, if gettimeofday failed, call clock_gettime, if it failed again, call time.
struct timeval now;
if (gettimeofday(&now, NULL) != 0) {
struct timespec ts;
if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
now.tv_sec = ts.tv_sec;
now.tv_usec = ts.tv_nsec / 1000LL;
} else {
now.tv_sec = time(NULL);
now.tv_usec = 0;
}
}
jlong when = now.tv_sec * 1000LL + now.tv_usec / 1000LL;
__android_log_print(ANDROID_LOG_INFO, "TAG", "%lld", when);
What about fetching the timestamp natively by running "date +%s" command in Linux kernel?
Here, "+%s" is seconds since 1970-01-01 00:00:00 UTC. (GNU Coreutils 8.24 Date manual)
try {
// Run the command
Process process = Runtime.getRuntime().exec("date +%s");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
// Grab the results
StringBuilder log = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
log.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}
If you print this,
Log.e("unix_time: ", "" + log.toString());
You'll get an Unix timestamp e.g. 1502187111
To convert it back to a date object, multiply by 1000, since java is expecting milliseconds,
Date time = new Date(Long.parseLong(log.toString()) * 1000);
Log.e("date_time: ", "" + time.toString());
This will give you a plain date format. e.g. Tue Aug 08 16:15:58 GMT+06:00 2017
Since you mentioned that most of the calls are getting the proper time and it only happens 30% of the cases, I would create an receiver for ACTION_TIME_CHANGED and ACTION_TIMEZONE_CHANGED intent broadcasts to find out when the time changes. Maybe this will give you a clue on what is changing the time.
Together with the ConnectivityManager you can detect if the device is connected and what type of a connection you have, maybe some connection is triggering the time change.
// init the register and register the intents, in onStart, using:
receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
getNetworkInfo();
if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction()))
Log.d(this.getClass().getName(), "Detected a time change. isWifiConn: " +
isWifiConn + " isMobileConn: " + isMobileConn);
if (Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction()))
Log.d(this.getClass().getName(), "Detected a timezone change. isWifiConn: " +
isWifiConn + " isMobileConn: " + isMobileConn);
}
};
IntentFilter filters = new IntentFilter();
filters.addAction(Intent.ACTION_TIME_CHANGED);
filters.addAction(Intent.ACTION_TIMEZONE_CHANGED);
registerReceiver(receiver, filters);
Log.d(DEBUG_TAG, "Receiver registered");
// do not forget to unregister the receiver, eg. onStop, using:
unregisterReceiver(receiver);
//...
private void getNetworkInfo() {
ConnectivityManager connMgr = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
isWifiConn = networkInfo.isConnected();
networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
isMobileConn = networkInfo.isConnected();
Log.d(DEBUG_TAG, "Wifi connected: " + isWifiConn);
Log.d(DEBUG_TAG, "Mobile connected: " + isMobileConn);
}
Does this device have a real hardware RTC? I couldn't find a definitive answer from googling and reading spec sheets. A run of:
$ dmesg -s 65535 | grep -i rtc
from a shell should give you an answer. You should see something like this (will vary with chipset and kernel version):
[ 3.816058] rtc_cmos 00:02: RTC can wake from S4
[ 3.816429] rtc_cmos 00:02: rtc core: registered rtc_cmos as rtc0
[ 3.816510] rtc_cmos 00:02: alarms up to one month, y3k, 242 bytes nvram, hpet irqs
If there is no message returned by grep (and you grepped against the whole kernel message buffer), then there's your answer. You have no clock to keep time on these devices. You will always need NTP and a working network connection to the Internet to keep such a device without a clock chip in sync with world time.
RTC: https://en.wikipedia.org/wiki/Real-time_clock

How to get the number of cores of an android device

I was looking to determine(or count) the number of cores in the embedded processor of android device.
I tried using /proc/cpuinfo, but it is not returning the number of cores of device !
I don't found the code anywhere on the Internet. Does anyone here know how can I determine this, then please answer. Thanks in advance
UPDATE:
The answers on this question How can you detect a dual-core cpu on an Android device from code? doesn't run well in some devices. I tested them is dual core & quad core processors, they returned 2 & 4 respectively, fine !
But, On Octa Core processor like in Samsung Note 3 it returned 4. (Perhaps in note 3 there are 2 sets of quad core processors running individually )
I was looking to solve this problem.
UPDATE
The app CPU-Z is returning the correct core count in my device Samsung note 3
Here it seems that there exists a possible solution...
You may use a combination of above answer with this one. This way it will perform faster on devices running API 17+ as this method is much faster than filtering out files.
private int getNumberOfCores() {
if(Build.VERSION.SDK_INT >= 17) {
return Runtime.getRuntime().availableProcessors()
}
else {
// Use saurabh64's answer
return getNumCoresOldPhones();
}
}
/**
* Gets the number of cores available in this device, across all processors.
* Requires: Ability to peruse the filesystem at "/sys/devices/system/cpu"
* #return The number of cores, or 1 if failed to get result
*/
private int getNumCoresOldPhones() {
//Private Class to display only CPU devices in the directory listing
class CpuFilter implements FileFilter {
#Override
public boolean accept(File pathname) {
//Check if filename is "cpu", followed by a single digit number
if(Pattern.matches("cpu[0-9]+", pathname.getName())) {
return true;
}
return false;
}
}
try {
//Get directory containing CPU info
File dir = new File("/sys/devices/system/cpu/");
//Filter to only list the devices we care about
File[] files = dir.listFiles(new CpuFilter());
//Return the number of cores (virtual CPU devices)
return files.length;
} catch(Exception e) {
//Default to return 1 core
return 1;
}
}
public int availableProcessors ()
Added in API level 1 Returns the number of processor cores available
to the VM, at least 1. Traditionally this returned the number
currently online, but many mobile devices are able to take unused
cores offline to save power, so releases newer than Android 4.2 (Jelly
Bean) return the maximum number of cores that could be made available
if there were no power or heat constraints.
Also there is information about number of cores inside a file located in
/sys/devices/system/cpu/present It reports the number of available CPUs in the following format:
0 -> single CPU/core
0-1 -> two CPUs/cores
0-3 -> four CPUs/cores
0-7 -> eight CPUs/cores
etc.
Also please check what is inside /sys/devices/system/cpu/possible on a note 3
Both files have r--r--r-- or 444 file permissions set on them, so you should be able to read them without a rooted device in code.
EDIT: Posting code to help you out
private void printNumberOfCores() {
printFile("/sys/devices/system/cpu/present");
printFile("/sys/devices/system/cpu/possible");
}
private void printFile(String path) {
InputStream inputStream = null;
try {
inputStream = new FileInputStream(path);
if (inputStream != null) {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line;
do {
line = bufferedReader.readLine();
Log.d(path, line);
} while (line != null);
}
} catch (Exception e) {
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
}
}
}
}
With the result
D//sys/devices/system/cpu/present﹕ 0-3
D//sys/devices/system/cpu/possible﹕ 0-3
The test was run on OnePlus One running BlissPop ROM with Android v5.1.1 and it prints as it should. Please try on your Samsung
Firstly, it seems like this is not possible, here is some information on this:
We will use the Samsung Exynos 5 Octa CPU as an example for this thread, but generally any ARM processor will work the same.
The Samsung Exynos 5 Octa CPU has 8 CPU cores. but in reality it has two processes with 4 cores each. This is called big.LITTLE. You have 1 fast processor and 1 power efficient processor.
Within the Exynos 5 CPU it has two different CPUs in built, a Cortex a15 and a Cortex a7...
Within big.LITTLE both CPUs cannot run at the same time, only one CPU can be active at any given time...
But there is also something called "big.LITTLE mp" in which both CPU's can be active at the same time, but here is the catch (again!) Only FOUR cores can be active to the software at any given time. Here is an image to explain this a bit better:
Now as you can see, a single CPU core is used from the more powerful processor here and then the other four cores are active from the more energy efficient a7 CPU cluster.
You can read more about the big.LITTLE architecture here :
http://www.arm.com/products/processors/technologies/biglittleprocessing.php
You can read more about the big.LITTLE mp architecture here:
http://www.arm.com/products/processors/technologies/biglittleprocessing.php
What you want to find out now, is if it is possible to know if the CPU architecture is Big.LITTLE or BIG.LITTLE mp. Then see if you can directly find out the CPU count from each individual CPU, but I cannot find any relevant code. There is a lot of documentation on ARMs website, but I am not too sure if it is possible to get this information. Either way though, only 4 CPU cores can be used therefor the code you have is technically correct as that is the number of useable cores.
I hope this helped, if you have any questions or want anything clearing up then let me know. There is a lot of information out there
Try this:
Runtime.getRuntime().availableProcessors();
This returns the number of CPU's available for THIS specific virtual machine, as I experienced. That may not be what you want, still, for a few purposes this is very handy. You can test this really easily: Kill all apps, run the above code. Open 10 very intensive apps, and then run the test again. Sure, this will only work on a multi cpu device, I guess.
You can try this method to get number of core.
private int getNumOfCores()
{
try
{
int i = new File("/sys/devices/system/cpu/").listFiles(new FileFilter()
{
public boolean accept(File params)
{
return Pattern.matches("cpu[0-9]", params.getName());
}
}).length;
return i;
}
catch (Exception e)
{
e.printstacktrace();
}
return 1;
}
Use this to get no. of cores.Do read this link very carefully and also see what the author is trying to tell between virtual device and a real device.
The code is referred from THIS SITE
/**
* Gets the number of cores available in this device, across all processors.
* Requires: Ability to peruse the filesystem at "/sys/devices/system/cpu"
* #return The number of cores, or 1 if failed to get result
*/
private int getNumCores() {
//Private Class to display only CPU devices in the directory listing
class CpuFilter implements FileFilter {
#Override
public boolean accept(File pathname) {
//Check if filename is "cpu", followed by a single digit number
if(Pattern.matches("cpu[0-9]+", pathname.getName())) {
return true;
}
return false;
}
}
try {
//Get directory containing CPU info
File dir = new File("/sys/devices/system/cpu/");
//Filter to only list the devices we care about
File[] files = dir.listFiles(new CpuFilter());
Log.d(TAG, "CPU Count: "+files.length);
//Return the number of cores (virtual CPU devices)
return files.length;
} catch(Exception e) {
//Print exception
Log.d(TAG, "CPU Count: Failed.");
e.printStackTrace();
//Default to return 1 core
return 1;
}
}
Here you go (Java):
try {
int ch, processorCount = 0;
Process process = Runtime.getRuntime().exec("cat /proc/cpuinfo");
StringBuilder sb = new StringBuilder();
while ((ch = process.getInputStream().read()) != -1)
sb.append((char) ch);
Pattern p = Pattern.compile("processor");
Matcher m = p.matcher(sb.toString());
while (m.find())
processorCount++;
System.out.println(processorCount);
} catch (IOException e) {
e.printStackTrace();
}
good thing is to use JNI, if possible in project
std::thread::hardware_concurrency()
Nice and simple solution in kotlin:
fun getCPUCoreNum(): Int {
val pattern = Pattern.compile("cpu[0-9]+")
return Math.max(
File("/sys/devices/system/cpu/")
.walk()
.maxDepth(1)
.count { pattern.matcher(it.name).matches() },
Runtime.getRuntime().availableProcessors()
)
}

Android app uses static method to transfer and plot data

I've been asking questions regarding my Android project that continually plots Bluetooth data in real-time.
Basically what I've already done is create a first version of my app by cobbling together some open source code Blueterm and OrientationSensorExample
It's been suggested that I add a thread, a handler, a Service, or use Async Task, or AIDL, etc. But I don't know how to use any of these and would appreciate an explanation.
Here's a description of the Blueterm open source code I started with (see link above). Blueterm is basically a terminal emulator program that communicates over Bluetooth. It consists of several activities with Blueterm being the most important. It discovers, pairs, and connects with a remote Bluetooth device that supports SPP/RfComm. When connected I can use Blueterm to configure the remote device by sending it commands to turn on sampling, change the number of channels to sample (to one channel), change to format of the incoming data (I like comma separated data), etc
Here's a description of the OrientationSensorExample open source code I started with (see link above). It's basically an example application of the AnroidPlot library. The OrientationSensor activity implements SensorEventListener. This includes overriding onSenorChanged() which is called whenever new orientation sensor data is taken, and it redraws the graph.
Having cobbled together these two open source projects (Blueterm and OrientationSensorExample) into one application (Blueterm) here's a description of how the overall application (Blueterm) works. When I start Blueterm the whole screen emulates a nice blue terminal. From the Options Menu I discover, pair with, connect to, and configure a remote bluetooth device as described above. Once I have configured the remote device, I go again to the Options Menu and select "Plot data" which launches the Plot activity. The terminal emulator goes away, and a nice scrolling real-time plot from the Plot activity shows up.
As far as I can tell there is a background thread that calls an update() method as follows:
/**
* Look for new input from the ptty, send it to the terminal emulator.
*/
private void update() {
int bytesAvailable = mByteQueue.getBytesAvailable();
int bytesToRead = Math.min(bytesAvailable, mReceiveBuffer.length);
try {
int bytesRead = mByteQueue.read(mReceiveBuffer, 0, bytesToRead);
append(mReceiveBuffer, 0, bytesRead);
//VTR use existing handler that calls update() to get data into plotting activity
Plot.plotData(mReceiveBuffer, 0, bytesRead);
} catch (InterruptedException e) {
//VTR OMG their swallowing this exception
}
}
In the update() method I found it convenient to call my Plot.plotData() method and pass it the same date that is passed to the append() method to plot the data. NOTE: This only works if plotData() is a static method. No one has been able to explain why.
Anyway plotData() is a static method and here's how it and it's helper methods look now:
private static StringBuffer strData = new StringBuffer("");
public static void plotData(byte[] buffer, int base, int length) {
Log.i("Entering: ", "plotData()");
/*
byte[] buffer = (byte[]) msg.obj;
int base = msg.arg1;
int length = msg.arg2;
*/
for (int i = 0; i < length; i++) {
byte b = buffer[base + i];
try {
if (true) {
char printableB = (char) b;
if (b < 32 || b > 126) {
printableB = ' ';
}
Log.w("Log_plotData", "'" + Character.toString(printableB)
+ "' (" + Integer.toString(b) + ")");
strData.append(Character.toString(printableB));
if (b == 10)
{
Log.i("End of line: ", "processBlueData()");
Log.i("strData", strData.toString());
splitData(strData);
strData = new StringBuffer("");
}
}
} catch (Exception e) {
Log.e("Log_plotData_exception", "Exception while processing character "
+ Integer.toString(i) + " code "
+ Integer.toString(b), e);
}
}
Log.i("Leaving: ", "plotData()");
}
private static void splitData(StringBuffer strBuf) {
String strDash = strBuf.toString().trim();
String[] strDashSplit = strDash.split("-");
for (int ndx = 0; ndx < strDashSplit.length; ndx++)
{
if (strDashSplit[ndx].length() > 0)
Log.i("strDashSplit", ndx + ":" + strDashSplit[ndx]);
String strComma = strDashSplit[ndx].trim();
String[] strCommaSplit = strComma.split(",");
for (int mdx = 0; mdx < strCommaSplit.length; mdx++)
{
if (strCommaSplit[mdx].length() > 0)
Log.i("strCommaSplit", mdx + ":" + strCommaSplit[mdx]);
if (mdx == 1)
{
int raw = Integer.parseInt(strCommaSplit[1],16);
Log.i("raw", Integer.toString(raw));
float rawFloat = raw;
Log.i("rawFloat", Float.toString(rawFloat));
float ratio = (float) (rawFloat/65535.0);
Log.i("ratio", Float.toString(ratio));
float voltage = (float) (5.0*ratio);
Log.i("voltage", Float.toString(voltage));
nowPlotData(voltage);
}
}
}
}
public static void nowPlotData(float data) {
// get rid the oldest sample in history:
if (plotHistory.size() > HISTORY_SIZE) {
plotHistory.removeFirst();
}
// add the latest history sample:
plotHistory.addLast(data);
// update the plot with the updated history Lists:
plotHistorySeries.setModel(plotHistory, SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
//VTR null pointer exception?
if (plotHistoryPlot == null)
Log.i("aprHistoryPlot", "null pointer exception");
// redraw the Plots:
plotHistoryPlot.redraw();
}
If it is strongly recommended that plotData() not be a static method and that I should do something else please explain here and how. Thanks!
This might be a question much better suited for Code Review, rather than here. Perhaps you can reformulate to post it there, or trim it a lot to repost it here.
Furthermore, to answer: "It's been suggested that I add a thread, a handler, a Service, or use Async Task, or AIDL, etc. But I don't know how to use any of these and would appreciate an explanation.", the best advise would be to link you to a book about android, such as: http://commonsware.com/Android/ . Chapters 35 and 36 deal with services, while chapter 20 is about threads. You will never get an answer as complete as those chapters here.

Categories

Resources