(M)SIM卡开机流程分析之默认APN设置

近日,一直在研究,默认APN的设置
当我们从代码和手机中看到,默认APN的显示是从content://telephony/carriers/preferapn的数据中查询到的,而这个是通过shared preference来保存的,当手机第一次插卡开机后,我们将preferapn.xml文件导出来后,我们发现其中已经写有值了,但是从TelephonyProvider.Java文件中,我们却没有发现任何写入的地方,那么这个默认APN究竟是如何写入和设定的呢?
结合LOG查看了插卡开机流程多日后,终于找到了其写入的位置,仅以此篇记录近期的成果。

首先,SIM卡的开机流程中,我们知道,是从PhoneFactory.java文件的makeDefaultPhone方法中开始的,而在其中,我们可以看到如下段

for (int i = 0; i < numPhones; i++) {  
        PhoneBase phone = null;  
        int phoneType = TelephonyManager.getPhoneType(networkModes[i]);  
        if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {  
            phone = new GSMPhone(context,  
                    sCommandsInterfaces[i], sPhoneNotifier, i);  
        } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {  
            phone = new CDMALTEPhone(context,  
                    sCommandsInterfaces[i], sPhoneNotifier, i);  
        }  
        Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " sub = " + i);  
      
        sProxyPhones[i] = new PhoneProxy(phone);  
    }
创建Phone对象,今天我们就以GSMPhone对象来分析这个默认APN的流程

public  
    GSMPhone(Context context, CommandsInterface ci, PhoneNotifier notifier, int phoneId) {  
        this(context, ci, notifier, false, phoneId);  
}
public  
    GSMPhone(Context context, CommandsInterface ci,  
            PhoneNotifier notifier, boolean unitTestMode, int phoneId) {  
        super("GSM", notifier, context, ci, unitTestMode, phoneId);  
      
        if (ci instanceof SimulatedRadioControl) {  
            mSimulatedRadioControl = (SimulatedRadioControl) ci;  
        }  
      
        mCi.setPhoneType(PhoneConstants.PHONE_TYPE_GSM);  
        mCT = new GsmCallTracker(this);  
      
        mSST = new GsmServiceStateTracker(this);  
        mDcTracker = new DcTracker(this);  
      
        if (!unitTestMode) {  
            mSimPhoneBookIntManager = new SimPhoneBookInterfaceManager(this);  
            mSubInfo = new PhoneSubInfo(this);  
        }  
      
        mCi.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);  
        mCi.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);  
        mCi.registerForOn(this, EVENT_RADIO_ON, null);  
        mCi.setOnUSSD(this, EVENT_USSD, null);  
        mCi.setOnSuppServiceNotification(this, EVENT_SSN, null);  
        mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null);  
        mCi.setOnSs(this, EVENT_SS, null);  
        setProperties();  
      
        log("GSMPhone: constructor: sub = " + mPhoneId);  
      
        setProperties();  
}
其中,有创建DcTracker对象,查看这个类的构造方法

//***** Constructor  
    public DcTracker(PhoneBase p) {  
        super(p);  
        ......  
        update();  
        ......  
}
首先,先调用了其父类的构造方法

/**  
     * Default constructor  
     */  
    protected DcTrackerBase(PhoneBase phone) {  
        super();  
        mPhone = phone;  
        ......  
        mUiccController = UiccController.getInstance();  
        mUiccController.registerForIccChanged(this, DctConstants.EVENT_ICC_CHANGED, null);  
        ......  
    } 
再继续回到DcTracker的构造方法中,可以看到还调用了update方法,进入update方法中查看

public void update() {  
        ......  
        registerForAllEvents();  
        onUpdateIcc();  
        ......  
    } 
进入onUpdateIcc方法

@Override  
    protected void onUpdateIcc() {  
        ......  
        IccRecords newIccRecords = getUiccRecords(UiccController.APP_FAM_3GPP);  
      
        IccRecords r = mIccRecords.get();  
        if (r != newIccRecords) {  
            ......  
            if (newIccRecords != null) {  
                if (mPhone.getSubId() >= 0) {  
                    ......  
                    mIccRecords.set(newIccRecords);  
                    newIccRecords.registerForRecordsLoaded(  
                            this, DctConstants.EVENT_RECORDS_LOADED, null);  
                }  
            }  
            ......  
        }  
}
通过IccRecords注册DctConstants.EVENT_RECORDS_LOADED消息,当全部加载完成后,发出EVENT_RECORDS_LOADED消息,在DcTracker中接收,并处理

@Override  
    public void handleMessage (Message msg) {  
        ......  
      
        switch (msg.what) {  
            case DctConstants.EVENT_RECORDS_LOADED:  
                onRecordsLoaded();  
                break;  
        }  
    } 
