carrier_config服務研究

<h2 align = "center">carrier_config相關研究</h2>


概述: 在PhoneGlobals創(chuàng)建時,會同時創(chuàng)建carrier_config 服務,實現(xiàn)carrier_config 的類是CarrierConfigLoader。CarrierConfigLoader的主要目的是,綁定特定運營商的App,從而獲取特定運營的配置信息。

1. 相關類和aidl

  1. frameworks/base/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl
  1. packages/services/Telephony/src/com/android/phone/CarrierConfigLoader.java
  1. frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java

2. 初始化

    private CarrierConfigLoader(Context context) {
        mContext = context;

        // 監(jiān)聽開機
        IntentFilter bootFilter = new IntentFilter();
        bootFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
        context.registerReceiver(mBootReceiver, bootFilter);

        // Register for package updates. Update app or uninstall app update will have all 3 intents,
        // in the order or removed, added, replaced, all with extra_replace set to true.
        // 監(jiān)聽安裝或者卸載
        IntentFilter pkgFilter = new IntentFilter();
        pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        pkgFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
        pkgFilter.addDataScheme("package");
        context.registerReceiverAsUser(mPackageReceiver, UserHandle.ALL, pkgFilter, null, null);

        int numPhones = TelephonyManager.from(context).getPhoneCount();
        // 運營商默認配置
        mConfigFromDefaultApp = new PersistableBundle[numPhones];
        // 運營商App設置
        mConfigFromCarrierApp = new PersistableBundle[numPhones];
        mServiceConnection = new CarrierServiceConnection[numPhones];
        // Make this service available through ServiceManager.
        // 讓外部可以通過ServiceManager訪問carrier_config
        ServiceManager.addService(Context.CARRIER_CONFIG_SERVICE, this);
        log("CarrierConfigLoader has started");
        mHandler.sendEmptyMessage(EVENT_CHECK_SYSTEM_UPDATE);
    }

在構(gòu)造函數(shù)初始化完成后,發(fā)送了EVENT_CHECK_SYSTEM_UPDATE事件,Handler對于該事件的處理如下:

case EVENT_CHECK_SYSTEM_UPDATE:
    SharedPreferences sharedPrefs =
            PreferenceManager.getDefaultSharedPreferences(mContext);
    final String lastFingerprint = sharedPrefs.getString(KEY_FINGERPRINT, null);
    // 如果系統(tǒng)的構(gòu)建信息發(fā)生了變化,即系統(tǒng)更新了,那么就需要清除緩存。
    if (!Build.FINGERPRINT.equals(lastFingerprint)) {
        log("Build fingerprint changed. old: "
                + lastFingerprint + " new: " + Build.FINGERPRINT);
        // 清除緩存
        clearCachedConfigForPackage(null);
        // 更新系統(tǒng)構(gòu)建信息
        sharedPrefs.edit().putString(KEY_FINGERPRINT, Build.FINGERPRINT).apply();
    }
    break;

清除緩存的代碼如下,運營商的信息都是通過xml文件保存下來的,packageName為null時會清除所有的xml文件。

private boolean clearCachedConfigForPackage(final String packageName) {
    File dir = mContext.getFilesDir();
    File[] packageFiles = dir.listFiles(new FilenameFilter() {
        public boolean accept(File dir, String filename) {
            if (packageName != null) {
                return filename.startsWith("carrierconfig-" + packageName + "-");
            } else {
                // 因為packageName==null,所以會清除掉所有以carrierconfig-開頭的文件。
                return filename.startsWith("carrierconfig-");
            }
        }
    });
    if (packageFiles == null || packageFiles.length < 1) return false;
    for (File f : packageFiles) {
        log("deleting " + f.getName());
        f.delete();
    }
    return true;
}

3. 開機更新運營商信息

接收到開機廣播后mHandler接收到EVENT_SYSTEM_UNLOCKED事件,對該事件的處理如下:

case EVENT_SYSTEM_UNLOCKED:
                    for (int i = 0; i < TelephonyManager.from(mContext).getPhoneCount(); ++i) {
                        // 如果是單卡,phoneId為0, 雙卡:phoneId 為0,1
                        // 更新每個卡的運營商信息
                        updateConfigForPhoneId(i);
                    }
                    break;

更新內(nèi)存中的運營商信息,如果特定運營商app不存在,那么清空對應的緩存:

private void updateConfigForPhoneId(int phoneId) {
    // Clear in-memory cache for carrier app config, so when carrier app gets uninstalled, no
    // stale config is left.
    if (mConfigFromCarrierApp[phoneId] != null &&
            getCarrierPackageForPhoneId(phoneId) == null) {
        mConfigFromCarrierApp[phoneId] = null;
    }
    // 重新獲取運營商信息
    mHandler.sendMessage(mHandler.obtainMessage(EVENT_FETCH_DEFAULT, phoneId, -1));
}

