android Data call disable(移动网络--关闭数据业务部分)

just draft now, no pics or other comment for details。

source code is from https://android.googlesource.com/

branch is jb-mr2-release

file:ConnectivityManager.java

    public void setMobileDataEnabled(boolean enabled) {
        try {
            mService.setMobileDataEnabled(enabled);
        } catch (RemoteException e) {
        }
...
 private final IConnectivityManager mService;
 

here request will transfer to the system server by AIDL(Binder underline) from the mService rpxy into the remote ConnectivityService. their relatetionship simply by following

file:IConnectivitymanager.aidl

    void setMobileDataEnabled(boolean enabled);
file ConnectivityService.java

public class ConnectivityService extends IConnectivityManager.Stub {
...
file:SystemServer.java
                try {
                    Slog.i(TAG, "Connectivity Service");
                    connectivity = new ConnectivityService(
                            context, networkManagement, networkStats, networkPolicy);
                    ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
                    ...
                } catch (Throwable e) { 
                    reportWtf("starting Connectivity Service", e);
                }

after that the service module for the request is as below.
file:ConnectivityService.java

    public void setMobileDataEnabled(boolean enabled) {
        enforceChangePermission();
        mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_MOBILE_DATA,
                (enabled ? ENABLED : DISABLED), 0));
    }
    private class InternalHandler extends Handler {
...
        @Override
        public void handleMessage(Message msg) {
...
                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);
        }


file:MobileDataStateTracker.java

    public void setUserDataEnable(boolean enabled) {
        final AsyncChannel channel = mDataConnectionTrackerAc;
        if (channel != null) {
            channel.sendMessage(DctConstants.CMD_SET_USER_DATA_ENABLE,
                    enabled ? DctConstants.ENABLED : DctConstants.DISABLED);
            mUserDataEnabled = enabled;
        }

here message will be sent to DcTracker by AsyncChannel through mDataConnectionTrackerAc, which is connected by

    public void supplyMessenger(Messenger messenger) {
        if (VDBG) log(mApnType + " got supplyMessenger");
        AsyncChannel ac = new AsyncChannel();
        ac.connect(mContext, MobileDataStateTracker.this.mHandler, messenger);
    }
    static class MdstHandler extends Handler {
        private MobileDataStateTracker mMdst;
...
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
                        if (VDBG) {
                            mMdst.log("MdstHandler connected");
                        }
                        mMdst.mDataConnectionTrackerAc = (AsyncChannel) msg.obj;

file:DcTrackerBase.java

    @Override
    public void handleMessage(Message msg) {
...
        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;
        }

below is the function handle the message

    protected void onSetUserDataEnabled(boolean enabled) {
        synchronized (mDataEnabledLock) {
            final boolean prevEnabled = getAnyDataEnabled();
            if (mUserDataEnabled != enabled) {
                mUserDataEnabled = enabled;
                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);
                    }
                }
            }
        }
    }

there the field mUserDataEnabled decides the status of mobile data should be on or off, and your can find that when it changed, the value also be stored into settings database as the code above showed "Settings.Global.putInt(..."

there in the getAnyDataEnabled(), it will find if there should have data enabled, details ignore here first.

le's go ahead

as there we concentrate on disable flow, we'l go into onCleanUpAllConnections

    protected void onCleanUpAllConnections(String cause) {
        cleanUpAllConnections(true, cause);
    }
	
	private void cleanUpConnection(boolean tearDown, ApnContext apnContext) {
	
	    DcAsyncChannel dcac = apnContext.getDcAc();
		
		if (tearDown) {
...
                        Message msg = obtainMessage(DctConstants.EVENT_DISCONNECT_DONE, apnContext);
                        if (disconnectAll) {
                            apnContext.getDcAc().tearDownAll(apnContext.getReason(), msg);
                        } else {
                            apnContext.getDcAc()
                                .tearDown(apnContext, apnContext.getReason(), msg);
                        }
                        apnContext.setState(DctConstants.State.DISCONNECTING);

here we also igonre some details here, and both of the calling of tearnDownAll and teardown will be in to the same code, so we just have a look at tearDown.

be notice that the msg of DctConstants.EVENT_DISCONNECT_DONE, which is not used to send msg to the lower layer but for response, do not be confused when you see below part of code.(:-D

file:DcAsyncChannel.java

    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));
    }

