天天看點

【Android架構Telephony篇】資料業務(2)RILJ

Android: 6.0
Desktop: Ubuntu 15.04
更新日期:2017-04-28
           

http://blog.csdn.net/u013686019/article/details/49719897

Telephony的Overview見:

【Android架構Telephony篇】資料業務(1)總覽

一、Telephony整體流程

Telephony執行的完整流程如下:

【Android架構Telephony篇】資料業務(2)RILJ

下面自上而下,隻關注主幹,分層看下代碼走向。

二、Telephony資料業務的RILJ層

1、App層

使用者點選系統【設定】進行開啟/關閉資料業務,調用:

DataUsageSummary.java (packages\apps\settings\src\com\android\settings)
private void setMobileDataEnabled(int subId, boolean enabled) {
	mTelephonyManager.setDataEnabled(subId, enabled);
}
           

TelephonyManager作為"phone"系統服務的管理類,其擷取方式可以:

DataUsageSummary#onCreate()
-->mTelephonyManager = TelephonyManager.from(context);

TelephonyManager.java (frameworks\base\telephony\java\android\telephony)
public static TelephonyManager from(Context context) {
	return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
}
           

在上圖中,把"phone"系統服務擷取放在了frameworks層,這是因為在frameworks中可以越過Manager類直接擷取服務,如:

TelephonyManager.java (frameworks\base\telephony\java\android\telephony)
private ITelephony getITelephony() {
	return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
}

public void setDataEnabled(int subId, boolean enable) {
	ITelephony telephony = getITelephony();
	telephony.setDataEnabled(subId, enable);
}
           

Telephony提供的操作無線模組的方法可以通過ITelephony.aidl檔案檢視:

frameworks/base/telephony/java/com/android/internal/telephony/ITelephony.aidl
           

2、Framework層

(1)"phone"系統服務注冊

系統服務絕大部分都在frameworks/base/services/java/com/android/server/SystemServer.java檔案中統一注冊,但TELEPHONY_SERVICE("phone")服務很是另類,其注冊流程:

PhoneApp.java (packages\services\telephony\src\com\android\phone)
public class PhoneApp extends Application {}
PhoneApp#onCreate()
-->PhoneGlobals#onCreate()
---->PhoneInterfaceManager#init()
------>PhoneInterfaceManager#publish()
-------->PhoneInterfaceManager#ServiceManager.addService("phone", this);
           

adb shell進去系統,通過service指令可以檢視系統注冊的所有服務:

# service list                                             
Found 102 services:
1	phone: [com.android.internal.telephony.ITelephony]
           

(2) 繼續資料業務流程

PhoneInterfaceManager是"phone"服務實作方,對于資料開啟/關閉:

PhoneInterfaceManager.java (packages\services\telephony\src\com\android\phone)
/**
 * Set mobile data enabled
 * Used by the user through settings etc to turn on/off mobile data
 */
@Override
public void setDataEnabled(int subId, boolean enable) {
	enforceModifyPermission(); -->a
	int phoneId = mSubscriptionController.getPhoneId(subId); -->b
	Phone phone = PhoneFactory.getPhone(phoneId);
	phone.setDataEnabled(enable);
}
           

這裡的Phone是一個interface。在【Android架構Telephony篇】資料業務(1)總覽提到,無線通信有TD-LTE/FDD-LTE/TD-SCDMA/WCDMA/EVDO/CDMA1X/GSM等等很多的網絡制式,不同子產品可以支援其中的一種或多種。它們之間的差別最簡單的例子,比如常說的電信卡、聯通卡等等。從軟體層面進行抽象就是:

【Android架構Telephony篇】資料業務(2)RILJ

Phone的建立:

PhoneFactory.java (frameworks\opt\telephony\src\java\com\android\internal\telephony)
public static void makeDefaultPhone(Context context) {
	PhoneBase phone = null;
	int phoneType = TelephonyManager.getPhoneType(networkModes[i]);
	if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
		phone = new GSMPhone(context,
				sCommandsInterfaces[i], sPhoneNotifier, i);
	} else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
		phone = new CDMALTEPhone(context,
				sCommandsInterfaces[i], sPhoneNotifier, i);
	}
}
           

