telephonyManager.listen 在将 targetSdkVersion 设置为 31 或 Android 12 后不起作用。

10

我将目标SDK版本升级到31后,telephonyManager.listen无法工作。请考虑以下代码行:

telephonyManager.listen(phoneStateListener,PhoneStateListener.LISTEN_CALL_STATE);

logcat错误消息如下:

2022-02-27 13:51:36.313 6426-6426/com.example.app E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.app, PID: 6426
java.lang.RuntimeException: Unable to start service com.example.app.myPlayService@7ace64e with Intent { cmp=com.example.app/.myPlayService (has extras) }: java.lang.SecurityException: listen
    at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:4657)
    at android.app.ActivityThread.access$2000(ActivityThread.java:247)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2091)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loopOnce(Looper.java:201)
    at android.os.Looper.loop(Looper.java:288)
    at android.app.ActivityThread.main(ActivityThread.java:7839)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
 Caused by: java.lang.SecurityException: listen
    at android.os.Parcel.createExceptionOrNull(Parcel.java:2425)
    at android.os.Parcel.createException(Parcel.java:2409)
    at android.os.Parcel.readException(Parcel.java:2392)
    at android.os.Parcel.readException(Parcel.java:2334)
    at com.android.internal.telephony.ITelephonyRegistry$Stub$Proxy.listenWithEventList(ITelephonyRegistry.java:1036)
    at android.telephony.TelephonyRegistryManager.listenFromListener(TelephonyRegistryManager.java:250)
    at android.telephony.TelephonyManager.listen(TelephonyManager.java:5999)
    at com.example.app.myPlayService.onStartCommand(myPlayService.java:186)
    at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:4639)
    at android.app.ActivityThread.access$2000(ActivityThread.java:247) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2091) 
    at android.os.Handler.dispatchMessage(Handler.java:106) 
    at android.os.Looper.loopOnce(Looper.java:201) 
    at android.os.Looper.loop(Looper.java:288) 
    at android.app.ActivityThread.main(ActivityThread.java:7839) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003) 
 Caused by: android.os.RemoteException: Remote stack trace:
    at com.android.internal.telephony.TelephonyPermissions.enforceCarrierPrivilege(TelephonyPermissions.java:634)
    at com.android.internal.telephony.TelephonyPermissions.checkReadPhoneState(TelephonyPermissions.java:149)
    at com.android.internal.telephony.TelephonyPermissions.checkCallingOrSelfReadPhoneState(TelephonyPermissions.java:94)
    at com.android.server.TelephonyRegistry.checkListenerPermission(TelephonyRegistry.java:3093)
    at com.android.server.TelephonyRegistry.listen(TelephonyRegistry.java:1024)

我在 Android Manifest 文件中添加了以下权限,但仍然不起作用。

<uses-permission android:name="android.permission.READ_PHONE_STATE" />

在 Android 12 或以下版本中,它可以完美地运行。如何解决在 Android SDK 31 上的问题?提前感谢。


我正在调用该API,并且我正在针对API 31进行操作,所以应该是可行的。而且我不需要READ_PHONE_STATE权限。 - kingston
5个回答

16

当目标API为31时,TelephonyManager.listen会报错: java.lang.SecurityException: listen

同时,TelephonyManager.registerTelephonyCallback也会出现相同的情况,除非禁用ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION选项。

开发者选项 / 应用兼容性更改 / 选择应用程序 / 启用目标SDK版本 >= 31

由于在API级别31中已弃用TelephonyManager.listen并添加了TelephonyManager.registerTelephonyCallback,因此您应该将代码编写如下:

更新 添加了callStateListenerphoneStateListener示例

    private void registerCallStateListener() {
        if (!callStateListenerRegistered) {
            TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
                    telephonyManager.registerTelephonyCallback(getMainExecutor(), callStateListener);
                    callStateListenerRegistered = true;
                }
            } else {
                telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
                callStateListenerRegistered = true;
            }
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.S)
    private static abstract class CallStateListener extends TelephonyCallback implements TelephonyCallback.CallStateListener {
        @Override
        abstract public void onCallStateChanged(int state);
    }

    private boolean callStateListenerRegistered = false;

    private CallStateListener callStateListener = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) ?
            new CallStateListener() {
                @Override
                public void onCallStateChanged(int state) {
                    // Handle call state change
                }
            }
            : null;

    private PhoneStateListener phoneStateListener = (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) ?
            new PhoneStateListener() {
                @Override
                public void onCallStateChanged(int state, String phoneNumber) {
                    // Handle call state change
                }
            }
            : null;

