web-dev-qa-db-fra.com

Désactivation de Samsung "Auto Network Switch" pour la connexion WiFi

Dans notre application, nous gérons l'établissement d'une connexion WiFi à un appareil qui diffuse son propre point d'accès sans fil (sans connexion Internet) pour une communication directe.

Cela fonctionne très bien sur tous nos appareils de test; cependant, nous recevons des rapports d'utilisateurs selon lesquels sur certains appareils Samsung (Galaxy S4, Galaxy Note 3) il y a un paramètre sous Paramètres Wi-Fi appelé "Auto Network Switch" que Samsung a ajouté qui recherche les réseaux "instables", et se déconnectera automatiquement et reviendra aux données mobiles. Malheureusement, comme notre appareil n'a pas de connexion Internet, Samsung le signale comme un réseau instable et se déconnecte immédiatement.

Je n'ai aucun de ces appareils disponibles pour les tests, donc je suis curieux de savoir si quelqu'un d'autre est au courant de ce problème ou connaît un moyen de désactiver par programmation ce paramètre ou de le contourner?

Le code que nous utilisons pour la connexion est:

/**
 * Attempt to connect to an open wifi network with the given SSID
 * @param ssid the SSID of the unsecured wireless network to connect to
 */
public static void connectToOpenNetwork (String ssid) {
    WifiManager mgr = getManager();
    WifiConfiguration configuration = getOpenWifiConfiguration(ssid);
    mgr.addNetwork(configuration);
    mgr.saveConfiguration();

    int res = findOpenNetworkId(ssid);
    if (res != INVALID_NETWORK_ID) {
        mgr.enableNetwork(res, true);
        mgr.reconnect();
    } else {
        Log.e(TAG, "Received request to connect to network " + ssid + " but the network was not found in the configurations.");
    }
}

/**
 * Get a WifiConfiguration object configured for an unsecured wireless network with the
 * given SSID.
 * @param ssid the SSID of the network to configure
 * @return a WifiConfiguration object that can be passed to
 * {@link WifiManager#addNetwork(Android.net.wifi.WifiConfiguration)}
 */
private static WifiConfiguration getOpenWifiConfiguration (String ssid) {
    WifiConfiguration config = new WifiConfiguration();

    config.SSID = "\"" + ssid + "\"";
    config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);

    return config;
}
13
kcoppock

EDIT: Donc, des recherches supplémentaires pour les personnes intéressées, il semble que ce soit une fonctionnalité ajoutée dans les versions Samsung Touchwiz basées sur 4.3. En interne, le paramètre est nommé "wifi_watchdog_connectivity_check". J'utilise toujours le code ci-dessous pour voir si je peux vérifier sûr si le paramètre est activé ou non, mais sinon je devrais supposer qu'il est activé.

Donc, ce que j'ai découvert, c'est qu'après avoir tenté d'établir une connexion et que le système d'exploitation s'éloigne du réseau, la configuration Wi-Fi est à l'état "désactivé". Ainsi, une fois le problème survenu, vous pouvez détecter avec certitude qu'il s'est produit en vérifiant l'état de la configuration à partir de WifiManager.

WifiManager m = (WifiManger) getSystemService(Context.WIFI_SERVICE);
List<WifiConfiguration> networks = m.getConfiguredNetworks();
String mySsid = "My Network";
mySsid = "\"" + mySsid + "\"";

boolean isDisabled = false;
for (WifiConfiguration config : networks) {
    if (mySsid.equals(config.SSID)) {
        if (config.status = WifiConfiguration.Status.DISABLED) {
            isDisabled = true;
            break;
        }
    }
}

//If isDisabled is true, the network was disabled by the OS

Vous pouvez ensuite essayer de résoudre le nom du paramètre à partir de l'application des paramètres système:

/** Gets the resources of another installed application */
private static Resources getExternalResources(Context ctx, String namespace) {
    PackageManager pm = ctx.getPackageManager();
    try {
        return (pm == null) ? null : pm.getResourcesForApplication(namespace);
    } catch (PackageManager.NameNotFoundException ex) {
        return null;
    }
}

/** Gets a resource ID from another installed application */
private static int getExternalIdentifier(Context ctx, String namespace, 
        String key, String type) {
    Resources res = getExternalResources(ctx, namespace);
    return (res == null) ? 0 : res.getIdentifier(key, type, namespace);
}

/** Gets a String resource from another installed application */
public static String getExternalString(Context ctx, String namespace, 
        String key, String defVal) {
    int resId = getExternalIdentifier(ctx, namespace, key, "string");
    if (resId != 0) {
        Resources res = getExternalResources(ctx, namespace);
        return res.getString(resId);
    } else {
        return defVal;
    }
}

