更改移动网络模式(GSM、WCDMA、自动)

13

我希望能够通过编程方式从代码中更改首选网络模式,即gsm或wcdma或auto,在Android上。

这是否可行,如果是,如何实现?


你可以查看Android设置的源代码,看看它是如何实现的。我猜你只需要"安全设置"权限,然后就可以直接从你的代码中修改值了。不过我不确定。 - Jonas Czech
5个回答

9

这是可能的,我已经做到了。

为了使此功能正常工作,您的应用程序必须使用系统密钥或拥有运营商特权签署。否则,该应用程序将抛出java.lang.SecurityException: No modify permission or carrier privilege.

我的应用程序运行在Android 5.1 Lollipop(API 22)上,并使用系统密钥签名,因此这是我唯一可以确认确实有效的配置。我无法确认运营商特权方法。

AndroidManifest.xml

请将此权限添加到您的应用程序清单中。如果您正在使用Android Studio,则它可能会将此行标记为错误,因为只有系统应用程序才能拥有此权限。如果您可以使用系统密钥签署应用程序,请不要担心。

<uses-permission android:name="android.permission.MODIFY_PHONE_STATE"/>

获取首选网络

返回值在RILConstants.java中定义,例如RILConstants.NETWORK_MODE_WCDMA_PREF

public int getPreferredNetwork() {
    Method method = getHiddenMethod("getPreferredNetworkType", TelephonyManager.class, null);
    int preferredNetwork = -1000;
    try {
        preferredNetwork = (int) method.invoke(mTelephonyManager);
        Log.i(TAG, "Preferred Network is ::: " + preferredNetwork);
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }

    return preferredNetwork;
}

设置首选方法。

参数必须基于RILConstants.java,例如:RILConstants.NETWORK_MODE_LTE_ONLY

