Получение истории использования мобильных данных с помощью 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);
...
К сожалению, это бросает 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 ответа
Есть способ получить доступ к NetworkStateManager
без получения доступа к приватному API. Вот шаги:
Объявите необходимые разрешения в
AndroidManifest.xml
:<uses-permission android:name="android.permission.READ_PHONE_STATE"/> <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" tools:ignore="ProtectedPermissions"/>
- Спросите разрешения в
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
, Это нужно только тогда, когда вам нужно получить статистику мобильных данных. Однако это нормальное разрешение, поэтому его можно запросить как любое другое разрешение.- использование
NetworkStatsManager
:
- Спросите разрешения в
Сделал пример репозитория Github, демонстрирующий использование.
/*
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 "";
}