当前位置:   article > 正文

监听sim卡的插入与拨出_sim_application_state_changed需要权限吗

sim_application_state_changed需要权限吗
  1. 添加权限:<uses-permission android:name="android.permission.READ_PHONE_STATE"/>,6.0以上需要动态申请。如果是小米手机的无法申请,需要手动到设置中允许权限。

  2. 注册"android.intent.action.SIM_STATE_CHANGED"广播

    registerReceiver(receiver, IntentFilter("android.intent.action.SIM_STATE_CHANGED"))
    
    • 1
  3. 获取intent中的ss字段

    class SimCardReceiver : BroadcastReceiver() {
    
        override fun onReceive(context: Context, intent: Intent) {
            intent.extras?.keySet()?.forEach { key -> Timber.i("$key = ${intent.extras?.get(key)}") }	       
            val ss = intent.getStringExtra("ss")
            when (ss) {
                "LOADED" -> Timber.i("有sim卡")
                "ABSENT" -> Timber.i("没有sim卡")
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

在我的小米手机上,插入的双卡,卡1电信,卡2联通,运行结果如下:

android.telephony.extra.SUBSCRIPTION_INDEX = 1
slot_id = 1
subscription_id = 1
phone_id = 1
android.telephony.extra.SLOT_INDEX = 1
phoneName = Phone
reason = null
rebroadcastOnUnlock = true
ss = LOADED
phone = 1
subscription = 1
有sim卡
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

这是一个粘性广播,一注册就会立马收到广播,返回的结果中SLOT_INDEX为1,索引是从0开始的,说明这是返回的卡2的信息。

此时我将卡拨出,两个卡都会被同时拨出,结果如下:

slot_id = 0
subscription_id = -1
phone_id = 0
android.telephony.extra.SLOT_INDEX = 0
phoneName = Phone
reason = null
ss = ABSENT
phone = 0
没有sim卡


slot_id = 1
subscription_id = -1
phone_id = 1
android.telephony.extra.SLOT_INDEX = 1
phoneName = Phone
reason = null
ss = ABSENT
phone = 1
没有sim卡
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

这里可以看到卡1卡2同时被拨出来了。

我再把两张卡同时插回去:

android.telephony.extra.SUBSCRIPTION_INDEX = 1
slot_id = 1
subscription_id = 1
phone_id = 1
android.telephony.extra.SLOT_INDEX = 1
phoneName = Phone
reason = null
ss = NOT_READY
phone = 1
subscription = 1


android.telephony.extra.SUBSCRIPTION_INDEX = 2
slot_id = 0
subscription_id = 2
phone_id = 0
android.telephony.extra.SLOT_INDEX = 0
phoneName = Phone
reason = null
ss = NOT_READY
phone = 0
subscription = 2


android.telephony.extra.SUBSCRIPTION_INDEX = 2
slot_id = 0
subscription_id = 2
phone_id = 0
android.telephony.extra.SLOT_INDEX = 0
phoneName = Phone
reason = null
ss = READY
phone = 0
subscription = 2


android.telephony.extra.SUBSCRIPTION_INDEX = 1
slot_id = 1
subscription_id = 1
phone_id = 1
android.telephony.extra.SLOT_INDEX = 1
phoneName = Phone
reason = null
ss = READY
phone = 1
subscription = 1


android.telephony.extra.SUBSCRIPTION_INDEX = 2
slot_id = 0
subscription_id = 2
phone_id = 0
android.telephony.extra.SLOT_INDEX = 0
phoneName = Phone
reason = null
ss = LOADED
phone = 0
subscription = 2
有sim卡


android.telephony.extra.SUBSCRIPTION_INDEX = 1
slot_id = 1
subscription_id = 1
phone_id = 1
android.telephony.extra.SLOT_INDEX = 1
phoneName = Phone
reason = null
ss = LOADED
phone = 1
subscription = 1
有sim卡
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

这里收到了6条广播,每张卡3条广播,每张卡的ss分别经历3个状态,如下:

NOT_READY
READY
LOADED
  • 1
  • 2
  • 3

当把卡拨掉的时候它返回的是ABSENT,所以可以认为如果是ABSENT就是无卡,其它状态就是有卡。

两张卡已经插入的情况下,此时再注册广播只会收到卡2的一条广播,ss为LOADED,为什么会这样我也搞不清楚。所以,这种方式适合判断单卡的设备是否插入了sim卡,如果是双卡设备,你就无法判断是否插入了双卡,因为当你注册的时候它只接收一条卡的广播信息,此时你拨卡再插卡的时候才会收到两张卡的广播信息。 所以它只适合判断单卡,只要你一注册你就能获得那个卡槽的信息,无论是否当前有卡还是无卡。

在双卡的情况下,插入了两张卡,拨卡的时候能收到两张卡的广播信息,但是,当我注册的时候,如果双卡之前就已经拨掉了,则此时我只收到一条广播,如下:

slot_id = 1
subscription_id = -1
phone_id = 1
android.telephony.extra.SLOT_INDEX = 1
phoneName = Phone
reason = null
ss = ABSENT
phone = 1
没有sim卡
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

可以看到,收到的是卡2的信息。

"android.intent.action.SIM_STATE_CHANGED"实际上为Intent.ACTION_SIM_STATE_CHANGED,但它是系统API,我们无法直接调用,所以只能直接使用它的值。查看该常量已经过期,推荐使用TelephonyManager.ACTION_SIM_CARD_STATE_CHANGEDTelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED,这两也是系统API,所以只能使用它们的值来注册。

首先看TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED的源码,它的值为"android.telephony.action.SIM_CARD_STATE_CHANGED",收到广播后,它通过TelephonyManager.EXTRA_SIM_STATE来获取状态,状态包括:

  • SIM_STATE_ABSENT SIM card not found
  • SIM_STATE_CARD_IO_ERROR SIM card IO error
  • SIM_STATE_CARD_RESTRICTED SIM card is restricted
  • SIM_STATE_PRESENT SIM card is present

Requires the READ_PRIVILEGED_PHONE_STATE permission. The current state can also be queried using getSimCardState(). This is a protected intent that can only be sent by the system.

通过阅读API文档可知,这要求系统App才能收到这个广播,因为只有系统APP才能申请READ_PRIVILEGED_PHONE_STATE权限。且这个SIM卡的状态也可通过TelephonyManager.getSimCardState()来获取。

对于TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED,它获取的状态有:

  • SIM_STATE_NOT_READY SIM card applications not ready
  • SIM_STATE_PIN_REQUIRED SIM card PIN locked
  • SIM_STATE_PUK_REQUIRED SIM card PUK locked
  • SIM_STATE_NETWORK_LOCKED SIM card network locked
  • SIM_STATE_PERM_DISABLED SIM card permanently disabled due to PUK failures
  • SIM_STATE_LOADED SIM card data loaded

该广播同样需要系统级App才能接收。该状态可通过TelephonyManager.getSimApplicationState()函数获取。

通过查看源码,发现TelephonyManager中还有一个ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED"广播,对应状态可使用getUiccSlotsInfo()函数获取。

经过试验,我发现即使我的应用有系统签名,也无法接收TelephonyManager.ACTION_SIM_CARD_STATE_CHANGEDTelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED这两个广播。运行的是在Android7.1.1版本,不知道是否是系统版本的问题,有时间再Android11中再试一下。

现在能肯定的是第一种方式是OK,即便在我的小米11pro(Android13)中依旧可以监听到sim卡状态的变化,完整示例代码如下:

class SimCardReceiver : BroadcastReceiver() {

    private lateinit var callback: (Boolean) -> Unit

    override fun onReceive(context: Context, intent: Intent) {
		val noCard = intent.getStringExtra("ss") == "ABSENT"
		callback(noCard)
    }

    companion object {
        private const val ACTION_SIM_STATE_CHANGED: String = "android.intent.action.SIM_STATE_CHANGED" // 等于:Intent.ACTION_SIM_STATE_CHANGED        
        private var isRegister = false
        private val receiver: SimCardReceiver by lazy { SimCardReceiver() }

        fun register(context: Context, action: String,callback: (Boolean) -> Unit) {
            receiver.callback = callback
            unregister(context)
            context.registerReceiver(receiver, IntentFilter(ACTION_SIM_STATE_CHANGED))
            isRegister = true
        }

        fun unregister(context: Context) {
            if (isRegister) {
                context.unregisterReceiver(receiver)
                isRegister = false
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

调用:

SimCardReceiver.register(this) { noCard ->
    Timber.i(if (noCard) "无卡" else "有卡")
}

SimCardReceiver.unregister(this)
  • 1
  • 2
  • 3
  • 4
  • 5
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/235621
推荐阅读
相关标签
  

闽ICP备14008679号