【Android架构Telephony篇】之数据业务(二)

来源:互联网 发布:药品网络经营许可证 编辑:程序博客网 时间:2024/06/05 05:21
Android: 4.4.4Desktop: Ubuntu 15.04

这篇梳理Telephony数据业务的JAVA方法走向。

http://write.blog.csdn.net/postedit/49719897

更新日期:2016-12-12


涉及到的文件见:

【Android架构Telephony篇】之数据业务(一)

三、数据业务的Framework层

点击流量开启/关闭按钮执行的相关代码如下:

\packages\apps\settings\src\com\android\settings\DataUsageSummary.javapublic class DataUsageSummary extends Fragment {private static final String TAG = "DataUsage";    private static final boolean LOGD = true;private Switch mDataEnabled;private View mDataEnabledView;private ConnectivityManager mConnService;mDataEnabled = new Switch(inflater.getContext());    mDataEnabledView = inflatePreference(inflater, mNetworkSwitches, mDataEnabled);    mDataEnabled.setOnCheckedChangeListener(mDataEnabledListener);/**     * Update body content based on current tab. Loads     * {@link NetworkStatsHistory} and {@link NetworkPolicy} from system, and     * binds them to visible controls.     */    private void updateBody() {        final String currentTab = mTabHost.getCurrentTabTag();        final boolean isOwner = ActivityManager.getCurrentUser() == UserHandle.USER_OWNER;        if (currentTab == null) {            Log.w(TAG, "no tab selected; hiding body");            mListView.setVisibility(View.GONE);            return;        } else {            mListView.setVisibility(View.VISIBLE);        }        final boolean tabChanged = !currentTab.equals(mCurrentTab);        mCurrentTab = currentTab;        if (LOGD) Log.d(TAG, "updateBody() with currentTab=" + currentTab);        mDataEnabledView.setVisibility(isOwner ? View.VISIBLE : View.GONE);        // TODO: remove mobile tabs when SIM isn't ready        final TelephonyManager tele = TelephonyManager.from(context);        if (TAB_MOBILE.equals(currentTab)) {// 确保选中的是MOBILE/* R.string.data_usage_enable_mobile: 移动数据 */            setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_mobile);            setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_mobile_limit);            mTemplate = buildTemplateMobileAll(getActiveSubscriberId(context));        }    }private void setMobileDataEnabled(boolean enabled) {        if (LOGD) Log.d(TAG, "setMobileDataEnabled()");        mConnService.setMobileDataEnabled(enabled);        mMobileDataEnabled = enabled;        updatePolicy(false);    }private OnCheckedChangeListener mDataEnabledListener = new OnCheckedChangeListener() {        @Override        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {            if (mBinding) return;            final boolean dataEnabled = isChecked;            final String currentTab = mCurrentTab;            if (TAB_MOBILE.equals(currentTab)) {                if (dataEnabled) {                    setMobileDataEnabled(true);                } else {                    // disabling data; show confirmation dialog which eventually                    // calls setMobileDataEnabled() once user confirms.                    ConfirmDataDisableFragment.show(DataUsageSummary.this);                }            }            updatePolicy(false);        }    };}
一旦选中数据流量选项卡,updateBody()方法得到执行,【开启/关闭】的按钮等就呈现在界面上。
OnCheckedChangeListener()监听器在点击【开启/关闭】按钮使按钮状态发生改变后调用,相应的就打开或关闭数据业务。
起作用的方法是setMobileDataEnabled(true)并最终调用了ConnectivityManager类的setMobileDataEnabled()方法。

ConnectivityManager类作用在注释里说的很清楚,我们专注于方法调用流程:

\frameworks\base\core\java\android\net\ConnectivityManager.java/** * Class that answers queries about the state of network connectivity. It also * notifies applications when network connectivity changes. Get an instance * of this class by calling * {@link android.content.Context#getSystemService(String) Context.getSystemService(Context.CONNECTIVITY_SERVICE)}. * <p> * The primary responsibilities of this class are to: * <ol> * <li>Monitor network connections (Wi-Fi, GPRS, UMTS, etc.)</li> * <li>Send broadcast intents when network connectivity changes</li> * <li>Attempt to "fail over" to another network when connectivity to a network * is lost</li> * <li>Provide an API that allows applications to query the coarse-grained or fine-grained * state of the available networks</li> * </ol> */public class ConnectivityManager {    private static final String TAG = "ConnectivityManager";private final IConnectivityManager mService;/* return Whether mobile data is enabled. */    public boolean getMobileDataEnabled() {        try {            return mService.getMobileDataEnabled();        } catch (RemoteException e) {            return true;        }    }    /* param enabled Whether the user wants the mobile data connection used */        public void setMobileDataEnabled(boolean enabled) {        try {            mService.setMobileDataEnabled(enabled);        } catch (RemoteException e) {        }    }}
这里,ConnectivityManager类又调到了IConnectivityManager的setMobileDataEnabled()方法,但是IConnectivityManager是一个接口,用于进程间通信(IPC)、远程函数调用(RPC):


\frameworks\base\core\java\android\net\IConnectivityManager.aidl/** * 类或API是否开放,通过doc的注释{@hide}来控制的 * 比如,android.media.Metadata这个类定义Metadata类之前有/**{@hide}*/就表明android没有公开该类,所以Metadata类被定义为了非公开类——在android应用程序中无法直接访问的类。 *//** {@hide} */interface IConnectivityManager{    boolean getMobileDataEnabled();    void setMobileDataEnabled(boolean enabled);}

经验: 通常来说,XxxManager用来管理服务,而服务的具体实现在XxxService中

实现该接口setMobileDataEnabled()方法的类是ConnectivityService

\frameworks\base\services\java\com\android\server\ConnectivityService.javapublic class ConnectivityService extends IConnectivityManager.Stub {    private static final String TAG = "ConnectivityService";public static final int MAX_NETWORK_TYPE = 14;/** Handler used for internal events. */    private InternalHandler mHandler;/**     * Sometimes we want to refer to the individual network state     * trackers separately, and sometimes we just want to treat them     * abstractly.     */    private NetworkStateTracker mNetTrackers[];mNetTrackers = new NetworkStateTracker[                ConnectivityManager.MAX_NETWORK_TYPE+1];/**     * @see ConnectivityManager#setMobileDataEnabled(boolean)     */    public void setMobileDataEnabled(boolean enabled) {        enforceChangePermission();        if (DBG) log("setMobileDataEnabled(" + enabled + ")");        mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_MOBILE_DATA,                (enabled ? ENABLED : DISABLED), 0));    }    private class InternalHandler extends Handler {        public InternalHandler(Looper looper) {            super(looper);        }        @Override        public void handleMessage(Message msg) {            NetworkInfo info;            switch (msg.what) {                /* .... */                case EVENT_SET_MOBILE_DATA: {                    boolean enabled = (msg.arg1 == ENABLED);                    handleSetMobileData(enabled);                    break;                }             }        }    }private void handleSetMobileData(boolean enabled) {        if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) {                 mNetTrackers[ConnectivityManager.TYPE_MOBILE].setUserDataEnable(enabled);        }        if (mNetTrackers[ConnectivityManager.TYPE_WIMAX] != null) {               mNetTrackers[ConnectivityManager.TYPE_WIMAX].setUserDataEnable(enabled);        }    }}
该类通过扩展Handler,实现信息的发送和处理,于是现在绕到了NetworkStateTracker类的setUserDataEnable()方法。

