簡體   English   中英

targetSdkVersion 設置為 31 或 android 12 后,telephonyManager.listen 無法正常工作

[英]telephonyManager.listen not working after targetSdkVersion is set to 31 or android 12

在我將 targetSdkVersion 升級到 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 清單文件上添加了以下權限,但仍然無法正常工作。

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

在定位低於 31 或 Android 12 之前,它運行良好。 Android SDK 31 如何解決這個問題? 提前致謝。

當目標 API 31 TelephonyManager.listen給出: java.lang.SecurityException: listen

TelephonyManager.registerTelephonyCallback也是如此,除非禁用ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION

開發人員選項/應用程序兼容性更改/選擇應用程序/為 targetSdkVersion >= 31 啟用

由於 API 級別 31 中不推薦使用TelephonyManager.listen並且 API 級別 31 中添加了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並且此權限具有危險的保護級別,您應該按照本指南請求運行時權限: 請求應用權限

這就是我這樣做的方式:

    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 權限

您需要在清單中添加權限,正如您所做的那樣,還需要通過調用ActivityCompat.requestPermissions()動態請求它

相當詳盡的文檔: https ://developer.android.com/training/permissions/requesting

您在提供 LISTEN_CALL_STATE 時進行監聽的調用將觸發 onCallStateChanged() 回調。

telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);

正如您在文檔中看到的,如果您以 Android 31 或更高版本為目標,則 onCallStateChanged() 需要 READ_PHONE_STATE。

需要權限:針對 API 級別 31+ 的應用程序的 READ_PHONE_STATE。

看到這個: https://developer.android.com/reference/android/telephony/PhoneStateListener#onCallStateChanged(int,%20java.lang.String)

因此,要解決這個問題,只需在調用監聽調用之前請求權限(但僅當代碼在 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);

如果有人在這里尋找支持 API <= 30 和 API 31+(因為它們必須以不同方式處理)的此代碼的 Kotlin + Flow 版本,這里是 go。

這假定權限由調用 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 是呼叫.first.second是 API <= 30 中的電話號碼。

我在我的博客上有一個使用 Kotlin + Compose + Flows 的完整示例: https://tdcolvin.medium.com.network-type-in-android-watching-for-5g-4g-3g-2g-capability -a52db23bf3bf

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM