【Telephony】multi_sim_config_changed

该文详细阐述了Android系统中如何注册并处理多SIM卡配置变化的事件。当配置改变时,一系列的更新和广播过程发生,包括更新SubscriptionInfo、读取配置文件、通知订阅者以及更新订阅信息。整个流程涉及多个组件如PhoneFactory、TelephonyComponentFactory、SubscriptionInfoUpdater和CarrierConfigLoader,确保系统能正确响应SIM卡状态的变化。

1、注册观察者

--> PhoneFactory.makeDefaultPhones() 
--> TelephonyComponentFactory.makeSubscriptionInfoUpdater() 
--> new SubscriptionInfoUpdater() 
--> PhoneConfigurationManager.registerForMultiSimConfigChange(this, EVENT_MULTI_SIM_CONFIG_CHANGED, null)
--> sMultiSimConfigChangeRegistrants.addUnique(h, what, obj)    观察者注册到通知者中

到这步,注册了对 EVENT_MULTI_SIM_CONFIG_CHANGED 事件的观察,当收到通知者notify时,就由SubscriptionInfoUpdater这个Handler对象来处理事件。

其实只要某个类中调用了PhoneConfigurationManager.registerForMultiSimConfigChange(),就会产生一个观察者并注册到通知者中
比如下面这些类的方法中都有注册观察者
在这里插入图片描述

2、触发事件

--> PhoneInterfaceManager.switchMultiSimConfig() 
--> RadioConfig.setNumOfLiveModems()
--> RadioConfigProxy.setNumOfLiveModems()  
--> 下发modem请求
--> modem返回响应,回调处理事件 EVENT_SWITCH_DSDS_CONFIG_DONE
--> ConfigurationManagerHandler.handleMessage()

--> PhoneConfigurationManager.onMultiSimConfigChanged()
--> broadcastMultiSimConfigChange()
--> notifyMultiSimConfigChange()   
--> sMultiSimConfigChangeRegistrants.notifyResult()   1.通知者通知观察者
--> SubscriptionInfoUpdater.handleMessage()
--> onMultiSimConfigChanged()
    private void onMultiSimConfigChanged() {
   
   
        int activeModemCount = ((TelephonyManager) sContext.getSystemService(
                Context.TELEPHONY_SERVICE)).getActiveModemCount();
        // For inactive modems, reset its states.
        for (int phoneId = activeModemCount; phoneId < SUPPORTED_MODEM_COUNT; phoneId++) {
   
   
            sIccId[phoneId] = null;
            sSimCardState[phoneId] = TelephonyManager.SIM_STATE_UNKNOWN;
            sSimApplicationState[phoneId] = TelephonyManager.SIM_STATE_UNKNOWN;
        }
    }
--> Intent intent = new Intent(ACTION_MULTI_SIM_CONFIG_CHANGED); sendBroadcast(intent)    2.发送广播
--> ConfigLoaderBroadcastReceiver.onReceive()
--> sendMessge(EVENT_MULTI_SIM_CONFIG_CHANGED)
--> ConfigHandler.handleMessage()
--> CarrierConfigLoader.onMultiSimConfigChanged()
--> updateConfigForPhoneId()
--> sendMessage(mHandler.obtainMessage(EVENT_DO_FETCH_DEFAULT, phoneId, -1));
--> handleMessage(EVENT_DO_FETCH_DEFAULT)
	config = restoreConfigFromXml(mPlatformCarrierConfigPackage, "", phoneId);
    if (config != null) {
   
   
         logd(
                 "Loaded config from XML. package="
                         + mPlatformCarrierConfigPackage
                         + " phoneId="
                         + phoneId);
         mConfigFromDefaultApp[phoneId] = config;
         Message newMsg = obtainMessage(EVENT_FETCH_DEFAULT_DONE, phoneId, -1);
         newMsg.getData().putBoolean("loaded_from_xml", true);
         mHandler.sendMessage(newMsg);
     } else {
   
   ...}

== 读取xml配置文件 config = restoreConfigFromXml(mPlatformCarrierConfigPackage, “”, phoneId);==

