首先我们来看看下面的关系图:
底层Settings.apk
在Settings -> Data Usage Summary中的某个SIM tab下开启数据开关
android/packages/apps/Settings/src/com/android/settings/DataUsageSummary.java
setMobileDataEnabled(true);
private View.OnClickListener mDataEnabledListener = new View.OnClickListener() { @Override public void onClick(View v) { if (mBinding) return; final boolean dataEnabled = !mDataEnabled.isChecked(); final String currentTab = mCurrentTab; if (TAB_MOBILE.equals(currentTab) || currentTab.startsWith(TAB_SIM)) { if (dataEnabled) { setMobileDataEnabled(true); if (mPolicyEditor.getPolicyWarningBytes(mTemplate) == WARNING_DISABLED) { mPolicyEditor.setPolicyWarningBytes(mTemplate, 2 * GB_IN_BYTES); } } else { // disabling data; show confirmation dialog which eventually // calls setMobileDataEnabled() once user confirms. ConfirmDataDisableFragment.show(DataUsageSummary.this); } } updatePolicy(false); } };
下面这部根据phoneId获得了当前获得了SubId
mTelephonyManager.setDataEnabled(subId[0],
enabled);
private void setMobileDataEnabled(boolean enabled) { if (LOGD) Log.d(TAG, "setMobileDataEnabled()"); // How about exposing sub based API like TelephonyManager.setDataEnabled(int subId); if (mCurrentTab.startsWith(TAB_SIM)) { int phoneId = multiSimGetCurrentSub(); // as per phone, set the individual flag android.provider.Settings.Global.putInt(getActivity().getContentResolver(), android.provider.Settings.Global.MOBILE_DATA + phoneId, enabled ? 1 : 0); int[] subId = SubscriptionManager.getSubId(phoneId); mTelephonyManager.setDataEnabled(subId[0], enabled); } else { mTelephonyManager.setDataEnabled(enabled); mMobileDataEnabled = enabled; } updatePolicy(false); }
frameworks/base
数据开关都会调用到TelephonyManager.setDataEnabled(),不管是从流量使用情况还是从下拉的“快速设置”(Quick Setting)里打开(这里会在后面补充说明)
android/frameworks/base/telephony/java/android/telephony/TelephonyManager.java
getITelephony().setDataEnabled(subId,
enable);
/** @hide */ @SystemApi public void setDataEnabled(int subId, boolean enable) { try { AppOpsManager appOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE); if (enable) { if (appOps.noteOp(AppOpsManager.OP_DATA_CONNECT_CHANGE) != AppOpsManager.MODE_ALLOWED) { Log.w(TAG, "Permission denied by user."); return; } } Log.d(TAG, "setDataEnabled: enabled=" + enable); getITelephony().setDataEnabled(subId, enable); } catch (RemoteException e) { Log.e(TAG, "Error calling setDataEnabled", e); } }
android/packages/services/Telephony/src/com/android/phone/PhoneInterfaceManager.java
到这里才是设置phone的数据开启
phone.setDataEnabled(enable);
/** * Set mobile data enabled * Used by the user through settings etc to turn on/off mobile data * * @param enable {@code true} turn turn data on, else {@code false} */ @Override public void setDataEnabled(int subId, boolean enable) { enforceModifyPermission(); int phoneId = mSubscriptionController.getPhoneId(subId); log("getDataEnabled: subId=" + subId + " phoneId=" + phoneId); Phone phone = PhoneFactory.getPhone(phoneId); if (phone != null) { log("setDataEnabled: subId=" + subId + " enable=" + enable); phone.setDataEnabled(enable); } else { loge("setDataEnabled: no phone for subId=" + subId); } }
android/frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GSMPhone.java
mDcTracker.setDataEnabled(enable);
@Override public void setDataEnabled(boolean enable) { mDcTracker.setDataEnabled(enable); }
android/rameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DcTrackerBase.java
发送打开数据开关的消息CMD_SET_USER_DATA_ENABLE
Message
msg =
obtainMessage(DctConstants.CMD_SET_USER_DATA_ENABLE);
/** * Modify {@link android.provider.Settings.Global#MOBILE_DATA} value. */ public void setDataEnabled(boolean enable) { Message msg = obtainMessage(DctConstants.CMD_SET_USER_DATA_ENABLE); msg.arg1 = enable ? 1 : 0; sendMessage(msg); }
处理消息
onSetUserDataEnabled(enabled);
@Override public void handleMessage(Message msg) { switch (msg.what) { case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { log("DISCONNECTED_CONNECTED: msg=" + msg); DcAsyncChannel dcac = (DcAsyncChannel) msg.obj; mDataConnectionAcHashMap.remove(dcac.getDataConnectionIdSync()); dcac.disconnected(); break; } ... case DctConstants.CMD_SET_USER_DATA_ENABLE: { final boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false; if (DBG) log("CMD_SET_USER_DATA_ENABLE enabled=" + enabled); onSetUserDataEnabled(enabled); break; } ...
onTrySetupData(Phone.REASON_DATA_ENABLED);
protected void onSetUserDataEnabled(boolean enabled) { synchronized (mDataEnabledLock) { if (mUserDataEnabled != enabled) { mUserDataEnabled = enabled; Settings.Global.putInt(mPhone.getContext().getContentResolver(), Settings.Global.MOBILE_DATA + mPhone.getPhoneId(), enabled ? 1 : 0); if (getDataOnRoamingEnabled() == false && mPhone.getServiceState().getRoaming() == true) { if (enabled) { notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON); } else { notifyOffApnsOfAvailability(Phone.REASON_DATA_DISABLED); } } if (enabled) { onTrySetupData(Phone.REASON_DATA_ENABLED); } else { onCleanUpAllConnections(Phone.REASON_DATA_SPECIFIC_DISABLED); } } } }
android/frameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
setupDataOnConnectableApns(reason);
@Override // TODO: We shouldnt need this. protected boolean onTrySetupData(String reason) { if (DBG) log("onTrySetupData: reason=" + reason); setupDataOnConnectableApns(reason); return true; }
trySetupData(apnContext);
<span style="background-color: rgb(255, 255, 255);"> private void setupDataOnConnectableApns(String reason) { if (DBG) log("setupDataOnConnectableApns: " + reason); for (ApnContext apnContext : mPrioritySortedApnContexts) { if (DBG) log("setupDataOnConnectableApns: apnContext " + apnContext); if (apnContext.getState() == DctConstants.State.FAILED) { apnContext.setState(DctConstants.State.IDLE); } if (apnContext.isConnectable()) { log("setupDataOnConnectableApns: isConnectable() call trySetupData"); apnContext.setReason(reason); trySetupData(apnContext); } } }</span>
下面这个方法比较长,有很多条件的检查
boolean
retValue =setupData(apnContext,
radioTech);
private boolean trySetupData(ApnContext apnContext) { ... if (apnContext.isConnectable() && (isEmergencyApn || (isDataAllowed(apnContext) && getAnyDataEnabled(checkUserDataEnabled) && !isEmergency()))) { if (apnContext.getState() == DctConstants.State.FAILED) { if (DBG) log("trySetupData: make a FAILED ApnContext IDLE so its reusable"); apnContext.setState(DctConstants.State.IDLE); } int radioTech = mPhone.getServiceState().getRilDataRadioTechnology(); if (apnContext.getState() == DctConstants.State.IDLE) { ArrayList<ApnSetting> waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech); if (waitingApns.isEmpty()) { notifyNoData(DcFailCause.MISSING_UNKNOWN_APN, apnContext); notifyOffApnsOfAvailability(apnContext.getReason()); if (DBG) log("trySetupData: X No APN found retValue=false"); return false; } else { apnContext.setWaitingApns(waitingApns); if (DBG) { log ("trySetupData: Create from mAllApnSettings : " + apnListToString(mAllApnSettings)); } } } if (DBG) { log("trySetupData: call setupData, waitingApns : " + apnListToString(apnContext.getWaitingApns())); } boolean retValue = setupData(apnContext, radioTech);//7 notifyOffApnsOfAvailability(apnContext.getReason()); if (DBG) log("trySetupData: X retValue=" + retValue); return retValue; } else { if (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT) && apnContext.isConnectable()) { mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType()); } notifyOffApnsOfAvailability(apnContext.getReason()); if (DBG) log ("trySetupData: X apnContext not 'ready' retValue=false"); return false; } }
dcac.bringUp(apnContext,
getInitialMaxRetry(), profileId, radioTech, mAutoAttachOnCreation,
msg);
private boolean setupData(ApnContext apnContext, int radioTech) { if (DBG) log("setupData: apnContext=" + apnContext); ApnSetting apnSetting; DcAsyncChannel dcac = null; apnSetting = apnContext.getNextWaitingApn(); if (apnSetting == null) { if (DBG) log("setupData: return for no apn found!"); return false; } int profileId = apnSetting.profileId; if (profileId == 0) { profileId = getApnProfileID(apnContext.getApnType()); } // On CDMA, if we're explicitly asking for DUN, we need have // a dun-profiled connection so we can't share an existing one // On GSM/LTE we can share existing apn connections provided they support // this type. if (apnContext.getApnType() != PhoneConstants.APN_TYPE_DUN || teardownForDun() == false) { dcac = checkForCompatibleConnectedApnContext(apnContext); if (dcac != null) { // Get the dcacApnSetting for the connection we want to share. ApnSetting dcacApnSetting = dcac.getApnSettingSync(); if (dcacApnSetting != null) { // Setting is good, so use it. apnSetting = dcacApnSetting; } } } if (dcac == null) { if (isOnlySingleDcAllowed(radioTech)) { if (isHigherPriorityApnContextActive(apnContext)) { if (DBG) { log("setupData: Higher priority ApnContext active. Ignoring call"); } return false; } // Only lower priority calls left. Disconnect them all in this single PDP case // so that we can bring up the requested higher priority call (once we receive // repsonse for deactivate request for the calls we are about to disconnect if (cleanUpAllConnections(true, Phone.REASON_SINGLE_PDN_ARBITRATION)) { // If any call actually requested to be disconnected, means we can't // bring up this connection yet as we need to wait for those data calls // to be disconnected. if (DBG) log("setupData: Some calls are disconnecting first. Wait and retry"); return false; } // No other calls are active, so proceed if (DBG) log("setupData: Single pdp. Continue setting up data call."); } dcac = findFreeDataConnection(); if (dcac == null) { dcac = createDataConnection(); } if (dcac == null) { if (DBG) log("setupData: No free DataConnection and couldn't create one, WEIRD"); return false; } } if (DBG) log("setupData: dcac=" + dcac + " apnSetting=" + apnSetting); apnContext.setDataConnectionAc(dcac); apnContext.setApnSetting(apnSetting); apnContext.setState(DctConstants.State.CONNECTING); mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType()); Message msg = obtainMessage(); msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE; msg.obj = apnContext; dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, radioTech, mAutoAttachOnCreation, msg);//8 if (DBG) log("setupData: initing!"); return true; }
android/frameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
sendMessage
/** * 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. * * @param apnContext is the Access Point Name to bring up a connection to * @param initialMaxRetry the number of retires for initial bringup. * @param profileId for the conneciton * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object. * With AsyncResult.userObj set to the original msg.obj, * AsyncResult.result = FailCause and AsyncResult.exception = Exception(). */ public void bringUp(ApnContext apnContext, int initialMaxRetry, int profileId, int rilRadioTechnology, boolean retryWhenSSChange, Message onCompletedMsg) { if (DBG) { log("bringUp: apnContext=" + apnContext + " initialMaxRetry=" + initialMaxRetry + " onCompletedMsg=" + onCompletedMsg); } sendMessage(DataConnection.EVENT_CONNECT,//9 new ConnectionParams(apnContext, initialMaxRetry, profileId, rilRadioTechnology, retryWhenSSChange, onCompletedMsg)); }
android/frameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
这其实是个状态即机 StateMachine,这个类或者或机制,本身也比较复杂,以后看明白了以后会单独在写一篇文章
现在是进入其中一个状态DcInactiveState。
DcInactiveState processmesssage
onConnect(mConnectionParams);
/** * The state machine is inactive and expects a EVENT_CONNECT. */ private class DcInactiveState extends State { ... @Override public boolean processMessage(Message msg) { ... case EVENT_CONNECT: if (DBG) log("DcInactiveState: mag.what=EVENT_CONNECT"); ConnectionParams cp = (ConnectionParams) msg.obj; if (initConnection(cp)) { onConnect(mConnectionParams);//10 transitionTo(mActivatingState); } else { if (DBG) { log("DcInactiveState: msg.what=EVENT_CONNECT initConnection failed"); } notifyConnectCompleted(cp, DcFailCause.UNACCEPTABLE_NETWORK_PARAMETER, false); } retVal = HANDLED; break;
mPhone.mCi.setupDataCall
/** * Begin setting up a data connection, calls setupDataCall * and the ConnectionParams will be returned with the * EVENT_SETUP_DATA_CONNECTION_DONE AsyncResul.userObj. * * @param cp is the connection parameters */ private void onConnect(ConnectionParams cp) { if (DBG) log("onConnect: carrier='" + mApnSetting.carrier + "' APN='" + mApnSetting.apn + "' proxy='" + mApnSetting.proxy + "' port='" + mApnSetting.port + "'"); // Check if we should fake an error. if (mDcTesterFailBringUpAll.getDcFailBringUp().mCounter > 0) { ... } mCreateTime = -1; mLastFailTime = -1; mLastFailCause = DcFailCause.NONE; // The data profile's profile ID must be set when it is created. int dataProfileId; if (mApnSetting.getApnProfileType() == ApnProfileType.PROFILE_TYPE_OMH) { dataProfileId = mApnSetting.getProfileId() + RILConstants.DATA_PROFILE_OEM_BASE; log("OMH profile, dataProfile id = " + dataProfileId); } else { dataProfileId = cp.mProfileId; } // msg.obj will be returned in AsyncResult.userObj; Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp); msg.obj = cp; int authType = mApnSetting.authType; if (authType == -1) { authType = TextUtils.isEmpty(mApnSetting.user) ? RILConstants.SETUP_DATA_AUTH_NONE : RILConstants.SETUP_DATA_AUTH_PAP_CHAP; } String protocol; if (mPhone.getServiceState().getRoaming()) { protocol = mApnSetting.roamingProtocol; } else { protocol = mApnSetting.protocol; } mPhone.mCi.setupDataCall(//11 Integer.toString(cp.mRilRat + 2), Integer.toString(dataProfileId), mApnSetting.apn, mApnSetting.user, mApnSetting.password, Integer.toString(authType), protocol, msg); }
android/frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java 这个类是通讯中的使用类。