Il metodo setMobileDataEnabled non è più richiamabile come di Android L e più tardi

? ChuongPham @ | Original: StackOverFlow

Ho effettuato l'accesso 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 con Google per quanto riguarda il metodo setMobileDataEnabled() essere più richiamabile tramite riflessione . Era callable dal Android 2.1 ( API 7) per Android 4.4 ( API 19 ) attraverso la riflessione, ma come di Android L e più tardi, anche con la radice, il metodo setMobileDataEnabled() non è callable .

La risposta ufficiale è che la questione è "chiuso" e lo stato impostato su " WorkingAsIntended " . Semplice spiegazione di Google è :

API private sono private perché non sono stabili e potrebbero scomparire senza preavviso .

Sì, Google, siamo consapevoli del rischio di usare riflessione chiamare nascosto metodo- prima ancora che Android è venuto sulla scene- ma è necessario fornire una risposta più solida come ad alternative, se del caso, per realizzare lo stesso risultato di setMobileDataEnabled() . ( Se si sta dispiaciuto con la decisione di Google come me, poi accedere 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 la stella che il maggior numero possibile di lasciare Google conoscere l'errore del loro modo . )

Quindi, la mia domanda è : Stiamo in un vicolo cieco quando si tratta di attivare o disattivare la funzione di rete mobile su un dispositivo Android di programmazione ? Questo approccio mano pesante da parte di Google in qualche modo non mi sta bene . Se si dispone di soluzione per Android 5.0 ( Lollipop ) e al di là, mi piacerebbe sentire la tua risposta / discussione in questo thread.

Ho usato il codice qui sotto per vedere se la setMobileDataEnabled() Metodo è disponibile :

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

Ma non lo è.

UPDATE : Attualmente, è possibile commutare rete mobile, se il dispositivo è radicata . Tuttavia, per i dispositivi non - radicata, è ancora un processo investigativo in quanto non esiste un metodo universale per passare da rete mobile .



Top 5 Respuesta

1Muzikant @

Solo per condividere alcuni ulteriori approfondimenti e possibile soluzione ( per dispositivi radicate e applicazioni di sistema) .

Solution #1

Sembra che il setMobileDataEnabled metodo non esiste più in ConnectivityManager e questa funzionalità è stato spostato a TelephonyManager con due metodi getDataEnabled e setDataEnabled . Ho provato a chiamare questi metodi con la riflessione, come si può vedere nel seguente codice :

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

Quando si esegue il codice si ottiene un SecurityException affermando che Neither user 10089 nor current process has android.permission.MODIFY_PHONE_STATE.

Quindi, sì, questo è un cambiamento previsto per l'API interna e non è più disponibile per le applicazioni che utilizzate che mod nelle versioni precedenti .

(inizio rant : che android.permission.MODIFY_PHONE_STATE permesso terribile ... fine rant ) .

La buona notizia è che nel caso in cui si sta creando un app che può acquisire l'autorizzazione MODIFY_PHONE_STATE ( solo applicazioni di sistema possono utilizzare tale ), è possibile utilizzare il codice qui sopra per attivare lo stato di dati mobile.

Solution #2

Per controllare lo stato attuale dei dati mobili è possibile utilizzare il campo mobile_data di Settings.Global ( non documentato in documentazione ufficiale ) .

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

E per abilitare / disabilitare dati cellulare è possibile utilizzare i comandi di shell su dispositivi radicate ( solo test di base eseguiti in modo tutte le risposte nei commenti è apprezzato ) . È possibile eseguire il seguente comando ( s ) come root ( 1 = consentono, 0 = disable ) :

settings put global mobile_data 1
settings put global mobile_data 0


2ChuongPham @

Per estendere di Muzikant Soluzione # 2, qualcuno può provare la soluzione qui di seguito su un dispositivo Android 5.0 radicata ( come io attualmente non possiedono uno) e fatemi sapere se funziona o non funziona .

Per attivare o disattivare i dati mobili, provare :

// 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: Il mobile_data variabile può essere trovato in API 21 codici sorgente di Android a /android-sdk/sources/android-21/android/provider/Settings.java e viene dichiarato come :

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

Mentre il android.intent.action.ANY_DATA_STATE Intent può essere trovato in Android API 21 codici sorgente a /android-sdk/sources/android-21/com/android/internal/telephony/TelephonyIntents.java e viene dichiarato come :

/**
 * 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 non si desidera implementare i codici Java sopra nell'applicazione Android, quindi è possibile eseguire il su comandi tramite una shell ( Linux) o prompt dei comandi ( Windows) come 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 si trova a directory /android-sdk/platform-tools/ . Il comando settings è supportato solo su Android 4.2 o versioni successive . Older versione Android riporterà un errore "sh: settings: not found" .

UPDATE 2 : Un altro modo per attivare rete mobile su un dispositivo radicata Android 5+ sarebbe quella di utilizzare il service il comando non documentato shell. Il seguente comando può essere eseguito tramite ADB per commutare rete mobile :

// 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 si trova a directory /android-sdk/platform-tools/ . Se non si desidera utilizzare ADB, eseguire il metodo tramite su nella vostra applicazione .

3rgruet @

Soluzione # 1 da Muzikant sembra funzionare se si effettua il " sistema " app spostando il .apk nella cartella /system/priv-app/, non al /system/app/ uno ( jaumard : forse è per questo il test non ha funzionato ) .

Quando il .apk si trova nella cartella /system/priv-app/, si può richiedere il successo terribile android.permission.MODIFY_PHONE_STATE autorizzazione del Manifesto e chiamare TelephonyManager.setDataEnabled e TelephonyManager.getDataEnabled .

Almeno che funziona su Nexus 5 / Android 5.0 . Le permanenti .apk sono 0144 . È necessario riavviare il dispositivo per il cambio da prendere in considerazione, forse questo potrebbe essere evitato - vedi http://stackoverflow.com/questions/26487750/android-5-0-lollipop-force-rescan-of-system- priv -app .

4Sahil Lombar @

Per correggere Muzikant Soluzione # 2

settings put global mobile_data 1

Non consentire solo la leva per i dati mobili, ma non fa nulla per la connettività . Solo la ginocchiera è abilitata . Al fine di ottenere i dati di lavoro utilizzando

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

Dà errore come il supplemento per

android.intent.action.ANY_DATA_STATE

Richiede String Oggetto mentre il parametro --ez viene utilizzato per booleano . Rif : PhoneGlobals.java & amp ; PhoneConstants.java . Dopo aver utilizzato il collegamento o collegato come usando il comando

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

Ancora doesnt fare nulla per abilitare i dati .