O método setMobileDataEnabled não é exigível a partir do Android L e mais tarde

? ChuongPham @ | Original: StackOverFlow
---

Tenho conectado https://code.google.com/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars&groupby=&sort=&id=78084 Google com relação ao método de setMobileDataEnabled() já não sendo exigível via reflexão. Era exigível desde Android 2.1 (API 7) para o Android 4.4 (API 19 ), através de reflexão, mas a partir de Android L e mais tarde, mesmo com a raiz, o método setMobileDataEnabled() não é exigível.

A resposta oficial é que a questão é " fechado " eo status definido como " WorkingAsIntended " . Explicação simples do Google é :

APIs privadas são privadas, porque eles não são estáveis ​​e podem desaparecer sem aviso prévio.

Sim, o Google, estamos cientes do risco do uso de reflexão para chamar escondido dologia mesmo antes Android veio no scene- mas você precisa dar uma resposta mais sólida quanto a alternativas, se houver, para realizar o mesmo resultado que setMobileDataEnabled() . ( Se você está descontente com a decisão do Google como eu sou, então registrar em https://code.google.com/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars&groupby=&sort=&id=78084 e estrela -lo o máximo possível para deixar o Google sabe o erro do seu caminho . )

Então, minha pergunta para você é : Será que estamos em um beco sem saída quando se trata de ativar ou desativar a função de rede móvel em um dispositivo Android de programação? Esta abordagem de mão pesada do Google de alguma forma não se sente bem comigo . Se você tiver uma solução para Android 5.0 ( Lollipop ) e mais além, eu gostaria de ouvir a sua resposta / discussão nesta discussão.

Eu tenho usado o código abaixo para ver se o setMobileDataEnabled() método está disponível :

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

Mas não é .

UPDATE: Atualmente, é possível alternar rede móvel, se o dispositivo está enraizada . No entanto, para dispositivos não enraizadas, ainda é um processo investigativo como não existe um método universal para alternar rede móvel.

---

Top 5 Responder

1Muzikant @

Só para compartilhar um pouco mais esclarecimentos e possível solução (para dispositivos enraizadas e aplicativos do sistema) .

Solution #1

Parece que o setMobileDataEnabled método não existe mais no ConnectivityManager e essa funcionalidade foi transferido para TelephonyManager com dois métodos getDataEnabled e setDataEnabled . Eu tentei chamar esses métodos com reflexão como você pode ver no código abaixo :

public void setMobileDataState(boolean mobileDataEnabled)
{
    try
    {
        TelephonyManager telephonyService = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);

        Method setMobileDataEnabledMethod = telephonyService.getClass().getDeclaredMethod("setDataEnabled", boolean.class);

        if (null != setMobileDataEnabledMethod)
        {
            setMobileDataEnabledMethod.invoke(telephonyService, mobileDataEnabled);
        }
    }
    catch (Exception ex)
    {
        Log.e(TAG, "Error setting mobile data state", ex);
    }
}

public boolean getMobileDataState()
{
    try
    {
        TelephonyManager telephonyService = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);

        Method getMobileDataEnabledMethod = telephonyService.getClass().getDeclaredMethod("getDataEnabled");

        if (null != getMobileDataEnabledMethod)
        {
            boolean mobileDataEnabled = (Boolean) getMobileDataEnabledMethod.invoke(telephonyService);

            return mobileDataEnabled;
        }
    }
    catch (Exception ex)
    {
        Log.e(TAG, "Error getting mobile data state", ex);
    }

    return false;
}

Ao executar o código que você obter um SecurityException, afirmando que Neither user 10089 nor current process has android.permission.MODIFY_PHONE_STATE.

Então, sim, este é um projecto de alteração do API interna e não está mais disponível para aplicativos que utilizem essa corte nas versões anteriores.

( iniciar discurso : que a permissão android.permission.MODIFY_PHONE_STATE terrível ... discurso final ) .

A boa notícia é que, no caso você está construindo um aplicativo que pode adquirir a permissão MODIFY_PHONE_STATE (apenas aplicativos do sistema pode usar isso), você pode usar o código acima para alternar estado de dados móvel.