文档链接:

因为 TelephonyCallback.CallStateListener 需要 Manifest.permission.READ_PHONE_STATE 权限,而该权限属于危险保护级别,所以您需要按照这个指南请求运行时权限:请求应用程序权限


请问您能否提供如何声明callStateListener的代码? - bhaskar
PhoneStateListener需要Manifest.permission.READ_PHONE_STATE权限吗? - Ajit Kumar Dubey
1
@AjitKumarDubey,PhoneStateListener不需要READ_PHONE_STATE权限。但是在API级别31中已被弃用。现在应该使用CallStateListener,它需要该权限。请在AndroidManifest.xml中添加该权限。 - user1801374
3
@user1801374 但我的问题是WhatsApp是如何做到的。在没有电话状态权限的情况下,他们在API级别31中处理呼叫状态。我已经验证过了,它运行良好。 - Ajit Kumar Dubey

6
你的通话监听会在提供LISTEN_CALL_STATE时触发onCallStateChanged()回调。
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);

正如文档中所述,如果您的目标是 Android 31 或更高版本,则 onCallStateChanged() 需要 READ_PHONE_STATE 权限。

需要权限:READ_PHONE_STATE,适用于 API 级别 31+ 的应用程序。

请参阅: https://developer.android.com/reference/android/telephony/PhoneStateListener#onCallStateChanged(int,%20java.lang.String)

因此,为解决此问题,在调用 listen 调用之前(仅在代码在 Android 31+ 上执行时),只需请求该权限即可,如下所示:

    // for API 31+: you need to first check for READ_PHONE_STATE permission to be able to listen to LISTEN_CALL_STATE
    if (Build.VERSION.SDK_INT >= 31)
    {
        if(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED)
            telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
    }
    else // no permission needed
        telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);

1
如果有人在这里寻找一个支持API <= 30和API 31+(因为它们必须被不同地处理)的Kotlin + Flow版本的代码,那么这里就是。

这假定权限由调用UI代码处理。

val callState = callbackFlow {
    val telephonyManager = application.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager

    // The thread Executor used to run the listener. This governs how threads are created and
    // reused. Here we use a single thread.
    val exec = Executors.newSingleThreadExecutor()

    if (Build.VERSION.SDK_INT >= 31) {
        // SDK >= 31 uses TelephonyManager.registerTelephonyCallback()

        val callback = object : TelephonyCallback(), TelephonyCallback.CallStateListener {
            override fun onCallStateChanged(state: Int) {
                trySend(Pair(state, null))
            }
        }
        telephonyManager.registerTelephonyCallback(exec, callback)

        awaitClose {
            telephonyManager.unregisterTelephonyCallback(callback)
            exec.shutdown()
        }
    }
    else {
        // SDK 30 uses TelephonyManager.listen()

        @Suppress("OVERRIDE_DEPRECATION")
        val callback = object : PhoneStateListener(exec) {
            override fun onCallStateChanged(state: Int, phoneNumber: String) {
                trySend(Pair(state, phoneNumber))
            }
        }
        telephonyManager.listen(callback, PhoneStateListener.LISTEN_CALL_STATE)

        awaitClose {
            telephonyManager.listen(callback, 0)
            exec.shutdown()
        }
    }
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null)

流程提供了一个Pair<Int, String?>,其中.first是呼叫状态,.second是API <= 30中的电话号码。
我在我的博客上有一个使用Kotlin + Compose + Flows的完整示例,链接在这里: https://tdcolvin.medium.com/network-type-in-android-watching-for-5g-4g-3g-2g-capability-a52db23bf3bf

0

这是我正在使用的方法:

    private suspend fun updateServiceState() {
        withContext(Dispatchers.Main) {
            telephonyManager.listen(object : PhoneStateListener() {
                override fun onServiceStateChanged(state: ServiceState) {
                    telephonyManager.listen(this, LISTEN_NONE)
                    ...
                }
            }, PhoneStateListener.LISTEN_SERVICE_STATE)

        }
    }

我正在针对Android 12,API 31进行开发,但我没有READ_PHONE_STATE权限。

你能把这段代码转换成Java吗?我不会Kotlin。 - user2081099
你是否在主线程中注册了监听器? - kingston
我已经在前台服务上注册了监听器。 - user2081099
那仍然不能告诉我它是否在主线程中,但我猜它是。 - kingston
是的,它在主线程上。 - user2081099

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接