private void onRecordsLoaded() {  
        ......  
        createAllApnList();  
        setInitialAttachApn();  
        if (mPhone.mCi.getRadioState().isOn()) {  
            if (DBG) log("onRecordsLoaded: notifying data availability");  
            notifyOffApnsOfAvailability(Phone.REASON_SIM_LOADED);  
        }  
        setupDataOnConnectableApns(Phone.REASON_SIM_LOADED);  
}
再查看createAllApnList方法

/** 
     * Based on the sim operator numeric, create a list for all possible 
     * Data Connections and setup the preferredApn. 
     */  
    private void createAllApnList() {  
        mAllApnSettings = new ArrayList();  
        IccRecords r = mIccRecords.get();  
        String operator = (r != null) ? r.getOperatorNumeric() : "";  
        if (operator != null) {  
            String selection = "numeric = '" + operator + "'";  
            String orderBy = "_id";  
            // query only enabled apn.  
            // carrier_enabled : 1 means enabled apn, 0 disabled apn.  
            // selection += " and carrier_enabled = 1";  
            if (DBG) log("createAllApnList: selection=" + selection);  
      
            Cursor cursor = mPhone.getContext().getContentResolver().query(  
                    Telephony.Carriers.CONTENT_URI, null, selection, null, orderBy);  
      
            if (cursor != null) {  
                if (cursor.getCount() > 0) {  
                    mAllApnSettings = createApnList(cursor);  
                }  
                cursor.close();  
            }  
        }  
      
        addEmergencyApnSetting();  
      
        dedupeApnSettings();  
      
        if (mAllApnSettings.isEmpty()) {  
            if (DBG) log("createAllApnList: No APN found for carrier: " + operator);  
            mPreferredApn = null;  
            // TODO: What is the right behavior?  
            //notifyNoData(DataConnection.FailCause.MISSING_UNKNOWN_APN);  
        } else {  
            mPreferredApn = getPreferredApn();  
            if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) {  
                mPreferredApn = null;  
                setPreferredApn(-1);  
            }  
            if (DBG) log("createAllApnList: mPreferredApn=" + mPreferredApn);  
        }  
        if (DBG) log("createAllApnList: X mAllApnSettings=" + mAllApnSettings);  
      
        setDataProfilesAsNeeded();  
}
可以看到,在这个方法中,主要是通过当前SIM卡的numeric字段,从content://telephony/carriers数据库中获取所有numeric字段对应的APN信息(数据库信息是从哪儿来的,请查看TelephonyProvider.java文件),并且将所有的数据均插入mAllApnSettings中,在这段代码下方可以看到,还有获取preferred apn的数据,由于此前并未设定,因此此处无法获取到
继续查看setupDataOnConnectableApns方法
private void setupDataOnConnectableApns(String reason) {  
        setupDataOnConnectableApns(reason, RetryFailures.ALWAYS);  
}
private void setupDataOnConnectableApns(String reason, RetryFailures retryFailures) {  
        ......  
        for (ApnContext apnContext : mPrioritySortedApnContexts) {  
            ArrayList waitingApns = null;  
            ......  
            if (apnContext.getState() == DctConstants.State.FAILED  
                    || apnContext.getState() == DctConstants.State.RETRYING) {  
                ......  
                    // RetryFailures.ONLY_ON_CHANGE - check if the apns have changed  
                    int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();  
                    ArrayList originalApns = apnContext.getOriginalWaitingApns();  
                    if (originalApns != null && originalApns.isEmpty() == false) {  
                        waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech);  
                        if (originalApns.size() != waitingApns.size() ||  
                                originalApns.containsAll(waitingApns) == false) {  
                            apnContext.releaseDataConnection(reason);  
                        } else {  
                            continue;  
                        }  
                    } else {  
                        continue;  
                    }  
                ......  
            }  
            if (apnContext.isConnectable()) {  
                log("setupDataOnConnectableApns: isConnectable() call trySetupData");  
                apnContext.setReason(reason);  
                trySetupData(apnContext, waitingApns);  
            }  
        }  
}
这边有一个buildWaitingApns方法和trySetupData方法比较重要,我们继续分析这段
/** 
     * Build a list of APNs to be used to create PDP's. 
     * 
     * @param requestedApnType 
     * @return waitingApns list to be used to create PDP 
     *          error when waitingApns.isEmpty() 
     */  
    private ArrayList buildWaitingApns(String requestedApnType, int radioTech) {  
        ......  
        ArrayList apnList = new ArrayList();  
        ......  
        IccRecords r = mIccRecords.get();  
        String operator = (r != null) ? r.getOperatorNumeric() : "";  
      
        ......  
        boolean usePreferred = true;  
        try {  
            // 返回usePreferred的值为true  
            usePreferred = ! mPhone.getContext().getResources().getBoolean(com.android.  
                    internal.R.bool.config_dontPreferApn);  
        } catch (Resources.NotFoundException e) {  
            if (DBG) log("buildWaitingApns: usePreferred NotFoundException set to true");  
            usePreferred = true;  
        }  
        // 我们知道此地,一样,无法取得值,返回mPreferredApn的值为null  
        if (usePreferred) {  
            mPreferredApn = getPreferredApn();  
        }  
        if (DBG) {  
            log("buildWaitingApns: usePreferred=" + usePreferred  
                    + " canSetPreferApn=" + mCanSetPreferApn  
                    + " mPreferredApn=" + mPreferredApn  
                    + " operator=" + operator + " radioTech=" + radioTech  
                    + " IccRecords r=" + r);  
        }  
      
        ......  
        if (mAllApnSettings != null) {  
            ......  
            // 刚刚通过createAllApnList方法获取到的值  
            for (ApnSetting apn : mAllApnSettings) {  
                if (apn.canHandleType(requestedApnType)) {  
                    if (ServiceState.bitmaskHasTech(apn.bearerBitmask, radioTech)) {  
                        if (DBG) log("buildWaitingApns: adding apn=" + apn);  
                        apnList.add(apn);  
                    } else {  
                        if (DBG) {  
                            log("buildWaitingApns: bearerBitmask:" + apn.bearerBitmask + " does " +  
                                    "not include radioTech:" + radioTech);  
                        }  
                    }  
                } else if (DBG) {  
                    log("buildWaitingApns: couldn't handle requesedApnType="  
                            + requestedApnType);  
                }  
            }  
        } else {  
            loge("mAllApnSettings is null!");  
        }  
        if (DBG) log("buildWaitingApns: X apnList=" + apnList);  
        return apnList;  
}
返回一个apnList,这个list是从mAllApnSettings中取到的,而其判断条件为该ApnSetting中是否包含需要的apnType,和是否符合我们的需要
再查看trySetupData方法,其是将刚刚的buildWaitingApns返回的apnList传入的
private boolean trySetupData(ApnContext apnContext, ArrayList waitingApns) {  
        ......  
          
        apnContext.setWaitingApns(waitingApns);  
      
        ......  
        boolean retValue = setupData(apnContext, radioTech);  
        notifyOffApnsOfAvailability(apnContext.getReason());  
        ......  
}
再看setupData方法

