Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android——4.2 - 3G移植之路之 APN (五)

Android——4.2 - 3G移植之路之 APN (五)

編輯:關於Android編程

APN,這東西對於剛接觸的人來說並不是那麼好理解,對於3G移植上網必不可少,這裡記錄一下。

 

 

概念:

APN(Access Point Name),也就是 接入點 ,移動設備使用數據流量上網必須配置的一個參數,代表以何種方式來連接服務台開啟數據流量功能.

一般有訪問WAP或者connect 因特網,國內的運營商2G,3G標識如下:

移動公司:2G:GSM、3G:TD-SCDMA
聯通公司:2G:GSM、3G:WCDMA
電信公司:2G:CDMA、3G:CDMA2000

關於具體某個運營商的幾G網絡 的APN 是什麼具體可參考/device/sample/etc/apns-full-conf.xml

這個xml文件中有google預置的多國常用的APN

 

 

使用:

apns-full-conf.xml

上面說道了apns-full-conf.xml 這個配置文件,這裡面基本上是這樣的模塊:

 

  

其它選項都是網絡參數,其中的apn就是我們最重要的接入點.也可自行添加apn屬性模塊.

 

 

移植3G時,就需要用到這個xml配置文件了,在android的device.mk 裡面加個PRODUCT_COPY_FILES:

PRODUCT_COPY_FILES += 

     device/sample/etc/apns-full-conf.xml:system/etc/apns-conf.xml

 

 

telephony.db

這個文件被加載的地方可參考/packages/providers/TelephonyProvider/src/com/android/providers/telephony/TelephonyProvider.java:

    private static final String DATABASE_NAME = telephony.db;  //數據庫db文件

    private static final String PARTNER_APNS_PATH = etc/apns-conf.xml;  //上面說到的copy到系統system/etc目錄下

...

    private static class DatabaseHelper extends SQLiteOpenHelper {
        // Context to access resources with
        private Context mContext;

        /**
         * DatabaseHelper helper class for loading apns into a database.
         *
         * @param context of the user.
         */
        public DatabaseHelper(Context context) {
            super(context, DATABASE_NAME, null, getVersion(context));
            mContext = context;
        }

...
        @Override
        public void onCreate(SQLiteDatabase db) {
            // Set up the database schema
            db.execSQL(CREATE TABLE  + CARRIERS_TABLE +    //建表
                (_id INTEGER PRIMARY KEY, +
                    name TEXT, +
                    numeric TEXT, +
                    mcc TEXT, +
                    mnc TEXT, +
                    apn TEXT, +
                    user TEXT, +
                    server TEXT, +
                    password TEXT, +
                    proxy TEXT, +
                    port TEXT, +
                    mmsproxy TEXT, +
                    mmsport TEXT, +
                    mmsc TEXT, +
                    authtype INTEGER, +
                    type TEXT, +
                    current INTEGER, +
                    protocol TEXT, +
                    roaming_protocol TEXT, +
                    carrier_enabled BOOLEAN, +
                    bearer INTEGER););

            initDatabase(db);
        }
    private void initDatabase(SQLiteDatabase db) {

...

            // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or /system.
            File confFile = new File(Environment.getRootDirectory(), PARTNER_APNS_PATH);      //這裡就是加載解析 load進db 了
            FileReader confreader = null;
            try {
                confreader = new FileReader(confFile);
                confparser = Xml.newPullParser();
                confparser.setInput(confreader);
                XmlUtils.beginDocument(confparser, apns);

                // Sanity check. Force internal version and confidential versions to agree
                int confversion = Integer.parseInt(confparser.getAttributeValue(null, version));
                if (publicversion != confversion) {
                    throw new IllegalStateException(Internal APNS file version doesn't match 
                            + confFile.getAbsolutePath());
                }

                loadApns(db, confparser);
            } 

...

   }

}


因為Content Provider采用的是懶加載機制,所以只有檢測load上sim卡的時候才會被創建這個db:


\

可使用sqlite3查看:

\

 

 

createAllApnList

 

在android中數據流量由/frameworks/opt/telephony/src/java/com/android/internal/telephony/DataConnectionTracker.java

以及它的子類GsmDataConnectionTracker.java(GSM模式) 或者 CdmaDataConnectionTracker.java(CDMA模式),(前者為移動,聯通,後者為電信專用)

來控制,其中啟動數據流量開關為onSetUserDataEnabled(boolean enabled).

