I want to know the data usage history and noticed the "new" android-6 NetworkStatsManager
which seems positive (I've used TrafficStats
a while but that won't cover anything previous a reboot).
From the API documentation:
NOTE: This API requires the permission PACKAGE_USAGE_STATS, which is a system-level permission and will not be granted to third-party apps. However, declaring the permission implies intention to use the API and the user of the device can grant permission through the Settings application. Profile owner apps are automatically granted permission to query data on the profile they manage (that is, for any query except querySummaryForDevice(int, String, long, long)). Device owner apps likewise get access to usage data of the primary user.
I want to know the data usage on a aggregated level and not down to which app that uses the data so I tried to use it like this:
NetworkStatsManager service = context.getSystemService(NetworkStatsManager.class);
NetworkStats.Bucket bucket =
service.querySummaryForDevice(ConnectivityManager.TYPE_MOBILE, null, from, to);
...
Unfortunately that throws a SecurityException
:
java.lang.SecurityException: NetworkStats: Neither user 10174 nor current process has android.permission.READ_NETWORK_USAGE_HISTORY.
at android.os.Parcel.readException(Parcel.java:1620)
at android.os.Parcel.readException(Parcel.java:1573)
at android.net.INetworkStatsSession$Stub$Proxy.getDeviceSummaryForNetwork(INetworkStatsSession.java:259)
at android.app.usage.NetworkStats.getDeviceSummaryForNetwork(NetworkStats.java:316)
at android.app.usage.NetworkStatsManager.querySummaryForDevice(NetworkStatsManager.java:100)
...
The android.permission.READ_NETWORK_USAGE_HISTORY
permission is not allowed for third party apps. So this seemed like a dead end.
However, I drilled down a bit into the internals and found out that you can use the internal/hidden API to do the same thing without requesting any permissions:
INetworkStatsService service =
INetworkStatsService.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
INetworkStatsSession session = service.openSession();
NetworkTemplate mobileTemplate = NetworkTemplate.buildTemplateMobileWildcard();
int fields = NetworkStatsHistory.FIELD_RX_BYTES | NetworkStatsHistory.FIELD_TX_BYTES;
NetworkStatsHistory mobileHistory = session.getHistoryForNetwork(mobileTemplate, fields);
for (int i = 0; i < mobileHistory.size(); i++) {
NetworkStatsHistory.Entry entry = mobileHistory.getValues(i, null);
...
}
session.close();
I really want to do the same with the public API, so, how do I do just that?
There is way to obtain the access to NetworkStateManager
without getting access to private API. Here are the steps:
Declare the required permissions in AndroidManifest.xml
:
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions"/>
Activity
android.permission.PACKAGE_USAGE_STATS
is not a normal permission, there cannot be simply requested. In order to check, whether the permission has been granted, check:
AppOpsManager appOps = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(), getPackageName());
if (mode == AppOpsManager.MODE_ALLOWED) {
return true;
}
To ask for this permission, simply call Intent
:
Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
startActivity(intent);
Another permmission is also needed: Manifest.permission.READ_PHONE_STATE
. It is needed only, when you need to get mobile data statistics. However, this is normal permission so can be requested as any other permission
NetworkStatsManager
:Made a sample Github repo demonstrating the usage.
/*
getting youtube usage for both mobile and wifi.
*/
public long getYoutubeTotalusage(Context context) {
String subId = getSubscriberId(context, ConnectivityManager.TYPE_MOBILE);
return getYoutubeUsage(ConnectivityManager.TYPE_MOBILE, subId) + getYoutubeUsage(ConnectivityManager.TYPE_WIFI, "");
}
private long getYoutubeUsage(int networkType, String subScriberId) {
NetworkStats networkStatsByApp;
long currentYoutubeUsage = 0L;
try {
networkStatsByApp = networkStatsManager.querySummary(networkType, subScriberId, 0, System.currentTimeMillis());
do {
NetworkStats.Bucket bucket = new NetworkStats.Bucket();
networkStatsByApp.getNextBucket(bucket);
if (bucket.getUid() == packageUid) {
//rajeesh : in some devices this is immediately looping twice and the second iteration is returning correct value. So result returning is moved to the end.
currentYoutubeUsage = (bucket.getRxBytes() + bucket.getTxBytes());
}
} while (networkStatsByApp.hasNextBucket());
} catch (RemoteException e) {
e.printStackTrace();
}
return currentYoutubeUsage;
}
private String getSubscriberId(Context context, int networkType) {
if (ConnectivityManager.TYPE_MOBILE == networkType) {
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
return tm.getSubscriberId();
}
return "";
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With