Android中APN的创建流程

时间:2021-06-07 15:55:03

APN全称是Access Point Name,是手机上网必须要配置的一个参数,用来决定手机是通过哪一种接入方式来访问网络,若APN缺少,4G、3G等移动网络异常!

从ApnSettings开始,当点击新建apn的时候,回调onOptionsItemSelected方法

@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_NEW:
// 新建APN
addNewApn();
return true;

case MENU_RESTORE:
                    // 恢复默认设置
restoreDefaultApn();
return true;
}
return super.onOptionsItemSelected(item);
}

新建APN

private void addNewApn() {
//  这里的intent对应的就是自己
Intent intent = new Intent(Intent.ACTION_INSERT, Telephony.Carriers.CONTENT_URI);
int subId = mSubscriptionInfo != null ? mSubscriptionInfo.getSubscriptionId()
: SubscriptionManager.INVALID_SUBSCRIPTION_ID;
intent.putExtra(SUB_ID, subId);
if (!TextUtils.isEmpty(mMvnoType) && !TextUtils.isEmpty(mMvnoMatchData)) {
intent.putExtra(MVNO_TYPE, mMvnoType);
intent.putExtra(MVNO_MATCH_DATA, mMvnoMatchData);
}
startActivity(intent);
}

这里的intent对应的就是自己

public static final java.lang.String ACTION_INSERT = "android.intent.action.INSERT";

<activity android:name="Settings$ApnEditorActivity"
android:label="@string/apn_edit">

<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.EDIT" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/telephony-carrier" />
</intent-filter>

<intent-filter>
<action android:name="android.intent.action.INSERT" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.dir/telephony-carrier" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.ApnEditor" />

</activity>

在ApnEditor中的onCreate方法中,会通过fillUi()方法更新加载的apn_editor界面,另外如果是添加VPN,我们会传递进去一个URI,此时会自动先插入一条空的记录

protected void onCreate(Bundle icicle) {
....
if (action.equals(Intent.ACTION_EDIT)) {
mUri = intent.getData();
} else if (action.equals(Intent.ACTION_INSERT)) {
if (mFirstTime || icicle.getInt(SAVED_POS) == 0) {
// 先插入一条空的记录
mUri = getContentResolver().insert(intent.getData(), new ContentValues());
} else {
mUri = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI,
icicle.getInt(SAVED_POS));
}
mNewApn = true;
....
setResult(RESULT_OK, (new Intent()).setAction(mUri.toString()));

} else {
finish();
return;
}

....

}

最后通过validateAndSave方法验证并且设置当前的APN