這裡就根據type建立不同的Phone,我們以GSMPhone為例,setDataEnabled()調用的就是:

GSMPhone.java (frameworks\opt\telephony\src\java\com\android\internal\telephony\gsm)
public void setDataEnabled(boolean enable) {
	mDcTracker.setDataEnabled(enable);
}
           

mDcTracker是一個Handler,

DcTrackerBase.java (frameworks\opt\telephony\src\java\com\android\internal\telephony\dataconnection)
public void setDataEnabled(boolean enable) {
	Message msg = obtainMessage(DctConstants.CMD_SET_USER_DATA_ENABLE);
	msg.arg1 = enable ? 1 : 0;
	sendMessage(msg);
}
public void handleMessage(Message msg) {
	switch (msg.what) {
	case DctConstants.CMD_SET_USER_DATA_ENABLE: {
		final boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false;
		onSetUserDataEnabled(enabled);
		break;
	}
}

DcTrackerBase#onSetUserDataEnabled()
-->DcTrackerBase#onTrySetupData()
           

DcTrackerBase發送CMD_SET_USER_DATA_ENABLE資訊,并自己處理,最終調用到onTrySetupData(),實作onTrySetupData()的地方:

DcTracker.java (opt\telephony\src\java\com\android\internal\telephony\dataconnection)
protected boolean onTrySetupData(String reason) {
	setupDataOnConnectableApns(reason);
	return true;
}
           

setupDataOnConnectableApns()顧名思義就是使用可用的APN建立資料連接配接,之後:

DcTracker.java (opt\telephony\src\java\com\android\internal\telephony\dataconnection)
setupDataOnConnectableApns(reason, RetryFailures.ALWAYS);
-->trySetupData(apnContext, waitingApns);
---->setupData(apnContext, radioTech);

private boolean setupData(ApnContext apnContext, int radioTech) {
	// 用于連接配接DcTracker和DataConnection
	DcAsyncChannel dcac = null;

	if (dcac == null) {
		// 擷取/建立DcAsyncChannel對象
	}
	
	// 更新apnContext
	apnContext.setDataConnectionAc(dcac);
	apnContext.setApnSetting(apnSetting);
	apnContext.setState(DctConstants.State.CONNECTING);
	mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());

	// 撥号成功後發送EVENT_DATA_SETUP_COMPLETE資訊
	Message msg = obtainMessage();
	msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
	msg.obj = new Pair<ApnContext, Integer>(apnContext, generation);
	dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, radioTech,
			mAutoAttachOnCreation.get(), msg, generation);
	return true;
}
           

調用DcAsyncChannel的bringUp(),向DataConnection發送建立連接配接消息EVENT_CONNECT:

DcAsyncChannel.java (opt\telephony\src\java\com\android\internal\telephony\dataconnection)
/**
 * Bring up a connection to the apn and return an AsyncResult in onCompletedMsg.
 * Used for cellular networks that use Acesss Point Names (APN) such
 * as GSM networks.
 */
public void bringUp(ApnContext apnContext, int initialMaxRetry, int profileId,
		int rilRadioTechnology, boolean retryWhenSSChange, Message onCompletedMsg,
		int connectionGeneration) {
	sendMessage(DataConnection.EVENT_CONNECT,
				new ConnectionParams(apnContext, initialMaxRetry, profileId,
						rilRadioTechnology, retryWhenSSChange, onCompletedMsg,
						connectionGeneration));
}
           

DataConnection處理EVENT_CONNECT:

DataConnection.java (opt\telephony\src\java\com\android\internal\telephony\dataconnection)
private class DcInactiveState extends State { -->a
	public boolean processMessage(Message msg) {
		case EVENT_CONNECT:
			ConnectionParams cp = (ConnectionParams) msg.obj;
			if (initConnection(cp)) { -->b
				onConnect(mConnectionParams); -->c
				transitionTo(mActivatingState);
			} else {
				log("DcInactiveState: msg.what=EVENT_CONNECT initConnection failed");
				notifyConnectCompleted(cp, DcFailCause.UNACCEPTABLE_NETWORK_PARAMETER,
						false);
			}
			retVal = HANDLED;
			break;
	}
}
           