public void setPreferredNetwork(int networkType) {
    try {
        Method setPreferredNetwork = getHiddenMethod("setPreferredNetworkType",
                TelephonyManager.class, new Class[] {int.class});
        Boolean success = (Boolean)setPreferredNetwork.invoke(mTelephonyManager,
                networkType);
        Log.i(TAG, "Could set Network Type ::: " + (success.booleanValue() ? "YES" : "NO"));
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
}

这是一个访问隐藏API方法的实用工具。
/**
 * Get a hidden method instance from a class
 * @param methodName The name of the method to be taken from the class
 * @param fromClass The name of the class that has the method
 * @return A Method instance that can be invoked
 */
public Method getHiddenMethod(String methodName, Class fromClass, Class[] params) {
    Method method = null;
    try {
        Class clazz = Class.forName(fromClass.getName());
        method = clazz.getMethod(methodName, params);
        method.setAccessible(true);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    }

    return method;
}

1
很棒的解决方案!请注意,我在没有签署我的应用程序的情况下使其工作(只需在Android Studio中点击运行),但将其移动到/system/priv-app/<appname>/<appname>.apk,并在清单中声明了MODIFY_PHONE_STATE权限。 - BamsBamx
@user1147688 是的,已经root了...记得给系统apk设置适当的权限。移动到系统后重启。 - BamsBamx
@BamsBamx,请问您能为我解释一下如何将它移动到“/system/priv-app/<appname>/<appname>.apk”吗?我不太明白。 - Nguyễn Hoàng
@NguyễnHoàng 所谓的“/ system / priv-app / <appname> / <appname> .apk”意味着您需要将生成的APK文件复制到设备存储器上的该路径。在该字符串中替换<appname>为您的应用程序名称即可。 - Tolio
我在setPreferredNetworkType和getPreferredNetworkType两个方法中都遇到了NoSuchMethodException异常。 - Mahdi-Malv
显示剩余4条评论

4
这不是一个答案,而是对Tulio-F的回答进行扩展。 RILConstants.java 包含以下内容:
// NETWORK_MODE_* See ril.h RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE 
int NETWORK_MODE_WCDMA_PREF              = 0;   // GSM/WCDMA (WCDMA preferred) 
int NETWORK_MODE_GSM_ONLY                = 1;   // GSM only 
int NETWORK_MODE_WCDMA_ONLY              = 2;   // WCDMA only 
int NETWORK_MODE_GSM_UMTS                = 3;   // GSM/WCDMA (auto mode, according to PRL)**    
int NETWORK_MODE_CDMA                    = 4;   // CDMA and EvDo (auto mode, according to PRL)**
int NETWORK_MODE_CDMA_NO_EVDO            = 5;   // CDMA only 
int NETWORK_MODE_EVDO_NO_CDMA            = 6;   // EvDo only 
int NETWORK_MODE_GLOBAL                  = 7;   // GSM/WCDMA, CDMA, and EvDo (auto mode, according to PRL)**
int NETWORK_MODE_LTE_CDMA_EVDO           = 8;   // LTE, CDMA and EvDo 
int NETWORK_MODE_LTE_GSM_WCDMA           = 9;   // LTE, GSM/WCDMA 
int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA = 10;  // LTE, CDMA, EvDo, GSM/WCDMA 
int NETWORK_MODE_LTE_ONLY                = 11;  // LTE Only mode. 
int NETWORK_MODE_LTE_WCDMA               = 12;  // LTE/WCDMA 
int NETWORK_MODE_TDSCDMA_ONLY            = 13;  // TD-SCDMA only 
int NETWORK_MODE_TDSCDMA_WCDMA           = 14;  // TD-SCDMA and WCDMA 
int NETWORK_MODE_LTE_TDSCDMA             = 15;  // TD-SCDMA and LTE 
int NETWORK_MODE_TDSCDMA_GSM             = 16;  // TD-SCDMA and GSM 
int NETWORK_MODE_LTE_TDSCDMA_GSM         = 17;  // TD-SCDMA,GSM and LTE 
int NETWORK_MODE_TDSCDMA_GSM_WCDMA       = 18;  // TD-SCDMA, GSM/WCDMA 
int NETWORK_MODE_LTE_TDSCDMA_WCDMA       = 19;  // TD-SCDMA, WCDMA and LTE 
int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA   = 20;  // TD-SCDMA, GSM/WCDMA and LTE 
int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA     = 21;  // TD-SCDMA,EvDo,CDMA,GSM/WCDMA
int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 22;  // TD-SCDMA/LTE/GSM/WCDMA, CDMA, and EvDo

int PREFERRED_NETWORK_MODE   = SystemProperties.getInt("ro.telephony.default_network", NETWORK_MODE_WCDMA_PREF);

在哪里:

** =“可用的应用程序设置菜单”


3

答案是

我们可以直接打开移动网络设置的设置应用程序,以在“2G”和“允许3G”网络之间切换。很遗憾,不能直接切换。

我们可以开发一些东西,它将显示当前网络并允许用户从应用程序中进行快捷方式来切换网络。


1
我认为这只是由于一个原因:安全性和用户隐私。 - MKJParekh
1
即使在ROOT过的手机上或使用Android NDK,也不可能吗? - Wojciech Owczarczyk
可以在已经获取了 root 权限的设备上实现。 - rebel_UA
请提供任何可用的链接...感谢您给予希望 :) @rebel_UA - MKJParekh
2
我不能,但你应该对默认设置应用进行逆向工程,将你的应用安装到/system/app/目录下,并在uses-permission中添加WRITE_SECURE_SETTINGS权限。我不知道为什么,但如果你愿意,可以试试看 =) - rebel_UA
显示剩余3条评论

2

对于那些仍然在寻找如何使用系统密钥签署应用程序的人。

您可能希望使用startActivityForResult方法将用户重定向到特定的activity_code

由于Android的不同版本,有些代码无法保证与匹配的活动相匹配。

在kotlin中,您可以使用类似以下的代码:

startActivityForResult(Intent(android.provider.Settings.ACTION_NETWORK_OPERATOR_SETTINGS), 0)

当然不要忘记导入所需的库。
请记住,用户将不得不在菜单中进行一些导航。因此,可能需要提供一些通用和文本解释,说明用户应该执行哪些输入/操作才能进入良好的设置页面。
注1:在某些设备上,可能没有选择首选网络的实现。
注2:如果您可以获得相应的特权,我绝对推荐Tulio F.的解决方案。

1
我有一个使用Android API 23的设备,并使用了Tulio F.的方法,但是在API 25的设备上会出现NoSuchMethodException异常。然而,我可以使用以下代码更改设置:
"Settings.Global.putInt(context.getContentResolver(), "preferred_network_mode1", [基于RILConstants.java的网络类型]);"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接