Getting mobile data usage history using NetworkStatsManager

ぐ巨炮叔叔 提交于 2019-12-17 17:42:03

问题


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?


回答1:


There is way to obtain the access to NetworkStateManager without getting access to private API. Here are the steps:

  1. 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"/>
    
    1. Ask for permission in 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

    1. Use NetworkStatsManager:

Made a sample Github repo demonstrating the usage.




回答2:


/*
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 "";
}


来源:https://stackoverflow.com/questions/36702621/getting-mobile-data-usage-history-using-networkstatsmanager

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!