使用NetworkStatsManager获取移动数据使用历史记录

时间:2016-04-18 19:22:15

标签: android permissions android-6.0-marshmallow networkstatsmanager

我想知道数据使用历史,并注意到了#34; new" android-6 NetworkStatsManager似乎是积极的(我已经使用了TrafficStats一段时间,但不会覆盖重启之前的任何事情。

来自API文档:

  

注意:此API需要权限PACKAGE_USAGE_STATS,即   系统级权限,不会授予第三方应用。   但是,声明权限意味着有意使用API​​和   设备的用户可以通过“设置”授予权限   应用。个人资料所有者应用会自动获得权限   查询他们管理的配置文件的数据(即任何查询   除了querySummaryForDevice(int,String,long,long))。设备所有者   应用程序同样可以访问主要用户的使用数据。

我想知道聚合级别的数据使用情况,而知道使用数据的应用程序,所以我尝试使用它:

NetworkStatsManager service = context.getSystemService(NetworkStatsManager.class);

NetworkStats.Bucket bucket = 
        service.querySummaryForDevice(ConnectivityManager.TYPE_MOBILE, null, from, to);
...

不幸的是,它会抛出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)
...

第三方应用不允许android.permission.READ_NETWORK_USAGE_HISTORY权限。所以这似乎是一个死胡同。

但是,我向内部钻了一下,发现你可以使用内部/隐藏API来做同样的事情,而不需要任何权限:

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();

我真的想对公共API做同样的事情,那么,我该怎么办呢?

2 个答案:

答案 0 :(得分:17)

无法访问私有API,无法访问NetworkStateManager。以下是步骤:

  1. AndroidManifest.xml中声明所需的权限:

    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission
        android:name="android.permission.PACKAGE_USAGE_STATS"
        tools:ignore="ProtectedPermissions"/>
    
    1. Activity
    2. 中征得同意

      android.permission.PACKAGE_USAGE_STATS不是正常的权限,不能简单地请求。要检查是否已授予权限,请检查:

      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;
      }
      

      要请求此权限,只需致电Intent

      Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
      startActivity(intent);
      

      还需要另外的许可:Manifest.permission.READ_PHONE_STATE。只有在您需要获得mobile data statistics时才需要它。但是,这是正常的权限,因此可以requested as any other permission

      1. 使用NetworkStatsManager

      制作样本Github repo以证明其用法。

答案 1 :(得分:0)

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