web-dev-qa-db-fra.com

Obtenir l'historique d'utilisation des données mobiles à l'aide de NetworkStatsManager

Je veux connaître l'historique d'utilisation des données et remarqué le "nouvel" Android-6 NetworkStatsManager qui semble positif (j'ai utilisé TrafficStats depuis un moment, mais cela ne couvrira rien d'un redémarrage précédent). 

De la documentation de l'API:

REMARQUE: cette API nécessite l'autorisation PACKAGE_USAGE_STATS, qui est un l'autorisation au niveau du système et ne sera pas accordée aux applications tierces . Cependant, la déclaration de l'autorisation implique l'intention d'utiliser l'API et l'utilisateur du périphérique peut accorder une autorisation via les paramètres application. Les applications du propriétaire du profil bénéficient automatiquement de l'autorisation pour interroger des données sur le profil qu'ils gèrent (c'est-à-dire pour toute requête sauf querySummaryForDevice (int, String, long, long)). Propriétaire de l'appareil De même, les applications ont accès aux données d'utilisation de l'utilisateur principal.

Je veux connaître l'utilisation des données à un niveau agrégé et not jusqu'à quelle application utilise les données, j'ai donc essayé de l'utiliser de la manière suivante:

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

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

Malheureusement, cela jette une 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)
...

L'autorisation Android.permission.READ_NETWORK_USAGE_HISTORY n'est pas autorisée pour les applications tierces. Donc, cela semblait être une impasse.

Cependant, j'ai exploré un peu les éléments internes et découvert que vous pouvez utiliser l'API interne/cachée pour faire la même chose sans demander aucune autorisation:

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

Je veux vraiment faire la même chose avec l'API publique, alors comment faire cela?

15
dacwe

Il existe un moyen d'obtenir l'accès à NetworkStateManager sans avoir accès à une API privée. Voici les étapes:

  1. Déclarez les autorisations requises dans AndroidManifest.xml:

    <uses-permission Android:name="Android.permission.READ_PHONE_STATE"/>
    <uses-permission
        Android:name="Android.permission.PACKAGE_USAGE_STATS"
        tools:ignore="ProtectedPermissions"/>
    
    1. Demander la permission dans Activity

    Android.permission.PACKAGE_USAGE_STATS n'est pas une permission normale, il ne peut pas être simplement demandé. Afin de vérifier si l'autorisation a été accordée, vérifiez:

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

    Pour demander cette permission, appelez simplement Intent:

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

    Une autre autorisation est également nécessaire: Manifest.permission.READ_PHONE_STATE. Il est nécessaire uniquement lorsque vous devez obtenir statistiques de données mobiles . Cependant, ceci est une permission normale donc peut être demandé comme n'importe quelle autre permission

    1. Utilisez NetworkStatsManager:

Faites un exemple Github repo démontrant l’utilisation.

15
R. Zagórski
/*
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 "";
}
0
rajeesh