In my app, multiple App Widgets can be placed on the home screen, with each widget fetching remote data on a periodic basis.
My goals are:
- track data usage associated with each of multiple widgets separately
- WiFi and mobile data usage
- of my own app only (not interested in data usage of other apps)
- no extra permissions
- allow widgets to perform network operations concurrently without double counting of data usage
- include data usage in
WebViewactivity initiated from widget - report results in-app (not just when tethered to Network Traffic Tool)
It's easy enough to log data usage for your own UID before and after a widget update with TrafficStats, by using getUidRxBytes() and getUidTxBytes() (passing your own app's UID), and then taking the difference. Whilst this rough-and-ready approach gives sensible values when the widgets run in series, when they are running concurrently there is inevitably a degree of double (triple etc) counting going on.
I'd rather not force the widgets to update in series, because I'd rather let the job scheduling engine of the device to work out what is best. So I need a way to separate data usage for each widget from each other.
It seems that the use of tags might be the way to go... tag network operations with the appWidgetId and then filter the stats based on this appWidgetId. I'm not convinced this approach will actually enable data usage of one widget to be distinguished from another (maybe the buckets will become confused), but the fact is I can't get tagging to work at all so I can't yet tell.
You can set the tag with setThreadStatsTag(tag) before a network operation starts, but I can't see any method in the TrafficStats class for retrieving results filtered by tag. Does anyone know any different?
So I then looked at NetworkStatsManager, and in particular NetworkStatsManager.queryDetailsForUidTag(... , tag). This seems to allow you to filter based on a specific tag, and works without any extra permissions (for your own app's UID) which is good. But I can't get that method to return any buckets with data, despite setting TrafficStats.setThreadStatsTag(appWidgetId) before network operations using HttpURLConnection, and querying with the same tag afterwards by calling NetworkStatsManager.queryDetailsForUidTag(... , appWidgetId).
On the other hand, NetworkStatsManager.queryDetailsForUid() (no tag parameter) does return buckets with valid data, so it seems to be just the tagging that isn't working, rather than anything fundamental I'm doing wrong with NetworkStatsManager per se.
I set the tag with:
@Override
protected void onPreExecute() {
TrafficStats.setThreadStatsTag(appWidgetId);
}
@Override
protected Bitmap doInBackground(String... baseUrls) {
// fetch data with HttpURLConnection
}
@Override
protected void onPostExecute(Bitmap bmp) {
TrafficStats.clearThreadStatsTag();
}
And (for WiFi data) I query the stats with:
void networkStatsStuff() {
NetworkStats networkStats;
NetworkStats networkStatsTagged;
int networkType = ConnectivityManager.TYPE_WIFI;
try {
networkStats = networkStatsManager.queryDetailsForUid(networkType, "", startTime, endTime, myUid);
} catch (RemoteException e) {
return;
}
networkStatsTagged = networkStatsManager.queryDetailsForUidTag(networkType, "", startTime, endTime, myUid, appWidgetId);
long totalData = getTotalData(networkStats);
long totalDataTagged = getTotalData(networkStatsTagged);
Log.d(TAG, "totalData: " + totalData); // this seems to work
Log.d(TAG, "totalDataTagged: " + totalDataTagged); // this is always zero
}
long getTotalData(NetworkStats networkStats) {
long totalData = 0;
do {
NetworkStats.Bucket bucket = new NetworkStats.Bucket();
networkStats.getNextBucket(bucket);
totalData = totalData + bucket.getRxBytes() + bucket.getTxBytes();
} while (networkStats.hasNextBucket());
return totalData;
}
What am I doing wrong? (EDIT: partial answer is below.) Or maybe there is a completely different way to achieve my aims?