首先從本地文件中讀取配置信息,如果本地不能存在,那么嘗試綁定android.service.carrier.CarrierService,綁定成功后,從該服務的接口中讀取信息。

case EVENT_FETCH_DEFAULT:
                    iccid = getIccIdForPhoneId(phoneId);
                    // 從文件中讀取默認的配置
                    config = restoreConfigFromXml(DEFAULT_CARRIER_CONFIG_PACKAGE, iccid);
                    if (config != null) {
                        log("Loaded config from XML. package=" + DEFAULT_CARRIER_CONFIG_PACKAGE
                                + " phoneId=" + phoneId);
                        // xml文件存在并加載成功,發(fā)送加載默認配置成功事件
                        mConfigFromDefaultApp[phoneId] = config;
                        Message newMsg = obtainMessage(EVENT_LOADED_FROM_DEFAULT, phoneId, -1);
                        newMsg.getData().putBoolean("loaded_from_xml", true);
                        mHandler.sendMessage(newMsg);
                    } else {
                        // xml文件不存在,綁定android.service.carrier.CarrierService,該服務的實現(xiàn)不具體分析。
                        if (bindToConfigPackage(DEFAULT_CARRIER_CONFIG_PACKAGE,
                                phoneId, EVENT_CONNECTED_TO_DEFAULT)) {
                            sendMessageDelayed(obtainMessage(EVENT_BIND_DEFAULT_TIMEOUT, phoneId, -1),
                                    BIND_TIMEOUT_MILLIS);
                        } else {
                            // Send bcast if bind fails
                            // 告訴監(jiān)聽者,運營商信息已經(jīng)發(fā)生了變化,這個時候,特定運營商信息為空。
                            broadcastConfigChangedIntent(phoneId);
                        }
                    }
                    break;

上面EVENT_BIND_DEFAULT_TIMEOUT事件比較簡單, 首先unBind服務,再通過broadcastConfigChangedIntent通知監(jiān)聽者。

所以應該是在綁定服務成功后,就獲取運營商信息,BIND_TIMEOUT_MILLIS是綁定服務的超時時間。下面看bindToConfigPackage:

/** Binds to the default or carrier config app. */
private boolean bindToConfigPackage(String pkgName, int phoneId, int eventId) {
    log("Binding to " + pkgName + " for phone " + phoneId);
    Intent carrierService = new Intent(CarrierService.CARRIER_SERVICE_INTERFACE);
    carrierService.setPackage(pkgName);
    // 很顯然綁定成功后的邏輯在CarrierServiceConnection類里面。
    mServiceConnection[phoneId] = new CarrierServiceConnection(phoneId, eventId);
    try {
        return mContext.bindService(carrierService, mServiceConnection[phoneId],
                Context.BIND_AUTO_CREATE);
    } catch (SecurityException ex) {
        return false;
    }
}

綁定服務成功后給mHandler發(fā)送了EVENT_CONNECTED_TO_DEFAULT事件:

case EVENT_CONNECTED_TO_DEFAULT:
    // 移除超時事件。
    removeMessages(EVENT_BIND_DEFAULT_TIMEOUT);
    carrierId = getCarrierIdForPhoneId(phoneId);
    conn = (CarrierServiceConnection) msg.obj;
    // If new service connection has been created, unbind.
    if (mServiceConnection[phoneId] != conn || conn.service == null) {
        mContext.unbindService(conn);
        break;
    }
    try {
        ICarrierService carrierService = ICarrierService.Stub
                .asInterface(conn.service);
        // 從運營商服務中獲取到配置。
        config = carrierService.getCarrierConfig(carrierId);
        iccid = getIccIdForPhoneId(phoneId);
        // 保存配置到xml文件
        saveConfigToXml(DEFAULT_CARRIER_CONFIG_PACKAGE, iccid, config);
        // 更新內(nèi)存中的配置
        mConfigFromDefaultApp[phoneId] = config;
        sendMessage(obtainMessage(EVENT_LOADED_FROM_DEFAULT, phoneId, -1));
    } catch (Exception ex) {
        // The bound app could throw exceptions that binder will pass to us.
        loge("Failed to get carrier config: " + ex.toString());
    } finally {
        mContext.unbindService(mServiceConnection[phoneId]);
    }
    break;

無論是從本地xml文件中獲取到運營商配置,還是從服務中獲取到運營商配置,最終都會回到mHandler的EVENT_LOADED_FROM_DEFAULT事件:

case EVENT_LOADED_FROM_DEFAULT:
    // If we attempted to bind to the app, but the service connection is null, then
    // config was cleared while we were waiting and we should not continue.
    // 我們是從服務中獲取到的配置,但是服務卻斷開了,當然不能繼續(xù)了。
    if (!msg.getData().getBoolean("loaded_from_xml", false)
            && mServiceConnection[phoneId] == null) {
        break;
    }
    carrierPackageName = getCarrierPackageForPhoneId(phoneId);
    // 特定的運營商app存在,那么需要從特定的運營商中再次獲取配置。
    if (carrierPackageName != null) {
        log("Found carrier config app: " + carrierPackageName);
        sendMessage(obtainMessage(EVENT_FETCH_CARRIER, phoneId));
    } else {
        broadcastConfigChangedIntent(phoneId);
    }
    break;

