最近在研究MTK双卡手机,4.1的系统。要在程序里控制移动数据的开关,碰到难题了。因为发现,以前用反射的方法调用ConnectivityManager 类的setMobileDataEnabled方法失效了提示的信息显示找不到该方法,第一的反应是,难道4.1系统没有这个方法了,想想也不可能啊。
查源码,果断还是跟原先一样的。再次进行调试,取到了ConnectivityManager 类中所有的函数,也有此方法的存在。
查原因,可能是因为私有方法,无法调用到。但改代码能调用私有方法后,加权限,加系统签名后,还是如此,不知是我的代码有问题还是怎样,大家可以看下,勿笑。
private void setGprsEnable(boolean isEnable) {
int result = 0;
ConnectivityManager mCM = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
try {
Class clazz = Class.forName(mCM.getClass().getName());
Constructor[] cons = clazz.getDeclaredConstructors();
Constructor con = clazz.getConstructor();//getDeclaredConstructors();
con.setAccessible(true);
Field iConnectivityManagerField = clazz.getDeclaredField("mService");
iConnectivityManagerField.setAccessible(true);
Object iConnectivityManager = iConnectivityManagerField.get(mCM);
//Class iConnectivityManagerClass = Class.forName(iConnectivityManager.getClass().getName());
ConnectivityManager cm = (ConnectivityManager)con.newInstance(iConnectivityManager);
Class[] argClasses = new Class[1];
argClasses[0] = Boolean.class;
Method ms = clazz.getDeclaredMethod("setMobileDataEnabled", argClasses);
ms.setAccessible(true);
Object obj = ms.invoke(cm, isEnable);
result = (Integer) obj;
} catch (ClassNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
既然此方法不通了,那就另外找方法了。先下了个海卓上网大师,结果提示需要系统签名才能控制数据开关,重启后app移动system/app中了,果断可以实现了,但不知用的是什么方法啊,无源码可查,google也查无资料。只能认真地观察下logcat了,看能不能有些发现。
皇天不负有心人啊,找到些信息。当开启数据连接时,logcat输出了一些关键信息
Provider/Settings(506): put string name = gprs_connection_setting , value = 1 userHandle = 0
SettingsProvider(506): insert(content://settings/system) for user 0 by 0
SettingsProvider(506): redundant, old Value: 0 new value: 1
SettingsProvider(506): system <- value=1 name=gprs_connection_setting for user 0
Provider/Settings(506): put string name = gprs_connection_sim_setting , value = 3 userHandle = 0
SettingsProvider(506): insert(content://settings/system) for user 0 by 0
SettingsProvider(506): redundant, old Value: 0 new value: 3
SettingsProvider(506): system <- value=3 name=gprs_connection_sim_setting for user 0
Provider/Settings(506): Global.putString(name=mobile_data, value=1 for 0
Provider/Settings(506): put string name = mobile_data , value = 1 userHandle = 0
SettingsProvider(506): redundant, old Value: 0 new value: 1
SettingsProvider(506): global <- value=1 name=mobile_data for user 0
难道只要改这三个值就可以啦?果断无比欣喜尝试之。
ContentResolver cr = getWindow().getContext().getContentResolver();
Settings.System.putInt(cr, "gprs_connection_setting", 1);
Settings.System.putInt(cr, "gprs_connection_sim_setting", 3);
Settings.Global.putInt(cr, "mobile_data", 1);
再加上 WRITE_SECURE_SETTINGS 和 WRITE_SETTINGS 权限 ,加系统签名再试。结果依然无法开启,但发现,下拉菜单里的快捷按钮,状态却已经改变了,数据连接显示已经打开了,就是信号值那少了个数据连接的H或者E图标。正纳闷着系统怎么没有对配置信息进行更新时,我试了下打开了飞行模式,开起来后又关了飞行模式,结果,数据连上了。飞行模式关闭后,系统开始重新配置网络了,将网络配置更新了,原先改的值开始生效了。
既然网络状态发生改变后,系统就能更新网络配置了,那是不是发送网络改变的广播就能立即更新我们修改的配置了呢?试着发了几个广播,可惜不行,不解。不过如果对wifi进行开关,效果跟飞行模式一样,依然能达到我想要的结果。so,时间有限,就先用这个方法吧。对wifi的开关我是如此操作的,因为wifi已经连接时,其实数据连接是否打开是无关紧要的,wifi关了后系统便会自动更新配置。
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
switch(msg.what){
case 0:
//wifi打开,但未连接
if(wifiManager.isWifiEnabled()&&mActivity.getWifiState()==200){
wifiManager.setWifiEnabled(false);
sendEmptyMessageDelayed(2, 200);
}
else if(wifiManager.isWifiEnabled()){
}
else{
wifiManager.setWifiEnabled(true);
sendEmptyMessageDelayed(1, 200);
}
break;
case 1:
wifiManager.setWifiEnabled(false);
break;
case 2:
wifiManager.setWifiEnabled(true);
break;
}
super.handleMessage(msg);
}
};
现在问题就解决了。由于我用的是MTK双卡的系统,所以这里还要稍微处理下
int defaultsim = getDefaultSim();
Settings.System.putInt(cr, "gprs_connection_setting", defaultsim);
getDefaultSim()方法是MTK的接口,用反射就能取到了。这里就不再贴出来了。对了,别忘了加权限 CHANGE_NETWORK_STATE、CHANGE_WIFI_STATE、ACCESS_WIFI_STATE。
这个方法虽然不完美,但总是能用了,OMG,有时间再慢慢研究吧