Android P 开启和关闭移动数据流程

开启或关闭移动数据有两个地方:1、状态栏里快捷键;2、设置页面。响应开启或关闭,会直接调用 TelephonyManager#setDataEnabled 方法。

Android P 开启移动数据流程

时序图

Android P 开启和关闭移动数据流程

TelephonyManager#setDataEnabled

@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void setDataEnabled(boolean enable) {
   setDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
}
/**
* @hide
* @deprecated use {@link #setDataEnabled(boolean)} instead.
*/
@SystemApi
@Deprecated
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void setDataEnabled(int subId, boolean enable) {
   try {
       Log.d(TAG, "setDataEnabled: enabled=" + enable);
       ITelephony telephony = getITelephony();
       if (telephony != null)
           telephony.setUserDataEnabled(subId, enable);
   } catch (RemoteException e) {
       Log.e(TAG, "Error calling ITelephony#setUserDataEnabled", e);
   }
}

ITelephony 是接口,看其实现类 PhoneInterfaceManager#setUserDataEnabled 方法。

PhoneInterfaceManager#setUserDataEnabled

@Override
public void setUserDataEnabled(int subId, boolean enable) {
   TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
           mApp, subId, "setUserDataEnabled");
   final long identity = Binder.clearCallingIdentity();
   try {
       int phoneId = mSubscriptionController.getPhoneId(subId);
       if (DBG) log("setUserDataEnabled: subId=" + subId + " phoneId=" + phoneId);
       Phone phone = PhoneFactory.getPhone(phoneId);
       if (phone != null) {
           if (DBG) log("setUserDataEnabled: subId=" + subId + " enable=" + enable);
           phone.getDataEnabledSettings().setUserDataEnabled(enable);
       } else {
           loge("setUserDataEnabled: no phone found. Invalid subId=" + subId);
       }
   } finally {
       Binder.restoreCallingIdentity(identity);
   }
}

继续调用 DataEnabledSettings#setUserDataEnabled 方法。

DataEnabledSettings#setUserDataEnabled

public synchronized void setUserDataEnabled(boolean enabled) {
   localLog("UserDataEnabled", enabled);
   //更新数据库设置,注意双卡模式下的异同
   Settings.Global.putInt(mResolver, getMobileDataSettingName(), enabled ? 1 : 0);
   mPhone.notifyUserMobileDataStateChanged(enabled);
   updateDataEnabledAndNotify(REASON_USER_DATA_ENABLED);
}

private String getMobileDataSettingName() {
   // For single SIM phones, this is a per phone property. Or if it's invalid subId, we
   // read default setting.
   int subId = mPhone.getSubId();
   if (TelephonyManager.getDefault().getSimCount() == 1
           || !SubscriptionManager.isValidSubscriptionId(subId)) {
       return Settings.Global.MOBILE_DATA;
   } else {
       return Settings.Global.MOBILE_DATA + mPhone.getSubId();
   }
}

继续看 DataEnabledSettings#updateDataEnabledAndNotify 方法。

DataEnabledSettings#updateDataEnabledAndNotify

private synchronized void updateDataEnabledAndNotify(int reason) {
   boolean prevDataEnabled = mIsDataEnabled;
   updateDataEnabled();
   if (prevDataEnabled != mIsDataEnabled) {
       notifyDataEnabledChanged(!prevDataEnabled, reason);
   }
}

又调用了 DataEnabledSettings#notifyDataEnabledChanged 方法。

DataEnabledSettings#notifyDataEnabledChanged

private void notifyDataEnabledChanged(boolean enabled, int reason) {
   mOverallDataEnabledChangedRegistrants.notifyResult(new Pair<>(enabled, reason));
}
public void registerForDataEnabledChanged(Handler h, int what, Object obj) {
   mOverallDataEnabledChangedRegistrants.addUnique(h, what, obj);
   notifyDataEnabledChanged(isDataEnabled(), REASON_REGISTERED);
}

这里需要看 DataEnabledChanged 哪里被注册,搜索看到是 DcTracker 构造方法里被注册了。

DcTracker 构造方法

public DcTracker(Phone phone, int transportType) {
   //省略部分代码
   mDataEnabledSettings.registerForDataEnabledChanged(this,
           DctConstants.EVENT_DATA_ENABLED_CHANGED, null);
   //省略部分代码
}

然后再看 EVENT_DATA_ENABLED_CHANGED handleMessage 处理的地方。

DcTracker#handleMessage

@Override
public void handleMessage (Message msg) {
   if (VDBG) log("handleMessage msg=" + msg);
   switch (msg.what) {
      //省略部分代码
       case DctConstants.EVENT_DATA_ENABLED_CHANGED:
           AsyncResult ar = (AsyncResult) msg.obj;
           if (ar.result instanceof Pair) {
               Pair<Boolean, Integer> p = (Pair<Boolean, Integer>) ar.result;
               boolean enabled = p.first;
               int reason = p.second;
               onDataEnabledChanged(enabled, reason);
           }
           break;
       default:
           Rlog.e("DcTracker", "Unhandled event=" + msg);
           break;
   }
}

继续调用了 DcTracker#onDataEnabledChanged 方法。

DcTracker#onDataEnabledChanged

