修复 SecurityException: getDataNetworkTypeForSubscriber 问题

1.问题

  • 用户切换改变网络的过程中,应用概率会出现崩溃,日志如下

API level: '30'
OS version: '11'
ABI list: 'arm64-v8a,armeabi-v7a,armeabi'
Manufacturer: 'OnePlus'
Brand: 'OnePlus'
Model: 'GM1900'
Build fingerprint: 'OnePlus/OnePlus7_CH/OnePlus7:11/RKQ1.201022.002/2108161921:user/release-keys'
pid: 29126, tid: 29126, name: main  >>> package name <<<

java stacktrace:
java.lang.SecurityException: getDataNetworkTypeForSubscriber
    at android.os.Parcel.createExceptionOrNull(Parcel.java:2373)
    at android.os.Parcel.createException(Parcel.java:2357)
    at android.os.Parcel.readException(Parcel.java:2340)
    at android.os.Parcel.readException(Parcel.java:2282)
    at com.android.internal.telephony.ITelephony$Stub$Proxy.getNetworkTypeForSubscriber(ITelephony.java:8803)
    at android.telephony.TelephonyManager.getNetworkType(TelephonyManager.java:3070)
    at android.telephony.TelephonyManager.getNetworkType(TelephonyManager.java:3034)
    at j.k.b0.a.e.b.c(Unknown Source:51)
    at j.k.b.q.c.w.onCreateView(:14)
    at androidx.fragment.app.Fragment.performCreateView(Unknown Source:15)
    at h.l.d.q.V(:18)
    at h.l.d.q.T(:1)
    at h.l.d.q.U(Unknown Source:47)
    at h.l.d.a.n(Unknown Source:182)
    at h.l.d.q.E(:7)
    at h.l.d.q.b0(Unknown Source:84)
    at h.l.d.q.D(Unknown Source:31)
    at h.l.d.a.e(:2)
    at h.l.d.v.finishUpdate(Unknown Source:12)
    at androidx.viewpager.widget.ViewPager.r(:2)
    at androidx.viewpager.widget.ViewPager.onMeasure(:1)
    at android.view.View.measure(View.java:25671)
    at androidx.constraintlayout.widget.ConstraintLayout.e(:2)
    at androidx.constraintlayout.widget.ConstraintLayout.onMeasure(:70)
    at android.view.View.measure(View.java:25671)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6987)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
    at androidx.appcompat.widget.ContentFrameLayout.onMeasure(Unknown Source:154)
    at android.view.View.measure(View.java:25671)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6987)
    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
    at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
    at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
    at android.view.View.measure(View.java:25671)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6987)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
    at android.view.View.measure(View.java:25671)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6987)
    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
    at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
    at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
    at android.view.View.measure(View.java:25671)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6987)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
    at com.android.internal.policy.DecorView.onMeasure(DecorView.java:776)
    at android.view.View.measure(View.java:25671)
    at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:3713)
    at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:2496)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2771)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2182)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8730)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1352)
    at android.view.Choreographer.doCallbacks(Choreographer.java:1149)
    at android.view.Choreographer.doFrame(Choreographer.java:1049)
    at android.view.Choreographer$FrameHandler.handleMessage(Choreographer.java:1275)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:233)
    at android.app.ActivityThread.main(ActivityThread.java:8010)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:631)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:978)

另外一种场景就是集成了华为的二维码扫描  com.huawei.hms:scanplus:1.3.2.300 ,这个  SDK 比较奇葩的地方在于,使用 Wi-Fi 网络情况下是不会出现问题的。

但是在手机流量的情况下,不申请 android.permission.READ_PHONE_STATE 权限,初始化这个 SDK 会导致应用闪退。

崩溃堆栈如下:

java.lang.SecurityException: getNetworkTypeForSubscriber
	at android.os.Parcel.createExceptionOrNull(Parcel.java:2437)
	at android.os.Parcel.createException(Parcel.java:2421)
	at android.os.Parcel.readException(Parcel.java:2404)
	at android.os.Parcel.readException(Parcel.java:2346)
	at com.android.internal.telephony.ITelephony$Stub$Proxy.getNetworkTypeForSubscriber(ITelephony.java:9325)
	at android.telephony.TelephonyManager.getNetworkType(TelephonyManager.java:3002)
	at android.telephony.TelephonyManager.getNetworkType(TelephonyManager.java:2964)
	at com.huawei.hms.mlkit.common.ha.d.b(HianalyticsLogUtils.java:68)
	at com.huawei.hms.mlkit.common.ha.HianalyticsLogProvider.logEnd(HianalyticsLogProvider.java:6315)
	at com.huawei.hms.ml.camera.g$a.a(HiAnalyticsThread.java:109)
	at com.huawei.hms.ml.camera.g$a.handleMessage(HiAnalyticsThread.java:78)
	at android.os.Handler.dispatchMessage(Handler.java:114)
	at android.os.Looper.loopOnce(Looper.java:206)
	at android.os.Looper.loop(Looper.java:296)
	at com.huawei.hms.ml.camera.g.run(HiAnalyticsThread.java:51)

