GPRS API 变更
从 Android2.1 <API 7>到 Android 4.4 <API 19>
ConnectivityManager
/**
* Gets the value of the setting for enabling Mobile data.
*
* @return Whether mobile data is enabled.
* @hide
*/
public boolean getMobileDataEnabled() {
try {
return mService.getMobileDataEnabled();
} catch (RemoteException e) {
return true;
}
}
/**
* Sets the persisted value for enabling/disabling Mobile data.
*
* @param enabled Whether the mobile data connection should be
* used or not.
* @hide
*/
public void setMobileDataEnabled(boolean enabled) {
try {
mService.setMobileDataEnabled(enabled);
} catch (RemoteException e) {
}
}
实际
IConnectivityManager.setMobileDataEnabled(enabled);
IConnectivityManager.getMobileDataEnabled();
setMobileDataEnabled()可以通过反射方式被调用了
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());
}
}
Android L以后
即使你有root权限, setMobileDataEnabled() method 也不能被调用,捷径没有了!
ConnectivityManager getMobileDataEnabled存在 setMobileDataEnabled 已经不存在了
IConnectivityManager 中的方法也不存在
Deprecated:
Talk to TelephonyManager directly
Hide:
1291
1292 public boolean getMobileDataEnabled() {
1293 IBinder b = ServiceManager.getService(Context.TELEPHONY_SERVICE);
1294 if (b != null) {
1295 try {
1296 ITelephony it = ITelephony.Stub.asInterface(b);
1297 return it.getDataEnabled();
1298 } catch (RemoteException e) { }
1299 }
1300 return false;
1301 }
setMobileDataEnabled 这个方法 TelephonyManager
getDataEnabled
Hide:
3669
3670 @SystemApi
3671 public boolean getDataEnabled(int subId) {
3672 boolean retVal = false;
3673 try {
3674 retVal = getITelephony().getDataEnabled(subId);
3675 } catch (RemoteException e) {
3676 Log.e(TAG, "Error calling ITelephony#getDataEnabled", e);
3677 } catch (NullPointerException e) {
3678 }
3679 Log.d(TAG, "getDataEnabled: retVal=" + retVal);
3680 return retVal;
3681 }
Hide:
3646
3647 @SystemApi
3648 public void More ...setDataEnabled(boolean enable) {
3649 setDataEnabled(SubscriptionManager.getDefaultDataSubId(), enable);
3650 }
这个API hide 就算了,可以发射
@SystemApi 我就无语了 对于一般app就哭了
安卓5.0 LOLLIPOP<API 21>之前的判断移动数据是否打开的做法:
/**
* 反射获得IConnectivityManager实例
*
* @param context
* @return
*/
private Object getConnectivityManager(Context context) {
ConnectivityManager conMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
Class<?> conMgrClass = null;
Object iConMgr = null;
try {
conMgrClass = Class.forName(conMgr.getClass().getName());//ConnectivityManager
Field iConMgrField = conMgrClass.getDeclaredField("mService");//IConnectivityManager
iConMgrField.setAccessible(true);
iConMgr = iConMgrField.get(conMgr);//获得ConnectivityManager的IConnectivityManager实例
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return iConMgr;
}
private boolean isMobileEnabled(Context context) {
try {
Object iConMgr = getConnectivityManager(context);//IConnectivityManager
Class<?> iConMgrClass = Class.forName(iConMgr.getClass().getName());
Method getMobileDataEnabledMethod = iConMgrClass.getDeclaredMethod("getMobileDataEnabled");
getMobileDataEnabledMethod.setAccessible(true);
return (Boolean) getMobileDataEnabledMethod.invoke(getConnectivityManager(context));
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
private void setValue(Context context, boolean isopen) {
try {
Object iConMgr = getConnectivityManager(context);//IConnectivityManager
Class<?> iConMgrClass = Class.forName(iConMgr.getClass().getName());
Method setMobileDataEnabledMethod = iConMgrClass.getDeclaredMethod("setMobileDataEnabled", Boolean.class);
setMobileDataEnabledMethod.setAccessible(true);
setMobileDataEnabledMethod.invoke(iConMgr, isopen);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 判断GPRS是否打开
*
* @return
*/
public boolean isOn() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return isMobileDataEnabledFromLollipop(mContext);
}
return isMobileEnabled(mContext);
}
private 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;
}
5.0之后可以这样判断网络是否打开
Settings.Global.getInt(context.getContentResolver(), “mobile_data”, 0),
但是没有ROOT权限依然很难主动打开GPRS
setNetworkPreference
ConnectivityManager.setNetworkPreference(ConnectivityManager.TYPE_WIFI);
android 5.0 中 方法还存在 但是 内部实现 删除了
IConnectivityManager 中 方法也删除了
setAcceptUnvalidated 接受未验证网络
下载源码定位到
/**
* Informs the system whether it should switch to {@code network} regardless of whether it is
* validated or not. If {@code accept} is true, and the network was explicitly selected by the
* user (e.g., by selecting a Wi-Fi network in the Settings app), then the network will become
* the system default network regardless of any other network that's currently connected. If
* {@code always} is true, then the choice is remembered, so that the next time the user
* connects to this network, the system will switch to it.
*
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}
*
* @param network The network to accept.
* @param accept Whether to accept the network even if unvalidated.
* @param always Whether to remember this choice in the future.
*
* @hide
*/
public void setAcceptUnvalidated(Network network, boolean accept, boolean always) {
try {
mService.setAcceptUnvalidated(network, accept, always);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
Settings WifiNoInternetDialog 中使用 只是hide
反射下 发现要求权限 android.permission.CONNECTIVITY_INTERNAL 这个权限恶心了 系统权限
WifiNoInternetDialog
ConnectivityService 系统调用
if (DBG) log("handlePromptUnvalidated " + network);
NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
// Only prompt if the network is unvalidated and was explicitly selected by the user, and if
// we haven't already been told to switch to it regardless of whether it validated or not.
// Also don't prompt on captive portals because we're already prompting the user to sign in.
if (nai == null || nai.everValidated || nai.everCaptivePortalDetected ||
!nai.networkMisc.explicitlySelected || nai.networkMisc.acceptUnvalidated) {
return;
}
Intent intent = new Intent(ConnectivityManager.ACTION_PROMPT_UNVALIDATED);
intent.setData(Uri.fromParts("netId", Integer.toString(network.netId), null));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClassName("com.android.settings",
"com.android.settings.wifi.WifiNoInternetDialog");
PendingIntent pendingIntent = PendingIntent.getActivityAsUser(
mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
setProvNotificationVisibleIntent(true, nai.network.netId, NotificationType.NO_INTERNET,
nai.networkInfo.getType(), nai.networkInfo.getExtraInfo(), pendingIntent, true);
总结
1 找到强制使用wiifiAPI
1.1 setNetworkPreference 方法 android 5.0 已经删除
失败 接口删除
1.2 强制使用wiifiAPI
ConnectivityManager.setAcceptUnvalidated(mNetwork, accept, always);
反射调用失败 要求权限 android.permission.CONNECTIVITY_INTERNAL 这个权限恶心了 系统权限
失败 系统权限
2 关闭手机GPRS
android 5.0 之后 标识为 @SystemApi 一般应用 调用不了
编译系统签名的app
http://gqdy365.iteye.com/blog/2111949 实际商业项目不合适都有自己的签名
失败 系统API
3 使用 提示用户关闭GPRS
3.1 主动 调用 WifiNoInternetDialog 提示用户
系统调用
3.2 APP修改UI提示用户