private void onDataEnabledChanged(boolean enable,
                                 @DataEnabledChangedReason int enabledChangedReason) {
   if (DBG) {
       log("onDataEnabledChanged: enable=" + enable + ", enabledChangedReason="
               + enabledChangedReason);
   }
   if (enable) {//开启移动数据业务
       reevaluateDataConnections();
       onTrySetupData(Phone.REASON_DATA_ENABLED);
   } else {//关闭移动数据业务
       String cleanupReason;
       switch (enabledChangedReason) {
           case DataEnabledSettings.REASON_INTERNAL_DATA_ENABLED:
               cleanupReason = Phone.REASON_DATA_DISABLED_INTERNAL;
               break;
           case DataEnabledSettings.REASON_DATA_ENABLED_BY_CARRIER:
               cleanupReason = Phone.REASON_CARRIER_ACTION_DISABLE_METERED_APN;
               break;
           case DataEnabledSettings.REASON_USER_DATA_ENABLED:
           case DataEnabledSettings.REASON_POLICY_DATA_ENABLED:
           case DataEnabledSettings.REASON_PROVISIONED_CHANGED:
           case DataEnabledSettings.REASON_PROVISIONING_DATA_ENABLED_CHANGED:
           default:
               cleanupReason = Phone.REASON_DATA_SPECIFIC_DISABLED;
               break;
       }
       cleanUpAllConnectionsInternal(true, cleanupReason);
   }
}

这里有两个分支,先看开启移动数据业务,看 DcTracker#onTrySetupData 方法。

DcTracker#onTrySetupData

// TODO: We shouldnt need this.
private boolean onTrySetupData(String reason) {
   if (DBG) log("onTrySetupData: reason=" + reason);
   setupDataOnConnectableApns(reason);
   return true;
}

private void setupDataOnConnectableApns(String reason) {
   setupDataOnConnectableApns(reason, RetryFailures.ALWAYS);
}
private void setupDataOnConnectableApns(String reason, RetryFailures retryFailures) {
   //省略部分代码
   for (ApnContext apnContext : mPrioritySortedApnContexts) {
       if (VDBG) log("setupDataOnConnectableApns: apnContext " + apnContext);
       if (apnContext.getState() == DctConstants.State.FAILED
               || apnContext.getState() == DctConstants.State.SCANNING) {
           if (retryFailures == RetryFailures.ALWAYS) {
               apnContext.releaseDataConnection(reason);
           } else if (apnContext.isConcurrentVoiceAndDataAllowed() == false &&
                   mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
               // RetryFailures.ONLY_ON_CHANGE - check if voice concurrency has changed
               apnContext.releaseDataConnection(reason);
           }
       }
       //可连接的 apnContext
       if (apnContext.isConnectable()) {
           log("isConnectable() call trySetupData");
           //设置开启移动数据业务的原因
           apnContext.setReason(reason);
           trySetupData(apnContext);
       }
   }
}

继续看 DcTracker#trySetupData 方法。

DcTracker#trySetupData

private boolean trySetupData(ApnContext apnContext) {
   if (mPhone.getSimulatedRadioControl() != null) {
       // Assume data is connected on the simulator
       // FIXME  this can be improved
       apnContext.setState(DctConstants.State.CONNECTED);
       mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
       log("trySetupData: X We're on the simulator; assuming connected retValue=true");
       return true;
   }
   DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
   boolean isDataAllowed = isDataAllowed(apnContext, dataConnectionReasons);
   String logStr = "trySetupData for APN type " + apnContext.getApnType() + ", reason: "
           + apnContext.getReason() + ". " + dataConnectionReasons.toString();
   if (DBG) log(logStr);
   apnContext.requestLog(logStr);
   if (isDataAllowed) {
       if (apnContext.getState() == DctConstants.State.FAILED) {
           String str = "trySetupData: make a FAILED ApnContext IDLE so its reusable";
           if (DBG) log(str);
           apnContext.requestLog(str);
           apnContext.setState(DctConstants.State.IDLE);
       }
       int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
       apnContext.setConcurrentVoiceAndDataAllowed(mPhone.getServiceStateTracker()
               .isConcurrentVoiceAndDataAllowed());
       if (apnContext.getState() == DctConstants.State.IDLE) {
           ArrayList<ApnSetting> waitingApns =
                   buildWaitingApns(apnContext.getApnType(), radioTech);
           if (waitingApns.isEmpty()) {
               notifyNoData(DataFailCause.MISSING_UNKNOWN_APN, apnContext);
               notifyOffApnsOfAvailability(apnContext.getReason());
               String str = "trySetupData: X No APN found retValue=false";
               if (DBG) log(str);
               apnContext.requestLog(str);
               return false;
           } else {
               apnContext.setWaitingApns(waitingApns);
               if (DBG) {
                   log ("trySetupData: Create from mAllApnSettings : "
                               + apnListToString(mAllApnSettings));
               }
           }
       }
       boolean retValue = setupData(apnContext, radioTech);
       notifyOffApnsOfAvailability(apnContext.getReason());
       if (DBG) log("trySetupData: X retValue=" + retValue);
       return retValue;
   } else {
       //省略部分代码
       return false;
   }
}

继续看 DcTracker#setupData 方法。

DcTracker#setupData

