监听sim卡的插入与拨出

  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"))
    
  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联通,运行结果如下:

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卡

这是一个粘性广播,一注册就会立马收到广播,返回的结果中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同时被拨出来了。

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

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卡

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

NOT_READY
READY
LOADED

当把卡拨掉的时候它返回的是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卡

可以看到,收到的是卡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
            }
        }
    }
}

调用:

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

SimCardReceiver.unregister(this)
  • 17
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
监听 Android 设备的 SIM状态,可以使用 `PhoneStateListener` 类。以下是一个示例代码: ```java import android.content.Context; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.util.Log; public class SimStateReceiver { private static final String TAG = "SimStateReceiver"; private Context mContext; private TelephonyManager mTelephonyManager; private PhoneStateListener mPhoneStateListener; public SimStateReceiver(Context context) { mContext = context; mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); mPhoneStateListener = new PhoneStateListener() { @Override public void onCallStateChanged(int state, String phoneNumber) { super.onCallStateChanged(state, phoneNumber); Log.d(TAG, "onCallStateChanged: state = " + state + ", phoneNumber = " + phoneNumber); } @Override public void onServiceStateChanged(ServiceState serviceState) { super.onServiceStateChanged(serviceState); Log.d(TAG, "onServiceStateChanged: serviceState = " + serviceState); } @Override public void onSimStateChanged(int state) { super.onSimStateChanged(state); Log.d(TAG, "onSimStateChanged: state = " + state); switch (state) { case TelephonyManager.SIM_STATE_ABSENT: // SIM 卡不存在或未插入 break; case TelephonyManager.SIM_STATE_NETWORK_LOCKED: // SIM 卡已被网络锁定 break; case TelephonyManager.SIM_STATE_PIN_REQUIRED: // SIM 卡需要 PIN 解锁 break; case TelephonyManager.SIM_STATE_PUK_REQUIRED: // SIM 卡需要 PUK 解锁 break; case TelephonyManager.SIM_STATE_READY: // SIM 卡已准备好 break; case TelephonyManager.SIM_STATE_UNKNOWN: // SIM状态未知 break; } } }; } public void start() { mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE | PhoneStateListener.LISTEN_SERVICE_STATE | PhoneStateListener.LISTEN_SIM_STATE); } public void stop() { mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); } } ``` 在上面的代码,我们创建了一个 `SimStateReceiver` 类,它有一个 `start()` 方法和一个 `stop()` 方法,用于开始和停止监听 SIM状态。在 `SimStateReceiver` 的构造方法,我们创建了一个 `PhoneStateListener` 对象,并重写了其的 `onSimStateChanged()` 方法,用于监听 SIM状态的变化。在 `onSimStateChanged()` 方法,我们根据不同的 SIM状态打印不同的日志信息。最后,在 `start()` 方法,我们通过 `TelephonyManager` 的 `listen()` 方法注册了 `PhoneStateListener` 对象,开始监听 SIM状态。在 `stop()` 方法,我们通过 `TelephonyManager` 的 `listen()` 方法取消了 `PhoneStateListener` 对象的注册,停止监听 SIM状态

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

android_cai_niao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值