zoukankan      html  css  js  c++  java
  • Android——4.2

                APN,这东西对于刚接触的人来说并非那么好理解。对于3G移植上网不可缺少,这里记录一下。


                                                  撰写不易,转载请注明出处:http://blog.csdn.net/jscese/article/details/41248939


    概念:

     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 carrier="沃3G连接互联网 (China Unicom)"
          mcc="460"
          mnc="01"
          apn="3gnet"
          type="default,supl"
      />

    其他选项都是网络參数,当中的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<ApnSetting>();
            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<ApnSetting> 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                                                                            <
    <?

    xml version='1.0' encoding='utf-8' standalone='yes' ?> <map> <long name="apn_id" value="1124" /> </map>


    这是代表打开数据流量成功之后保存的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 拨号上网 (三)






  • 相关阅读:
    MongoDB 搭建可复制群集
    jquery获取json对象中的key小技巧,遍历json串所有key,value
    21-spring学习-springMVC实现CRUD
    java线程--volatile实现可见性
    java线程-synchronized实现可见性代码
    java线程-java多线程之可见性
    java反射--通过反射了解集合泛型的本质
    java反射--方法反射的基本操作
    java反射--获取成员变量信息
    java反射-获取方法信息
  • 原文地址:https://www.cnblogs.com/yfceshi/p/7039268.html
Copyright © 2011-2022 走看看