Android APN的显示流程源代码分析

2024-05-30 12:49

本文主要是介绍Android APN的显示流程源代码分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一.名词介绍

1.PLMN(Public Land Mobile Network,公共陆地移动网络)由政府或它所批准的经营者,为公众提供陆地移动通信业务目的而建立和经营的网络。一句话:一个移动通信网络,比如中国的PLMN主要有三种,中国移动中国联通和中国电信。在手机开发中,PLMN一般指网络代号,而PLMN代号=MCC+MNC。如下是常见的PLMN参照表

MCC     MNC     运营商
460     00      中国移动
460     01      中国联通
460     02      中国移动
460     03      中国电信
460     04      中国卫通
460     05      中国电信
460     06      中国联通
460     07      中国移动
460     20      中国铁通

2.MCC(Mobile Country Code,移动国家码)MCC的资源由国际电联(ITU)统一分配和管理,唯一识别移动用户所属的国家,共3位,中国为460。
3.MNC(Mobile Network Code,移动网络号码),用于识别移动客户所属的移动网络,2~3位数字组成.
MCC MNC PLMN都是常量,一般只能从SIM卡或者配置文件读取,而不可以随意指定。
4.SPN(Service Provider Name,运营商名称),其内容是写在USIM/SIM卡中EFspn字段中的,与当前注册的网络无关。如中国移动卡,漫游到任何网络,其SPN都是「CMCC」或「中国移动」
5.APN(Access Point Name,接入点名称)
手机可以接入各种网络,例如:Internet、WAP网站、集团企业内部网络。而不同的接入点所能访问的范围网络以及接入的方式是不同的,网络侧如何知道手机激活以后要访问哪个网络从而分配哪个网段的IP呢,这就要靠APN来区分了,即APN决定了手机最终连接的网络是哪个。

二.APN的配置文件结构

配置文件的基本格式:由若干如下形式的节点组成

  <apn carrier="Cosmote Internet"mcc="202"mnc="01"apn=""type="ia"protocol="IPV4V6"mvno_type="gid"mvno_match_data="FF"/><apn carrier="Cosmote Internet"mcc="202"mnc="01"apn="internet"type="default,supl"protocol="IPV4V6"roaming_protocol="IP"mvno_type="gid"mvno_match_data="FF"/>