private boolean setupData(ApnContext apnContext, int radioTech) {
   if (DBG) log("setupData: apnContext=" + apnContext);
   apnContext.requestLog("setupData");
   ApnSetting apnSetting;
   DataConnection dataConnection = null;
   apnSetting = apnContext.getNextApnSetting();
   if (apnSetting == null) {
       if (DBG) log("setupData: return for no apn found!");
       return false;
   }
   // profile id is only meaningful when the profile is persistent on the modem.
   int profileId = DATA_PROFILE_INVALID;
   if (apnSetting.isPersistent()) {
       profileId = apnSetting.getProfileId();
       if (profileId == DATA_PROFILE_DEFAULT) {
           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().equals(PhoneConstants.APN_TYPE_DUN)
           || ServiceState.isGsm(mPhone.getServiceState().getRilDataRadioTechnology())) {
       dataConnection = checkForCompatibleConnectedApnContext(apnContext);
       if (dataConnection != null) {
           // Get the apn setting used by the data connection
           ApnSetting dataConnectionApnSetting = dataConnection.getApnSetting();
           if (dataConnectionApnSetting != null) {
               // Setting is good, so use it.
               apnSetting = dataConnectionApnSetting;
           }
       }
   }
   if (dataConnection == null) {
       if (isOnlySingleDcAllowed(radioTech)) {
           if (isHigherPriorityApnContextActive(apnContext)) {
               if (DBG) {
                   log("setupData: Higher priority ApnContext active.  Ignoring call");
               }
               return false;
           }
           if (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IMS)) {
               // 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
               // response for deactivate request for the calls we are about to disconnect
               if (cleanUpAllConnectionsInternal(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.");
       }
       dataConnection = findFreeDataConnection();
       if (dataConnection == null) {
          //创建 DataConnection 对象
           dataConnection = createDataConnection();
       }
       if (dataConnection == null) {
           if (DBG) log("setupData: No free DataConnection and couldn't create one, WEIRD");
           return false;
       }
   }
   final int generation = apnContext.incAndGetConnectionGeneration();
   if (DBG) {
       log("setupData: dc=" + dataConnection + " apnSetting=" + apnSetting + " gen#="
               + generation);
   }
   apnContext.setDataConnection(dataConnection);
   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 = new Pair<ApnContext, Integer>(apnContext, generation);
   //使用 bringUp 激活移动数据业务
   dataConnection.bringUp(apnContext, profileId, radioTech, msg, generation);
   if (DBG) log("setupData: initing!");
   return true;
}

继续看 DataConnection#bringUp 方法。

DataConnection#bringUp

public void bringUp(ApnContext apnContext, int profileId, int rilRadioTechnology,
                   Message onCompletedMsg, int connectionGeneration) {
   if (DBG) {
       log("bringUp: apnContext=" + apnContext + " onCompletedMsg=" + onCompletedMsg);
   }
   sendMessage(DataConnection.EVENT_CONNECT,
           new ConnectionParams(apnContext, profileId, rilRadioTechnology, onCompletedMsg,
                   connectionGeneration));
}

通过 sendMessage 发送 DataConnection.EVENT_CONNECT 信息,问题来了,哪里接受处理该 Message 消息呢?因为这里搜索有很多地方接受了。

那就是由 StateMachine 对象 SmHandler  handleMessage 方法响应该消息。由 StateMachine 运行机制和业务流程,可以确定 DataConnection.mInactiveState(非活动状态) 对象的 processMessage 方法将响应并处理此 Message 消息,业务逻辑的详情 如下。

DcInactiveState#processMessage

@Override
public boolean processMessage(Message msg) {
   boolean retVal;
   switch (msg.what) {
       //省略部分代码
       case EVENT_CONNECT:
           if (DBG) log("DcInactiveState: mag.what=EVENT_CONNECT");
           ConnectionParams cp = (ConnectionParams) msg.obj;
           if (initConnection(cp)) {
               onConnect(mConnectionParams);
               //完成 DataConnection 状态切换
               transitionTo(mActivatingState);
           } else {
               if (DBG) {
                   log("DcInactiveState: msg.what=EVENT_CONNECT initConnection failed");
               }
               notifyConnectCompleted(cp, DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER,
                       false);
           }
           retVal = HANDLED;
           break;
        //省略部分代码
   }
   return retVal;
}

继续看 DataConnection#onConnect 方法。

DataConnection#onConnect

private void onConnect(ConnectionParams cp) {
   if (DBG) {
       log("onConnect: carrier='" + mApnSetting.getEntryName()
               + "' APN='" + mApnSetting.getApnName()
               + "' proxy='" + mApnSetting.getProxyAddressAsString()
               + "' port='" + mApnSetting.getProxyPort() + "'");
   }
   if (cp.mApnContext != null) cp.mApnContext.requestLog("DataConnection.onConnect");
   // Check if we should fake an error.
   if (mDcTesterFailBringUpAll.getDcFailBringUp().mCounter  > 0) {
       DataCallResponse response = new DataCallResponse(
               mDcTesterFailBringUpAll.getDcFailBringUp().mFailCause,
               mDcTesterFailBringUpAll.getDcFailBringUp().mSuggestedRetryTime, 0, 0, "", "",
               null, null, null, null, PhoneConstants.UNSET_MTU);
       Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
       AsyncResult.forMessage(msg, response, null);
       sendMessage(msg);
       if (DBG) {
           log("onConnect: FailBringUpAll=" + mDcTesterFailBringUpAll.getDcFailBringUp()
                   + " send error response=" + response);
       }
       mDcTesterFailBringUpAll.getDcFailBringUp().mCounter -= 1;
       return;
   }
   mCreateTime = -1;
   mLastFailTime = -1;
   mLastFailCause = DataFailCause.NONE;
   Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
   msg.obj = cp;
   DataProfile dp = DcTracker.createDataProfile(mApnSetting, cp.mProfileId,
           mApnSetting.equals(mDct.getPreferredApn()));
   // We need to use the actual modem roaming state instead of the framework roaming state
   // here. This flag is only passed down to ril_service for picking the correct protocol (for
   // old modem backward compatibility).
   boolean isModemRoaming = mPhone.getServiceState().getDataRoamingFromRegistration();
   // Set this flag to true if the user turns on data roaming. Or if we override the roaming
   // state in framework, we should set this flag to true as well so the modem will not reject
   // the data call setup (because the modem actually thinks the device is roaming).
   boolean allowRoaming = mPhone.getDataRoamingEnabled()
           || (isModemRoaming && !mPhone.getServiceState().getDataRoaming());
   //注意这里的参数msg,移动数据完成激活会通过 EVENT_SETUP_DATA_CONNECTION_DONE 回调。
   mDataServiceManager.setupDataCall(
           ServiceState.rilRadioTechnologyToAccessNetworkType(cp.mRilRat), dp, isModemRoaming,
           allowRoaming, DataService.REQUEST_REASON_NORMAL, null, msg);
   TelephonyMetrics.getInstance().writeSetupDataCall(mPhone.getPhoneId(), cp.mRilRat,
           dp.getProfileId(), dp.getApn(), dp.getProtocol());
}

继续看 DataServiceManager.setupDataCall 方法往下跟踪。

DataServiceManager.setupDataCall

public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
                         boolean allowRoaming, int reason, LinkProperties linkProperties,
                         Message onCompleteMessage) {
   if (DBG) log("setupDataCall");
   if (!mBound) {
       loge("Data service not bound.");
       sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
       return;
   }
   CellularDataServiceCallback callback = null;
   if (onCompleteMessage != null) {
       callback = new CellularDataServiceCallback();
       mMessageMap.put(callback.asBinder(), onCompleteMessage);
   }
   try {
       mIDataService.setupDataCall(mPhone.getPhoneId(), accessNetworkType, dataProfile,
               isRoaming, allowRoaming, reason, linkProperties, callback);
   } catch (RemoteException e) {
       loge("Cannot invoke setupDataCall on data service.");
       if (callback != null) {
           mMessageMap.remove(callback.asBinder());
       }
       sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
   }
}

继续调用 mIDataService.setupDataCall,由 IDataServiceWrapper.setupDataCall 实现。

IDataServiceWrapper#setupDataCall

@Override
public void setupDataCall(int slotId, int accessNetworkType, DataProfile dataProfile,
                         boolean isRoaming, boolean allowRoaming, int reason,
                         LinkProperties linkProperties, IDataServiceCallback callback) {
   mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, slotId, 0,
           new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming,
                   allowRoaming, reason, linkProperties, callback))
           .sendToTarget();
}