Solution #2

Para verificar se há estado atual de dados móvel que você pode usar o campo mobile_data de Settings.Global (não documentado na documentação oficial).

Settings.Global.getInt(contentResolver, "mobile_data");

E para ativar / desativar dados móvel que você pode usar comandos shell em dispositivos enraizadas (Just testes básicos realizados de modo algum feedback nos comentários é apreciado ) . Você pode executar o seguinte comando (s) como root ( 1 = permitir, 0 = disable ):

settings put global mobile_data 1
settings put global mobile_data 0
2ChuongPham @

Para estender de Muzikant Solução # 2, alguém por favor pode tentar a solução abaixo em um dispositivo enraizada Android 5.0 (como eu atualmente não possuem um) e deixe-me saber se funciona ou não funciona.

Para ativar ou desativar dados móveis, tente:

// 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

Nota: A variável mobile_data pode ser encontrado na API 21 códigos-fonte do Android em /android-sdk/sources/android-21/android/provider/Settings.java e é declarada como :

/**
 * Whether mobile data connections are allowed by the user.  See
 * ConnectivityManager for more info.
 * @hide
*/
public static final String MOBILE_DATA = "mobile_data";

Enquanto o android.intent.action.ANY_DATA_STATE Intenção podem ser encontrados no Android API 21 códigos-fonte em /android-sdk/sources/android-21/com/android/internal/telephony/TelephonyIntents.java e é declarada como :

/**
 * 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";

Update 1: Se você não quiser implementar os códigos Java acima em seu aplicativo Android, então você pode executar o su comandos através de um shell (Linux) ou prompt de comando (Windows) como segue:

adb shell "su -c 'settings put global mobile_data 1; am broadcast -a android.intent.action.ANY_DATA_STATE --ez state 1'"

Nota: adb Está localizado no diretório /android-sdk/platform-tools/ . O comando settings é suportado apenas no Android 4.2 ou posterior . Versão mais antiga do Android irá relatar um erro "sh: settings: not found" .

UPDATE 2 : Outra forma de ativar rede móvel em um dispositivo enraizada Android 5+ seria usar o service command shell em situação irregular . O seguinte comando pode ser executado via ADB para alternar rede móvel :

// 1: Enable; 0: Disable
adb shell "su -c 'service call phone 83 i32 1'"

Or just:

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

Nota: adb Está localizado no diretório /android-sdk/platform-tools/ . Se você não quiser usar ADB, executar o método via su em seu app .

3rgruet @

Solução # 1 de Muzikant parece funcionar se você fizer o app " sistema ", movendo o .apk para a pasta /system/priv-app/, não para o /system/app/ one ( jaumard : talvez por isso o teste não funcionou ) .

Quando o .apk está na pasta /system/priv-app/, ele pode solicitar com sucesso o terrível android.permission.MODIFY_PHONE_STATE permissão no manifesto e chamar TelephonyManager.setDataEnabled e TelephonyManager.getDataEnabled .

Pelo menos que funciona em Nexus 5 / Android 5.0. As permanentes .apk são 0144 . Você precisa reiniciar o dispositivo para a mudança a ser levado em conta, talvez isso poderia ser evitado - ver http://stackoverflow.com/questions/26487750/android-5-0-lollipop-force-rescan-of-system- priv -app .

4Sahil Lombar @

Para corrigir Muzikant Solução # 2

settings put global mobile_data 1

Será que permitir apenas a alternância de dados móveis, mas não faz nada para a conectividade . Somente a alternância está habilitado. A fim de obter os dados de trabalho usando

su -c am broadcast -a android.intent.action.ANY_DATA_STATE --ez state 1

Dá erro como o extra para

android.intent.action.ANY_DATA_STATE

Requer objeto String enquanto parâmetro --ez é usado para boolean . Ref : PhoneGlobals.java & amp ; PhoneConstants.java . Depois de usar a conexão ou conectados como extra usando comando

su -c am broadcast -a android.intent.action.ANY_DATA_STATE --es state connecting

Ainda esquentar fazer nada para permitir que os dados .