private boolean validateAndSave(boolean force) {
String name = checkNotSet(mName.getText());
String apn = checkNotSet(mApn.getText());
String mcc = checkNotSet(mMcc.getText());
String mnc = checkNotSet(mMnc.getText());

if (getErrorMsg() != null && !force) {
showDialog(ERROR_DIALOG_ID);
return false;
}

if (!mCursor.moveToFirst()) {
Log.w(TAG,
"Could not go to the first row in the Cursor when saving data.");
return false;
}

// If it's a new APN and a name or apn haven't been entered, then erase the entry
if (force && mNewApn && name.length() < 1 && apn.length() < 1) {
getContentResolver().delete(mUri, null, null);
mUri = null;
return false;
}

ContentValues values = new ContentValues();

// Add a dummy name "Untitled", if the user exits the screen without adding a name but
// entered other information worth keeping.
values.put(Telephony.Carriers.NAME,
name.length() < 1 ? getResources().getString(R.string.untitled_apn) : name);
values.put(Telephony.Carriers.APN, apn);
values.put(Telephony.Carriers.PROXY, checkNotSet(mProxy.getText()));
values.put(Telephony.Carriers.PORT, checkNotSet(mPort.getText()));
values.put(Telephony.Carriers.MMSPROXY, checkNotSet(mMmsProxy.getText()));
values.put(Telephony.Carriers.MMSPORT, checkNotSet(mMmsPort.getText()));
values.put(Telephony.Carriers.USER, checkNotSet(mUser.getText()));
values.put(Telephony.Carriers.SERVER, checkNotSet(mServer.getText()));
values.put(Telephony.Carriers.PASSWORD, checkNotSet(mPassword.getText()));
values.put(Telephony.Carriers.MMSC, checkNotSet(mMmsc.getText()));

String authVal = mAuthType.getValue();
if (authVal != null) {
values.put(Telephony.Carriers.AUTH_TYPE, Integer.parseInt(authVal));
}

values.put(Telephony.Carriers.PROTOCOL, checkNotSet(mProtocol.getValue()));
values.put(Telephony.Carriers.ROAMING_PROTOCOL, checkNotSet(mRoamingProtocol.getValue()));

values.put(Telephony.Carriers.TYPE, checkNotSet(mApnType.getText()));

values.put(Telephony.Carriers.MCC, mcc);
values.put(Telephony.Carriers.MNC, mnc);

values.put(Telephony.Carriers.NUMERIC, mcc + mnc);

if (mCurMnc != null && mCurMcc != null) {
if (mCurMnc.equals(mnc) && mCurMcc.equals(mcc)) {
values.put(Telephony.Carriers.CURRENT, 1);
}
}

Set<String> bearerSet = mBearerMulti.getValues();
int bearerBitmask = 0;
for (String bearer : bearerSet) {
if (Integer.parseInt(bearer) == 0) {
bearerBitmask = 0;
break;
} else {
bearerBitmask |= ServiceState.getBitmaskForTech(Integer.parseInt(bearer));
}
}
values.put(Telephony.Carriers.BEARER_BITMASK, bearerBitmask);

int bearerVal;
if (bearerBitmask == 0 || mBearerInitialVal == 0) {
bearerVal = 0;
} else if (ServiceState.bitmaskHasTech(bearerBitmask, mBearerInitialVal)) {
bearerVal = mBearerInitialVal;
} else {
// bearer field was being used but bitmask has changed now and does not include the
// initial bearer value -- setting bearer to 0 but maybe better behavior is to choose a
// random tech from the new bitmask??
bearerVal = 0;
}
values.put(Telephony.Carriers.BEARER, bearerVal);

values.put(Telephony.Carriers.MVNO_TYPE, checkNotSet(mMvnoType.getValue()));
values.put(Telephony.Carriers.MVNO_MATCH_DATA, checkNotSet(mMvnoMatchData.getText()));

values.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled.isChecked() ? 1 : 0);
// 更新当前mUri所对应的记录
getContentResolver().update(mUri, values, null, null);

return true;
}

加载预置APN流程

telephony.db数据库的建立对应TelephonyProvider.Java文件,

Android的APN(Access Point Name)数据预置在/system/etc/apns-conf.xml中

private void loadApns(SQLiteDatabase db, XmlPullParser parser) {
if (parser != null) {
try {
db.beginTransaction();
XmlUtils.nextElement(parser);
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
                        // 从parser中解析出当前的ContentValues
ContentValues row = getRow(parser);
if (row == null) {
throw new XmlPullParserException("Expected 'apn' tag", parser, null);
}
                        // 插入到表中
insertAddingDefaults(db, row);
XmlUtils.nextElement(parser);
}
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();
}
}
}




private void insertAddingDefaults(SQLiteDatabase db, ContentValues row) {
row = setDefaultValue(row);
try {
db.insertWithOnConflict(CARRIERS_TABLE, null, row,
SQLiteDatabase.CONFLICT_ABORT);
if (VDBG) log("dbh.insertAddingDefaults: db.insert returned >= 0; insert " +
"successful for cv " + row);
} catch (SQLException e) {
.....
}
}

TelephonyManager.setDataEnabled

我们都知道,在应用中,可以使用

TelephonyManager.from(this).setDataEnabled(true)

来打开数据流量,其最终会走到DcTracker#sendDataConnectionSettingToModem方法,调用流程如下:

TelephonyManager#setDataEnabled-->PhoneInterfaceManager#setDataEnabled-->GsmCdmaPhone#setDataEnabled-->DcTracker#setDataEnabled-->DcTracker#onSetUserDataEnabled-->DcTracker#sendDataConnectionSetting-->DcTracker#sendDataConnectionSettingToModem
private void sendDataConnectionSettingToModem(boolean isApnAvailable) {
....
long param = 0;
if (isApnAvailable && (mUserDataEnabled || mRequestMmsWithDefaultApn)) {
param |= BIT_DATA_CONNECTION;
}
mPhone.invokeOemRilRequestRaw(reqBuffer.array(), null);
}

可以看到如果当前系统没有合理的APN配置,即使可以打开数据流量,却是不能使用的。