private boolean setupData(ApnContext apnContext, int radioTech) {  
        ......  
        ApnSetting apnSetting;  
        DcAsyncChannel dcac = null;  
      
        // 直接获取mWaitingApn List中的第一个ApnSetting  
        apnSetting = apnContext.getNextWaitingApn();  
        ......  
      
        apnContext.setDataConnectionAc(dcac);  
        apnContext.setApnSetting(apnSetting);  
        apnContext.setState(DctConstants.State.CONNECTING);  
        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());  
      
        Message msg = obtainMessage();  
        msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;  
        msg.obj = new Pair(apnContext, generation);  
        dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, radioTech,  
                mAutoAttachOnCreation.get(), msg, generation);  
      
        if (DBG) log("setupData: initing!");  
        return true;  
}
创建DataConnection,并设置其参数,设置完成后发送EVENT_DATA_SETUP_COMPLETE消息,在其父类DcTrackerBase.java文件中接收,并处理

@Override  
    public void handleMessage(Message msg) {  
        switch (msg.what) {  
            ......  
            case DctConstants.EVENT_DATA_SETUP_COMPLETE:  
                onDataSetupComplete((AsyncResult) msg.obj);  
                break;  
        }  
    } 
在DcTracker的onDataSetupComplete方法中进行处理

/** 
     * A SETUP (aka bringUp) has completed, possibly with an error. If 
     * there is an error this method will call {@link #onDataSetupCompleteError}. 
     */  
    @Override  
    protected void onDataSetupComplete(AsyncResult ar) {  
      
        DcFailCause cause = DcFailCause.UNKNOWN;  
        boolean handleError = false;  
        ApnContext apnContext = getValidApnContext(ar, "onDataSetupComplete");  
      
        ......  
      
        if (ar.exception == null) {  
            DcAsyncChannel dcac = apnContext.getDcAc();  
      
            ......  
              
                ApnSetting apn = apnContext.getApnSetting();  
                ......  
      
                // everything is setup  
                if(TextUtils.equals(apnContext.getApnType(),PhoneConstants.APN_TYPE_DEFAULT)) {  
                    SystemProperties.set(PUPPET_MASTER_RADIO_STRESS_TEST, "true");  
                    if (mCanSetPreferApn && mPreferredApn == null) {  
                        if (DBG) log("onDataSetupComplete: PREFERED APN is null");  
                        mPreferredApn = apn;  
                        if (mPreferredApn != null) {  
                            setPreferredApn(mPreferredApn.id);  
                        }  
                    }  
                }  
            ......  
        }  
        ......  
}

可以看到,preferred apn就是在此处进行第一次设定的,而ApnSetting就是我们之前在buildWaitingApn中获取到的apnList的第一个ApnSetting
好了,到此结束
    仅以此篇暂时记录目前的研究成果,若有错误,后续改正

你可能感兴趣的:(SIM卡开机流程分析)