error - android studio fingerprint




使用NetworkStatsManager獲取移動數據使用記錄 (2)

有辦法獲得對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"/>
    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;
    }

    要請求這個權限,只需調用Intent

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

    還需要另一個權限: Manifest.permission.READ_PHONE_STATE 。 只有在需要獲取移動數據統計信息時才需要。 但是,這是正常的許可,所以可以作為任何其他許可請求

    1. 使用NetworkStatsManager

做了一個示例Github回購示範使用。

我想知道數據使用的歷史,並註意到“新”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,所以,我該怎麼做?


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




networkstatsmanager