Android TrafficStats getTotalRxBytes() returns always zero - android

Im trying to measure how much bytes my app has received.
Im doing it this way:
long receivedBytesBefore = TrafficStats.getTotalRxBytes();
...
doSomething();
...
long receivedBytesAfter = TrafficStats.getTotalRxBytes();
long receivedBytes = receivedBytesAfter - receivedBytesBefore;
My problem is that getTotalRxBytes() always returns 0. So my result is 0 no matter what I do.
I have found out that the method is just reading some textfiles like
/sys/class/net/rmnet0/statistics/rx_bytes
So I looked into these files and they all contain just "0".
Do I miss something or do I have to activate this function somehow?
Is there another way to measure how much bytes my app has received?
My Android device is a Samsung Galaxy Ace S5830 running Android 2.3.3

I can verify this was happening to me as well.
From behavior I've observed, it appears that getTotalRxBytes only works when wifi is connected. But something to be aware of is that if you are trying to get an accurate number of bytes received for a file for example, there seams to be some extra bytes sent.
So if you don't need it to be super accurate. You can use getMobileRxBytes() for when wifi is not active and getTotalRxBytes() for when wifi is active.
Here is a simple example.
ie:
ConnectivityManager connManager;
connManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
mWifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
mMobile = connManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
long initialBytes = 0;
long finalBytes = 0;
long byteDifference = 0;
boolean onWifi= false;
if (mWifi.isConnected())
{
//wifi connected
initialBytes = TrafficStats.getTotalRxBytes();
onWifi = true;
}
else if (mMobile.isConnected())
{
//if 3g/4g connected
initialBytes = TrafficStats.getMobileRxBytes();
onWifi = true;
}
else
{
//Something funny going on
Log.e("Error", "Something funny going on");
return;
}
// Process whatever you want to process
if(onWifi)
{
finalBytes = TrafficStats.getTotalRxBytes();
}
else
{
finalBytes = TrafficStats.getMobileRxBytes();
}
byteDifference = finalBytes - initialBytes;
Something along these lines. Hopefully this helps.

These may not be supported on your device, and can return UNSUPPORTED, which might be 0.

Related

Detect 5G NR (SA/NSA) in my Android Application