在这里,代码定义了一个NetworkStateTracker类型的数组:

mNetTrackers = new NetworkStateTracker[                ConnectivityManager.MAX_NETWORK_TYPE+1];
可知NetworkStateTracker除了Mobile,还要服务于Wi-Fi、4G等网络。

但是,NetworkStateTracker又是一个接口:

\frameworks\base\core\java\android\net\NetworkStateTracker.java/** * Interface provides the {@link com.android.server.ConnectivityService} * with three services. Events to the ConnectivityService when * changes occur, an API for controlling the network and storage * for network specific information. */public interface NetworkStateTracker {/**     * User control of data connection through this network, typically persisted     * internally.     */    public void setUserDataEnable(boolean enabled);}
BaseNetworkStateTracker类以抽象类的形式implements该NetworkStateTracker接口类,为的是屏蔽具体网络的细节—这不就是面向对象存在的一大目嘛。

来看BaseNetworkStateTracker

\frameworks\base\core\java\android\net\BaseNetworkStateTracker.java/** * Interface to control and observe state of a specific network, hiding * network-specific details from {@link ConnectivityManager}. Surfaces events * through the registered {@link Handler} to enable {@link ConnectivityManager} * to respond to state changes over time. */public abstract class BaseNetworkStateTracker implements NetworkStateTracker {@Override    public void setUserDataEnable(boolean enabled) {        // Base tracker doesn't handle enabled flags    }}


setUserDataEnable()方法的最终实现实在MobileDataStateTracker类中(继承,用自己的方式实现父类的方法).但是,它也仅仅采用异步的方式,把改变通知其他线程而已:

\frameworks\base\core\java\android\net\MobileDataStateTracker.java/** * Track the state of mobile data connectivity. This is done by * receiving broadcast intents from the Phone process whenever * the state of data connectivity changes. */public class MobileDataStateTracker extends BaseNetworkStateTracker {private AsyncChannel mDataConnectionTrackerAc;    @Override    public void setUserDataEnable(boolean enabled) {        if (DBG) log("setUserDataEnable: E enabled=" + enabled);        final AsyncChannel channel = mDataConnectionTrackerAc;        if (channel != null) {            channel.sendMessage(DctConstants.CMD_SET_USER_DATA_ENABLE,                    enabled ? DctConstants.ENABLED : DctConstants.DISABLED);            mUserDataEnabled = enabled;        }        if (VDBG) log("setUserDataEnable: X enabled=" + enabled);    }}


channel.sendMessage(DctConstants.CMD_SET_USER_DATA_ENABLE,enabled ? DctConstants.ENABLED : DctConstants.DISABLED)通知走向哪里了呢?继续看:

\frameworks\base\telephony\java\com\android\internal\telephony\DctConstants.java (h:public class DctConstants {/***** Event Codes *****/    public static final int BASE = Protocol.BASE_DATA_CONNECTION_TRACKER;public static final int CMD_SET_USER_DATA_ENABLE = BASE + 30;}\frameworks\opt\telephony\src\java\com\android\internal\telephony\dataconnection\DcTrackerBase.javapublic abstract class DcTrackerBase extends Handler {@Override    public void handleMessage(Message msg) {        switch (msg.what) {    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;            }}}protected void onSetUserDataEnabled(boolean enabled) {        synchronized (mDataEnabledLock) {            final boolean prevEnabled = getAnyDataEnabled();            if (mUserDataEnabled != enabled) {                mUserDataEnabled = enabled;                /* 把状态更新到本地数据库中                 * 因为有的地方是通过读取数据库中MOBILE_DATA属性来确定移动网络是否开启                 */                Settings.Global.putInt(mPhone.getContext().getContentResolver(),                        Settings.Global.MOBILE_DATA, enabled ? 1 : 0);                if (getDataOnRoamingEnabled() == false &&                        mPhone.getServiceState().getRoaming() == true) {                    if (enabled) {                        notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON);                    } else {                        notifyOffApnsOfAvailability(Phone.REASON_DATA_DISABLED);                    }                }                if (prevEnabled != getAnyDataEnabled()) {                    if (!prevEnabled) {                        onTrySetupData(Phone.REASON_DATA_ENABLED);                    } else {                        onCleanUpAllConnections(Phone.REASON_DATA_DISABLED);                    }                }            }        }    }    // abstract methods    protected abstract boolean onTrySetupData(String reason);}
前面九曲八弯的,在源码分析时遇到Interface总是费点周折,,,这里DcTrackerBase才是干实事的,但是在onSetUserDataEnabled()也只干了一部分:

1、如果数据业务的状态(启动/关闭)发生了变化(mUserDataEnabled != enabled),更新mUserDataEnabled状态;

2、把新状态写到Settings里;

3、把状态改变的消息通知其他所有相关工作人员;

4、继续往下走onTrySetupData();,直到完成硬件操作。

但是onTrySetupData()是抽象方法,由不得再去周折去搜寻了:


\frameworks\opt\telephony\src\java\com\android\internal\telephony\dataconnection\DcTracker.javapublic final class DcTracker extends DcTrackerBase {// \frameworks\opt\telephony\src\java\com\android\internal\telephony\dataconnection\ApnContext.javapublic boolean isConnectable() {        return isReady() && ((mState == DctConstants.State.IDLE)                                || (mState == DctConstants.State.SCANNING)                                || (mState == DctConstants.State.RETRYING)                                || (mState == DctConstants.State.FAILED));    }private boolean isDataAllowed(ApnContext apnContext) {        return apnContext.isReady() && isDataAllowed();    }/**     * Report on whether data connectivity is enabled for any APN.     * @return {@code false} if data connectivity has been explicitly disabled,     * {@code true} otherwise.     */    @Override    public boolean getAnyDataEnabled() {        synchronized (mDataEnabledLock) {            if (!(mInternalDataEnabled && mUserDataEnabled && sPolicyDataEnabled)) return false;            for (ApnContext apnContext : mApnContexts.values()) {                // Make sure we don't have a context that is going down                // and is explicitly disabled.                if (isDataAllowed(apnContext)) {                    return true;                }            }            return false;        }    }/* DcTrackerBase类的方法,这里放在这里 */protected boolean isEmergency() {        final boolean result;        synchronized (mDataEnabledLock) {            result = mPhone.isInEcm() || mPhone.isInEmergencyCall();        }        log("isEmergency: result=" + result);        return result;    }    @Override    // TODO: We shouldnt need this.    protected boolean onTrySetupData(String reason) {        if (DBG) log("onTrySetupData: reason=" + reason);        setupDataOnConnectableApns(reason);        return true;    }    protected boolean onTrySetupData(ApnContext apnContext) {        if (DBG) log("onTrySetupData: apnContext=" + apnContext);        return trySetupData(apnContext);    }private boolean trySetupData(ApnContext apnContext) {        if (apnContext.isConnectable() &&                isDataAllowed(apnContext) && getAnyDataEnabled() && !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));                    }                }            }/* 建立连接 */            boolean retValue = setupData(apnContext, radioTech);            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;        }    }}
数据业务建条件立前是需要检查是否满足需要条件的,这里进行了四个有效性检查:
1、isConnectable():判断当前APN是否已经被激活;
2、isDataAllowed():判断是否已经ATTACH成功,SIM是否初始化完毕,当前手机服务是否支持,漫游下是否允许上网等;
3、getAnyDataEnabled():判断数据允许的开关是否打开;
4、isEmergency():当前APN是否为紧急APN。

以上四个条件同时满足以后,就调用setupData()进行数据连接:


public final class DcTracker extends DcTrackerBase {private boolean setupData(ApnContext apnContext, int radioTech) {        ApnSetting apnSetting;        DcAsyncChannel dcac;                Message msg = obtainMessage();        msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;        msg.obj = apnContext;        dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, radioTech, msg);        if (DBG) log("setupData: initing!");        return true;    }}
但是这里并没有实行具体的连接动作,而是调用DcAsyncChannel类的bringUp()方法:


\frameworks\opt\telephony\src\java\com\android\internal\telephony\dataconnection\DcAsyncChannel.java (h:public class DcAsyncChannel extends AsyncChannel {/**     * 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, Message onCompletedMsg) {/* \frameworks\opt\telephony\src\java\com\android\internal\telephony\dataconnection\DataConnection.java的static内部类 * Used internally for saving connecting parameters * static class ConnectionParams {}         */          sendMessage(DataConnection.EVENT_CONNECT,                    new ConnectionParams(apnContext, initialMaxRetry, profileId,                            rilRadioTechnology, onCompletedMsg));    }}


消息发往状态机类DataConnection类处理:

/**  * DataConnection StateMachine.  */  \frameworks\opt\telephony\src\java\com\android\internal\telephony\dataconnection\DataConnection.java  public final class DataConnection extends StateMachine {  /* 之前提到过,它封装了之前提到的参数 */  static class ConnectionParams {  }    /**  * Begin setting up a data connection, calls setupDataCall  */  private void onConnect(ConnectionParams cp) {/** onConnect: carrier='China Unicom 3G' APN='3gnet' proxy='null' port='80' */log("onConnect: carrier='" + mApnSetting.carrier+ "' APN='" + mApnSetting.apn+ "' proxy='" + mApnSetting.proxy + "' port='" + mApnSetting.port + "'");mPhone.mCi.setupDataCall(  Integer.toString(cp.mRilRat + 2),  Integer.toString(cp.mProfileId),  mApnSetting.apn, mApnSetting.user, mApnSetting.password,  Integer.toString(authType),  protocol, msg);  }    /**  * Initialize connection, this will fail if the  * apnSettings are not compatible.  *  * @param cp the Connection paramemters  * @return true if initialization was successful.  */  private boolean initConnection(ConnectionParams cp) {  return true;  }    /**  * The state machine is inactive and expects a EVENT_CONNECT.  */  private class DcInactiveState extends State {  @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);  transitionTo(mActivatingState);  }  retVal = HANDLED;  break;  }  }  }  }  

