筆者最近遇到一個非常有意思的bug,貼出來和大家分享下。
那是一個溫暖的早晨,陽光曬得人很舒服。一封bug郵件像一片葉子飄到我的郵箱。
一番交流,筆者確認負責的Widget開關在Android5.0以上系統沒有作用。相信很多做過移動網絡開關的朋友都知道,傳統的方法是在ConnectivityManager中通過反射兩個方法setMobileDataEnabled和getMobileDataEnabled來控制移動網絡開和關的。
/**
* 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) {
}
}
但是打開5.0以上的源碼,這兩個方法已經不存在了。
推薦一個不錯的在線看源碼的網站。在線源碼網址。
此時老大的提醒幫了我,我打開5.0以上代碼的TelephonyMananger類嘗試通過反射獲取setDataEnabled和getDataEnabled類完成操作。
源碼
/** @hide */
@SystemApi
public void setDataEnabled(boolean enable) {
try {
getITelephony().setDataEnabled(enable);
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#setDataEnabled", e);
}
}
/** @hide */
@SystemApi
public boolean getDataEnabled() {
try {
return getITelephony().getDataEnabled();
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#getDataEnabled", e);
}
return false;
}
我的反射方法:
public void setMobileDataState(Context cxt, boolean mobileDataEnabled) {
TelephonyManager telephonyService = (TelephonyManager) cxt.getSystemService(Context.TELEPHONY_SERVICE);
try {
Method setMobileDataEnabledMethod = telephonyService.getClass().getDeclaredMethod("setDataEnabled", boolean.class);
if (null != setMobileDataEnabledMethod)
{
setMobileDataEnabledMethod.invoke(telephonyService, mobileDataEnabled);
}
}
catch (Exception e) {
LogHelper.v(TAG, "Error setting" + ((InvocationTargetException)e).getTargetException() + telephonyService);
}
}
public boolean getMobileDataState(Context cxt) {
TelephonyManager telephonyService = (TelephonyManager) cxt.getSystemService(Context.TELEPHONY_SERVICE);
try {
Method getMobileDataEnabledMethod = telephonyService.getClass().getDeclaredMethod("getDataEnabled");
if (null != getMobileDataEnabledMethod)
{
boolean mobileDataEnabled = (Boolean) getMobileDataEnabledMethod.invoke(telephonyService);
return mobileDataEnabled;
}
}
catch (Exception e) {
LogHelper.v(TAG, "Error getting" + ((InvocationTargetException)e).getTargetException() + telephonyService);
}
return false;
}
但是報了InvocationTargetException錯誤。通過:
((InvocationTargetException)e).getTargetException()
方法得知是執行反射執行時發生如下錯誤:
Error settingjava.lang.SecurityException: Neither user 10240 nor current process has android.permission.MODIFY_PHONE_STATE.android.telephony.TelephonyManager
這個時候老大提醒我把APP移動到system/app下試試。
幾番嘗試,仍是同樣的結果。
這個時候一個我無意的操作,將APP移動到system/priv-app,竟然成功了。在這個目錄下,開關又可以重新工作了。
當然,我們的應用不可能裝在這個目錄下,只能採取折中的方案。但整個過程還是很開心的,這個bug算是我第一次發現了不同Android版本之間源碼的差異,感覺很有意思。希望對大家有所啓發。