直接看 DATA_SERVICE_REQUEST_SETUP_DATA_CALL 处理。

DataServiceHandler#handleMessage

case DATA_SERVICE_REQUEST_SETUP_DATA_CALL:
   if (serviceProvider == null) break;
   SetupDataCallRequest setupDataCallRequest = (SetupDataCallRequest) message.obj;
   serviceProvider.setupDataCall(setupDataCallRequest.accessNetworkType,
           setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming,
           setupDataCallRequest.allowRoaming, setupDataCallRequest.reason,
           setupDataCallRequest.linkProperties,
           (setupDataCallRequest.callback != null)
                   ? new DataServiceCallback(setupDataCallRequest.callback)
                   : null);
   break;

点击这里 SetupDataCallRequest#setupDataCall 会跳到 DataServiceProvider#setupDataCall,DataServiceProvider 是抽象类,看它继承类 CellularDataServiceProvider#setupDataCall。

CellularDataServiceProvider#setupDataCall

@Override
public void setupDataCall(int radioTechnology, DataProfile dataProfile, boolean isRoaming,
                         boolean allowRoaming, int reason, LinkProperties linkProperties,
                         DataServiceCallback callback) {
   if (DBG) log("setupDataCall " + getSlotId());
   Message message = null;
   // Only obtain the message when the caller wants a callback. If the caller doesn't care
   // the request completed or results, then no need to pass the message down.
   if (callback != null) {
       message = Message.obtain(mHandler, SETUP_DATA_CALL_COMPLETE);
       mCallbackMap.put(message, callback);
   }
   mPhone.mCi.setupDataCall(radioTechnology, dataProfile, isRoaming, allowRoaming, reason,
           linkProperties, message);
}

上面代码中最关键的是对 mPhone . mCi .setupDataCall 方法的调用, mCi 即 RILJ 对象,最终由 RIL 完成 Data Call 移动数据业务的处理。