DataConnection类,数据连接建立的请求就快到了最后一站:

1、通过initConnection()对连接进行必要的初始化
2、在onConnect()中调用CommandsInterface的setupDataCall()方法按照传入的参数进行工作:
又得但是,CommandsInterface还是一个接口,提供跟HAL层Ril交互的发送命令和接受命令,注册状态传递回调接口,向上层传递状态变化:


\frameworks\opt\telephony\src\java\com\android\internal\telephony\CommandsInterface.javapublic interface CommandsInterface {/**     * Setup a packet data connection On successful completion, the result     * message will return a {@link com.android.internal.telephony.dataconnection.DataCallResponse}     * object containing the connection information. */    public void setupDataCall(String radioTechnology, String profile,            String apn, String user, String password, String authType,            String protocol, Message result);}
setupDataCall()方法的具体实现在RIL.java文件中:
\frameworks\opt\telephony\src\java\com\android\internal\telephony\RIL.java/** * RIL implementation of the CommandsInterface. */public final class RIL extends BaseCommands implements CommandsInterface {RILSender mSender;class RILSender extends Handler implements Runnable {handleMessage(Message msg) {RILRequest rr = (RILRequest)(msg.obj);RILRequest req = null;switch (msg.what) {case EVENT_SEND:// 与rild通信socketLocalSocket s;s = mSocket;          // 加入请求队列synchronized (mRequestsList) {mRequestsList.add(rr);mRequestMessagesWaiting++;}byte[] data;data = rr.mp.marshall();// 将将数据通过socket传入到rild进程中s.getOutputStream().write(dataLength);s.getOutputStream().write(data);break;}}}class RILReceiver implements Runnable {}@Override    public void    setupDataCall(String radioTechnology, String profile, String apn,            String user, String password, String authType, String protocol,            Message result) { // 获取一个RILRequest对象        RILRequest rr = RILRequest.obtain(RIL_REQUEST_SETUP_DATA_CALL, result);        rr.mParcel.writeInt(7);// 将数据打包成parcel        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线程中处理        send(rr);    }private void    send(RILRequest rr) {        Message msg;        msg = mSender.obtainMessage(EVENT_SEND, rr);        acquireWakeLock();        msg.sendToTarget();    }}
RIL类中,有两个内部类:负责数据发送的RILSender和负责数据接收的RILReceiver,意图很明显。

Telephony中,Framework层和HAL层通信采用的是socket机制:

out/target/product/KoolRegister/root/init.rcservice ril-daemon /system/bin/rild -l /system/lib/libreference-ril.so -- -d /dev/mux2    socket rild stream 660 root radio    socket rild-debug stream 660 radio system

九曲十八弯,拐来拐去最终,数据在RILSender的线程中通过socket传递给rild进程处理。

未完,见:

【Android架构Telephony篇】之数据业务(三)

0 0
原创粉丝点击