a, DataConnection是一個狀态機,其初始狀态:

private DcInactiveState mInactiveState = new DcInactiveState();
private DataConnection() {
	setInitialState(mInactiveState);
}
           

是以這裡調用DcInactiveState類的processMessage()處理EVENT_CONNECT資訊。

b, 檢測參數合法性

c, 調用onConnect()啟動連接配接建立

/**
 * Begin setting up a data connection, calls setupDataCall
 * and the ConnectionParams will be returned with the
 * EVENT_SETUP_DATA_CONNECTION_DONE AsyncResul.userObj.
 */
private void onConnect(ConnectionParams cp) {

	// msg.obj will be returned in AsyncResult.userObj;
	Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
	msg.obj = cp;

	mPhone.mCi.setupDataCall(
			Integer.toString(cp.mRilRat + 2),
			Integer.toString(cp.mProfileId),
			mApnSetting.apn, mApnSetting.user, mApnSetting.password,
			Integer.toString(authType),
			protocol, msg);
}
           

mCi是一個CommandsInterface,實作它的是RIL:

RIL.java (opt\telephony\src\java\com\android\internal\telephony)
public final class RIL extends BaseCommands implements CommandsInterface {
}
           

這裡,就來到了開篇流程圖中的“RILJ”,在繼續下去之前,稍微提下Android中 本地socket通信。

(3)socket程序間通信

Linux中的socket除了可以用于不同機器之間的網絡通信,還可以用于同一台機器的程序間通信。以Telephony為例,RILJ的java程序和RILC的c守護程序之間就是通過"/dev/socket/rild"這個socket進行通信的。

a, socket建立

RIL.java (opt\telephony\src\java\com\android\internal\telephony)
LocalSocket mSocket;
String rilSocket = "rild";
s = new LocalSocket();
l = new LocalSocketAddress(rilSocket, LocalSocketAddress.Namespace.RESERVED);
s.connect(l);

mSocket = s;
           

b, 資料發送

RIL.java (opt\telephony\src\java\com\android\internal\telephony)
class RILSender extends Handler implements Runnable {
@Override public void handleMessage(Message msg) {
	switch (msg.what) {
	case EVENT_SEND:
		try {
			LocalSocket s;
			s = mSocket;

			synchronized (mRequestList) {
				mRequestList.append(rr.mSerial, rr);
			}

			byte[] data;

			data = rr.mParcel.marshall();
			rr.mParcel.recycle();
			rr.mParcel = null;

			// parcel length in big endian
			dataLength[0] = dataLength[1] = 0;
			dataLength[2] = (byte)((data.length >> 8) & 0xff);
			dataLength[3] = (byte)((data.length) & 0xff);

			s.getOutputStream().write(dataLength);
			s.getOutputStream().write(data);
		}
	}
}
           

c, 資料接收

RIL.java (opt\telephony\src\java\com\android\internal\telephony)
class RILReceiver implements Runnable {
	try {
		InputStream is = mSocket.getInputStream();
		for (;;) {
			Parcel p;
			length = readRilMessage(is, buffer);
			if (length < 0) {
				// End-of-stream reached
				break;
			}

			p = Parcel.obtain();
			p.unmarshall(buffer, 0, length);
			p.setDataPosition(0);
			processResponse(p); // 資料處理
			p.recycle();
		}
	}
}
           

有了這個知識,RILJ就容易了解了。

(4)RILJ

RIL.java (opt\telephony\src\java\com\android\internal\telephony)
@Override
public void  setupDataCall(String radioTechnology, String profile, String apn,
        String user, String password, String authType, String protocol,
        Message result) {
    RILRequest rr
            = RILRequest.obtain(RIL_REQUEST_SETUP_DATA_CALL, result);

    rr.mParcel.writeInt(7);

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

    send(rr);
}

private void send(RILRequest rr) {
    Message msg;

    if (mSocket == null) {
        rr.onError(RADIO_NOT_AVAILABLE, null);
        rr.release();
        return;
    }

    msg = mSender.obtainMessage(EVENT_SEND, rr);

    acquireWakeLock();

    msg.sendToTarget();
}
           

無需解釋。

【Android架構Telephony篇】資料業務(3)RILC

繼續閱讀