這裡單以GSM模式來說,在SIM 被load時調用:

 

    private void onRecordsLoaded() {
        if (DBG) log(onRecordsLoaded: createAllApnList);
        createAllApnList();
        if(!mUserDataEnabled)
            return;//jscese add judgement
        if (mPhone.mCM.getRadioState().isOn()) {
            if (DBG) log(onRecordsLoaded: notifying data availability);
            notifyOffApnsOfAvailability(Phone.REASON_SIM_LOADED);
        }
        setupDataOnReadyApns(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() {
        mAllApns = new ArrayList();
        IccRecords r = mIccRecords.get();
        String operator = (r != null) ? r.getOperatorNumeric() : ;
        if (operator != null) {
            String selection = numeric = ' + operator + ';
            // 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(   //用當前SIM卡對應的運營商查詢系統的所有APN,往下調用createApnList
                    Telephony.Carriers.CONTENT_URI, null, selection, null, null);

            if (cursor != null) {
                if (cursor.getCount() > 0) {
                    mAllApns = createApnList(cursor);  //可以跟進去看查詢Telephony.Carriers並返回一個Apn的list
                }
                cursor.close();
            }
        }

        if (mAllApns.isEmpty()) {
            if (DBG) log(createAllApnList: No APN found for carrier:  + operator);
            mPreferredApn = null;
            // TODO: What is the right behaviour?
            //notifyNoData(GsmDataConnection.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 mAllApns= + mAllApns);
    }

 

 

onSetUserDataEnabled(true):

代表打開數據流量,最終調用到

 

private boolean trySetupData(ApnContext apnContext)

{

...

       if (apnContext.getState() == DctConstants.State.IDLE) {
                ArrayList waitingApns = buildWaitingApns(apnContext.getApnType());

//使用用戶設置的preferred APN構建一個可用於數據連接的備選APN列表,即waitingApns列表(當有preferred APN,該列表就只有一個)。

//若用戶沒有設置preferred APN,則將所有類型匹配的APN添加到waitingApns列表(如default類型)

                if (waitingApns.isEmpty()) {
                    if (DBG) log(trySetupData: No APN found);
                    notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN, apnContext);
                    notifyOffApnsOfAvailability(apnContext.getReason());
                    return false;
                } else {
                    apnContext.setWaitingApns(waitingApns);
                    if (DBG) {
                        log (trySetupData: Create from mAllApns :  + apnListToString(mAllApns));
                    }
                }
            }

            if (DBG) {
                log (Setup watingApns :  + apnListToString(apnContext.getWaitingApns()));
            }
            // apnContext.setReason(apnContext.getReason());
            boolean retValue = setupData(apnContext);   // waitingApns列表中有可用的APN時,嘗試建立連接
            notifyOffApnsOfAvailability(apnContext.getReason());
            return retValue;

...

}

另外幾個對apn操作的幾個函數都在這個文件裡面,分別的作用如下:

 

onApnChanged:當APN被用戶更改時,將調用到此函數,重新建立數據連接

 

setPreferredApn:當用戶沒有設置preferred APN時,將當前數據連接成功的那個APN設置為preferred APN。可以去看onDataSetupComplete時的操作。

getPreferredApn:用戶獲取用戶設置的preferred APN,這個在上面說到的createAllApnList時會去獲取一次,看是否存在.

對於這個preferredApn會以xml的形式保存在:

 

shell@android:/data/data/com.android.providers.telephony/shared_prefs # cat preferred-apn.xml
ml                                                                            <


這是代表打開數據流量成功之後保存的apn map,可以像上面一樣去 telephony.db裡面查查看看:

 

 

sqlite> select * from carriers where _id='1124';
1124|沃3G連接互聯網 (China Unicom)|46001|460|01|3gnet|||||||||-1|default,supl|1|IP|IP|1|0

 

 

 


這裡只是分析了一下apn的由來以及在framework層的使用,最終是通過RIL.java 的setupDataCall通過一個rild 的socket發請求到hardware的ril.cpp:

 

public void
    setupDataCall(String radioTechnology, String profile, String apn, /*上面傳下來的apn*/
            String user, String password, String authType, String protocol,
            Message result) {
        RILRequest rr
                = RILRequest.obtain(RIL_REQUEST_SETUP_DATA_CALL, result);

        rr.mp.writeInt(7);

        rr.mp.writeString(radioTechnology);
        rr.mp.writeString(profile);
        rr.mp.writeString(apn);
        rr.mp.writeString(user);
        rr.mp.writeString(password);
        rr.mp.writeString(authType);
        rr.mp.writeString(protocol);

        if (RILJ_LOGD) riljLog(rr.serialString() + > 
                + requestToString(rr.mRequest) +   + radioTechnology +  
                + profile +   + apn +   + user +  
                + password +   + authType +   + protocol);

        send(rr); //裡面就是 socket了
    }

 

再之後怎麼獲取到這個socket event處理並且交給reference-ril 發送這個apn接入網路可參考我前面的博客:

Android——RIL 機制源碼分析

Android——4.2 - 3G移植之路之 reference-ril .pppd 撥號上網 (三)

 

 

 

 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved