編輯:關於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 這個配置文件,這裡面基本上是這樣的模塊:
移植3G時,就需要用到這個xml配置文件了,在android的device.mk 裡面加個PRODUCT_COPY_FILES:
PRODUCT_COPY_FILES +=
device/sample/etc/apns-full-conf.xml:system/etc/apns-conf.xml
這個文件被加載的地方可參考/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查看:

在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);
}
代表打開數據流量,最終調用到
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;
...
}
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 <
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 撥號上網 (三)
Android studio 百度地圖開發(10)語音播報的問題
一.前言第一次做導航時,並沒有關注語音播報,今天特意把這個功能完善一下。但是發現關於語音播報的實現也遇到了一些問題,在官方的討論區也發現關於語音播報的問題特別多,問題基本
android ViewPager 例子
今天用到viewpager,要實現多view動畫切換。自己動手做了一個。 先上效果圖,只是很簡單的例子。 步驟:1、在main布局文件裡添加viewPager布局
服務Service的基本用法
作為 Android四大組件之一, 服務也少不了有很多非常重要的知識點,那自然要從最基本的用法開始學習了。定義一個服務:public class MyService ex
ViewPager 實現 Galler 效果, 中間大圖顯示,兩邊小圖展示
正常情況下, ViewPager 一頁只能顯示一項數據, 但是我們常常看到網上,特別是電視機頂盒的首頁經常出現中間大圖顯示兩端也都露出一點來,這種效果怎麼實現呢?先上一張