RIL 完成激活 Data Call 移动数据业务处理之后,EVENT_SETUP_DATA_CONNECTION_DONE 作为 setupDataCall 方法调用的参数之一,会使用此 Message 对象发起  Callback 调用 。

DataConnection . SmHandler 对象的 handleMessage 方法进行晌应, StateMachine 的状态已经 切换到 DataConnection.mActivatingState ,因此 mActivatingState 对象的 processMessage 方法将响应此消息回调。

DcActivatingState#processMessage

@Override
public boolean processMessage(Message msg) {
   boolean retVal;
   AsyncResult ar;
   ConnectionParams cp;
   if (DBG) log("DcActivatingState: msg=" + msgToString(msg));
   switch (msg.what) {
       case EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED:
       case EVENT_CONNECT:
           // Activating can't process until we're done.
           deferMessage(msg);
           retVal = HANDLED;
           break;
       case EVENT_SETUP_DATA_CONNECTION_DONE:
           cp = (ConnectionParams) msg.obj;
           DataCallResponse dataCallResponse =
                   msg.getData().getParcelable(DataServiceManager.DATA_CALL_RESPONSE);
           SetupResult result = onSetupConnectionCompleted(msg.arg1, dataCallResponse, cp);
           if (result != SetupResult.ERROR_STALE) {
               if (mConnectionParams != cp) {
                   loge("DcActivatingState: WEIRD mConnectionsParams:"+ mConnectionParams
                           + " != cp:" + cp);
               }
           }
           if (DBG) {
               log("DcActivatingState onSetupConnectionCompleted result=" + result
                       + " dc=" + DataConnection.this);
           }
           if (cp.mApnContext != null) {
               cp.mApnContext.requestLog("onSetupConnectionCompleted result=" + result);
           }
           switch (result) {
               case SUCCESS://成功激活 Data Call 移动数据业务
                   // All is well
                   mDcFailCause = DataFailCause.NONE;
                   transitionTo(mActiveState);
                   break;
               case ERROR_RADIO_NOT_AVAILABLE:
                   // Vendor ril rejected the command and didn't connect.
                   // Transition to inactive but send notifications after
                   // we've entered the mInactive state.
                   mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
                   transitionTo(mInactiveState);
                   break;
               case ERROR_INVALID_ARG:
                   // The addresses given from the RIL are bad
                   tearDownData(cp);
                   transitionTo(mDisconnectingErrorCreatingConnection);
                   break;
               case ERROR_DATA_SERVICE_SPECIFIC_ERROR:
                   // Retrieve the suggested retry delay from the modem and save it.
                   // If the modem want us to retry the current APN again, it will
                   // suggest a positive delay value (in milliseconds). Otherwise we'll get
                   // NO_SUGGESTED_RETRY_DELAY here.
                   long delay = getSuggestedRetryDelay(dataCallResponse);
                   cp.mApnContext.setModemSuggestedDelay(delay);
                   String str = "DcActivatingState: ERROR_DATA_SERVICE_SPECIFIC_ERROR "
                           + " delay=" + delay
                           + " result=" + result
                           + " result.isRadioRestartFailure="
                           + DataFailCause.isRadioRestartFailure(mPhone.getContext(),
                           result.mFailCause, mPhone.getSubId())
                           + " isPermanentFailure=" +
                           mDct.isPermanentFailure(result.mFailCause);
                   if (DBG) log(str);
                   if (cp.mApnContext != null) cp.mApnContext.requestLog(str);
                   // Save the cause. DcTracker.onDataSetupComplete will check this
                   // failure cause and determine if we need to retry this APN later
                   // or not.
                   mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
                   transitionTo(mInactiveState);
                   break;
               case ERROR_STALE:
                   loge("DcActivatingState: stale EVENT_SETUP_DATA_CONNECTION_DONE"
                           + " tag:" + cp.mTag + " != mTag:" + mTag);
                   break;
               default:
                   throw new RuntimeException("Unknown SetupResult, should not happen");
           }
           retVal = HANDLED;
           break;
       default:
           if (VDBG) {
               log("DcActivatingState not handled msg.what=" +
                       getWhatToString(msg.what) + " RefCount=" + mApnContexts.size());
           }
           retVal = NOT_HANDLED;
           break;
   }
   return retVal;
}

这样开启移动数据业务流程分析完毕。

Android P 关闭移动数据流程

时序图

Android P 开启和关闭移动数据流程

DcTracker#onDataEnabledChanged

DcTracker#onDataEnabledChanged 有两个分支,开启和关闭移动数据业务。

