如何监控SIM卡状态变化

28
我希望能在SIM卡状态发生改变时执行一些操作,例如当需要输入SIM卡PIN码时播放声音,但我认为没有广播事件可以被广播接收器拦截到...注册 android.intent.action.PHONE_STATE 只会告诉你通话状态何时改变..另一种方法是启动一个服务,该服务注册 PhoneStateListener并根据 LISTEN_SERVICE_STATE(当状态为OUT-OF-STATE时可以从 TelephonyManager 中获取SIM卡状态并查看状态是否为 SIM_STATE_PIN_REQUIRED)做出反应。所以,我的问题是:

1)有没有广播意图可以用来拦截SIM卡状态或服务状态的更改?

2)在服务中安装 PhoneStateListener 并使用它将意图传递给服务本身,以响应由 PhoneStateListener 接收到的电话状态更改通知,这是不是一个不好的主意?

3个回答

33

当SIM卡状态改变时,会广播意图android.intent.action.SIM_STATE_CHANGED。例如,在我的装有T-Mobile SIM卡的HTC Desire上,如果我将设备置于飞行模式下,则会广播以下意图:

  • 意图:android.intent.action.SIM_STATE_CHANGED,带有额外信息:ss = NOT_READY,reason = null

如果我然后将其从飞行模式中取出,则会广播以下意图:

  • 意图:android.intent.action.SIM_STATE_CHANGED,带有额外信息:ss = LOCKED,reason = PIN
  • 意图:android.intent.action.SIM_STATE_CHANGED,带有额外信息:ss = READY,reason = null
  • 意图:android.intent.action.SIM_STATE_CHANGED,带有额外信息:ss = IMSI,reason = null
  • 意图:android.intent.action.SIM_STATE_CHANGED,带有额外信息:ss = LOADED,reason = null

不同制造商和不同型号可能会有不同的行为。正如他们所说,“结果可能因人而异”。


1
非常感谢!我应该在Android参考文档的哪里找到这些信息?这个intent是在哪个类中记录的? - Gianni Costanzi
6
我不知道这是否有记录以及记录的位置。我是通过观察日志记录(Intents)来发现的,当特定的电话事件发生时,编写小型测试程序来监听这些事件并输出它们的内容。由于很多情况下都是供应商特定的,所以你必须自己去弄清楚。很高兴我能提供一些帮助。 - David Wasser
既然你向我展示了这个广播事件SIM_STATE_CHANGED,给了我很大的帮助,我想问你另一件事情...我想知道如何确定支持此广播意图的API级别,因为它没有在任何地方记录...我在这里提出了一个问题(http://stackoverflow.com/questions/10861340/from-which-sdk-level-is-android-intent-action-sim-state-changed-broadcast-intent)。 - Gianni Costanzi
3
文档中未提到 Android 源代码具有一个名为 TelephonyIntents 的类,其中包含这些 Intent,并警告不要监听它们。https://android.googlesource.com/platform/frameworks/base/+/cd92588/telephony/java/com/android/internal/telephony/TelephonyIntents.java - Illegal Argument
@IllegalArgument 我们在哪里可以找到这个警告? - Tim
显示剩余5条评论

5

David的回答很到位。我想加入一些示例代码以帮助人们开始实施这样的状态监视器。

/**
 * Handles broadcasts related to SIM card state changes.
 * <p>
 * Possible states that are received here are:
 * <p>
 * Documented:
 * ABSENT
 * NETWORK_LOCKED
 * PIN_REQUIRED
 * PUK_REQUIRED
 * READY
 * UNKNOWN
 * <p>
 * Undocumented:
 * NOT_READY (ICC interface is not ready, e.g. radio is off or powering on)
 * CARD_IO_ERROR (three consecutive times there was a SIM IO error)
 * IMSI (ICC IMSI is ready in property)
 * LOADED (all ICC records, including IMSI, are loaded)
 * <p>
 * Note: some of these are not documented in
 * https://developer.android.com/reference/android/telephony/TelephonyManager.html
 * but they can be found deeper in the source code, namely in com.android.internal.telephony.IccCardConstants.
 */
public class SimStateChangedReceiver extends BroadcastReceiver {

    /**
     * This refers to com.android.internal.telehpony.IccCardConstants.INTENT_KEY_ICC_STATE.
     * It seems not possible to refer it through a builtin class like TelephonyManager, so we
     * define it here manually.
     */
    private static final String EXTRA_SIM_STATE = "ss";

    @Override
    public void onReceive(Context context, Intent intent) {

        String state = intent.getExtras().getString(EXTRA_SIM_STATE);
        if (state == null) {
            return;
        }

        // Do stuff depending on state   
        switch (state) {      
            case "ABSENT": break;
            case "NETWORK_LOCKED": break;
            // etc.
        }
    }
}

2
第二种方法是在一个服务中使用PhoneStateListener监听onServiceStateChanged()事件,这对我很有效。我相信在一些设备上你将无法获取内部广播android.intent.action.SIM_STATE_CHANGED

我认为在某些设备上,如果应用程序尚未被用户启动或被强制停止,则您将无法获得内部广播,这是一种安全功能。 - Patrick

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