I know that we can get the network usage (total bandwidth used of mobile&Wifi so far, from some specific time) of a specified app by using something like that (asked in the past, here) :
private final static int[] NETWORKS_TYPES = new int[]{ConnectivityManager.TYPE_WIFI, ConnectivityManager.TYPE_MOBILE};
long rxBytes=0L, txBytes=0L;
final TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
final String subscriberId = telephonyManager.getSubscriberId();
final ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(packageName, 0);
final int uid = applicationInfo.uid;
for (int networkType : NETWORKS_TYPES) {
final NetworkStats networkStats = networkStatsManager.queryDetailsForUid(networkType, subscriberId, 0, System.currentTimeMillis(), uid);
final Bucket bucketOut = new Bucket();
while (true) {
networkStats.getNextBucket(bucketOut);
final long rxBytes = bucketOut.getRxBytes();
if (rxBytes >= 0)
totalRx += rxBytes;
final long txBytes = bucketOut.getTxBytes();
if (txBytes >= 0)
totalTx += txBytes;
if (!networkStats.hasNextBucket())
break;
}
}
}
Docs:
https://developer.android.com/reference/android/app/usage/NetworkStatsManager.html#queryDetailsForUid(int,%20java.lang.String,%20long,%20long,%20int)
It's also possible to get the global network usage (using TrafficStats.getUidRxBytes(applicationInfo.uid)
and TrafficStats.getUidTxBytes(applicationInfo.uid)
), but that's not what this thread is all about.
It seems Android Q is planned to cause a lot of device-identity functions to stop working anymore, and getSubscriberId
is one of them.
I tried to set the targetSdk to 29 (Q) and see what happens when I try to use this.
As expected, I got an exception that shows me that I can't do it anymore. It says :
019-06-11 02:08:01.871 13558-13558/com.android.myapplication E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.android.myapplication, PID: 13558
java.lang.SecurityException: getSubscriberId: The user 10872 does not meet the requirements to access device identifiers.
at android.os.Parcel.createException(Parcel.java:2069)
at android.os.Parcel.readException(Parcel.java:2037)
at android.os.Parcel.readException(Parcel.java:1986)
at com.android.internal.telephony.IPhoneSubInfo$Stub$Proxy.getSubscriberIdForSubscriber(IPhoneSubInfo.java:984)
at android.telephony.TelephonyManager.getSubscriberId(TelephonyManager.java:3498)
at android.telephony.TelephonyManager.getSubscriberId(TelephonyManager.java:3473)
Searching the Internet and here, I don't see this mentioned, but I have found about similar issues, of getting IMEI and other identifiers:
So for now I just made a bug report about it here (including a sample project) :
https://issuetracker.google.com/issues/134919382
Is it possible to get network usage of a specified app on Android Q (when targeting to it) ? Maybe without subscriberId?
If so, how?
If not, is it possible by having root, or via adb?
EDIT:
OK, I don't know how to officially use this, but at least for root access it is possible to get the subscriberId, using this solution, found from here.
Meaning something like that:
@SuppressLint("MissingPermission", "HardwareIds")
fun getSubscriberId(telephonyManager: TelephonyManager): String? {
try {
return telephonyManager.subscriberId
} catch (e: Exception) {
}
val commandResult = Root.runCommands("service call iphonesubinfo 1 | grep -o \"[0-9a-f]\\{8\\} \" | tail -n+3 | while read a; do echo -n \"\\u\${a:4:4}\\u\${a:0:4}\"; done")
val subscriberId = commandResult?.getOrNull(0)
return if (subscriberId.isNullOrBlank()) null else subscriberId
}
It's not an official solution, of course, but it's better than nothing...
EDIT: the part of getting it via root is wrong. It doesn't help in any way.
You can provide null value for API 29 and above. It returns values for both WIFI and Mobile Data.
Documentation:
If applicable, the subscriber id of the network interface. Starting with API level 29, the subscriberId is guarded by additional restrictions. Calling apps that do not meet the new requirements to access the subscriberId can provide a null value when querying for the mobile network type to receive usage for all mobile networks. For additional details see TelephonyManager.getSubscriberId().
Permissions (Don't forget to get permission from the user):
<uses-permission android:name="android.permission.READ_PHONE_STATE"
android:maxSdkVersion="28"/>
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
Example code:
//network stats
NetworkStatsManager networkStatsManager = (NetworkStatsManager) activity.getSystemService(Context.NETWORK_STATS_SERVICE);
int[] networkTypes = new int[]{NetworkCapabilities.TRANSPORT_CELLULAR, NetworkCapabilities.TRANSPORT_WIFI};
String subscriberId;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
TelephonyManager telephonyManager = (TelephonyManager) activity.getSystemService(Context.TELEPHONY_SERVICE);
try {
subscriberId = telephonyManager.getSubscriberId(); //MissingPermission
} catch (SecurityException e) {
subscriberId = null;
}
} else {
subscriberId = null;
}
Get NetworkStats for an app:
long receivedBytes = 0;
long transmittedBytes = 0;
for (int networkType : networkTypes) {
NetworkStats networkStats;
try {
networkStats = networkStatsManager
.queryDetailsForUid(networkType,
subscriberId,
0,
System.currentTimeMillis(),
appUid);
} catch (SecurityException e) {
networkStats = null;
}
if(networkStats != null) {
NetworkStats.Bucket bucketOut = new NetworkStats.Bucket();
while (networkStats.hasNextBucket()) {
networkStats.getNextBucket(bucketOut);
long rxBytes = bucketOut.getRxBytes();
long txBytes = bucketOut.getTxBytes();
if (rxBytes >= 0) {
receivedBytes += rxBytes;
}
if (txBytes >= 0) {
transmittedBytes += txBytes;
}
}
networkStats.close();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With