private void onDataEnabledChanged(boolean enable,
                                 @DataEnabledChangedReason int enabledChangedReason) {
   if (DBG) {
       log("onDataEnabledChanged: enable=" + enable + ", enabledChangedReason="
               + enabledChangedReason);
   }
   if (enable) {//开启移动数据业务
       reevaluateDataConnections();
       onTrySetupData(Phone.REASON_DATA_ENABLED);
   } else {//关闭移动数据业务
       String cleanupReason;
       switch (enabledChangedReason) {
           case DataEnabledSettings.REASON_INTERNAL_DATA_ENABLED:
               cleanupReason = Phone.REASON_DATA_DISABLED_INTERNAL;
               break;
           case DataEnabledSettings.REASON_DATA_ENABLED_BY_CARRIER:
               cleanupReason = Phone.REASON_CARRIER_ACTION_DISABLE_METERED_APN;
               break;
           case DataEnabledSettings.REASON_USER_DATA_ENABLED:
           case DataEnabledSettings.REASON_POLICY_DATA_ENABLED:
           case DataEnabledSettings.REASON_PROVISIONED_CHANGED:
           case DataEnabledSettings.REASON_PROVISIONING_DATA_ENABLED_CHANGED:
           default:
               cleanupReason = Phone.REASON_DATA_SPECIFIC_DISABLED;
               break;
       }
       cleanUpAllConnectionsInternal(true, cleanupReason);
   }
}

现在看关闭移动数据流程,来看 DcTracker#cleanUpAllConnectionsInternal 方法。

DcTracker#cleanUpAllConnectionsInternal

private boolean cleanUpAllConnectionsInternal(boolean tearDown, String reason) {
   if (DBG) log("cleanUpAllConnections: tearDown=" + tearDown + " reason=" + reason);
   boolean didDisconnect = false;
   boolean disableMeteredOnly = false;
   // reasons that only metered apn will be torn down
   if (!TextUtils.isEmpty(reason)) {
       disableMeteredOnly = reason.equals(Phone.REASON_DATA_SPECIFIC_DISABLED) ||
               reason.equals(Phone.REASON_ROAMING_ON) ||
               reason.equals(Phone.REASON_CARRIER_ACTION_DISABLE_METERED_APN) ||
               reason.equals(Phone.REASON_PDP_RESET);
   }
   for (ApnContext apnContext : mApnContexts.values()) {
       if (disableMeteredOnly) {
           // Use ApnSetting to decide metered or non-metered.
           // Tear down all metered data connections.
           ApnSetting apnSetting = apnContext.getApnSetting();
           if (apnSetting != null && ApnSettingUtils.isMetered(apnSetting, mPhone)) {
               if (apnContext.isDisconnected() == false) didDisconnect = true;
               if (DBG) log("clean up metered ApnContext Type: " + apnContext.getApnType());
               apnContext.setReason(reason);
               //将APN上下文与关联的数据连接分离,如果没有附加其他APN上下文,则将其删除。
               cleanUpConnectionInternal(tearDown, apnContext);
           }
       } else {
           // Exclude the IMS APN from single DataConenction case.
           if (reason.equals(Phone.REASON_SINGLE_PDN_ARBITRATION)
                   && apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IMS)) {
               continue;
           }
           // TODO - only do cleanup if not disconnected
           if (apnContext.isDisconnected() == false) didDisconnect = true;
           apnContext.setReason(reason);
           cleanUpConnectionInternal(tearDown, apnContext);
       }
   }
   stopNetStatPoll();
   stopDataStallAlarm();
   // TODO: Do we need mRequestedApnType?
   mRequestedApnType = ApnSetting.TYPE_DEFAULT;
   log("cleanUpConnectionInternal: mDisconnectPendingCount = " + mDisconnectPendingCount);
   if (tearDown && mDisconnectPendingCount == 0) {
       notifyAllDataDisconnected();
   }
   return didDisconnect;
}

DcTracker#cleanUpConnectionInternal 方法将APN上下文与关联的数据连接分离,如果没有附加其他APN上下文,则将其删除。

DcTracker#cleanUpConnectionInternal