Ensuite, utilisez-le pour obtenir la chaîne:

String autoNetworkSwitch = getExternalString(this, "com.Android.settings",
        "wifi_watchdog_connectivity_check", "Unknown");

Cela renverra une chaîne localisée pour la langue de l'utilisateur actuel, si la chaîne existe.


Pour toute personne intéressée par les résultats, il s'avère que cette option est en fait un stock Android paramètre, mais il semble être plus agressif sur ces appareils Samsung. Le paramètre est un paramètre caché trouvé dans Android.provider.Settings.Java:

/**
 * Setting to turn off poor network avoidance on Wi-Fi. Feature is enabled by default and
 * the setting needs to be set to 0 to disable it.
 * @hide
 */
public static final String WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED =
       "wifi_watchdog_poor_network_test_enabled";

Lequel est dedans Settings$Secure pour API == 15 || API == 16, ou Settings$Global pour API >= 17. Ce n'est pas un paramètre qui peut être activé ou désactivé par des applications tierces; cependant, il peut être détecté et mis en garde contre. Ma solution est la suivante:

import static Android.os.Build.VERSION.SDK_INT;
import static Android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1;
import static Android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;

/**
 * Checks whether the "Avoid poor networks" setting (named "Auto network switch" on 
 * some Samsung devices) is enabled, which can in some instances interfere with Wi-Fi.
 *
 * @return true if the "Avoid poor networks" or "Auto network switch" setting is enabled
 */
public static boolean isPoorNetworkAvoidanceEnabled (Context ctx) {
    final int SETTING_UNKNOWN = -1;
    final int SETTING_ENABLED = 1;
    final String AVOID_POOR = "wifi_watchdog_poor_network_test_enabled";
    final String WATCHDOG_CLASS = "Android.net.wifi.WifiWatchdogStateMachine";
    final String DEFAULT_ENABLED = "DEFAULT_POOR_NETWORK_AVOIDANCE_ENABLED";
    final ContentResolver cr = ctx.getContentResolver();

    int result;

    if (SDK_INT >= JELLY_BEAN_MR1) {
        //Setting was moved from Secure to Global as of JB MR1
        result = Settings.Global.getInt(cr, AVOID_POOR, SETTING_UNKNOWN);
    } else if (SDK_INT >= ICE_CREAM_SANDWICH_MR1) {
        result = Settings.Secure.getInt(cr, AVOID_POOR, SETTING_UNKNOWN);
    } else {
        //Poor network avoidance not introduced until ICS MR1
        //See Android.provider.Settings.Java
        return false;
    }

    //Exit here if the setting value is known
    if (result != SETTING_UNKNOWN) {
        return (result == SETTING_ENABLED);
    }

    //Setting does not exist in database, so it has never been changed.
    //It will be initialized to the default value.
    if (SDK_INT >= JELLY_BEAN_MR1) {
        //As of JB MR1, a constant was added to WifiWatchdogStateMachine to determine 
        //the default behavior of the Avoid Poor Networks setting.
        try {
            //In the case of any failures here, take the safe route and assume the 
            //setting is disabled to avoid disrupting the user with false information
            Class wifiWatchdog = Class.forName(WATCHDOG_CLASS);
            Field defValue = wifiWatchdog.getField(DEFAULT_ENABLED);
            if (!defValue.isAccessible()) defValue.setAccessible(true);
            return defValue.getBoolean(null);
        } catch (IllegalAccessException ex) {
            return false;
        } catch (NoSuchFieldException ex) {
            return false;
        } catch (ClassNotFoundException ex) {
            return false;
        } catch (IllegalArgumentException ex) {
            return false;
        }
    } else {
        //Prior to JB MR1, the default for the Avoid Poor Networks setting was
        //to enable it unless explicitly disabled
        return true;
    }
}

Pour faire bonne mesure, vous pouvez ensuite diriger vos utilisateurs vers les paramètres Wi-Fi avancés au moyen de Intent:

/**
 *  Ensure that an Activity is available to receive the given Intent
 */
public static boolean activityExists (Context ctx, Intent intent) {
    final PackageManager mgr = ctx.getPackageManager();
    final ResolveInfo info = mgr.resolveActivity(i, PackageManager.MATCH_DEFAULT_ONLY);
    return (info != null);
}

public static void showAdvancedWifiIfAvailable (Context ctx) {
    final Intent i = new Intent(Settings.ACTION_WIFI_IP_SETTINGS);
    if (activityExists(ctx, i)) {
        ctx.startActivity(i);
    }
}

Anecdote intéressante: ce Intent affiche le même Activity que dans Paramètres> Wi-Fi> Avancé, mais l'affichera avec un titre différent (Paramètres IP, vs Wi-Fi avancé).

21
kcoppock