9

背景

我知道我们可以通过使用类似的东西(过去问过,这里)来获取指定应用程序的网络使用情况(到目前为止,从某个特定时间开始,移动和 Wifi 使用的总带宽):

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

文件:

https://developer.android.com/reference/android/app/usage/NetworkStatsManager.html#queryDetailsForUid(int,%20java.lang.String,%20long,%20long,%20int)

也可以获取全球网络使用情况(使用TrafficStats.getUidRxBytes(applicationInfo.uid)and TrafficStats.getUidTxBytes(applicationInfo.uid)),但这不是这个线程的全部内容。

问题

似乎 Android Q 计划导致许多设备身份功能停止工作,并且getSubscriberId就是其中之一。

我试过的

我尝试将 targetSdk 设置为 29 (Q),看看当我尝试使用它时会发生什么。

正如预期的那样,我遇到了一个异常,表明我不能再这样做了。它说 :

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)

搜索互联网和这里,我没有看到提到这一点,但我发现了类似的问题,获取 IMEI 和其他标识符:

所以现在我只是在这里做了一个关于它的错误报告(包括一个示例项目):

https://issuetracker.google.com/issues/134919382

问题

是否可以在 Android Q 上获取指定应用程序的网络使用情况(针对它时)?也许没有subscriberId?

如果是这样,怎么做?

如果没有,是否可以通过root或adb?


编辑:

好的,我不知道如何正式使用它,但至少对于 root 访问,可以使用从这里找到的这个解决方案获取订阅者 ID 。

意思是这样的:

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

当然,这不是官方的解决方案,但总比没有好......

编辑:通过 root 获取它的部分是错误的。它没有任何帮助。

4

3 回答 3

1

谷歌团队在您提到的线程的评论中说:“状态:不会修复(预期行为)这是按预期工作的。IMEI 是个人标识符,不会将其提供给应用程序政策。没有解决方法。 “。所以我猜想类NetworkStatsManager中需要IMSI(也被认为是个人标识符)工作的方法(比如queryDetailsForUid(int, String, long, long, int))现在在Android Q中被破坏了。你可以使用这些方法来获取应用程序的Wifi使用细节(通过传递subscriberId 的空字符串),但为了获取移动使用详细信息,您现在必须依赖良好的旧TrafficStats类,直到问题被注意到并得到解决。

于 2019-06-27T18:19:58.970 回答
1

您可以为 API 29 及更高版本提供 null 值。它返回 WIFI 和移动数据的值。

文档:

如果适用,网络接口的订阅者 ID。从 API 级别 29 开始,subscriberId 受到额外限制的保护。调用不符合新要求的应用程序访问subscriberId 可以在查询移动网络类型时提供空值以接收所有移动网络的使用情况。有关其他详细信息,请参阅 TelephonyManager.getSubscriberId()。

权限(不要忘记获得用户的许可):

<uses-permission android:name="android.permission.READ_PHONE_STATE"
    android:maxSdkVersion="28"/>
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
    tools:ignore="ProtectedPermissions" />

示例代码:

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

获取应用的 NetworkStats:

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();
    }
}
于 2021-11-06T16:18:18.280 回答
0

我们正在使用 NetworkStatsManager.querySummaryForDevice()。由于一个偶然的错误,我们在 Q 中将 null 作为 MOBILE 的subscriberId 传递。它似乎在我们的设备上工作。我不确定这是错误还是功能,但这些值与我们预期的蜂窝使用情况相匹配。

总而言之,对于这个用例,我们可以只使用 TrafficStats,但在 Pie 之前它是不稳定的。

于 2020-05-21T15:02:05.910 回答