private void cleanUpConnectionInternal(boolean tearDown, ApnContext apnContext) {
   if (apnContext == null) {
       if (DBG) log("cleanUpConnectionInternal: apn context is null");
       return;
   }
   DataConnection dataConnection = apnContext.getDataConnection();
   String str = "cleanUpConnectionInternal: tearDown=" + tearDown + " reason="
           + apnContext.getReason();
   if (VDBG) log(str + " apnContext=" + apnContext);
   apnContext.requestLog(str);
   if (tearDown) {
       if (apnContext.isDisconnected()) {
           // The request is tearDown and but ApnContext is not connected.
           // If apnContext is not enabled anymore, break the linkage to the data connection.
           apnContext.setState(DctConstants.State.IDLE);
           if (!apnContext.isReady()) {
               if (dataConnection != null) {
                   str = "cleanUpConnectionInternal: teardown, disconnected, !ready";
                   if (DBG) log(str + " apnContext=" + apnContext);
                   apnContext.requestLog(str);
                   dataConnection.tearDown(apnContext, "", null);
               }
               apnContext.setDataConnection(null);
           }
       } else {
           // Connection is still there. Try to clean up.
           if (dataConnection != null) {
               if (apnContext.getState() != DctConstants.State.DISCONNECTING) {
                   boolean disconnectAll = false;
                   if (PhoneConstants.APN_TYPE_DUN.equals(apnContext.getApnType())) {
                       // CAF_MSIM is this below condition required.
                       // if (PhoneConstants.APN_TYPE_DUN.equals(PhoneConstants.APN_TYPE_DEFAULT)) {
                       if (teardownForDun()) {
                           if (DBG) {
                               log("cleanUpConnectionInternal: disconnectAll DUN connection");
                           }
                           // we need to tear it down - we brought it up just for dun and
                           // other people are camped on it and now dun is done.  We need
                           // to stop using it and let the normal apn list get used to find
                           // connections for the remaining desired connections
                           disconnectAll = true;
                       }
                   }
                   final int generation = apnContext.getConnectionGeneration();
                   str = "cleanUpConnectionInternal: tearing down"
                           + (disconnectAll ? " all" : "") + " using gen#" + generation;
                   if (DBG) log(str + "apnContext=" + apnContext);
                   apnContext.requestLog(str);
                   Pair<ApnContext, Integer> pair = new Pair<>(apnContext, generation);
                   Message msg = obtainMessage(DctConstants.EVENT_DISCONNECT_DONE, pair);
                   if (disconnectAll) {
                       dataConnection.tearDownAll(apnContext.getReason(), msg);
                   } else {
                       dataConnection.tearDown(apnContext, apnContext.getReason(), msg);
                   }
                   apnContext.setState(DctConstants.State.DISCONNECTING);
                   mDisconnectPendingCount++;
               }
           } else {
               // apn is connected but no reference to the data connection.
               // Should not be happen, but reset the state in case.
               apnContext.setState(DctConstants.State.IDLE);
               apnContext.requestLog("cleanUpConnectionInternal: connected, bug no dc");
               mPhone.notifyDataConnection(apnContext.getReason(),
                                           apnContext.getApnType());
           }
       }
   } else {
       // force clean up the data connection.
       if (dataConnection != null) dataConnection.reset();
       apnContext.setState(DctConstants.State.IDLE);
       mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
       apnContext.setDataConnection(null);
   }
   // Make sure reconnection alarm is cleaned up if there is no ApnContext
   // associated to the connection.
   if (dataConnection != null) {
       cancelReconnectAlarm(apnContext);
   }
   str = "cleanUpConnectionInternal: X tearDown=" + tearDown + " reason="
           + apnContext.getReason();
   if (DBG) log(str + " apnContext=" + apnContext + " dc=" + apnContext.getDataConnection());
   apnContext.requestLog(str);
}

DataConnection#tearDown&DataConnection#tearDownAll

public void tearDown(ApnContext apnContext, String reason, Message onCompletedMsg) {
   if (DBG) {
       log("tearDown: apnContext=" + apnContext
               + " reason=" + reason + " onCompletedMsg=" + onCompletedMsg);
   }
   sendMessage(DataConnection.EVENT_DISCONNECT,
           new DisconnectParams(apnContext, reason, onCompletedMsg));
}
public void tearDownAll(String reason, Message onCompletedMsg) {
   if (DBG) log("tearDownAll: reason=" + reason + " onCompletedMsg=" + onCompletedMsg);
   sendMessage(DataConnection.EVENT_DISCONNECT_ALL,
           new DisconnectParams(null, reason, onCompletedMsg));
}

StateMachine 的状态已经切换到 DataConnection.mActiveState ,因此 mActiveState 对象的 processMessage 方法将响应 EVENT_DISCONNECT 和 EVENT_DISCONNECT_ALL 回调。

DcActiveState#processMessage

@Override
public boolean processMessage(Message msg) {
   boolean retVal;
   switch (msg.what) {
       //省略部分代码
       case EVENT_DISCONNECT: {
           DisconnectParams dp = (DisconnectParams) msg.obj;
           if (DBG) {
               log("DcActiveState: EVENT_DISCONNECT dp=" + dp
                       + " dc=" + DataConnection.this);
           }
           if (mApnContexts.containsKey(dp.mApnContext)) {
               if (DBG) {
                   log("DcActiveState msg.what=EVENT_DISCONNECT RefCount="
                           + mApnContexts.size());
               }
               if (mApnContexts.size() == 1) {
                   mApnContexts.clear();
                   mDisconnectParams = dp;
                   mConnectionParams = null;
                   dp.mTag = mTag;
                   tearDownData(dp);
                   transitionTo(mDisconnectingState);
               } else {
                   mApnContexts.remove(dp.mApnContext);
                   // TODO (b/118347948): evaluate if it's still needed after assigning
                   // different scores to different Cellular network.
                   mDisabledApnTypeBitMask |= dp.mApnContext.getApnTypeBitmask();
                   mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities());
                   notifyDisconnectCompleted(dp, false);
               }
           } else {
               log("DcActiveState ERROR no such apnContext=" + dp.mApnContext
                       + " in this dc=" + DataConnection.this);
               notifyDisconnectCompleted(dp, false);
           }
           retVal = HANDLED;
           break;
       }
       case EVENT_DISCONNECT_ALL: {
           if (DBG) {
               log("DcActiveState EVENT_DISCONNECT clearing apn contexts,"
                       + " dc=" + DataConnection.this);
           }
           DisconnectParams dp = (DisconnectParams) msg.obj;
           mDisconnectParams = dp;
           mConnectionParams = null;
           dp.mTag = mTag;
           tearDownData(dp);
           transitionTo(mDisconnectingState);
           retVal = HANDLED;
           break;
       }
       //省略部分代码
       default:
           if (VDBG) {
               log("DcActiveState not handled msg.what=" + getWhatToString(msg.what));
           }
           retVal = NOT_HANDLED;
           break;
   }
   return retVal;
}