look, here we really send msg with the preivious msg parameter in!

file:DataConnection.java

    private class DcActiveState extends State {
...
	           case EVENT_DISCONNECT: {
                    DisconnectParams dp = (DisconnectParams) msg.obj;
                    if (mApnContexts.contains(dp.mApnContext)) {

                        if (mApnContexts.size() == 1) {
                            mApnContexts.clear();
                            mDisconnectParams = dp;
                            mConnectionParams = null;
                            dp.mTag = mTag;
                            tearDownData(dp);
                            transitionTo(mDisconnectingState);

in fact the DataConnection class is a stateMachine, but also as it is, only when it is Active State, the teardown request will be really handled.

look, it tearDownData and transfer to the next state at once. but we will not go to the next state here!

    private void tearDownData(Object o) {
        int discReason = RILConstants.DEACTIVATE_REASON_NONE;

        if (mPhone.mCi.getRadioState().isOn()) {
            mPhone.mCi.deactivateDataCall(mCid, discReason,
                    obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, o));
        } else {
            AsyncResult ar = new AsyncResult(o, null, null);
            sendMessage(obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, ar));
        }

here it is! if mobile Radio is on, it will call deactivateDataCall(it is easy for you to find that now it passes an msg again "EVENT_DEACTIVATE_DONE"); or reply msg of EVENT_DEACTIVATE_DONE directly!

as the function mPhone.mCi.deactivateDataCall is so long, i prefer to tell you that it is RIL.java to avoid too much cost on finding where it comes from!

that may come out to be a long topi we cannot cover here right now! ^_-

file:RIL.java

    public void
    deactivateDataCall(int cid, int reason, Message result) {
        RILRequest rr
                = RILRequest.obtain(RIL_REQUEST_DEACTIVATE_DATA_CALL, result);

        rr.mParcel.writeInt(2);
        rr.mParcel.writeString(Integer.toString(cid));
        rr.mParcel.writeString(Integer.toString(reason));

        if (RILJ_LOGD) riljLog(rr.serialString() + "> " +
                requestToString(rr.mRequest) + " " + cid + " " + reason);

        send(rr);
    }

there is another Handler and sockets here in the RIL class. in short, i just tell you that send() will simply wraps the request into msg and leaving the left to the Handler,

Handler got the msg and then marsh the request into socket data, sending it to the RIL module, then IPC to modem...

do you think it is completed now?

no! from all above, did you find any return value which to indecate if the request finished OK or not?

we had to wait for the result now, how? there is no poll or read like block operations to waiting for response here, no callbacks too!

do you remember the passed in parameters of msges? some code that in tearDownData may give you some tips. we just rely on them! 8-D

here we go!

still in RIL.java

in fact the waiting for result code is in RILReceiver thread, which share the same socket with RILSender(the above code that send(rr) runs in).

    private void
    processResponse (Parcel p) {
        int type;

        type = p.readInt();

        if (type == RESPONSE_UNSOLICITED) {
            processUnsolicited (p);
        } else if (type == RESPONSE_SOLICITED) {
            processSolicited (p);
        }

        releaseWakeLockIfDone();
    }

as we are waiting for our request's response, the response will be RESPONSE_SOLICITED, the other typeRESPONSE_UNSOLICITED is for events.

    private void
    processSolicited (Parcel p) {
        RILRequest rr;

        rr = findAndRemoveRequestFromList(serial);
        if (error == 0 || p.dataAvail() > 0) {
            // either command succeeds or command fails but with data payload
            try {switch (rr.mRequest) {
            case RIL_REQUEST_DEACTIVATE_DATA_CALL: ret =  responseVoid(p); break;
        if (rr.mResult != null) {
            AsyncResult.forMessage(rr.mResult, ret, null);
            rr.mResult.sendToTarget();
        }

        rr.release();

here we left one part when sending request before, here you find findAndRemoveRequestFromList(), in fact when we sending, the request was stored in the requestList, so here we will pick it up now!

look, msg be sent at last!

this msg is just the one that we passed when sending our request! so our request is back.

here is backwards path now!

do you remember where we went when sending our request?

yes, after seding our request in DataConnection, we goto DcDisconnectingState  and be ready to recieve the EVENT_DEACTIVATE_DONE now!

file:DataConnection.java

    private class DcDisconnectingState extends State {
        @Override
        public boolean processMessage(Message msg) {
...
                case EVENT_DEACTIVATE_DONE:
                    AsyncResult ar = (AsyncResult) msg.obj;
                    DisconnectParams dp = (DisconnectParams) ar.userObj;
                    if (dp.mTag == mTag) {
                        mInactiveState.setEnterNotificationParams((DisconnectParams) ar.userObj);
                        transitionTo(mInactiveState);

enter inactive state!

        public void enter() {
...
            if (mConnectionParams != null) {
                notifyConnectCompleted(mConnectionParams, mDcFailCause, true);
            }
            if (mDisconnectParams != null) {
                notifyDisconnectCompleted(mDisconnectParams, true);
            }
            if (mDisconnectParams == null && mConnectionParams == null && mDcFailCause != null) {
                notifyAllDisconnectCompleted(mDcFailCause);
            }

            // Remove ourselves from cid mapping, before clearSettings
            mDcController.removeActiveDcByCid(DataConnection.this);

            clearSettings();
        }

infact i just ignore the function call setEnterNotificationParams(DisconnectParams dp) before enter the state, it will setmDisconnectParams while left the mConnectionParams NULL.
so there will go to notifyDisconnectCompleted

    private void notifyDisconnectCompleted(DisconnectParams dp, boolean sendAll) {
        if (dp != null && dp.mOnCompletedMsg != null) {
            // Get the completed message but only use it once
            Message msg = dp.mOnCompletedMsg;
            dp.mOnCompletedMsg = null;
            if (msg.obj instanceof ApnContext) {
                alreadySent = (ApnContext)msg.obj;
            }
            reason = dp.mReason;
            AsyncResult.forMessage(msg);
            msg.sendToTarget();
        }
        if (sendAll) {
            notifyAllWithEvent(alreadySent, DctConstants.EVENT_DISCONNECT_DONE, reason);

see, msg sent again!

do you remember our msg parameters from cleanUpConnection to DearDown in DcTrackerBase class?

so we back to DcTrackerBase now

file:DcTrackerBase.java

            case DctConstants.EVENT_DISCONNECT_DONE:
                log("DataConnectionTracker.handleMessage: EVENT_DISCONNECT_DONE msg=" + msg);
                onDisconnectDone(msg.arg1, (AsyncResult) msg.obj);
                break;

file:DcTracker.java

    protected void onDisconnectDone(int connId, AsyncResult ar) {
	        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());

        // if all data connection are gone, check whether Airplane mode request was
        // pending.
        if (isDisconnected()) {
            if (mPhone.getServiceStateTracker().processPendingRadioPowerOffAfterDataOff()) {
                // Radio will be turned off. No need to retry data setup
                apnContext.setApnSetting(null);
                apnContext.setDataConnectionAc(null);
                return;
            }
        }
        if (apnContext.isReady() && retryAfterDisconnected(apnContext.getReason())) {
            SystemProperties.set(PUPPET_MASTER_RADIO_STRESS_TEST, "false");
            startAlarmForReconnect(APN_DELAY_MILLIS, apnContext);
        }

simply from the file name, you should know that DcTracker inherits the abstract class DcTrackerBase.

1.the function first notify the changes to phone. and then

2.infact, for the first "if" case, the function will power off radio if there is pending radioOff request, here we just ignore the part to make it simple.

and in our flow, there is no pending reuqest of power off radio.

3.at last, try to see if it is needed to retry to connect the data call(of course here the conditions is not met so nothing will be done).

here we follow the step1(step3 will be another topic[:-(] )

file:PhoneBase.java

    public void notifyDataConnection(String reason, String apnType) {
        mNotifier.notifyDataConnection(this, reason, apnType, getDataConnectionState(apnType));
    }

tell you that the PhoneNotifier here is DefaultPhoneNotifier, so

file:DefaultPhoneNotifier.java

    public void notifyDataConnection(Phone sender, String reason, String apnType,
            PhoneConstants.DataState state) {
        doNotifyDataConnection(sender, reason, apnType, state);
    }
    private void doNotifyDataConnection(Phone sender, String reason, String apnType,
            PhoneConstants.DataState state) {
        try {
            mRegistry.notifyDataConnection(
                    convertDataState(state),
                    sender.isDataConnectivityPossible(apnType), reason,
                    sender.getActiveApnHost(apnType),
                    apnType,
                    linkProperties,
                    linkCapabilities,
                    ((telephony!=null) ? telephony.getNetworkType() :
                    TelephonyManager.NETWORK_TYPE_UNKNOWN),
                    roaming);
        } catch (RemoteException ex) {
            // system process is dead
        }

and the mRegistry is TelephonyRegistry

file:TelephonyRegistry.java

    public void notifyDataConnection(int state, boolean isDataConnectivityPossible,
            String reason, String apn, String apnType, LinkProperties linkProperties,
            LinkCapabilities linkCapabilities, int networkType, boolean roaming) {
        broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
                apnType, linkProperties, linkCapabilities, roaming);
				
    private void broadcastDataConnectionStateChanged(int state,
            boolean isDataConnectivityPossible,
            String reason, String apn, String apnType, LinkProperties linkProperties,
            LinkCapabilities linkCapabilities, boolean roaming) {
		Intent intent = new Intent(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
		mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);

here it sends out an broadcast to notify all over the system about the status change.

and our trip is already done now.

file:MobileDataStateTracker.java

    private class MobileDataStateReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(TelephonyIntents.
                    ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
...
                if (mMobileDataState != state) {
                    mMobileDataState = state;
                    switch (state) {
                        case DISCONNECTED:
                            if(isTeardownRequested()) {
                                setTeardownRequested(false);
                            }

                            setDetailedState(DetailedState.DISCONNECTED, reason, apnName);
    private void setDetailedState(NetworkInfo.DetailedState state, String reason,
            String extraInfo) {
        if (state != mNetworkInfo.getDetailedState()) {
            boolean wasConnecting = (mNetworkInfo.getState() == NetworkInfo.State.CONNECTING);
            String lastReason = mNetworkInfo.getReason();
            if (wasConnecting && state == NetworkInfo.DetailedState.CONNECTED && reason == null
                    && lastReason != null)
                reason = lastReason;
            mNetworkInfo.setDetailedState(state, reason, extraInfo);
            Message msg = mTarget.obtainMessage(EVENT_STATE_CHANGED, new NetworkInfo(mNetworkInfo));
            msg.sendToTarget();
        }

file:ConnectivityService.java

    private class NetworkStateTrackerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            NetworkInfo info;
            switch (msg.what) {
                case NetworkStateTracker.EVENT_STATE_CHANGED:
                    } else if (state == NetworkInfo.State.DISCONNECTED) {
                        handleDisconnect(info);
    private void handleDisconnect(NetworkInfo info) {
...
        Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
...
        final Intent immediateIntent = new Intent(intent);
        immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE);
        sendStickyBroadcast(immediateIntent);
        sendStickyBroadcastDelayed(intent, getConnectivityChangeDelay());

------------------------------------------------------

roughly done!

for the mobile data disable flow, here from APP back to APP now!

just trace the code, any part wrong, help your self to correct me!

Needs:flow chart, class diagram


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值