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

dac*_*cwe 16 permissions android android-6.0-marshmallow networkstatsmanager

我想知道数据使用历史,并注意到"新"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);
...
Run Code Online (Sandbox Code Playgroud)

不幸的是,抛出一个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)
...
Run Code Online (Sandbox Code Playgroud)

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();
Run Code Online (Sandbox Code Playgroud)

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

R. *_*ski 19

有办法获得访问权限而NetworkStateManager无法访问私有API.以下是步骤:

  1. 在以下位置声明所需的权限AndroidManifest.xml:

    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission
        android:name="android.permission.PACKAGE_USAGE_STATS"
        tools:ignore="ProtectedPermissions"/>
    
    Run Code Online (Sandbox Code Playgroud)
    1. 请求许可 Activity

    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;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    要获得此许可,只需致电Intent:

    Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
    startActivity(intent);
    
    Run Code Online (Sandbox Code Playgroud)

    还需要另外一个许可:Manifest.permission.READ_PHONE_STATE.当您需要获取移动数据统计信息时,仅需要它.但是,这是正常的权限,因此可以像任何其他权限一样请求

    1. 用途NetworkStatsManager:

制作了一个示例Github repo来演示使用情况.

  • 我被 READ_NETWORK_USAGE_HISTORY 困住了。在 6.0.1 上尝试了 repo,并从 NetworkStatsManager 端得到了奇怪的数字。你确定这个黑客仍然有效吗?万分感谢! (2认同)