DataConnection#tearDownData

private void tearDownData(Object o) {
   int discReason = DataService.REQUEST_REASON_NORMAL;
   ApnContext apnContext = null;
   if ((o != null) && (o instanceof DisconnectParams)) {
       DisconnectParams dp = (DisconnectParams) o;
       apnContext = dp.mApnContext;
       if (TextUtils.equals(dp.mReason, Phone.REASON_RADIO_TURNED_OFF)
               || TextUtils.equals(dp.mReason, Phone.REASON_PDP_RESET)) {
           discReason = DataService.REQUEST_REASON_SHUTDOWN;
       }
   }
   String str = "tearDownData. mCid=" + mCid + ", reason=" + discReason;
   if (DBG) log(str);
   if (apnContext != null) apnContext.requestLog(str);
   mDataServiceManager.deactivateDataCall(mCid, discReason,
           obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, o));
}

下面的流程和开启移动数据业务基本相识了,最终也是由 RIL 停用 Data Call 移动数据业务的处理。

DataServiceManager#deactivateDataCall

public void deactivateDataCall(int cid, int reason, Message onCompleteMessage) {
   if (DBG) log("deactivateDataCall");
   if (!mBound) {
       loge("Data service not bound.");
       sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
       return;
   }
   CellularDataServiceCallback callback = null;
   if (onCompleteMessage != null) {
       callback = new CellularDataServiceCallback();
       mMessageMap.put(callback.asBinder(), onCompleteMessage);
   }
   try {
       mIDataService.deactivateDataCall(mPhone.getPhoneId(), cid, reason, callback);
   } catch (RemoteException e) {
       loge("Cannot invoke deactivateDataCall on data service.");
       if (callback != null) {
           mMessageMap.remove(callback.asBinder());
       }
       sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
   }
}

继续调用 mIDataService.setupDataCall,由 IDataServiceWrapper.setupDataCall 实现。

IDataServiceWrapper#deactivateDataCall

@Override
public void deactivateDataCall(int slotId, int cid, int reason,
                              IDataServiceCallback callback) {
   mHandler.obtainMessage(DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL, slotId, 0,
           new DeactivateDataCallRequest(cid, reason, callback))
           .sendToTarget();
}

直接看 DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL 处理。

DataServiceHandler#handleMessage

case DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL:
   if (serviceProvider == null) break;
   DeactivateDataCallRequest deactivateDataCallRequest =
           (DeactivateDataCallRequest) message.obj;
   serviceProvider.deactivateDataCall(deactivateDataCallRequest.cid,
           deactivateDataCallRequest.reason,
           (deactivateDataCallRequest.callback != null)
                   ? new DataServiceCallback(deactivateDataCallRequest.callback)
                   : null);
   break;

点击这里 DeactivateDataCallRequest#deactivateDataCall 会跳到 DataServiceProvider#deactivateDataCall,DataServiceProvider 是抽象类,看它继承类 CellularDataServiceProvider#deactivateDataCall。

CellularDataServiceProvider#deactivateDataCall

@Override
public void deactivateDataCall(int cid, int reason, DataServiceCallback callback) {
   if (DBG) log("deactivateDataCall " + getSlotId());
   Message message = null;
   // Only obtain the message when the caller wants a callback. If the caller doesn't care
   // the request completed or results, then no need to pass the message down.
   if (callback != null) {
       message = Message.obtain(mHandler, DEACTIVATE_DATA_ALL_COMPLETE);
       mCallbackMap.put(message, callback);
   }
   mPhone.mCi.deactivateDataCall(cid, reason, message);
}

mCi 即 RILJ 对象,最终由 RIL 停用 Data Call 移动数据业务的处理,处理成功会回调到 EVENT_DEACTIVATE_DONE,DataConnection.mDisconnectingState  对象的 processMessage 方法将响应此消息回调。

DisconnectingState#processMessage

@Override
public boolean processMessage(Message msg) {
   boolean retVal;
   switch (msg.what) {
       case EVENT_DEACTIVATE_DONE:
           ConnectionParams cp = (ConnectionParams) msg.obj;
           if (cp.mTag == mTag) {
               String str = "DcDisconnectionErrorCreatingConnection" +
                       " msg.what=EVENT_DEACTIVATE_DONE";
               if (DBG) log(str);
               if (cp.mApnContext != null) cp.mApnContext.requestLog(str);
               // Transition to inactive but send notifications after
               // we've entered the mInactive state.
               mInactiveState.setEnterNotificationParams(cp,
                       DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER);
               transitionTo(mInactiveState);
           } else {
               if (DBG) {
                   log("DcDisconnectionErrorCreatingConnection stale EVENT_DEACTIVATE_DONE"
                           + " dp.tag=" + cp.mTag + ", mTag=" + mTag);
               }
           }
           retVal = HANDLED;
           break;
       default:
           if (VDBG) {
               log("DcDisconnectionErrorCreatingConnection not handled msg.what="
                       + getWhatToString(msg.what));
           }
           retVal = NOT_HANDLED;
           break;
   }
   return retVal;
}

这样关闭移动数据业务流程也分析完毕了。


上一篇:手写promise


下一篇:es进行聚合操作时提示Fielddata is disabled on text fields by default