6

I've written a nice little Android app to check data usage, unfortunately it relies heavily on android.net.TrafficStats which was introduced with Froyo (Android 2.2).

I'm attempting to back-port this class for my non-Froyo users, and what I'm able to determine from the Android source is:

  1. TrafficStats.java is just a native pointer to a c file
  2. The c file opens two files (see below) and reads their contents
  3. If either contains a numeric value it spits it back as the "bytes used" count

Here's my challenge... when I call TrafficStats via the API on my device, I get a reading (ex. 1113853 bytes). When I open the two files and check their contents, one file doesn't exist and the other file is 0 bytes.

So clearly I mis-understood what TrafficStats is doing. Can anyone shed some light on how it's working it's magic?

Thanks for the help.

(here is my attempt to port the c file to java)

package com.suttco.net;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

import com.suttco.IOUtils;
import com.suttco.StringUtils;

import android.util.Log;

public class TrafficStatsFile {

 private static final String mobileRxFile_1 = "/sys/class/net/rmnet0/statistics/rx_bytes";
 private static final String mobileRxFile_2 = "/sys/class/net/ppp0/statistics/rx_bytes";
 private static final String mobileTxFile_1 = "/sys/class/net/rmnet0/statistics/tx_bytes";
 private static final String mobileTxFile_2 = "/sys/class/net/ppp0/statistics/tx_bytes";

 private static final String LOGGING_TAG = TrafficStatsFile.class.getSimpleName();

 public long getMobileRxBytes() {
  return tryBoth(mobileRxFile_1, mobileRxFile_2);
 }

 public long getMobileTxBytes() {
  return tryBoth(mobileTxFile_1, mobileTxFile_2);
 }

 // Return the number from the first file which exists and contains data
 private static long tryBoth(String a, String b) {
  long num = readNumber(a);
  return num >= 0 ? num : readNumber(b);
 }

 // Returns an ASCII decimal number read from the specified file, -1 on error.
 private static long readNumber(String filename) {
  File f = new File(filename);
  if(f.exists()) {
   if(f.canRead()) {
    try {
     Log.d(LOGGING_TAG, "f.length() = " + f.length());
     String contents = IOUtils.readFileAsString(f);
     if(StringUtils.IsNotNullOrEmpty(contents)) {
      try {
       return Long.parseLong(contents);
      }
      catch(NumberFormatException nfex) {
       Log.w(LOGGING_TAG, "File contents are not numeric: " + filename); 
      }
     }
     else {
      Log.w(LOGGING_TAG, "File contents are empty: " + filename); 
     }
    }
    catch (FileNotFoundException fnfex) {
     Log.w(LOGGING_TAG, "File not found: " + filename, fnfex);
    }
    catch(IOException ioex) {
     Log.w(LOGGING_TAG, "IOException: " + filename, ioex);
    }
   }
   else {
    Log.w(LOGGING_TAG, "Unable to read file: " + filename);
   }
  }
  else {
   Log.w(LOGGING_TAG, "File does not exist: " + filename);
  }
  return -1;
 }
}
David Snabel-Caunt
  • 57,804
  • 13
  • 114
  • 132
Skylar Sutton
  • 4,632
  • 3
  • 27
  • 39
  • Link to the source got mangled, try this: http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=core/jni/android_net_TrafficStats.cpp;h=ff46bdd2ba39380a0a673cbf8aadfc5a3529156c;hb=refs/heads/master – Skylar Sutton Oct 27 '10 at 00:56
  • Or a cache copy here: http://google.com/codesearch#uX1GffpyOZk/core/jni/android_net_TrafficStats.cpp&q=TrafficStats.cpp – Volo Sep 02 '11 at 13:06
  • The link is broken, and even if you find the android_net_TrafficStats.cpp, the code that deals with those counters was moved from there on the latest revision. So, the link to the right revision is here: https://github.com/android/platform_frameworks_base/blob/3762c311729fe9f3af085c14c5c1fb471d994c03/core/jni/android_net_TrafficStats.cpp – Eduardo Nov 26 '12 at 02:21

2 Answers2

11

Here's a working, modified version of the code above. This one is using RandomAccessFile and doesn't rely on custom imports but uses only built-in String functions with their Exceptions.

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

import android.util.Log;

public class TrafficStatsFile {

private static final String mobileRxFile_1 = "/sys/class/net/rmnet0/statistics/rx_bytes";
private static final String mobileRxFile_2 = "/sys/class/net/ppp0/statistics/rx_bytes";
private static final String mobileTxFile_1 = "/sys/class/net/rmnet0/statistics/tx_bytes";
private static final String mobileTxFile_2 = "/sys/class/net/ppp0/statistics/tx_bytes";

private static final String LOGGING_TAG = TrafficStatsFile.class.getSimpleName();

public long getMobileRxBytes() {
    return tryBoth(mobileRxFile_1, mobileRxFile_2);
}

public long getMobileTxBytes() {
    return tryBoth(mobileTxFile_1, mobileTxFile_2);
}

// Return the number from the first file which exists and contains data
private static long tryBoth(String a, String b) {
    long num = readNumber(a);
    return num >= 0 ? num : readNumber(b);
}

// Returns an ASCII decimal number read from the specified file, -1 on error.
private static long readNumber(String filename) {
    try {
        RandomAccessFile f = new RandomAccessFile(filename, "r");
        try {
            Log.d(LOGGING_TAG, "f.length() = " + f.length());
            String contents = f.readLine();
            if(!contents.isEmpty() && contents!=null) {
                try {
                    return Long.parseLong(contents);
                }
                catch(NumberFormatException nfex) {
                    Log.w(LOGGING_TAG, "File contents are not numeric: " + filename); 
                }
            }
            else {
                Log.w(LOGGING_TAG, "File contents are empty: " + filename); 
            }
        }
        catch (FileNotFoundException fnfex) {
            Log.w(LOGGING_TAG, "File not found: " + filename, fnfex);
        }
        catch(IOException ioex) {
            Log.w(LOGGING_TAG, "IOException: " + filename, ioex);
        }   
    }catch(FileNotFoundException ffe){
        Log.w(LOGGING_TAG, "File not found: " + filename, ffe);
    }
    return -1;
}

}
IBoS
  • 550
  • 5
  • 15
  • None of the following directories are present in my device: rmnet0, ppp0, rmnet0 and ppp0. I am using Samsung SM-T111. Is that device dependent ? – Gem Sep 17 '14 at 08:49
4

Changing it to a RandomAccessFile instead of File worked.

Edit: See IBoS's answer for working code. Changing the accepted answer to his.

Skylar Sutton
  • 4,632
  • 3
  • 27
  • 39