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

本文详细解析了Android系统中禁用移动数据的功能实现过程,从应用层到内核层,涉及AIDL通信、Binder机制及RIL交互等关键技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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


<think>好的,我现在要帮用户解决如何使用ADB命令禁用三星Tab A的移动数据功能。首先,我得回想一下ADB相关的知识,特别是关于网络控制的命令。 用户提供的引用资料里提到了几个基本命令,比如adb devices、adb shell等,但没有直接提到关闭移动数据的方法。根据之前的知识,我知道Android系统中移动数据通常由系统服务管理,可能需要通过adb shell发送服务命令或者修改系统设置。 首先,可能需要使用adb shell进入设备。然后,检查是否有root权限,因为部分系统设置需要root才能修改,引用[2]也提到这点。所以需要确认用户是否已经root了设备,或者三星Tab A是否允许通过adb修改这些设置。 接下来,Android系统中移动数据的开关可能存储在全局设置中。常用的方法是使用settings命令,比如`settings put global mobile_data 0`。但不同设备可能有所不同,特别是三星可能使用了定制化的设置项。另外,有些设备可能需要使用service call命令来调用系统服务的方法。 例如,关闭移动数据可能的命令是: `svc data disable` 不过这个命令可能需要root权限,或者特定的系统权限。如果用户没有root,可能需要通过其他方式,比如修改全局设置。 另外,查看当前移动数据状态可以用`settings get global mobile_data`,但不确定是否适用三星设备。如果直接使用settings命令无效,可能需要查找三星特定的设置项。可能需要查找三星移动数据的设置键名,比如`mobile_data`或者类似的自定义键。 还需要考虑Android版本的影响,不同版本的API可能有所变化。用户的三星Tab A可能运行的是较新的Android版本,因此需要确认命令的兼容性。 此外,用户可能需要先连接设备,确保adb可以识别设备,使用`adb devices`列出设备序列号,然后使用`adb -s serialNumber shell`进入指定设备的shell环境,如引用[1]所述。 总结可能的步骤: 1. 连接设备,确认adb连接正常。 2. 进入adb shell,尝试使用settings或svc命令关闭移动数据。 3. 如果权限不足,检查设备是否已root,或者是否有其他方法获取必要权限。 4. 可能需要查找三星特定的设置项或服务调用方式。 还需要注意潜在风险,错误修改系统设置可能导致网络功能异常,建议用户谨慎操作,并备份数据。如果常规方法无效,可能需要深入查找三星的文档或开发者资源,但公开资料可能有限。</think>要使用ADB命令禁用三星Tab A的移动数据功能,需通过系统服务或全局设置操作。以下是分步说明: ### 方法1:通过系统服务命令 1. **连接设备**:确保设备已开启开发者模式并连接电脑 ```bash adb devices # 确认设备序列号可见[^1] adb -s [设备序列号] shell ``` 2. **尝试关闭移动数据**(需root权限): ```bash svc data disable # 直接禁用移动数据[^2] ``` 若提示权限不足,需先获取root权限: ```bash su svc data disable ``` ### 方法2:通过全局设置修改 若设备未root,可尝试修改系统设置项: ```bash adb shell settings put global mobile_data 0 # 部分设备可能生效 ``` ### 注意事项 - 三星设备可能存在定制化限制,上述命令可能不兼容所有型号 - 修改后重启设备可能导致设置恢复 - 若命令无效,需结合三星开发文档或内核日志分析(如`adb pull data/log/kernel.log`[^3])
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值