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
: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:To ask for this permission, simply call
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 permissionNetworkStatsManager
:Made a sample Github repo demonstrating the usage.