APN配置的几个属性解释:
carrier:apn list和apn详细信息中的apn名称
apn
现在我们涉及到的APN具体有两种,一种是通过手机浏览器上网使用的,另一种是通过客户端软件来登陆服务器。
国内的情况:中国联通的2G业务WAP浏览器中使用的APN为“UNIWAP”,3G业务WAP浏览器使用的APN为“3GWAP”;中国联通的2G上公网使用的APN为“UNINET”,3G业务上网卡及上公网使用的APN为“3GNET”。 中国移动上内网的APN为“CMWAP”,上网卡及上公网使用的APN为“CMNET”。 中国电信上内网的APN为“CTWAP”,上网卡及上公网使用的APN为“CTNET”。
好在现在国内销售的手机都已经将APN配置预先做好了,因此您不用为了APN的配置而太担心。
type:主要有5种
a.default
The Mobile data connection. When active, all data traffic will use this network type’s interface by default (it has a default route)
手机默认数据连接,激活时所有数据传输都会默认使用这种网络类型的接口
b.mms
An MMS-specific Mobile data connection. This network type may use the
same network interface as {@link #TYPE_MOBILE} or it may use a different
one. This is used by applications needing to talk to the carrier’s
Multimedia Messaging Service servers.
使用彩信服务时,必须有mms类型的接入点,不必选中,应用程序会自动使用此接入
c.supl
A SUPL-specific Mobile data connection. This network type may use the
same network interface as {@link #TYPE_MOBILE} or it may use a different
one. This is used by applications needing to talk to the carrier’s
Secure User Plane Location servers for help locating the device.
是Secure User Plane Location“安全用户面定位”的简写,这种网络类型可能被需要和载体的Secure User Plane Location servers传输数据的应用使用来帮助定位,使用场景:需要自动切换wap与net接入点的、需要把手机当临时AP(热点)的
d.dun
A DUN-specific Mobile data connection. This network type may use the
same network interface as {@link #TYPE_MOBILE} or it may use a different
one. This is sometimes by the system when setting up an upstream connection
for tethering so that the carrier is aware of DUN traffic.
Dial Up Networking拨号网络的简称,此连接用于执行一个拨号网络网桥,使载体能知道拨号网络流量的应用程序
适用场合:需要使用运营商无线热点的,CMCC、ChinaNet等
e.hipri
A High Priority Mobile data connection. This network type uses the
same network interface as {@link #TYPE_MOBILE} but the routing setup
is different.
高优先级网络,路由设置和默认方式不同。
这些属性不都是必要的,可以不写或者使用空字符串,此时在apn详细信息中显示的就是unset或者未设置。5个类型中default和mms比较重要,其他的可以不用详细了解。以上英文部分摘自android.net.ConnectivityManager

三.APN的显示流程代码分析(以MTK提供的源码为例,重点)

APN的显示流程大概是这样的:首次开机,创建数据库对应表,从配置文件(XML文件)读取配置到数据库,进入APN列表时根据sim卡参数从数据库筛选出正确的APN显示
1.新建db
手机第一次开机时,会读取该配置文件,对xml进行解析,并存储到数据库中,解析XML的并存储到数据库的代码一般在
packages/providers/TelephonyProvider/src/com/android/providers/telephony/TelephonyProvider.java文件中
数据库的保存位置为:
/data/data/com.android.providers.telephony/databases/ telephony.db/Carriers表(7.0之前)
/data/user_de/0/com.android.providers.telephony/databases/telephony.db/Carriers表(7.0之后,包括7.0)
第一步新建数据库和数据库表。
新建DB代码:

       public DatabaseHelper(Context context) {super(context, DATABASE_NAME, null, getVersion(context));mContext = context;mVersion = getVersion(mContext);if (DBG) log("Version: [" + getVersion(mContext) + "]");if (!BSP_PACKAGE) {try {mTelephonyProviderExt =MPlugin.createInstance(ITelephonyProviderExt.class.getName(), mContext);} catch (Exception e) {e.printStackTrace();}}}

网络上有人说

super(context, DATABASE_NAME, null, getVersion(context));

是创建数据库的地方,但是根据源码的解释,是在调用getReadable或者getWritabledatabase时才真正创建数据库的,原文(可以跟踪下源码):

package android.database.sqlite;
...
public abstract class SQLiteOpenHelper {
.../*** Create a helper object to create, open, and/or manage a database.* This method always returns very quickly.  The database is not actually* created or opened until one of {@link #getWritableDatabase} or* {@link #getReadableDatabase} is called.** @param context to use to open or create the database* @param name of the database file, or null for an in-memory database* @param factory to use for creating cursor objects, or null for the default* @param version number of the database (starting at 1); if the database is older,*     {@link #onUpgrade} will be used to upgrade the database; if the database is*     newer, {@link #onDowngrade} will be used to downgrade the database*/public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {this(context, name, factory, version, null);}
...
}

2.新建CARRIERS表

        @Overridepublic void onCreate(SQLiteDatabase db) {if (DBG) log("dbh.onCreate:+ db=" + db);createSimInfoTable(db);createCarriersTable(db, CARRIERS_TABLE);//创建表initDatabase(db);if (DBG) log("dbh.onCreate:- db=" + db);}

具体的创建代码

        private void createCarriersTable(SQLiteDatabase db, String tableName) {// Set up the database schemaif (DBG) log("dbh.createCarriersTable start");String columns = "(_id INTEGER PRIMARY KEY," +NAME + " TEXT DEFAULT ''," +NUMERIC + " TEXT DEFAULT ''," +MCC + " TEXT DEFAULT ''," +MNC + " TEXT DEFAULT ''," +APN + " TEXT DEFAULT ''," +USER + " TEXT DEFAULT ''," +SERVER + " TEXT DEFAULT ''," +PASSWORD + " TEXT DEFAULT ''," +PROXY + " TEXT DEFAULT ''," +PORT + " TEXT DEFAULT ''," +MMSPROXY + " TEXT DEFAULT ''," +MMSPORT + " TEXT DEFAULT ''," +MMSC + " TEXT DEFAULT ''," +AUTH_TYPE + " INTEGER DEFAULT -1," +TYPE + " TEXT DEFAULT ''," +CURRENT + " INTEGER," +SOURCE_TYPE + " INTEGER DEFAULT 0," +CSD_NUM + " TEXT DEFAULT ''," +PROTOCOL + " TEXT DEFAULT IP," +ROAMING_PROTOCOL + " TEXT DEFAULT IP,";/// M: add for OMACP serviceif (OMACP_SUPPORT) {columns += OMACP_ID + " TEXT DEFAULT ''," +NAP_ID + " TEXT DEFAULT ''," +PROXY_ID + " TEXT DEFAULT '',";}columns += CARRIER_ENABLED + " BOOLEAN DEFAULT 1," +BEARER + " INTEGER DEFAULT 0," +BEARER_BITMASK + " INTEGER DEFAULT 0," +SPN + " TEXT DEFAULT ''," +IMSI + " TEXT DEFAULT ''," +PNN +  " TEXT DEFAULT ''," +PPP +  " TEXT DEFAULT ''," +MVNO_TYPE + " TEXT DEFAULT ''," +MVNO_MATCH_DATA + " TEXT DEFAULT '',";columns += SUBSCRIPTION_ID + " INTEGER DEFAULT " +SubscriptionManager.INVALID_SUBSCRIPTION_ID + "," +PROFILE_ID + " INTEGER DEFAULT 0," +MODEM_COGNITIVE + " BOOLEAN DEFAULT 0," +MAX_CONNS + " INTEGER DEFAULT 0," +WAIT_TIME + " INTEGER DEFAULT 0," +MAX_CONNS_TIME + " INTEGER DEFAULT 0," +MTU + " INTEGER DEFAULT 0," +EDITED + " INTEGER DEFAULT " + UNEDITED + "," +READ_ONLY + " BOOLEAN DEFAULT 0," + USER_VISIBLE + " BOOLEAN DEFAULT 1, " +// Uniqueness collisions are used to trigger merge code so if a field is listed// here it means we will accept both (user edited + new apn_conf definition)// Columns not included in UNIQUE constraint: name, current, edited,// user, server, password, authtype, type, protocol, roaming_protocol, sub_id,// modem_cognitive, max_conns, wait_time, max_conns_time, mtu, bearer_bitmask,// user_visible"UNIQUE (" + TextUtils.join(", ", CARRIERS_UNIQUE_FIELDS) + "));";db.execSQL("CREATE TABLE " + tableName + columns);db.execSQL("DROP TABLE IF EXISTS " + CARRIERS_DM_TABLE);db.execSQL("CREATE TABLE " + CARRIERS_DM_TABLE + columns);if (DBG) log("dbh.createCarriersTable:-");}

代码比较多但是很简单,最终就是执行了一个sql语句罢了
3.解析xml导入数据库
在上一步中看到创建数据库之后执行了initDatabase(db);该方法就是解析xml导入数据库的流程

    /***  This function adds APNs from xml file(s) to db. The db may or may not be empty to begin*  with.*/private void initDatabase(SQLiteDatabase db) {if (VDBG) log("dbh.initDatabase:+ db=" + db);// Read internal APNS dataResources r = mContext.getResources();XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns);int publicversion = -1;try {XmlUtils.beginDocument(parser, "apns");publicversion = Integer.parseInt(parser.getAttributeValue(null, "version"));loadApns(db, parser);} catch (Exception e) {loge("Got exception while loading APN database." + e);} finally {parser.close();}// Read external APNS data (partner-provided)XmlPullParser confparser = null;File confFile = getApnConfFile();FileReader confreader = null;if (DBG) log("confFile = " + confFile);try {confreader = new FileReader(confFile);confparser = Xml.newPullParser();confparser.setInput(confreader);XmlUtils.beginDocument(confparser, "apns");// Sanity check. Force internal version and confidential versions to agreeint confversion = Integer.parseInt(confparser.getAttributeValue(null, "version"));if (publicversion != confversion) {log("initDatabase: throwing exception due to version mismatch");throw new IllegalStateException("Internal APNS file version doesn't match "+ confFile.getAbsolutePath());}db.beginTransaction();try {loadApns(db, confparser);db.setTransactionSuccessful();} finally {db.endTransaction();}} catch (FileNotFoundException e) {// It's ok if the file isn't found. It means there isn't a confidential file// Log.e(TAG, "File not found: '" + confFile.getAbsolutePath() + "'");} catch (Exception e) {loge("initDatabase: Exception while parsing '" + confFile.getAbsolutePath() + "'" +e);} finally {// Get rid of user/carrier deleted entries that are not present in apn xml file.// Those entries have edited value USER_DELETED/CARRIER_DELETED.if (VDBG) {log("initDatabase: deleting USER_DELETED and replacing "+ "DELETED_BUT_PRESENT_IN_XML with DELETED");}// Delete USER_DELETEDdb.delete(CARRIERS_TABLE, IS_USER_DELETED + " or " + IS_CARRIER_DELETED, null);// Change USER_DELETED_BUT_PRESENT_IN_XML to USER_DELETEDContentValues cv = new ContentValues();cv.put(EDITED, USER_DELETED);db.update(CARRIERS_TABLE, cv, IS_USER_DELETED_BUT_PRESENT_IN_XML, null);// Change CARRIER_DELETED_BUT_PRESENT_IN_XML to CARRIER_DELETEDcv = new ContentValues();cv.put(EDITED, CARRIER_DELETED);db.update(CARRIERS_TABLE, cv, IS_CARRIER_DELETED_BUT_PRESENT_IN_XML, null);if (confreader != null) {try {confreader.close();} catch (IOException e) {// do nothing}}// Update the stored checksumsetApnConfChecksum(getChecksum(confFile));}if (VDBG) log("dbh.initDatabase:- db=" + db);}

可以看出,这里解析一共有两次
一次是
Read internal APNS data
一次是
Read external APNS data (partner-provided)
但是流程都是类似的拿到XmlPullParser XML解析对象,拿到文件对象,调用loadApns解析xml。
loadApns的代码如下

    /** Loads apns from xml file into the database** @param db the sqlite database to write to* @param parser the xml parser**/private void loadApns(SQLiteDatabase db, XmlPullParser parser) {if (parser != null) {try {db.beginTransaction();XmlUtils.nextElement(parser);/// M: for 02839078, when switch between multi-user, there will be two phone// process at the same time for a short while, calling to SubscriptionManager// API may become IPC call, which takes a lot of time in "while loop" and leads// to ANR, so call it only once hereint subId = SubscriptionManager.getDefaultSubscriptionId();while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {ContentValues row = getRow(parser);if (row != null) {if (!BSP_PACKAGE) {// Add operator customized configuration in onLoadApns if needtry {mTelephonyProviderExt.onLoadApns(row);} catch (Exception e) {e.printStackTrace();}}/// M: for 02839078, pass the subId instead of getting it every time// insertAddingDefaults(db, row);insertAddingDefaults(db, row, subId);XmlUtils.nextElement(parser);} else {//throw new XmlPullParserException("Expected 'apn' tag", parser, null);break;  // do we really want to skip the rest of the file?}}db.setTransactionSuccessful();} catch (XmlPullParserException e) {loge("Got XmlPullParserException while loading apns." + e);} catch (IOException e) {loge("Got IOException while loading apns." + e);} catch (SQLException e) {loge("Got SQLException while loading apns." + e);} finally {db.endTransaction();}}}

android的xml解析大致有三种:
pull解析 Sax解析和Dom解析,可以看到上面采用的是pull解析
具体的解析方式对比可以参照:
Android XML数据解析http://www.runoob.com/w3cnote/android-tutorial-xml.html
如果xml没有读到end节点(parser.getEventType() != XmlPullParser.END_DOCUMENT),使用getRow方法从xml读出数据,再调用insertAddingDefaults方法将读到的数据插入创建的数据库表格,之后遍历下一个节点
getRow方法(解析一个xml apn节点变成一个map):

     /*** Gets the next row of apn values.** @param parser the parser* @return the row or null if it's not an apn*/private ContentValues getRow(XmlPullParser parser) {if (!"apn".equals(parser.getName())) {return null;}ContentValues map = new ContentValues();String mcc = parser.getAttributeValue(null, "mcc");String mnc = parser.getAttributeValue(null, "mnc");String numeric = mcc + mnc;map.put(NUMERIC, numeric);map.put(MCC, mcc);map.put(MNC, mnc);map.put(NAME, parser.getAttributeValue(null, "carrier"));// do not add NULL to the map so that default values can be inserted in dbaddStringAttribute(parser, "apn", map, APN);addStringAttribute(parser, "user", map, USER);addStringAttribute(parser, "server", map, SERVER);addStringAttribute(parser, "password", map, PASSWORD);addStringAttribute(parser, "proxy", map, PROXY);addStringAttribute(parser, "port", map, PORT);addStringAttribute(parser, "mmsproxy", map, MMSPROXY);addStringAttribute(parser, "mmsport", map, MMSPORT);addStringAttribute(parser, "mmsc", map, MMSC);addStringAttribute(parser, "type", map, TYPE);addStringAttribute(parser, "protocol", map, PROTOCOL);addStringAttribute(parser, "roaming_protocol", map, ROAMING_PROTOCOL);addIntAttribute(parser, "authtype", map, AUTH_TYPE);addIntAttribute(parser, "bearer", map, BEARER);addIntAttribute(parser, "profile_id", map, PROFILE_ID);addIntAttribute(parser, "max_conns", map, MAX_CONNS);addIntAttribute(parser, "wait_time", map, WAIT_TIME);addIntAttribute(parser, "max_conns_time", map, MAX_CONNS_TIME);addIntAttribute(parser, "mtu", map, MTU);addBoolAttribute(parser, "carrier_enabled", map, CARRIER_ENABLED);addBoolAttribute(parser, "modem_cognitive", map, MODEM_COGNITIVE);addBoolAttribute(parser, "user_visible", map, USER_VISIBLE);addBoolAttribute(parser, "read_only", map, READ_ONLY);int bearerBitmask = 0;String bearerList = parser.getAttributeValue(null, "bearer_bitmask");if (bearerList != null) {bearerBitmask = ServiceState.getBitmaskFromString(bearerList);}map.put(BEARER_BITMASK, bearerBitmask);String ppp = parser.getAttributeValue(null, "ppp");if (ppp != null) {map.put(Telephony.Carriers.PPP, ppp);}//keep for old versionString spn = parser.getAttributeValue(null, "spn");if (spn != null) {map.put(Telephony.Carriers.SPN, spn);}String imsi = parser.getAttributeValue(null, "imsi");if (imsi != null) {map.put(Telephony.Carriers.IMSI, imsi);}String pnn = parser.getAttributeValue(null, "pnn");if (pnn != null) {map.put(Telephony.Carriers.PNN, pnn);}String mvno_type = parser.getAttributeValue(null, "mvno_type");if (mvno_type != null) {String mvno_match_data = parser.getAttributeValue(null, "mvno_match_data");if (mvno_match_data != null) {map.put(MVNO_TYPE, mvno_type);map.put(MVNO_MATCH_DATA, mvno_match_data);}}return map;}

至此,xml中的数据完全导入到数据库中
4.界面显示
界面显示的逻辑在一个fragment
packages/apps/Settings/src/com/android/settings/ApnSettings.java
逻辑也很清晰
在OnCreate进行变量初始化

public void onCreate(Bundle icicle) {super.onCreate(icicle);final Activity activity = getActivity();final int subId = activity.getIntent().getIntExtra(SUB_ID,SubscriptionManager.INVALID_SUBSCRIPTION_ID);mMobileStateFilter = new IntentFilter(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);/// M: for Airplane mode checkmMobileStateFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);setIfOnlyAvailableForAdmins(true);mSubscriptionInfo = SubscriptionManager.from(activity).getActiveSubscriptionInfo(subId);mUiccController = UiccController.getInstance();/// M: for [SIM Hot Swap] @{mSimHotSwapHandler = new SimHotSwapHandler(getActivity().getApplicationContext());mSimHotSwapHandler.registerOnSimHotSwap(new OnSimHotSwapListener() {@Overridepublic void onSimHotSwap() {Log.d(TAG, "onSimHotSwap, finish activity");if (getActivity() != null) {getActivity().finish();}}});/// @}CarrierConfigManager configManager = (CarrierConfigManager)getSystemService(Context.CARRIER_CONFIG_SERVICE);PersistableBundle b = configManager.getConfig();mHideImsApn = b.getBoolean(CarrierConfigManager.KEY_HIDE_IMS_APN_BOOL);mAllowAddingApns = b.getBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL);mUserManager = UserManager.get(activity);}

onActivityCreated中设置布局文件

    @Overridepublic void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);getEmptyTextView().setText(R.string.apn_settings_not_available);mUnavailable = isUiRestricted();setHasOptionsMenu(!mUnavailable);if (mUnavailable) {setPreferenceScreen(new PreferenceScreen(getPrefContext(), null));getPreferenceScreen().removeAll();return;}addPreferencesFromResource(R.xml.apn_settings);}

onResume注册监听并填充list,其中填充list是重点,即fillList方法。
@Override

  public void onResume() {Log.v("chj","onResume");super.onResume();if (mUnavailable) {return;}getActivity().registerReceiver(mMobileStateReceiver, mMobileStateFilter);if (!mRestoreDefaultApnMode) {fillList();/// M: In case dialog not dismiss as activity is in background, so when resume back,// need to remove the dialog @{removeDialog(DIALOG_RESTORE_DEFAULTAPN);/// @}}/// M: for plug-inmApnExt.updateTetherState();}

fillList方法的代码如下:

private void fillList() {final TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);final String mccmnc = mSubscriptionInfo == null ? "": tm.getSimOperator(mSubscriptionInfo.getSubscriptionId());String where = "numeric=\"" + mccmnc +"\" AND NOT (type='ia' AND (apn=\"\" OR apn IS NULL)) AND user_visible!=0";/// M: for plug-inwhere = mApnExt.getFillListQuery(where, mccmnc);Log.d(TAG, "fillList where: " + where);/// M: for CU default APN set./*Cursor cursor = getContentResolver().query(Telephony.Carriers.CONTENT_URI, new String[] {"_id", "name", "apn", "type", "mvno_type", "mvno_match_data"}, where.toString(),null, Telephony.Carriers.DEFAULT_SORT_ORDER);*/String order = mApnExt.getApnSortOrder(Telephony.Carriers.DEFAULT_SORT_ORDER);Log.d(TAG, "fillList sort: " + order);Cursor cursor = getContentResolver().query(Telephony.Carriers.CONTENT_URI,new String[] { "_id", "name", "apn", "type", "mvno_type", "mvno_match_data","sourcetype","read_only" }, where, null, order);/// @}if (cursor != null) {Log.d(TAG, "fillList, cursor count: " + cursor.getCount());IccRecords r = null;if (mUiccController != null && mSubscriptionInfo != null) {r = mUiccController.getIccRecords(SubscriptionManager.getPhoneId(mSubscriptionInfo.getSubscriptionId()), UiccController.APP_FAM_3GPP);}PreferenceGroup apnList = (PreferenceGroup) findPreference("apn_list");apnList.removeAll();/// M: for plug-in, use Preference instead ApnPreference for the// convenience of plug-in sideArrayList<Preference> mnoApnList = new ArrayList<Preference>();ArrayList<Preference> mvnoApnList = new ArrayList<Preference>();ArrayList<Preference> mnoMmsApnList = new ArrayList<Preference>();ArrayList<Preference> mvnoMmsApnList = new ArrayList<Preference>();mSelectedKey = getSelectedApnKey();cursor.moveToFirst();while (!cursor.isAfterLast()) {String name = cursor.getString(NAME_INDEX);String apn = cursor.getString(APN_INDEX);String key = cursor.getString(ID_INDEX);String type = cursor.getString(TYPES_INDEX);String mvnoType = cursor.getString(MVNO_TYPE_INDEX);String mvnoMatchData = cursor.getString(MVNO_MATCH_DATA_INDEX);/// M: check source type, some types are not editableint sourcetype = cursor.getInt(SOURCE_TYPE_INDEX);/// M: for plug-inname = mApnExt.updateApnName(name, sourcetype);ApnPreference pref = new ApnPreference(getPrefContext());pref.setKey(key);pref.setTitle(name);pref.setSummary(apn);pref.setPersistent(false);pref.setOnPreferenceChangeListener(this);boolean isEdit = mApnExt.isAllowEditPresetApn(type, apn, mccmnc, sourcetype); //true 表示允许编辑boolean isReadOnly = cursor.getInt(READ_ONLY_INDEX) == 1; //true表示不允许编辑pref.setApnEditable(isEdit && !isReadOnly);pref.setSubId(mSubscriptionInfo == null ? null : mSubscriptionInfo.getSubscriptionId());/// M: for ALPS02500557, do not select emergency APNboolean selectable = ((type == null) || (!type.equals("mms")&& !type.equals("ia") && !type.equals("ims")&& !type.equals("emergency")))/// M: for plug-in&& mApnExt.isSelectable(type);pref.setSelectable(selectable);Log.d(TAG, "mSelectedKey = " + mSelectedKey + " key = " + key + " name = " + name +" selectable=" + selectable);if (selectable) {/// M: select prefer APN later, as the apn list are not solid now @{/*if ((mSelectedKey != null) && mSelectedKey.equals(key)) {pref.setChecked();}*//// @}addApnToList(pref, mnoApnList, mvnoApnList, r, mvnoType, mvnoMatchData);/// M: For CT feature,when apns-conf.xml add type extra value "supl",//     selectable maybe ture when 46011 mms, so need this method.mApnExt.customizeUnselectableApn(type, mnoApnList, mvnoApnList,mSubscriptionInfo == null ? null : mSubscriptionInfo.getSubscriptionId());} else {addApnToList(pref, mnoMmsApnList, mvnoMmsApnList, r, mvnoType, mvnoMatchData);/// M: for plug-inmApnExt.customizeUnselectableApn(type, mnoMmsApnList, mvnoMmsApnList,mSubscriptionInfo == null ? null : mSubscriptionInfo.getSubscriptionId());}cursor.moveToNext();}cursor.close();if (!mvnoApnList.isEmpty()) {mnoApnList = mvnoApnList;mnoMmsApnList = mvnoMmsApnList;// Also save the mvno info}for (Preference preference : mnoApnList) {apnList.addPreference(preference);}for (Preference preference : mnoMmsApnList) {apnList.addPreference(preference);}/// M: always set a prefer APNsetPreferApnChecked(mnoApnList);/// M: update screen enable state according to airplane mode, SIM radio status, etc.updateScreenEnableState(getActivity());}}

其逻辑分析如下:
从sim卡中得到卡里记录的mcc mnc,并将mcc mnc写入查询语句,比如插入一张移动卡,查询语句应该是类似这样的
select * from carriers where mcc = ‘460’ and mnc = ‘00’
查询的apn放入cursor对象,遍历cursor,进行过滤和显示
首先清空界面list数据
创建四个list用于存储apn
mnoApnList:存储母运营商apn(mno)
mvnoApnList:存储虚拟运营商apn(mvno)
mnoMmsApnList:存储母运营商Mms apn(mno)
mvnoMmsApnList:存储虚拟运营商Mms apn(mvno)
带Mms和不带Mms的list区别在与是否可选择,mms的是不可选择的,而不带mms的界面item后面有个radiobutton,可以选择,表示当前优先使用的apn
mno和mvno的区别在于是否有mvno_match_data和mvno_type,mno是没有的之后会详细说
代码继续解释:得到cursor的内容,存储到ApnPreference对象用于之后显示
判断apn是否可选择,根据结果设置显示式样
如果可选择,调用addApnToList(pref, mnoApnList, mvnoApnList, r, mvnoType, mvnoMatchData);将apn填充到mnoApnList, mvnoApnList
否则填充到mnoMmsApnList, mvnoMmsApnList
最后判断mvnoApnList.isEmpty,如果是空的,则说明是母运营商(mvno_match_data和mvno_type为空),显示的其实是mnoApnList,如果不是空,则显示mvnoApnList,mnoMmsApnList和mvnoMmsApnList的逻辑是一样的。

最后再看下addApnToList方法

private void addApnToList(ApnPreference pref, ArrayList<Preference> mnoList,ArrayList<Preference> mvnoList, IccRecords r, String mvnoType,String mvnoMatchData) {Log.d(TAG, "mvnoType = " + mvnoType + ", mvnoMatchData = " + mvnoMatchData);if (r != null && !TextUtils.isEmpty(mvnoType) && !TextUtils.isEmpty(mvnoMatchData)) {if (ApnSetting.mvnoMatches(r, mvnoType, mvnoMatchData)) {mvnoList.add(pref);// Since adding to mvno list, save mvno infomMvnoType = mvnoType;mMvnoMatchData = mvnoMatchData;}} else {mnoList.add(pref);}}

代码中可以看出mvnoList和mnoList的区别就在mMvnoType和mMvnoMatchData是否为空,而上面一步中mvnoApnList.isEmpty的判断也是由addApnToList决定的。

那么,apn的显示流程就分析完了,如有错误,请帮忙指正
注:以上所有源码中,为了便于阅读和理解,删除了部分代码

这篇关于Android APN的显示流程源代码分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1016233

相关文章

Android实现打开本地pdf文件的两种方式

《Android实现打开本地pdf文件的两种方式》在现代应用中,PDF格式因其跨平台、稳定性好、展示内容一致等特点,在Android平台上,如何高效地打开本地PDF文件,不仅关系到用户体验,也直接影响... 目录一、项目概述二、相关知识2.1 PDF文件基本概述2.2 android 文件访问与存储权限2.

Android Studio 配置国内镜像源的实现步骤

《AndroidStudio配置国内镜像源的实现步骤》本文主要介绍了AndroidStudio配置国内镜像源的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录一、修改 hosts,解决 SDK 下载失败的问题二、修改 gradle 地址,解决 gradle

Python 迭代器和生成器概念及场景分析

《Python迭代器和生成器概念及场景分析》yield是Python中实现惰性计算和协程的核心工具,结合send()、throw()、close()等方法,能够构建高效、灵活的数据流和控制流模型,这... 目录迭代器的介绍自定义迭代器省略的迭代器生产器的介绍yield的普通用法yield的高级用法yidle

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

在Android平台上实现消息推送功能

《在Android平台上实现消息推送功能》随着移动互联网应用的飞速发展,消息推送已成为移动应用中不可或缺的功能,在Android平台上,实现消息推送涉及到服务端的消息发送、客户端的消息接收、通知渠道(... 目录一、项目概述二、相关知识介绍2.1 消息推送的基本原理2.2 Firebase Cloud Me

kotlin中const 和val的区别及使用场景分析

《kotlin中const和val的区别及使用场景分析》在Kotlin中,const和val都是用来声明常量的,但它们的使用场景和功能有所不同,下面给大家介绍kotlin中const和val的区别,... 目录kotlin中const 和val的区别1. val:2. const:二 代码示例1 Java

Go标准库常见错误分析和解决办法

《Go标准库常见错误分析和解决办法》Go语言的标准库为开发者提供了丰富且高效的工具,涵盖了从网络编程到文件操作等各个方面,然而,标准库虽好,使用不当却可能适得其反,正所谓工欲善其事,必先利其器,本文将... 目录1. 使用了错误的time.Duration2. time.After导致的内存泄漏3. jsO

Android中Dialog的使用详解

《Android中Dialog的使用详解》Dialog(对话框)是Android中常用的UI组件,用于临时显示重要信息或获取用户输入,本文给大家介绍Android中Dialog的使用,感兴趣的朋友一起... 目录android中Dialog的使用详解1. 基本Dialog类型1.1 AlertDialog(

Spring事务中@Transactional注解不生效的原因分析与解决

《Spring事务中@Transactional注解不生效的原因分析与解决》在Spring框架中,@Transactional注解是管理数据库事务的核心方式,本文将深入分析事务自调用的底层原理,解释为... 目录1. 引言2. 事务自调用问题重现2.1 示例代码2.2 问题现象3. 为什么事务自调用会失效3

找不到Anaconda prompt终端的原因分析及解决方案

《找不到Anacondaprompt终端的原因分析及解决方案》因为anaconda还没有初始化,在安装anaconda的过程中,有一行是否要添加anaconda到菜单目录中,由于没有勾选,导致没有菜... 目录问题原因问http://www.chinasem.cn题解决安装了 Anaconda 却找不到 An