DcTracker.java
firstly, needs to get usable DcAsyncChannel for the current ApnContext to establish datacall.
the process try to find out if any channel is able to be used directly: checkForCompatibleConnectedApnContext() and findFreeDataConnection().
the first check if any connected APN type that whose ApnSetting is compatible with the current requested one exists so that it can be reused directly; while the other try to get freed DcAsyncChannel from mDataConnectionAcHashMap directly.
if no existed one found, create one instance by calling createDataConnection().
at last, a msg will be created and passed to the dcac's API bringUp(). the msg will be used when we get response from lower layer so that the result will be send to back to DcTracker. we will find the point where it is used later.
private boolean setupData(ApnContext apnContext) {
...
dcac = checkForCompatibleConnectedApnContext(apnContext);
...
if (dcac == null) {
dcac = findFreeDataConnection();
if (dcac == null) {
dcac = createDataConnection();
}
...
...
Message msg = obtainMessage();
msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
msg.obj = apnContext;
dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, msg);
DcAsyncChannel.java
in fact the API here is very simple, just pass related params to DataConnection with wrapped new msg.
public void bringUp(ApnContext apnContext, int initialMaxRetry, int profileId,
Message onCompletedMsg) {
sendMessage(DataConnection.EVENT_CONNECT,
new ConnectionParams(apnContext, initialMaxRetry, profileId, onCompletedMsg));
}
DataConnection.java
different state will have different behaviour. for example, if in DefaultState, will reply directy as below code showed:
private class DcDefaultState extends State {
...
public boolean processMessage(Message msg) {
boolean retVal = HANDLED;
AsyncResult ar;
...
switch (msg.what) {
...
case EVENT_CONNECT:
ConnectionParams cp = (ConnectionParams) msg.obj;
notifyConnectCompleted(cp, DcFailCause.UNKNOWN, false);
break;
here we suppose that all are OK and the request should go on, so the progress will be like
private class DcInactiveState extends State {
...
public boolean processMessage(Message msg) {
boolean retVal;
switch (msg.what) {
...
case EVENT_CONNECT:
if (initConnection(cp)) {
onConnect(mConnectionParams);
transitionTo(mActivatingState);
} else {
...
initConnection() will check APNSettings and records the request into both mConnectionParams and ApnContexts, at last it will set the retry config for use later if there is any problem from modem when data call not setup OK.
then we go to onConnect and the state transition to ActiviatingState
private void onConnect(ConnectionParams cp) {
...
Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
msg.obj = cp;
...
mPhone.mCi.setupDataCall(
Integer.toString(getRilRadioTechnology()),
Integer.toString(cp.mProfileId),
mApnSetting.apn, mApnSetting.user, mApnSetting.password,
Integer.toString(authType),
protocol, msg);
here also just wrap the ConnectionParams into Message and then call setupDataCall with the message. this wrapped msg will be used by RIL to notify the result to DataConnection.
after this, the connection request is passed into RIL.java
for the rough flow of request handler in RIL.java, you can reference my previous article.
so, waiting for completing............
private class DcActivatingState extends State {
@Override
public boolean processMessage(Message msg) {
boolean retVal;
AsyncResult ar;
ConnectionParams cp;
...
switch (msg.what) {
...
case EVENT_SETUP_DATA_CONNECTION_DONE:
ar = (AsyncResult) msg.obj;
cp = (ConnectionParams) ar.userObj;
DataCallResponse.SetupResult result = onSetupConnectionCompleted(ar);
switch (result) {
we will not always so lucky to make sure the results is OK, with diff result, the behaviour will also diff...
if all OK, it will go to ActiveState to do what we all expect to do. :-D
case SUCCESS:
// All is well
mDcFailCause = DcFailCause.NONE;
transitionTo(mActiveState);
else if failed from modem and has chance to reestablish, get the configed retry delay settings and startRetryAlarm, go to RetryingState to waiting for our retry chance.
case ERR_RilError:
int delay = mDcRetryAlarmController.getSuggestedRetryTime(
DataConnection.this, ar);
...
if (delay >= 0) {
mDcRetryAlarmController.startRetryAlarm(EVENT_RETRY_CONNECTION,
mTag, delay);
transitionTo(mRetryingState);
} else {
here supposed that we get all right now and go into activeState now.
private class DcActiveState extends State {
...
if (mRetryManager.getRetryCount() != 0) {
mRetryManager.setRetryCount(0);
}
notifyAllOfConnected(Phone.REASON_CONNECTED);
mRetryManager.restoreCurMaxRetryCount();
mDcController.addActiveDcByCid(DataConnection.this);
first reset the retry count and then notify to the upper layer, at last add itself into DcController.
the notify function sends the msg back to DcTracker
private void notifyAllOfConnected(String reason) {
notifyAllWithEvent(null, DctConstants.EVENT_DATA_SETUP_COMPLETE, reason);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
...
case DctConstants.EVENT_DATA_SETUP_COMPLETE:
mCidActive = msg.arg1;
onDataSetupComplete((AsyncResult) msg.obj);
break;
see, we back now!
protected void onDataSetupComplete(AsyncResult ar) {
...
if(ar.userObj instanceof ApnContext){
apnContext = (ApnContext)ar.userObj;
...
if (ar.exception == null) {
DcAsyncChannel dcac = apnContext.getDcAc();
...
if (dcac == null) {
log("onDataSetupComplete: no connection to DC, handle as error");
cause = DcFailCause.CONNECTION_TO_DATACONNECTIONAC_BROKEN;
handleError = true;
} else {
ApnSetting apn = apnContext.getApnSetting();
...
ProxyProperties proxy = new ProxyProperties(apn.proxy,
Integer.parseInt(port), null);
dcac.setLinkPropertiesHttpProxySync(proxy);
...
notifyDefaultData(apnContext);
}
} else {
cause = (DcFailCause) (ar.result);
if (cause.isPermanentFail()) apnContext.decWaitingApnsPermFailCount();
apnContext.removeWaitingApn(apnContext.getApnSetting());
handleError = true;
}
if (handleError) {
onDataSetupCompleteError(ar);
}
if all go well, set proxy into LinkProperties and call notifyDefaultData() to info upper layer.
if any error detected, goto error handle flow onDataSetupCompleteError().
private void notifyDefaultData(ApnContext apnContext) {
apnContext.setState(DctConstants.State.CONNECTED);
mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
startNetStatPoll();
startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
}
the function just updates the ApnContext state, notify overall the system about the data connection change, and start net stats poll process.
mPhone.notifyDataConnection() will notify MobileDataStateTracker, which send msg to ConnectivityService infoing that current Mobile type connected.
after this, ConnectivityService will change the network related config such as DNS/Routing and then send Broadcast of CONNECTIVITY_CHANGE, which is used for all interested modules.
NOTICE:
more simple for the total flow, no much details.
error handle and retry part can be a topic.
ConnectivityService's handle of network change and config can also be one huge topic.