The setMobileDataEnabled method is no longer callable as of Android L and later

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26539445/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 11:45:29  来源:igfitidea点击:

The setMobileDataEnabled method is no longer callable as of Android L and later

androidreflectionandroid-5.0-lollipop

提问by ChuongPham

I have logged Issue 78084with Google regarding the setMobileDataEnabled()method being no longer callable via reflection. It was callable since Android 2.1 (API 7) to Android 4.4 (API 19) via reflection, but as of Android L and later, even with root, the setMobileDataEnabled()method is not callable.

I have logged Issue 78084with Google regarding the setMobileDataEnabled()method being no longer callable via reflection. It was callable since Android 2.1 (API 7) to Android 4.4 (API 19) via reflection, but as of Android L and later, even with root, the setMobileDataEnabled()method is not callable.

The official response is that the issue is "Closed" and the status set to "WorkingAsIntended". Google's simple explanation is:

The official response is that the issue is "Closed" and the status set to "WorkingAsIntended". Google's simple explanation is:

Private APIs are private because they are not stable and might disappear without notice.

Private APIs are private because they are not stable and might disappear without notice.

Yes, Google, we are aware of the risk of using reflection to call hidden method- even before Android came on the scene- but you need to provide a more solid answer as to alternatives, if any, for accomplishing the same result as setMobileDataEnabled(). (If you are displeased with Google's decision as I am, then log into Issue 78084and star it as many as possible to let Google know the error of their way.)

Yes, Google, we are aware of the risk of using reflection to call hidden method- even before Android came on the scene- but you need to provide a more solid answer as to alternatives, if any, for accomplishing the same result as setMobileDataEnabled(). (If you are displeased with Google's decision as I am, then log into Issue 78084and star it as many as possible to let Google know the error of their way.)

So, my question to you is: Are we at a dead end when it comes to programmatically enable or disable mobile network function on an Android device? This heavy-handed approach from Google somehow does not sit well with me. If you have workaround for Android 5.0 (Lollipop) and beyond, I would love to hear your answer/discussion in this thread.

So, my question to you is: Are we at a dead end when it comes to programmatically enable or disable mobile network function on an Android device? This heavy-handed approach from Google somehow does not sit well with me. If you have workaround for Android 5.0 (Lollipop) and beyond, I would love to hear your answer/discussion in this thread.

I have used the code below to see if the setMobileDataEnabled()method is available:

I have used the code below to see if the setMobileDataEnabled()method is available:

final Class<?> conmanClass = Class.forName(context.getSystemService(Context.CONNECTIVITY_SERVICE).getClass().getName());
final Field iConnectivityManagerField = conmanClass.getDeclaredField("mService");
iConnectivityManagerField.setAccessible(true);
final Object iConnectivityManager = iConnectivityManagerField.get(context.getSystemService(Context.CONNECTIVITY_SERVICE));
final Class<?> iConnectivityManagerClass = Class.forName(iConnectivityManager.getClass().getName());
final Method[] methods = iConnectivityManagerClass.getDeclaredMethods();
for (final Method method : methods) {
    if (method.toGenericString().contains("set")) {
        Log.i("TESTING", "Method: " + method.getName());
    }
}

But it's not.

But it's not.

UPDATE: Currently, it's possible to toggle mobile network if the device is rooted. However, for non-rooted devices, it's still an investigative process as there is no universal method to toggle mobile network.

UPDATE: Currently, it's possible to toggle mobile network if the device is rooted. However, for non-rooted devices, it's still an investigative process as there is no universal method to toggle mobile network.

回答by ChuongPham

To extend Muzikant's Solution #2, can someone please try the solution below on an Android 5.0 rooted device (as I currently do not possess one) and let me know if it works or does not work.

To extend Muzikant's Solution #2, can someone please try the solution below on an Android 5.0 rooted device (as I currently do not possess one) and let me know if it works or does not work.

To enable or disable mobile data, try:

// 1: Enable; 0: Disable
su -c settings put global mobile_data 1
su -c am broadcast -a android.intent.action.ANY_DATA_STATE --ez state 1

Note: The mobile_datavariable can be found in Android API 21 source codes at /android-sdk/sources/android-21/android/provider/Settings.javaand is declared as:

/**
 * Whether mobile data connections are allowed by the user.  See
 * ConnectivityManager for more info.
 * @hide
*/
public static final String MOBILE_DATA = "mobile_data";

While the android.intent.action.ANY_DATA_STATEIntent can be found in Android API 21 source codes at /android-sdk/sources/android-21/com/android/internal/telephony/TelephonyIntents.javaand is declared as:

/**
 * Broadcast Action: The data connection state has changed for any one of the
 * phone's mobile data connections (eg, default, MMS or GPS specific connection).
 *
 * <p class="note">
 * Requires the READ_PHONE_STATE permission.
 * <p class="note">This is a protected intent that can only be sent by the system.
 *
 */
public static final String ACTION_ANY_DATA_CONNECTION_STATE_CHANGED
        = "android.intent.action.ANY_DATA_STATE";

UPDATE 1: If you don't want to implement the above Java codes in your Android application, then you can run the sucommands via a shell (Linux) or command prompt (Windows) as follow:

adb shell "su -c 'settings put global mobile_data 1; am broadcast -a android.intent.action.ANY_DATA_STATE --ez state 1'"

Note: adbis located at /android-sdk/platform-tools/directory. The settingscommand is only supported on Android 4.2 or later. Older Android version will report a "sh: settings: not found"error.

UPDATE 2: Another way to toggle mobile network on a rootedAndroid 5+ device would be to use the undocumented serviceshell command. The following command can be executed via ADB to toggle mobile network:

// 1: Enable; 0: Disable
adb shell "su -c 'service call phone 83 i32 1'"

Or just:

// 1: Enable; 0: Disable
adb shell service call phone 83 i32 1

Note 1: The transaction code 83used in the service call phonecommand might change between Android versions. Please check com.android.internal.telephony.ITelephonyfor the value of the TRANSACTION_setDataEnabledfield for your version of Android. Also, instead of hardcoding 83, you would be better off using Reflection to get the value of the TRANSACTION_setDataEnabledfield. This way, it will work across all mobile brands running Android 5+ (If you don't know how to use Reflection to get the value of the TRANSACTION_setDataEnabledfield, see solution from PhongLe below- save me from duplicating it here.) Important: Please note that transaction code TRANSACTION_setDataEnabledhas only been introduced in Android 5.0 and later versions. Running this transaction code on earlier versions of Android will do nothing as the transaction code TRANSACTION_setDataEnableddoes not exist.

To enable or disable mobile data, try:

// 1: Enable; 0: Disable
su -c settings put global mobile_data 1
su -c am broadcast -a android.intent.action.ANY_DATA_STATE --ez state 1

Note: The mobile_datavariable can be found in Android API 21 source codes at /android-sdk/sources/android-21/android/provider/Settings.javaand is declared as:

/**
 * Whether mobile data connections are allowed by the user.  See
 * ConnectivityManager for more info.
 * @hide
*/
public static final String MOBILE_DATA = "mobile_data";

While the android.intent.action.ANY_DATA_STATEIntent can be found in Android API 21 source codes at /android-sdk/sources/android-21/com/android/internal/telephony/TelephonyIntents.javaand is declared as:

/**
 * Broadcast Action: The data connection state has changed for any one of the
 * phone's mobile data connections (eg, default, MMS or GPS specific connection).
 *
 * <p class="note">
 * Requires the READ_PHONE_STATE permission.
 * <p class="note">This is a protected intent that can only be sent by the system.
 *
 */
public static final String ACTION_ANY_DATA_CONNECTION_STATE_CHANGED
        = "android.intent.action.ANY_DATA_STATE";

UPDATE 1: If you don't want to implement the above Java codes in your Android application, then you can run the sucommands via a shell (Linux) or command prompt (Windows) as follow:

adb shell "su -c 'settings put global mobile_data 1; am broadcast -a android.intent.action.ANY_DATA_STATE --ez state 1'"

Note: adbis located at /android-sdk/platform-tools/directory. The settingscommand is only supported on Android 4.2 or later. Older Android version will report a "sh: settings: not found"error.

UPDATE 2: Another way to toggle mobile network on a rootedAndroid 5+ device would be to use the undocumented serviceshell command. The following command can be executed via ADB to toggle mobile network:

// 1: Enable; 0: Disable
adb shell "su -c 'service call phone 83 i32 1'"

Or just:

// 1: Enable; 0: Disable
adb shell service call phone 83 i32 1

Note 1: The transaction code 83used in the service call phonecommand might change between Android versions. Please check com.android.internal.telephony.ITelephonyfor the value of the TRANSACTION_setDataEnabledfield for your version of Android. Also, instead of hardcoding 83, you would be better off using Reflection to get the value of the TRANSACTION_setDataEnabledfield. This way, it will work across all mobile brands running Android 5+ (If you don't know how to use Reflection to get the value of the TRANSACTION_setDataEnabledfield, see solution from PhongLe below- save me from duplicating it here.) Important: Please note that transaction code TRANSACTION_setDataEnabledhas only been introduced in Android 5.0 and later versions. Running this transaction code on earlier versions of Android will do nothing as the transaction code TRANSACTION_setDataEnableddoes not exist.

Note 2: adbis located at /android-sdk/platform-tools/directory. If you do not wish to use ADB, execute the method via suin your app.

Note 2: adbis located at /android-sdk/platform-tools/directory. If you do not wish to use ADB, execute the method via suin your app.

Note 3: See UPDATE 3 below.

Note 3: See UPDATE 3 below.

UPDATE 3: Many Android developers have emailed me questions regarding switching mobile network on/off for Android 5+, but instead of answering individual emails, I'll post my answer here so everyone can use it and adapt it for their Android apps.

UPDATE 3: Many Android developers have emailed me questions regarding switching mobile network on/off for Android 5+, but instead of answering individual emails, I'll post my answer here so everyone can use it and adapt it for their Android apps.

First thing first, let's clear up some misconception and misunderstanding regarding:

First thing first, let's clear up some misconception and misunderstanding regarding:

svc data enable
svc data disable

The above methods would only turn background data on/off, notthe subscription service, so the battery will drain a fair bit since the subscription service- an Android system service- will still be running in the background. For Android devices supporting multiple sim cards, this scenario is worse as the subscription service constantly scans for available mobile network(s) to use with the active SIM cards available in the Android device. Use this method at your own risk.

The above methods would only turn background data on/off, notthe subscription service, so the battery will drain a fair bit since the subscription service- an Android system service- will still be running in the background. For Android devices supporting multiple sim cards, this scenario is worse as the subscription service constantly scans for available mobile network(s) to use with the active SIM cards available in the Android device. Use this method at your own risk.

Now, the proper way to switch off mobile network, including its corresponding subscription service via the SubscriptionManagerclass introduced in API 22, is:

Now, the proper way to switch off mobile network, including its corresponding subscription service via the SubscriptionManagerclass introduced in API 22, is:

public static void setMobileNetworkfromLollipop(Context context) throws Exception {
    String command = null;
    int state = 0;
    try {
        // Get the current state of the mobile network.
        state = isMobileDataEnabledFromLollipop(context) ? 0 : 1;
        // Get the value of the "TRANSACTION_setDataEnabled" field.
        String transactionCode = getTransactionCode(context);
        // Android 5.1+ (API 22) and later.
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
            SubscriptionManager mSubscriptionManager = (SubscriptionManager) context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
            // Loop through the subscription list i.e. SIM list.
            for (int i = 0; i < mSubscriptionManager.getActiveSubscriptionInfoCountMax(); i++) {                    
                if (transactionCode != null && transactionCode.length() > 0) {
                    // Get the active subscription ID for a given SIM card.
                    int subscriptionId = mSubscriptionManager.getActiveSubscriptionInfoList().get(i).getSubscriptionId();
                    // Execute the command via `su` to turn off
                    // mobile network for a subscription service.
                    command = "service call phone " + transactionCode + " i32 " + subscriptionId + " i32 " + state;
                    executeCommandViaSu(context, "-c", command);
                }
            }
        } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) {
            // Android 5.0 (API 21) only.
            if (transactionCode != null && transactionCode.length() > 0) {
                // Execute the command via `su` to turn off mobile network.                     
                command = "service call phone " + transactionCode + " i32 " + state;
                executeCommandViaSu(context, "-c", command);
            }
        }
    } catch(Exception e) {
        // Oops! Something went wrong, so we throw the exception here.
        throw e;
    }           
}

To check if the mobile network is enabled or not:

To check if the mobile network is enabled or not:

private static boolean isMobileDataEnabledFromLollipop(Context context) {
    boolean state = false;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        state = Settings.Global.getInt(context.getContentResolver(), "mobile_data", 0) == 1;
    }
    return state;
}

To get the value of the TRANSACTION_setDataEnabledfield (borrowed from PhongLe's solution below):

To get the value of the TRANSACTION_setDataEnabledfield (borrowed from PhongLe's solution below):

private static String getTransactionCode(Context context) throws Exception {
    try {
        final TelephonyManager mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 
        final Class<?> mTelephonyClass = Class.forName(mTelephonyManager.getClass().getName());
        final Method mTelephonyMethod = mTelephonyClass.getDeclaredMethod("getITelephony");
        mTelephonyMethod.setAccessible(true);
        final Object mTelephonyStub = mTelephonyMethod.invoke(mTelephonyManager);
        final Class<?> mTelephonyStubClass = Class.forName(mTelephonyStub.getClass().getName());
        final Class<?> mClass = mTelephonyStubClass.getDeclaringClass();
        final Field field = mClass.getDeclaredField("TRANSACTION_setDataEnabled");
        field.setAccessible(true);
        return String.valueOf(field.getInt(null));
    } catch (Exception e) {
        // The "TRANSACTION_setDataEnabled" field is not available,
        // or named differently in the current API level, so we throw
        // an exception and inform users that the method is not available.
        throw e;
    }
}

To execute command via su:

To execute command via su:

private static void executeCommandViaSu(Context context, String option, String command) {
    boolean success = false;
    String su = "su";
    for (int i=0; i < 3; i++) {
        // Default "su" command executed successfully, then quit.
        if (success) {
            break;
        }
        // Else, execute other "su" commands.
        if (i == 1) {
            su = "/system/xbin/su";
        } else if (i == 2) {
            su = "/system/bin/su";
        }       
        try {
            // Execute command as "su".
            Runtime.getRuntime().exec(new String[]{su, option, command});
        } catch (IOException e) {
            success = false; 
            // Oops! Cannot execute `su` for some reason.
            // Log error here.
        } finally {
            success = true;
        }
    }
}

Hope this update clears up any misconception, misunderstanding, or question you may have about switching mobile network on/off on rooted Android 5+ devices.

Hope this update clears up any misconception, misunderstanding, or question you may have about switching mobile network on/off on rooted Android 5+ devices.

回答by A.J.

I noticed that the service call method posted by ChuongPham does not work consistently on all devices.

I noticed that the service call method posted by ChuongPham does not work consistently on all devices.

I have found the following solution which, I think, will work without any issue on all ROOTED devices.

I have found the following solution which, I think, will work without any issue on all ROOTED devices.

Execute the following via su

Execute the following via su

To enable mobile data

To enable mobile data

svc data enable

To disable mobile data

To disable mobile data

svc data disable

I think this is the simplest and best method.

I think this is the simplest and best method.

Edit: 2 downvotes were for what I believe to be commercial reasons. The person has deleted his comment now. Try it yourself, it works! Also confirmed to work by guys in comments.

Edit: 2 downvotes were for what I believe to be commercial reasons. The person has deleted his comment now. Try it yourself, it works! Also confirmed to work by guys in comments.

回答by Muzikant

Just to share a few more insights and possible solution (for rooted devices and system apps).

Just to share a few more insights and possible solution (for rooted devices and system apps).

Solution #1

Solution #1

It seems like the setMobileDataEnabledmethod no longer exists in ConnectivityManagerand this functionality was moved to TelephonyManagerwith two methods getDataEnabledand setDataEnabled. I tried calling these methods with reflection as you can see in the code below:

It seems like the setMobileDataEnabledmethod no longer exists in ConnectivityManagerand this functionality was moved to TelephonyManagerwith two methods getDataEnabledand setDataEnabled. I tried calling these methods with reflection as you can see in the code below:

public void setMobileDataState(boolean mobileDataEnabled)
{
    try
    {
        TelephonyManager telephonyService = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);

        Method setMobileDataEnabledMethod = telephonyService.getClass().getDeclaredMethod("setDataEnabled", boolean.class);

        if (null != setMobileDataEnabledMethod)
        {
            setMobileDataEnabledMethod.invoke(telephonyService, mobileDataEnabled);
        }
    }
    catch (Exception ex)
    {
        Log.e(TAG, "Error setting mobile data state", ex);
    }
}

public boolean getMobileDataState()
{
    try
    {
        TelephonyManager telephonyService = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);

        Method getMobileDataEnabledMethod = telephonyService.getClass().getDeclaredMethod("getDataEnabled");

        if (null != getMobileDataEnabledMethod)
        {
            boolean mobileDataEnabled = (Boolean) getMobileDataEnabledMethod.invoke(telephonyService);

            return mobileDataEnabled;
        }
    }
    catch (Exception ex)
    {
        Log.e(TAG, "Error getting mobile data state", ex);
    }

    return false;
}

When executing the code you get a SecurityExceptionstating that Neither user 10089 nor current process has android.permission.MODIFY_PHONE_STATE.

When executing the code you get a SecurityExceptionstating that Neither user 10089 nor current process has android.permission.MODIFY_PHONE_STATE.

So, yes this is an intended change to the internal API and is no longer available to apps that used that hack in previous versions.

So, yes this is an intended change to the internal API and is no longer available to apps that used that hack in previous versions.

(start rant: that dreadful android.permission.MODIFY_PHONE_STATE permission... end rant).

(start rant: that dreadful android.permission.MODIFY_PHONE_STATE permission... end rant).

The good news are that in case you are building an app that can acquire the MODIFY_PHONE_STATE permission (only system apps can use that), you can use the above code to toggle mobile data state.

The good news are that in case you are building an app that can acquire the MODIFY_PHONE_STATE permission (only system apps can use that), you can use the above code to toggle mobile data state.

Solution #2

Solution #2

To check for current state of mobile data you can use the mobile_datafield of Settings.Global(not documented in official documentation).

To check for current state of mobile data you can use the mobile_datafield of Settings.Global(not documented in official documentation).

Settings.Global.getInt(contentResolver, "mobile_data");

And to enable/disable mobile data you can use shell commands on rooted devices (Just basic testing performed so any feedback in comments is appreciated). You can run the following command(s) as root (1=enable, 0=disable):

And to enable/disable mobile data you can use shell commands on rooted devices (Just basic testing performed so any feedback in comments is appreciated). You can run the following command(s) as root (1=enable, 0=disable):

settings put global mobile_data 1
settings put global mobile_data 0

回答by Muzikant

I don't have enough reputation to comment but I have tried all the answers and found the following:

I don't have enough reputation to comment but I have tried all the answers and found the following:

ChuongPham: Instead of using 83, I used reflection to get the value of the variable TRANSACTION_setDataEnabledfrom the com.android.internal.telephony.ITelephonyso it works across all Android 5+ devices, regardless of brands.

ChuongPham: Instead of using 83, I used reflection to get the value of the variable TRANSACTION_setDataEnabledfrom the com.android.internal.telephony.ITelephonyso it works across all Android 5+ devices, regardless of brands.

Muzikant: Work if the app is moved to /system/priv-app/directory (thanks to rgruet.) Else, it works via root, too! You just need to inform your users that the app will need a reboot before the changes to the mobile network will take place.

Muzikant: Work if the app is moved to /system/priv-app/directory (thanks to rgruet.) Else, it works via root, too! You just need to inform your users that the app will need a reboot before the changes to the mobile network will take place.

AJ: Work- sort of. Does not turn off subscription service so the devices I tested drained their batteries a fair bit. AJ's solution is NOTequivalent to Muzikant's solution despite the claim. I can confirm this by debugging different Samsung, Sony, and LG stock ROMs (I'm thorough) and can disprove AJ's claim that his solution is the same as Muzikant's. (Note: I can't get my hands on some Nexus and Motorola ROMs so haven't tested these ROMs with the proposed solutions.)

AJ: Work- sort of. Does not turn off subscription service so the devices I tested drained their batteries a fair bit. AJ's solution is NOTequivalent to Muzikant's solution despite the claim. I can confirm this by debugging different Samsung, Sony, and LG stock ROMs (I'm thorough) and can disprove AJ's claim that his solution is the same as Muzikant's. (Note: I can't get my hands on some Nexus and Motorola ROMs so haven't tested these ROMs with the proposed solutions.)

Anyway, hope it clears up any doubt over the solutions.

Anyway, hope it clears up any doubt over the solutions.

Happy coding! PL, Germany

Happy coding! PL, Germany

UPDATE: For those wondering how to get the value of the TRANSACTION_setDataEnabledfield via reflection, you can do the following:

UPDATE: For those wondering how to get the value of the TRANSACTION_setDataEnabledfield via reflection, you can do the following:

private static String getTransactionCodeFromApi20(Context context) throws Exception {
    try {
        final TelephonyManager mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 
        final Class<?> mTelephonyClass = Class.forName(mTelephonyManager.getClass().getName());
        final Method mTelephonyMethod = mTelephonyClass.getDeclaredMethod("getITelephony");
        mTelephonyMethod.setAccessible(true);
        final Object mTelephonyStub = mTelephonyMethod.invoke(mTelephonyManager);
        final Class<?> mTelephonyStubClass = Class.forName(mTelephonyStub.getClass().getName());
        final Class<?> mClass = mTelephonyStubClass.getDeclaringClass();
        final Field field = mClass.getDeclaredField("TRANSACTION_setDataEnabled");
        field.setAccessible(true);
        return String.valueOf(field.getInt(null));
    } catch (Exception e) {
        // The "TRANSACTION_setDataEnabled" field is not available,
        // or named differently in the current API level, so we throw
        // an exception and inform users that the method is not available.
        throw e;
    }
}

回答by nickkadrov

I found that su -c 'service call phone 83 i32 1'solution is most reliable for rooted devices. Thanks to Phong Le reference I have improved it by getting vendor/os specific transaction code using reflection. Maybe it will be useful for someone else. So, here is source code:

I found that su -c 'service call phone 83 i32 1'solution is most reliable for rooted devices. Thanks to Phong Le reference I have improved it by getting vendor/os specific transaction code using reflection. Maybe it will be useful for someone else. So, here is source code:

    public void changeConnection(boolean enable) {
        try{
            StringBuilder command = new StringBuilder();
            command.append("su -c ");
            command.append("service call phone ");
            command.append(getTransactionCode() + " ");
            if (Build.VERSION.SDK_INT >= 22) {
                SubscriptionManager manager = SubscriptionManager.from(context);
                int id = 0;
                if (manager.getActiveSubscriptionInfoCount() > 0)
                    id = manager.getActiveSubscriptionInfoList().get(0).getSubscriptionId();
                command.append("i32 ");
                command.append(String.valueOf(id) + " ");
            }
            command.append("i32 ");
            command.append(enable?"1":"0");
            command.append("\n");
            Runtime.getRuntime().exec(command.toString());
        }catch(IOException e){
            ...
        }
    }

    private String getTransactionCode() {
        try {
            TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
            Class telephonyManagerClass = Class.forName(telephonyManager.getClass().getName());
            Method getITelephonyMethod = telephonyManagerClass.getDeclaredMethod("getITelephony");
            getITelephonyMethod.setAccessible(true);
            Object ITelephonyStub = getITelephonyMethod.invoke(telephonyManager);
            Class ITelephonyClass = Class.forName(ITelephonyStub.getClass().getName());

            Class stub = ITelephonyClass.getDeclaringClass();
            Field field = stub.getDeclaredField("TRANSACTION_setDataEnabled");
            field.setAccessible(true);
            return String.valueOf(field.getInt(null));
        } catch (Exception e) {
            if (Build.VERSION.SDK_INT >= 22)
                return "86";
            else if (Build.VERSION.SDK_INT == 21)
                return "83";
        }
        return "";
    }

Update:

Update:

Some of my users report that they have problem with turning ON mobile network via this method (turning off works correct). Does anyone have solution?

Some of my users report that they have problem with turning ON mobile network via this method (turning off works correct). Does anyone have solution?

Update2:

Update2:

After some digging the Android 5.1 code I've found that they changed signature of transaction. Android 5.1 brings official support of multi-SIM. So, the transaction need so-called Subscription Id as first parameter (read more here). The result of this situation is that the command su -c 'service call phone 83 i32 1'doesn't turn on Mobile Net on Android 5.1. So, the full command on Android 5.1 should be like this su -c 'service call phone 83 i32 0 i32 1'(the i32 0is the subId, the i32 1is command 0 - off and 1 - on). I've update the code above with this fix.

After some digging the Android 5.1 code I've found that they changed signature of transaction. Android 5.1 brings official support of multi-SIM. So, the transaction need so-called Subscription Id as first parameter (read more here). The result of this situation is that the command su -c 'service call phone 83 i32 1'doesn't turn on Mobile Net on Android 5.1. So, the full command on Android 5.1 should be like this su -c 'service call phone 83 i32 0 i32 1'(the i32 0is the subId, the i32 1is command 0 - off and 1 - on). I've update the code above with this fix.

回答by rgruet

Solution #1 from Muzikant seems to work if you make the app "system" by moving the .apk to the /system/priv-app/folder, notto the /system/app/one (@jaumard: maybe that's why your test didn't work).

Solution #1 from Muzikant seems to work if you make the app "system" by moving the .apk to the /system/priv-app/folder, notto the /system/app/one (@jaumard: maybe that's why your test didn't work).

When the .apk is in the /system/priv-app/folder, it can successfully request the dreadful android.permission.MODIFY_PHONE_STATEpermission in the Manifest and call TelephonyManager.setDataEnabledand TelephonyManager.getDataEnabled.

When the .apk is in the /system/priv-app/folder, it can successfully request the dreadful android.permission.MODIFY_PHONE_STATEpermission in the Manifest and call TelephonyManager.setDataEnabledand TelephonyManager.getDataEnabled.

At least that works on Nexus 5/ Android 5.0. The .apk perms are 0144. You need to rebootthe device for the change to be taken into account, maybe this could be avoided - see this thread.

At least that works on Nexus 5/ Android 5.0. The .apk perms are 0144. You need to rebootthe device for the change to be taken into account, maybe this could be avoided - see this thread.

回答by Sahil Lombar

To correct Muzikant Solution #2

To correct Muzikant Solution #2

settings put global mobile_data 1

Does enable only the toggle for mobile data but does nothing to the connectivity. Only the toggle is enabled. In order to get the data working using

Does enable only the toggle for mobile data but does nothing to the connectivity. Only the toggle is enabled. In order to get the data working using

su -c am broadcast -a android.intent.action.ANY_DATA_STATE --ez state 1

Gives error as the extra for

Gives error as the extra for

android.intent.action.ANY_DATA_STATE

Requires String Object while --ez parameter is used for boolean. Ref: PhoneGlobals.java & PhoneConstants.java. After using connecting or connected as extra using command

Requires String Object while --ez parameter is used for boolean. Ref: PhoneGlobals.java & PhoneConstants.java. After using connecting or connected as extra using command

su -c am broadcast -a android.intent.action.ANY_DATA_STATE --es state connecting

Still doesnt do anything to enable the data.

Still doesnt do anything to enable the data.

回答by varotariya vajsi

I derived final code from @ChuongPham and @A.J. for enable and disable cellular data. for enable you can call setMobileDataEnabled(true);and for disable you can call setMobileDataEnabled(false);

I derived final code from @ChuongPham and @A.J. for enable and disable cellular data. for enable you can call setMobileDataEnabled(true);and for disable you can call setMobileDataEnabled(false);

public void setMobileDataEnabled(boolean enableOrDisable) throws Exception {
    String command = null;
    if (enableOrDisable) {
        command = "svc data enable";
    } else {
        command = "svc data disable";
    }


    executeCommandViaSu(mContext, "-c", command);
}

private static void executeCommandViaSu(Context context, String option, String command) {
    boolean success = false;
    String su = "su";
    for (int i = 0; i < 3; i++) {
        // Default "su" command executed successfully, then quit.
        if (success) {
            break;
        }
        // Else, execute other "su" commands.
        if (i == 1) {
            su = "/system/xbin/su";
        } else if (i == 2) {
            su = "/system/bin/su";
        }
        try {
            // Execute command as "su".
            Runtime.getRuntime().exec(new String[]{su, option, command});
        } catch (IOException e) {
            success = false;
            // Oops! Cannot execute `su` for some reason.
            // Log error here.
        } finally {
            success = true;
        }
    }
}

回答by user196994

The following solution works by enabling and disabling mobile data (as you would by clicking on the slider "Mobile data"). Requires root. Tested on LineageOS 16.0 (rooted):

The following solution works by enabling and disabling mobile data (as you would by clicking on the slider "Mobile data"). Requires root. Tested on LineageOS 16.0 (rooted):

Steps 1-3 are taken from the accepted answer at Turning off second SIM via adb shell/Tasker - using activities/intents:

Steps 1-3 are taken from the accepted answer at Turning off second SIM via adb shell/Tasker - using activities/intents:

  1. Download jadx from https://github.com/skylot/jadx
  2. ADB pull the devices framework.jar (adb pull /system/framework/framework.jar)
  3. Open the .jar file with 7-Zip and extract the *.dex files. Open each .dex file with jadx-gui until you find the one with the following tree: com.android.internal.telephony.ITelephony
  4. Find TRANSACTION_enableDataConnectivity and TRANSACTION_disableDataConnectivity, for me these are 38 and 39 respectively
  5. From a root shell (e.g., adb shell or Termux), run service call phone 38to enable data, and service call phone 39to disable data.
  1. Download jadx from https://github.com/skylot/jadx
  2. ADB pull the devices framework.jar (adb pull /system/framework/framework.jar)
  3. Open the .jar file with 7-Zip and extract the *.dex files. Open each .dex file with jadx-gui until you find the one with the following tree: com.android.internal.telephony.ITelephony
  4. Find TRANSACTION_enableDataConnectivity and TRANSACTION_disableDataConnectivity, for me these are 38 and 39 respectively
  5. From a root shell (e.g., adb shell or Termux), run service call phone 38to enable data, and service call phone 39to disable data.

回答by BADER EDDINE QODIA

Not all phones and versions of android have things Enable/disable mobile data the same. otherwise, this solution is tested on my phone (SAMSUNG SM-J100H)

Not all phones and versions of android have things Enable/disable mobile data the same. otherwise, this solution is tested on my phone (SAMSUNG SM-J100H)

To enable mobile data :

To enable mobile data :

adb shell service call phone 27

To disable mobile data :

To disable mobile data :

adb shell service call phone 28