I am trying to detect 5G network. I use the telephony manager to get NETWORK_TYPE. Even if I am in 5G network coverage and my phone shows 5G, I do not get NETWORK_TYPE_NR. The NETWORK_TYPE is always 13 i.e. LTE.
The Phones Engineering service mode shows NR related data.
Is there any way to detect NR (Standalone or Non-Standalone) mode?
I also need to get the Cell Information for NR data. I use telephonyManager.getAllCellinfo(), but I never get an instance of cellinfonr.
Any help is appreciated. Thanks
I faced the same problem for a few weeks ago. In my case, I want to detect 5G network on Galaxy S20-5G but the getDataNetworkType() always return 13 NETWORK_TYPE_LTE.
Following by netmonster-core strategy, and here is the code that I extract from them to solve my problem.
public boolean isNRConnected(TelephonyManager telephonyManager) {
try {
Object obj = Class.forName(telephonyManager.getClass().getName())
.getDeclaredMethod("getServiceState", new Class[0]).invoke(telephonyManager, new Object[0]);
// try extracting from string
String serviceState = obj.toString();
boolean is5gActive = serviceState.contains("nrState=CONNECTED") ||
serviceState.contains("nsaState=5") ||
(serviceState.contains("EnDc=true") &&
serviceState.contains("5G Allocated=true"));
if (is5gActive) {
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
Here is full detector class from netmonster-core:
(DetectorLteAdvancedNrServiceState.kt)

Android - Why ConnectivityManager.isActiveNetworkMetered always returning true (even when on Wifi)

In my music streaming app for Android, the code path below helps decides between a high bitrate stream vs. a lower bitrate based on values returned by the ConnectivityManager class instance.
I am trying to understand why ConnectivityManager.isActiveNetworkMetered() would return "true", when it's evident I'm on a local Wifi network. Take the following Android/Java code:
boolean isMetered = false;
boolean isWifi = false;
boolean isEthernet = false;
boolean isRoaming = false;
boolean isConnected = false;
NetworkInfo netinfo = connManager.getActiveNetworkInfo();
if (netinfo != null)
{
isWifi = (netinfo.getType() == ConnectivityManager.TYPE_WIFI);
isEthernet = (netinfo.getType() == ConnectivityManager.TYPE_ETHERNET);
isRoaming = netinfo.isRoaming();
isConnected = netinfo.isConnected();
Log.d(TAG, "active network type = " + netinfo.getTypeName());
Log.d(TAG, "active network subtype = " + netinfo.getSubtypeName());
Log.d(TAG, "isConnected = " + isConnected);
Log.d(TAG, "isWifi = " + isWifi);
Log.d(TAG, "isEthernet = " + isEthernet);
Log.d(TAG, "isRoaming = " + isRoaming);
}
// isActiveNetworkMetered was introduced in API 16 (Jelly Bean)
if (android.os.Build.VERSION.SDK_INT >= 16)
{
isMetered = connManager.isActiveNetworkMetered();
Log.d(TAG, "isMetered = " + isMetered);
}
When I have Wifi turned off and my only connection is to AT&T's LTE network, it prints out the following to the log console:
active network type = mobile
active network subtype = LTE
isConnected = true
isWifi = false
isEthernet = false
isRoaming = false
isMetered = true
Everything above matches expectations - I'm on a mobile carrier network that is metered.
Now switch to my home Wifi and the same block of code prints this:
active network type = WIFI
active network subtype =
isConnected = true
isWifi = true
isEthernet = false
isRoaming = false
isMetered = true
Why is isMetered, the result of isActiveNetworkMetered, still showing as true? This is causing the following code path to favor the lower bitrate stream even though I'm on my home wifi network:
if ((isWifi || isEthernet) && !isMetered)
{
result = BITRATE_HIGH_KBIT_SEC;
}
else
{
result = BITRATE_LOW_KBIT_SEC;
}
How does isActiveNetworkMetered() decide if the network is metered? Is this part of the WIFI negotiation or a bit in the SSID broadcast? A network setting on Android? I couldn't find any setting on my Android Kitkat device that let me toggle or discover the setting for a metered network.
Last week I my ISP (Frontier) sent me a new Wifi router. Possibly related? I didn't see anything on the router's firmware pages for such a setting.
I'm going to swing by work later today to see how it behaves on another network. In any case, I'm likely to edit the above code to skip the metered check unless I can prove its just an incorrect setting somewhere.
UPDATE - issue of always returning metered network reproduces on my HTC One M8 phone (running Kitkat), but not on my Nexus 7 from 2012 (also upgraded to Android 4.4 Kitkat).
PROBLEM SOLVED - it turns out my Wifi was flagged by my phone as a "Mobile Hotspot". More details on how to find and toggle this flag is here.
I've tested this too, but I'm observing the "expected" result: false for WiFi and true for 3G. This is in a Nexus 4 with Android 4.4.2.
Curiously enough the ConnectivityManagerCompat class in the support library does return false for WiFi.
final int type = info.getType();
switch (type) {
case TYPE_MOBILE:
return true;
case TYPE_WIFI:
return false;
default:
// err on side of caution
return true;
}
EDIT - Found it (I think)
NetworkPolicyManagerService seems to be the class that ultimately produces the result for this method. And according to it, WiFi connections can indeed be metered. It contains a BroadcastReceiver that "listen(s) for wifi state changes to catch metered hint" (line 567). This information is obtained from NetworkInfo.getMeteredHint(), which, on closer inspection, contains this interesting comment:
/**
* Flag indicating that AP has hinted that upstream connection is metered,
* and sensitive to heavy data transfers.
*/
private boolean mMeteredHint;
This flag is loaded from DhcpResults.hasMeteredHint()
/**
* Test if this DHCP lease includes vendor hint that network link is
* metered, and sensitive to heavy data transfers.
*/
public boolean hasMeteredHint() {
if (vendorInfo != null) {
return vendorInfo.contains("ANDROID_METERED");
} else {
return false;
}
}
So it would seem that, indeed, the AP may notify its WiFi clients that the underlying internet connection is metered by using this flag.
EDIT #2 Relevant information, from http://www.lorier.net/docs/android-metered
When an android phone is using another android phone's hotspot, it
knows it's on a "metered connection" and therefore disables the
expensive sync options ( eg: photo sync). How does it know this? The
android hotspot sends DHCP Option 43 (Vendor specific options) with
the value ANDROID_METERED. The client, if it sees ANDROID_METERED
anywhere in the option 43 values, turns on the "expensive data
connection" option.
Looks like this flag was added to "play nice" with a hotspot offered by another Android device.
EDIT #3 The commit that introduced this feature:
Connect metered DHCP hint for Wi-Fi networks.
When DHCP lease includes vendor info indicating that remote Wi-Fi
network is metered, advise NetworkPolicy. Users can still manually
change the metered flag in Settings.
You can access this setting by going:
Settings -> Data Usage -> (Overflow Menu) -> Mobile Hotspots

NetworkInfo TYPE_MOBILE_MMS feature

I want to send a MMS using the stock MMS source. Before more explanation, I want to say that it does work on some android versions but 4.0.3 and 4.0.4.
In my service, I ask the device to enable the MMS network feature using the following code:
createWakeLock();
int result = mConnMgr.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, PhoneEx.FEATURE_ENABLE_MMS);
Log.v(TAG, "beginMmsConnectivity: result=" + result);
switch (result) {
case PhoneEx.APN_ALREADY_ACTIVE:
case PhoneEx.APN_REQUEST_STARTED:
acquireWakeLock();
return result;
}
throw new IOException("Cannot establish MMS connectivity");
On some devices (Xperia T running 4.0.3), it throws an exception because result equals PhoneEx.APN_TYPE_NOT_AVAILABLE. The MMS is enabled in my phone settings and I can send one with the stock mms app.
On other devices (HTC Desire S running 4.0.4), the problem is located a bit further, in the ConnectivityBroadcastReceiver. Here is my code:
private class ConnectivityBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, Intent intent) {
String action = intent.getAction();
mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
mOtherNetworkInfo = (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO);
networkAttempt++;
if (networkAttempt < APN_RETRY_ATTEMPTS) {
// Check availability of the mobile network.
if ((mNetworkInfo == null) || (mNetworkInfo.getType() != ConnectivityManager.TYPE_MOBILE_MMS)) {
// ERROR is located here, it returns TYPE_MOBILE :s
Log.v(TAG, " type is not TYPE_MOBILE_MMS, bail");
return;
}
}
...
As you can see in the comment, mNetworkInfo.getType() returns TYPE_MOBILE but I expect TYPE_MOBILE_MMS.
So, my question is the following: Did I make something wrong ? Or, Is there another way to do that ?
Ps: It works on devices running Android 2.3 to 3.2 and 4.1 and above.
It appears on some devices, with some providers, the TYPE_MOBILE isn't the default MMS gateway, you have to use another one. Here is the solution I found that solved my problem.
I hope this could help someone else.
// Take a wake lock so we don't fall asleep before the message is downloaded.
createWakeLock();
// Let's try every type
int result = -1;
int[] apnTypes = new int[] {ConnectivityManager.TYPE_MOBILE, ConnectivityManager.TYPE_MOBILE_MMS, ConnectivityManager.TYPE_MOBILE_DUN, ConnectivityManager.TYPE_MOBILE_HIPRI, ConnectivityManager.TYPE_MOBILE_SUPL};
for (int i=0; i<apnTypes.length; i++)
{
result = mConnMgr.startUsingNetworkFeature(apnTypes[i], PhoneEx.FEATURE_ENABLE_MMS);
Log.v(TAG, "beginMmsConnectivity: result=" + result);
switch (result)
{
case PhoneEx.APN_ALREADY_ACTIVE:
case PhoneEx.APN_REQUEST_STARTED:
acquireWakeLock();
return result;
}
}
// None found
throw new IOException("Cannot establish MMS connectivity");

Android get wifi activity

I'm trying to find out from my application if the wifi is currently being used;
as in not just connected, but something is actually downloading or uploading at the moment.
Android (4.0+) seems to be aware of this since the wifi icon changes when data is being transferred. Any way for me to access this information?
Thanks in advance.
After some more research I found the TrafficStats class. Don't know how reliable this is but here's my code:
boolean isWifiInUse;
long lastTx,lastRx;
Runnable TestWifi = new Runnable() {
public void run() {
long cRx = TrafficStats.getTotalRxBytes() - TrafficStats.getMobileRxBytes();
long cTx = TrafficStats.getTotalTxBytes() - TrafficStats.getMobileTxBytes();
if (cTx - lastTx != 0 || cRx - lastRx != 0)
{
if (!isWifiInUse)
isWifiInUse = true;
}
else if (isWifiInUse)
{
isWifiInUse = false;
//ShowToast("Data no longer in use");
}
lastRx = cRx;
lastTx = cTx;
hand.postDelayed(this, 1000);
}
};
Hope this helps someone.

Best Practice maintaining SocketConnection while network (Mobile -> Wifi) changes

I have an android app which connects to a server using a socket connection which is kept open while the app is active. If the phone gets inactive (lock screen) or the user presses the home button, the application closes the socket connection and reopens it if the app becomes visible again.
This Pattern works fine on most of the android phones we have (about 15 devices), but the Motorola Milestone, Defy, SE Xperia Arc and the LG Optimus One need very long (>10 secs) to detect if a Wifi is available after and connect to it. So to wait for the best network connection I use the following code (before opening the socket to the server):
public static boolean waitNetworkConnection(Context context, int retries) {
ConnectivityManager cm = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = getNetworkToTest(cm);
if (ni == null || !ni.isConnected()) {
// sleep a short while to allow system to switch connecting networks.
Tools.sleep(1000);
int counter = 0;
while (counter < retries && (ni == null || (ni.isAvailable() &&
!ni.isConnected()))) {
Tools.sleep(500);
ni = getNetworkToTest(cm);
counter++;
}
}
return (cm.getActiveNetworkInfo() != null &&
cm.getActiveNetworkInfo().isConnected());
}
and this method (use by the one above) to get the connection to test, which prefers a Wifi-Connection if one (not necessary connected) is available:
private static NetworkInfo getNetworkToTest(ConnectivityManager cm) {
NetworkInfo[] nis = cm.getAllNetworkInfo();
NetworkInfo ni = cm.getActiveNetworkInfo();
for (int i = 0; i < nis.length; i++) {
if (nis[i].getType() == 1 /* Wifi */ && nis[i].isAvailable()) {
ni = nis[i];
return(ni);
}
}
return(ni);
}
This works fine for most of the devices, but for the mentioned ones this very often fails and this method tells me to use a mobile network connection and the device switches the connection type while I open a socket connection which leads to a SocketException with a very generic error message so I'm unable to determine if the socket connection is caused by this issue or because of some other network error.
Simply doing a retry doesn't fix this either, as this breaks the handling for the other network errors because it then takes very long to detect a socket timeout (because it is checked twice).
Has anyone else ran into this problem (very slowing connect to Wifi) and has a solution for this?
Yes, this is a tricky problem. One option would be to wait for the right network state broadcast using a BroadcastReceiver.
As described here: How to detect when WIFI Connection has been established in Android?
And here: How can I monitor the network connection status in Android?
There is a project called droidfu that has a HTTP wrapper, that gets round the wi-fi / 3g issue.
Here is a snippet from the code for the BetterHttpRequestBase class:
public BetterHttpResponse send() throws ConnectException {
BetterHttpRequestRetryHandler retryHandler = new BetterHttpRequestRetryHandler(maxRetries);
// tell HttpClient to user our own retry handler
httpClient.setHttpRequestRetryHandler(retryHandler);
HttpContext context = new BasicHttpContext();
// Grab a coffee now and lean back, I'm not good at explaining stuff. This code realizes
// a second retry layer on top of HttpClient. Rationale: HttpClient.execute sometimes craps
// out even *before* the HttpRequestRetryHandler set above is called, e.g. on a
// "Network unreachable" SocketException, which can happen when failing over from Wi-Fi to
// 3G or vice versa. Hence, we catch these exceptions, feed it through the same retry
// decision method *again*, and align the execution count along the way.
boolean retry = true;
IOException cause = null;
while (retry) {
try {
return httpClient.execute(request, this, context);
} catch (IOException e) {
cause = e;
retry = retryRequest(retryHandler, cause, context);
} catch (NullPointerException e) {
// there's a bug in HttpClient 4.0.x that on some occasions causes
// DefaultRequestExecutor to throw an NPE, see
// http://code.google.com/p/android/issues/detail?id=5255
cause = new IOException("NPE in HttpClient" + e.getMessage());
retry = retryRequest(retryHandler, cause, context);
} finally {
// if timeout was changed with this request using withTimeout(), reset it
if (oldTimeout != BetterHttp.getSocketTimeout()) {
BetterHttp.setSocketTimeout(oldTimeout);
}
}
}
// no retries left, crap out with exception
ConnectException ex = new ConnectException();
ex.initCause(cause);
throw ex;
}

Categories

Resources