private PersistableBundle restoreConfigFromXml(@Nullable String packageName,
            @NonNull String extraString, int phoneId, boolean isNoSimConfig) {
   
   
        if (packageName == null) {
   
   
            loge("Cannot restore config with null packageName");
        }
        final String version = getPackageVersion(packageName);
        if (version == null) {
   
   
            loge("Failed to get package version for: " + packageName);
            return null;
        }

        String fileName;
        String iccid = null;
        if (isNoSimConfig) {
   
   
            fileName = getFilenameForNoSimConfig(packageName);
        } else {
   
   
            if (SubscriptionManager.getSimStateForSlotIndex(phoneId)
                    != TelephonyManager.SIM_STATE_LOADED) {
   
   
                loge("Skip restore config because SIM records are not loaded.");
                return null;
            }

            iccid = getIccIdForPhoneId(phoneId);
            final int cid = getSpecificCarrierIdForPhoneId(phoneId);
            if (iccid == null) {
   
   
                loge("Cannot restore config with null iccid.");
                return null;
            }
            fileName = getFilenameForConfig(packageName, extraString, iccid, cid);
        }

        PersistableBundle restoredBundle = null;
        File file = null;
        
### 问题分析 在 Android 系统中,`ACTION_TELEPHONY_SERVICE_CHANGED` 广播用于通知应用 Telephony 服务状态发生了变化,例如电话服务是否可用、SIM 卡是否就绪等。当 `TelephonyManager` 服务初始化完成后,理论上应该能够接收到该广播,但在实际开发或调试过程中,可能会遇到广播未触发的问题。 #### 1. 广播未注册或注册方式不正确 如果应用没有正确注册 `ACTION_TELEPHONY_SERVICE_CHANGED` 广播,或者注册时机过早,可能导致无法接收到广播。例如,在 `onCreate()` 方法中动态注册广播接收器时,如果 `TelephonyManager` 尚未初始化完成,可能无法正确绑定广播监听器。 ```java IntentFilter filter = new IntentFilter(Intent.ACTION_TELEPHONY_SERVICE_CHANGED); context.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // 处理广播 } }, filter); ``` 在系统启动过程中,`TelephonyManager` 的初始化是由系统服务(如 `PhoneApp`)完成的。如果应用在系统服务初始化完成之前注册广播,可能无法正确接收到广播事件 [^1]。 #### 2. 权限缺失或未声明 某些与 Telephony 相关的广播需要特定权限才能接收。例如,如果应用未声明 `READ_PHONE_STATE` 权限,则可能无法接收到 `ACTION_TELEPHONY_SERVICE_CHANGED` 广播。 ```xml <uses-permission android:name="android.permission.READ_PHONE_STATE" /> ``` 此外,从 Android 6.0(API 23)开始,还需要在运行时动态申请该权限,否则即使在清单中声明,也可能因权限未授予而无法接收到广播 [^2]。 #### 3. 系统服务初始化延迟或未完成 在系统启动过程中,`TelephonyManager` 的初始化是异步进行的。如果应用在系统服务尚未完全初始化完成时尝试注册广播,可能会导致广播监听器未被正确绑定。例如,在 `PhoneApp` 的 `onCreate()` 方法中会创建 `PhoneGlobals` 实例,并调用其 `onCreate()` 方法来初始化电话服务 [^3]。如果应用在该过程完成之前注册广播,可能无法接收到广播。 #### 4. 系统优化或后台限制 在某些 Android 版本(尤其是 Android 8.0 及以上)中,系统对后台服务和广播接收器进行了限制,可能导致动态注册的广播接收器在应用进入后台后被系统停止,从而无法接收到广播。此外,部分厂商(如小米、华为等)也对后台广播进行了限制,进一步影响广播的接收。 #### 5. 广播发送逻辑未触发 `ACTION_TELEPHONY_SERVICE_CHANGED` 广播的发送依赖于系统内部逻辑,例如当电话服务状态发生变化时才会发送。如果系统中没有触发相关事件(如 SIM 卡状态变化、网络注册状态变化等),即使 `TelephonyManager` 已初始化完成,也不会发送该广播 [^2]。 --- ### 解决方案 #### 1. 确保广播注册时机合理 为了避免因注册时机过早导致广播未接收,可以在应用启动后延迟注册广播接收器,确保系统服务已完成初始化。例如: ```java new Handler(Looper.getMainLooper()).postDelayed(() -> { context.registerReceiver(broadcastReceiver, new IntentFilter(Intent.ACTION_TELEPHONY_SERVICE_CHANGED)); }, 5000); // 延迟5秒注册 ``` #### 2. 动态申请权限并处理授权结果 确保在运行时申请 `READ_PHONE_STATE` 权限,并在权限授予后才注册广播接收器: ```java if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.READ_PHONE_STATE}, REQUEST_CODE); } else { context.registerReceiver(broadcastReceiver, new IntentFilter(Intent.ACTION_TELEPHONY_SERVICE_CHANGED)); } ``` #### 3. 使用静态注册方式(适用于系统应用) 对于系统应用,可以考虑在 `AndroidManifest.xml` 中静态注册广播接收器,这样可以在系统服务初始化完成后自动接收到广播: ```xml <receiver android:name=".TelephonyReceiver"> <intent-filter> <action android:name="android.intent.action.TELEPHONY_SERVICE_CHANGED" /> </intent-filter> </receiver> ``` #### 4. 检查系统日志并调试广播发送逻辑 通过查看系统日志(如 `logcat`),可以确认系统是否在预期情况下发送了 `ACTION_TELEPHONY_SERVICE_CHANGED` 广播。例如: ```bash adb logcat -s "Telephony" ``` 同时,可以检查系统中广播发送的逻辑,例如在 `TelephonyRegistry` 或 `PhoneStateListener` 中是否有触发广播的代码路径 [^1]。 #### 5. 监听其他相关广播作为替代方案 如果 `ACTION_TELEPHONY_SERVICE_CHANGED` 广播未触发,可以考虑监听其他相关广播作为补充,例如: - `ACTION_PHONE_STATE_CHANGED` - `ACTION_SIM_STATE_CHANGED` - `ACTION_SERVICE_STATE_CHANGED` 这些广播通常会在电话服务状态变化时被发送,可以作为间接判断 Telephony 服务是否就绪的方式 [^2]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值