android - significa - Obteniendo historial de uso de datos móviles usando NetworkStatsManager
reiniciar uso de datos android (2)
Hay forma de obtener acceso a NetworkStateManager
sin tener acceso a API privada. Estos son los pasos:
Declare los permisos necesarios en
AndroidManifest.xml
:<uses-permission android:name="android.permission.READ_PHONE_STATE"/> <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" tools:ignore="ProtectedPermissions"/>
- Pedir permiso en
Activity
android.permission.PACKAGE_USAGE_STATS
no es un permiso normal, simplemente no se puede solicitar. Para verificar si el permiso ha sido otorgado, verifique: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; }
Para solicitar este permiso, simplemente llame a
Intent
:Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS); startActivity(intent);
Otra permmission también es necesaria:
Manifest.permission.READ_PHONE_STATE
. Solo se necesita cuando necesita obtener estadísticas de datos móviles . Sin embargo, este es un permiso normal, por lo que se puede solicitar como cualquier otro permiso- Use
NetworkStatsManager
:
- Pedir permiso en
Hice una muestra de repo de Github demostrando el uso.
Quiero saber el historial de uso de datos y noté el "nuevo" android-6 NetworkStatsManager
que parece positivo (he usado TrafficStats
un tiempo, pero eso no cubrirá nada previo a un reinicio).
De la documentación API:
NOTA: Esta API requiere el permiso PACKAGE_USAGE_STATS, que es un permiso de nivel de sistema y no se otorgará a aplicaciones de terceros. Sin embargo, declarar el permiso implica la intención de usar la API y el usuario del dispositivo puede otorgar el permiso a través de la aplicación Configuración. Las aplicaciones de los propietarios de perfil reciben automáticamente permiso para consultar datos en el perfil que administran (es decir, para cualquier consulta, excepto querySummaryForDevice (int, String, long, long)). Las aplicaciones del propietario del dispositivo también tienen acceso a los datos de uso del usuario principal.
Quiero saber el uso de datos en un nivel agregado y no hasta qué aplicación utiliza los datos, así que traté de usarlo así:
NetworkStatsManager service = context.getSystemService(NetworkStatsManager.class);
NetworkStats.Bucket bucket =
service.querySummaryForDevice(ConnectivityManager.TYPE_MOBILE, null, from, to);
...
Desafortunadamente eso arroja una 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)
...
El permiso android.permission.READ_NETWORK_USAGE_HISTORY
no está permitido para aplicaciones de terceros. Entonces esto parecía un callejón sin salida.
Sin embargo, profundicé un poco en las partes internas y descubrí que puedes usar la API interna / oculta para hacer lo mismo sin solicitar ningún permiso:
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();
Realmente quiero hacer lo mismo con la API pública, entonces, ¿cómo hago eso?
/*
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 "";
}