该setMobileDataEnabled方法不再调用由于Android升,后来 [英] The setMobileDataEnabled method is no longer callable as of Android L and later

查看:1395
本文介绍了该setMobileDataEnabled方法不再调用由于Android升,后来的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经登录<一个href="https://$c$c.google.com/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars&groupby=&sort=&id=78084"相对=nofollow>发行78084 与谷歌关于 setMobileDataEnabled()方法是不再通过反射调用。这是可调用由于Android 2.1(API 7),以通过反射的Andr​​oid 4.4(API 19),但由于Android L和以后,即使有根,在 setMobileDataEnabled()方法是不是可调用的。

I have logged Issue 78084 with 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.

该负责人的回答是,这个问题是封闭的状态设置为WorkingAsIntended。谷歌的简单解释是:

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

私有API是私有的,因为他们没有稳定,可能会消失,恕不另行通知。

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

是的,谷歌,我们都知道使用反射来调用隐藏于方法之前的Andr​​oid来到了scene-的风险,但你需要提供一个更坚实的答案,备选方案,如果有的话,为了实现相同的结果为 setMobileDataEnabled()。 (如果你是不满谷歌的决定,因为我,然后登录到<一个href="https://$c$c.google.com/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars&groupby=&sort=&id=78084"相对=nofollow>发行78084 和明星也尽可能多的让谷歌知道他们的方式错误。)

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 78084 and star it as many as possible to let Google know the error of their way.)

所以,我想问你的是:我们在一条死胡同的时候还以编程方式启用或Android设备上禁用手机的网络功能?从谷歌这样严厉的方式不知何故不跟我坐好。如果你有解决方法为Android 5.0(棒棒堂)及以后,我很想听到这个线程你的答案/讨论。

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.

我已经用下面的code,看 setMobileDataEnabled()方法是可行的:

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.

更新:目前,有可能触发移动网络设备是否扎根。然而,对于非root权限的设备,它仍然是一个调查的过程,因为没有切换的移动网络通用的方法。

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.

推荐答案

为了延长Muzikant的解决方案2,有人可以请尝试以下是Android 5.0植根设备上的解决方案(因为我目前所不具备的)和让我知道,如果它工作或不工作。

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

注: mobile_data 变量可以在Android的API 21源$ C ​​$ CS在 / Android的SDK /来源/安卓-21被发现/android/provider/Settings.java ,并声明为:

Note: The mobile_data variable can be found in Android API 21 source codes at /android-sdk/sources/android-21/android/provider/Settings.java and 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";

虽然 android.intent.action.ANY_DATA_STATE 意图可以在Android的API 21源$ C ​​$ CS在 / Android的SDK中找到/来源/ Android为21 / COM /安卓/内部/电话/ TelephonyIntents.java ,并声明为:

While the android.intent.action.ANY_DATA_STATE Intent can be found in Android API 21 source codes at /android-sdk/sources/android-21/com/android/internal/telephony/TelephonyIntents.java and 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";

更新1 :如果你不想执行上面的Java codeS的Andr​​oid应用程序,那么你可以运行

UPDATE 1: If you don't want to implement the above Java codes in your Android application, then you can run the su commands 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'"

注:亚洲开发银行位于 / Android的SDK /平台工具/ 目录。该设置命令只支持在Android 4.2或更高版本。较早的Andr​​oid版本将报告SH:设置:未找到。错误

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

更新2 :另一种方式来切换移动网络上的的Andr​​oid 5+设备将是使用无证服务 shell命令。下面的命令可以通过亚行执行切换的移动网络:

UPDATE 2: Another way to toggle mobile network on a rooted Android 5+ device would be to use the undocumented service shell 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'"

或者只是:

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

注1 :事务code 83 服务呼叫电话使用命令可能会之间进行切换Android的版本。请检查 com.android.internal.telephony.ITelephony TRANSACTION_setDataEnabled 字段,为您的Andr​​oid版本的价值。此外,而不是硬编码的 83 ,您会使用反射来获取 TRANSACTION_setDataEnabled 字段的值会更好。通过这种方式,它会在所有手机品牌的运行Android 5+(如果你不知道如何使用反射来获取 TRANSACTION_setDataEnabled 字段的值工作,看到的解决方案PhongLe如下─把我从这里复制)的 重要:请注意,交易code TRANSACTION_setDataEnabled 仅在被引入安卓5.0及更高版本。在早期版本的Andr​​oid上运行此交易code会做什么作为交易code TRANSACTION_setDataEnabled 不存在。

Note 1: The transaction code 83 used in the service call phone command might change between Android versions. Please check com.android.internal.telephony.ITelephony for the value of the TRANSACTION_setDataEnabled field for your version of Android. Also, instead of hardcoding 83, you would be better off using Reflection to get the value of the TRANSACTION_setDataEnabled field. 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_setDataEnabled field, see solution from PhongLe below- save me from duplicating it here.) Important: Please note that transaction code TRANSACTION_setDataEnabled has 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_setDataEnabled does not exist.

注2 亚洲开发银行位于 / Android的SDK /平台工具/ 目录。如果你不希望使用亚行,在你的应用程序通过执行的方法。

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

注意3 :请参见更新3以下

更新3 :很多Android开发者已经给我发电子邮件就开/关为Android 5+,而是回答个人的电子邮件交换移动网络的问题,我会后我的答案在这里,所以每个人都可以使用它适应它自己的Andr​​oid应用程序。

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:

svc data enable
svc data disable

以上方法只能开启/关闭,不可以订阅服务后台数据,因此电池会耗尽一个公平一点,因为该订阅服务 - 一个Android系统服务 - 将仍然运行在的背景。对于支持多个SIM卡的Andr​​oid设备,这种情况更糟糕的订阅服务不断扫描可用的移动网络(S),在Android设备提供有效的SIM卡使用。需要您自担风险使用此方法。

The above methods would only turn background data on/off, not the 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.

现在,正确的方法关闭移动网络,包括通过的 SubscriptionManager 类API 22推出,是:

Now, the proper way to switch off mobile network, including its corresponding subscription service via the SubscriptionManager class 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:

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

要获得 TRANSACTION_setDataEnabled 字段的值(从下面PhongLe的解决方案借来的):

To get the value of the TRANSACTION_setDataEnabled field (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;
    }
}

要通过执行命令:

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

希望此更新清除了所有的误解,误解或问题,你可能对/关上扎根的Andr​​oid 5+设备交换移动网络上。

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

这篇关于该setMobileDataEnabled方法不再调用由于Android升,后来的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