獲取特定運營商配置信息和獲取默認配置信息的邏輯是一樣的,代碼就不具體列舉了:

  1. 檢查本地緩存文件,緩存存在>>通知變更
  1. 緩存不存在,那么從特定運營商的服務中獲取對應的配置信息,然后緩存到本地,再通知變更。

4. 安裝卸載更新運營商信息

理解了上面的更新一個SIM卡的運營商信息的流程,下面就比較簡單了。安裝卸載會給mHandler發(fā)送EVENT_PACKAGE_CHANGED事件:

case EVENT_PACKAGE_CHANGED:
    carrierPackageName = (String) msg.obj;
    // Only update if there are cached config removed to avoid updating config
    // for unrelated packages.
    // 清除掉被卸載的運營商app對應的保存在本地的xml文件。
    if (clearCachedConfigForPackage(carrierPackageName)) {
        int numPhones = TelephonyManager.from(mContext).getPhoneCount();
        for (int i = 0; i < numPhones; ++i) {
            updateConfigForPhoneId(i);
        }
    }
    break;

上面的邏輯和開機更新運營商信息差不多,再結(jié)合前面的分析,我們知道大體的流程如下:

  1. 默認運營商配置更新

1.1 檢查本地緩存文件, 緩存存在,不需要更新。

  1. 特定運營商配置更新

2.1 檢查本地緩存文件,緩存不存在,需要從服務重新獲取配置信息。同時發(fā)現(xiàn)特定運營商app不存在,清除特定運營商配置緩存。

2.2 檢查特定運營商app,不存在,通知更新。

5. 主動更新

上面所說的都是carrier_config在接收到外部某個事件后,進行更新的邏輯。

carrier_config同樣支持主動更新。它為主動更新提供了2個接口:

  1. notifyConfigChangedForSubId(int subId) 主要是提供給 telephony services 更新它當前的運營商配置用的。
  1. updateConfigForPhoneId(int phoneId, String simState) 根據(jù)提供的sim狀態(tài),決定清除該卡的運營商配置還是加載該卡的運營商配置。

6. 獲取運營商配置的接口

僅僅是從內(nèi)存緩存中讀取配置信息,首先是讀取內(nèi)存預設好的配置,如果默認運營商配置存在,那么默認配置會覆蓋預設配置,然后,如果特定運營商配置存在,那么特定配置覆蓋默認配置。

PersistableBundle getConfigForSubId(int subId) {
    try {
        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE, null);
        // SKIP checking run-time READ_PHONE_STATE since using PRIVILEGED
    } catch (SecurityException e) {
        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, null);
    }
    int phoneId = SubscriptionManager.getPhoneId(subId);
    // 優(yōu)先級是 預設配置 < 默認配置 < 特定配置
    PersistableBundle retConfig = CarrierConfigManager.getDefaultConfig();
    if (SubscriptionManager.isValidPhoneId(phoneId)) {
        PersistableBundle config = mConfigFromDefaultApp[phoneId];
        if (config != null)
            retConfig.putAll(config);
        config = mConfigFromCarrierApp[phoneId];
        if (config != null)
            retConfig.putAll(config);
    }
    return retConfig;
}

7. 服務的調(diào)用

1) 因為服務加入到了ServiceManager里面,所以對于系統(tǒng)應用,可以通過ServiceManager.getService的方法獲取到該服務的接口。
IBinder carrierConfig = ServiceManager.getService(Context.CARRIER_CONFIG_SERVICE);
ICarrierConfigLoader loader = ICarrierConfigLoader.Stub.asInterface(carrierConfig);
try {
    PersistableBundle defaultConfig = loader.getConfigForSubId(SubscriptionManager.getDefaultSubId());
} catch (RemoteException e) {
    e.printStackTrace();
}
2) 因為該服務也注冊到系統(tǒng)服務里面了,所以第三方應用可以獲取該服務接口。
CarrierConfigManager ccm = (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
PersistableBundle defaultConfig = ccm.getConfigForSubId(SubscriptionManager.getDefaultSubId());

總結(jié)

最后需要說明的是,carrier_config 服務自身有一套很完善的更新邏輯,調(diào)用者一般情況下不需要主動去觸發(fā)更新。因為Telephony已經(jīng)管理好了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,628評論 19 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,159評論 25 708
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,276評論 6 342
  • 今天有點累。這段時間沒休息好,事情也做的雜,當主業(yè)沒有占據(jù)大頭,副業(yè)就失去了樂趣,變成瑣碎的待解決麻煩。某天沒有運...
    童顏姥姥閱讀 563評論 2 5

友情鏈接更多精彩內(nèi)容