2.分析

  • 根据 SecurityException: getDataNetworkTypeForSubscriber 可以看到,这是一个安全性异常,所以猜测应用在 Android11 的权限有关,由于缺少该权限导致无法访问接口而异常。

  • 找到网络状态检测方法,可以看到调用了 TelephonyManager.getNetworkType()接口获取网络类型,该方法是需要 READ_PHONE_STATE 权限的,该方法上面也有 RequiresPermission 注解声明

    @RequiresPermission(value = "android.permission.READ_PHONE_STATE")
    public int getNetworkState(Context context) {
        if (null == mConnectivityManager) { // 为空则认为无网络
            return NETWORK_NONE;
        }
        // 获取网络类型,如果为空,返回无网络
        NetworkInfo activeNetInfo = mConnectivityManager.getActiveNetworkInfo();
        if (activeNetInfo == null || !activeNetInfo.isAvailable()) {
            return NETWORK_NONE;
        }
        // 判断是否为WIFI
        NetworkInfo wifiInfo = mConnectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
        if (null != wifiInfo) {
            NetworkInfo.State state = wifiInfo.getState();
            if (null != state) {
                if (state == NetworkInfo.State.CONNECTED || state == NetworkInfo.State.CONNECTING) {
                    return NETWORK_WIFI;
                }
            }
        }
        // 若不是WIFI,则去判断是2G、3G、4G网
        TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        int networkType = telephonyManager.getNetworkType();
        ...
    }
  • 进一步分析,接口的调用堆栈为:

TelephonyManager.getNetworkType ->PhoneInterfaceManager.getNetworkTypeForSubscriber->TelephonyPermissions.checkCallingOrSelfReadPhoneState->TelephonyPermissions.checkReadPhoneState->ContextImpl.enforcePermission->ContextImpl.checkPermission->PermissionManager.checkPermission->PermissionTelephonyManager.getNetworkType ->
PhoneInterfaceManager.getNetworkTypeForSubscriber->
TelephonyPermissions.checkCallingOrSelfReadPhoneState->
TelephonyPermissions.checkReadPhoneState->
ContextImpl.enforcePermission->
ContextImpl.checkPermission->
PermissionManager.checkPermission->
PermissionManager.checkPermissionUncached->
ActivityManager.getService->
IActivityManager.checkPermission->

3.解决方法

private void requestPermission() {
        LogUtil.printE(HomeActivity.class, ">>> etrunc requestPermission SDK-VERSION= " + Build.VERSION.SDK_INT);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { //Android 11 授权读写权限
            XXPermissions.with(HomeActivity.this)
                    // 不适配 Android 11 可以这样写
                    //.permission(Permission.Group.STORAGE)
                    // 适配 Android 11 需要这样写,这里无需再写 Permission.Group.STORAGE
                    .permission(Permission.MANAGE_EXTERNAL_STORAGE, Permission.READ_PHONE_STATE)
                    .request(new OnPermissionCallback() {

                        @Override
                        public void onGranted(List<String> permissions, boolean all) {
                            if (all) {
                                ToastUtil.showToast(getApplication(), R.string.permission_success_tip);
                            }
                        }

                        @Override
                        public void onDenied(List<String> permissions, boolean never) {
                            if (never) {
                                ToastUtil.showToast(getApplication(), R.string.permissions_refuse_authorization);
                                // 如果是被永久拒绝就跳转到应用权限系统设置页面
                                XXPermissions.startPermissionActivity(HomeActivity.this, permissions);
                            } else {
                                ToastUtil.showToast(getApplication(), R.string.permissions_error);
                            }
                        }
                    });
        }
    }

参考链接


修复 SecurityException: getDataNetworkTypeForSubscriber 问题

发布者

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注