Android: reconnectez-vous au Wi-Fi après être entré dans la zone de couverture pendant que l'écran est éteint

j'ai eu du mal à allumer automatiquement Wi-Fi dès que l'appareil est à portée d'un point d'accès sans allumer l'écran. Il a été très frustrant de tester et de trouver une solution, d'autant plus que différents appareils ont des résultats complètement différents.

Essai De Base

Gardez l'écran éteint pendant ce test. La demande doit contenir un WifiLock.

  1. sortez de la couverture WiFi et restez-y une minute.
  2. retournez à la couverture.

résultat : le Wifi n'est pas reconnecté immédiatement et donc l'application ne se reconnecte pas au serveur. Selon l'appareil et les réglages, parfois il ne se reconnectait pas du tout jusqu'à ce que l'écran soit activé.

forçant le Wi-Fi à se reconnecter

Ok, cette fois, mon l'application appelle WifiManager.Reconnecter () à un intervalle si Wifi est déconnecté.

a répété l'essai. Résultats : il a Travaillé pour le S3, a échoué pour d'autres appareils.

essayé d'ajouter d'autres appels

J'ai essayé différentes combinaisons de WifiManager.Scan(), WifiManager.Réassocier(), ...etc. Finalement, il fonctionnait pour la plupart des appareils(HTC, S3) à l'exception du S4.

Code qui semble fonctionner pour tous les appareils

NetworkInfo wifiInfo = _androidConnectivityMgr.GetNetworkInfo(ConnectivityType.Wifi);
if (!_wifiManager.IsWifiEnabled || _wifiManager.WifiState == WifiState.Disabled || _wifiManager.WifiState == WifiState.Disabling)
{
    // Make sure the Wi-Fi is enabled, required for some devices when enable WiFi does not occur immediately
    _wifiManager.SetWifiEnabled(true);
}

if (!wifiInfo.IsConnectedOrConnecting)
{
    // Do not wait for the OS to initiate a reconnect to a Wi-Fi router
    _wifiManager.PingSupplicant();
    if (_wifiManager.WifiState == WifiState.Enabled)
    {
        try
        {
            // Brute force methods required for some devices
            _wifiManager.SetWifiEnabled(false);
            _wifiManager.SetWifiEnabled(true);
        }
        catch (Java.Lang.SecurityException)
        {
            // Catching exception which should not occur on most devices. OS bug details at :
            // https://code.google.com/p/android/issues/detail?id=22036
        }
    }
    _wifiManager.Disconnect();
    _wifiManager.StartScan();
    _wifiManager.Reassociate();
    _wifiManager.Reconnect();
}

Je ne suis même pas sûr que tout ce code est nécessaire car je n'ai pas pu trouver beaucoup d'informations en ligne. WifiFixer ne aider certains. Mais cela semble fonctionner pour les appareils que j'ai testés sur.

La Question

  • y a-t-il une meilleure façon de faire cela?
  • Do les fabricants modifient vraiment la base Android où je peux voir cette beaucoup de différence?
  • est-ce vraiment la mauvaise façon d'aborder cela?

Merci pour la lecture par le biais de tout cela :)

Notes Complémentaires

  1. parcours de Code pendant un intervalle de plus de 10 Secondes initié par L'AlarmManager. WakeLock est détenu uniquement pour le la durée de cet appel.
  2. avant que cette solution finale d'aspect effrayant / hack la "Politique de sommeil Wifi" affecté les résultats. Cela m'a troublé puisque je tiens un WifiLock tout le temps, que je pensais être l'équivalent de "jamais".
  3. changer la" Politique de sommeil Wifi " programmatique ne fonctionne pas pour le S4, quelqu'un d'autre peut-il confirmer?
  4. Oui, nous avons un besoin spécifique de le faire et sommes conscients de l'implication de la batterie.
3
demandé sur Alex 2013-10-03 04:06:39

3 réponses

mon scénario est légèrement différent - Je ne tiens pas une serrure wifi pour commencer par (et je suis sur android régulier donc j'ai dû traduire votre méthode).

écran désactivé, CPU désactivé, radio morte. L'alarme réveille mon service (éveillé) - je tiens un verrou de réveil (partiel).

ce que je veux est - si le wifi est activé pour se connecter au point d'accès, il a été connecté avant la mort de la radio - je fais l'acquisition d'une serrure wifi et J'appelle votre fonction - wakeWifiUp() . Quand la radio est morte ( !wifiInfo.IsConnectedOrConnecting est vrai) j'obtiens un réseau inaccessible quand j'essaie de me connecter. je la solution de contournement comme dans :

public final class NetworkService extends WakefulIntentService {

    // this is an intent service - runs on its own thread - otherwise it would
    // deadlock as I am using it. Moreover it holds a wakelock and woken up by
    // an AlarmManager's Receiver - works reliably
    private BroadcastReceiver mConnectionReceiver;
    private volatile static CountDownLatch latch;

    @Override
    protected void doWakefulWork(Intent intent) {
        WifiLock _wifiLock = null;
        WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
        boolean failedToConnect = true;
        if (wm != null && wm.isWifiEnabled()) {// Don't want to enable it myself
            _wifiLock = wm.createWifiLock(
            /* WifiManager.WIFI_MODE_FULL_HIGH_PERF */0x3, this.getClass()
                .getName() + ".WIFI_LOCK");
            _wifiLock.acquire();
            failedToConnect = !wakeWifiUp();
        }
        if (failedToConnect) {
            if (_wifiLock != null) _wifiLock.release();
            w("No connection !");
            return;
        }
        HttpURLConnection connection = null;
        try {
            connection = connection(); 
        } catch (IOException e) {/* won't throw - it doesn't do much*/}
        OutputStream serverOutputStream = null;
        try {
            serverOutputStream = connection.getOutputStream(); // now
            // this is really where the connection might seriously throw
            // .... Work ....
        } catch (IOException e) {
            w("IOException sending data " + e.getMessage());
            // I get here : Network unreachable when radio dies
        } finally {
            if (_wifiLock != null) _wifiLock.release();
            if (connection != null) connection.disconnect();
        }
    }

    private HttpURLConnection connection() throws MalformedURLException,
            IOException {
        HttpURLConnection connection = (HttpURLConnection) new URL("localhost")
            .openConnection();
        connection.setDoOutput(true); // triggers POST
        connection.setRequestProperty("Connection", "Keep-Alive");
        connection.setRequestProperty("User-Agent",
            "Android Multipart HTTP Client 1.1");
        return connection;
    }

    private boolean wakeWifiUp() {
        ConnectivityManager _androidConnectivityMgr = (ConnectivityManager)
                getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo wifiInfo = _androidConnectivityMgr
            .getNetworkInfo(ConnectivityManager.TYPE_WIFI);
        WifiManager _wifiManager = (WifiManager)
                getSystemService(Context.WIFI_SERVICE);
        final int wifiState = _wifiManager.getWifiState();
        if (!_wifiManager.isWifiEnabled()
            || wifiState == WifiManager.WIFI_STATE_DISABLED
            || wifiState == WifiManager.WIFI_STATE_DISABLING) {
            // Make sure the Wi-Fi is enabled, required for some devices when
            // enable WiFi does not occur immediately
            d("!_wifiManager.isWifiEnabled()");
            _wifiManager.setWifiEnabled(true);
            // do not enable if not enabled ! FIXME
            return false;
        }
        if (!wifiInfo.isConnectedOrConnecting()) {
            d("Wifi is NOT Connected Or Connecting - "
                + "wake it up and wait till is up");
            // Do not wait for the OS to initiate a reconnect to a Wi-Fi router
            _wifiManager.pingSupplicant();
            if (wifiState == WifiManager.WIFI_STATE_ENABLED) {
                try {
                    // Brute force methods required for some devices
                    _wifiManager.setWifiEnabled(false);
                    _wifiManager.setWifiEnabled(true);
                } catch (SecurityException e) {
                    // Catching exception which should not occur on most
                    // devices. OS bug details at :
                    // https://code.google.com/p/android/issues/detail?id=22036
                }
            }
            _wifiManager.disconnect();
            _wifiManager.startScan();
            _wifiManager.reassociate();
            _wifiManager.reconnect();
            // THIS IS WHAT I DO TO WAIT FOR A CONNECTION
            try {
                mConnectionReceiver = new WifiConnectionMonitor();
                startMonitoringConnection();
                latch = new CountDownLatch(1);
                w("I wait");
                latch.await();
                w("Woke up");
                return true; // made it
            } catch (InterruptedException e) {
                w("Interrupted while waiting for connection", e);
                return false;
            } finally {
                stopMonitoringConnection();
            }
        }
        return true;
    }

    static void downTheLatch() {
        latch.countDown();
    }

    private synchronized void startMonitoringConnection() {
        IntentFilter aFilter = new IntentFilter(
            ConnectivityManager.CONNECTIVITY_ACTION);
        aFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
        registerReceiver(mConnectionReceiver, aFilter);
    }

    private synchronized void stopMonitoringConnection() {
        unregisterReceiver(mConnectionReceiver);
    }

    private final class WifiConnectionMonitor extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent in) {
            String action = in.getAction();
            if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
                NetworkInfo networkInfo = in
                    .getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
                d(networkInfo + "");
                if (networkInfo.isConnected()) {
                    d("Wifi is connected!");
                    NetworkService.downTheLatch(); // HERE THE SERVICE IS WOKEN!
                }
            }
        }
    }
}

Btw tous les trucs dans wakeWifiUp() ne sont pas nécessaires (dans mon cas) et tous les !_wifiManager.isWifiEnabled() peuvent être ommités - comme je n'utilise le réseau que si activé par l'utilisateur. Je le laisse pour être complet.

Récapitulation: dans mon scénario votre méthode n'était pas suffisante (si je traduit correctement en java et n'a pas fait une erreur stupide, qui s'applique toujours - voir aussi mon connection() ). J'avais besoin d'attendre que la connexion soit établie - mais alors tout allait bien. Pas sûr encore comment exactement vous l'utilisiez - si comme je le fais alors la différence pourrait être que vous teniez une serrure wifi tout au long

HTC Nexus 1, 2.3.7, Cyanogen mod (ne tirez pas que j'ai été donné de tester).

vous tiendra au courant

2
répondu Mr_and_Mrs_D 2013-11-14 03:14:14

les approches par Alex et Mr_and_Mrs_D étaient proches mais pas entièrement cohérentes sous Android 4.4 KitKat (Nexus 4). Cela peut avoir à voir avec les politiques D'économie D'énergie plus agressives de Google à partir de KitKat. J'ai utilisé une combinaison de leurs approches avec des modifications.

l'idée générale est que lors d'un contrôle WiFi périodique, démarrez explicitement un scan, puis dans les résultats de scan, le handler call reassociate() et reconnect(). En outre, dans le NETWORK_STATE_CHANGED_ACTION callback, vérifiez si la connexion est établie avant de libérer le verrou de veille. La clé est de maintenir le verrou de sillage assez longtemps pour que la connexion WiFi s'établisse correctement (et évidemment pas plus longtemps que nécessaire).

Contrôle WiFi périodique qui déclenche les choses:

public static void CheckWiFi() {  
    mWakeLock.acquire();
    if (!WiFi_Mgr.isWifiEnabled()) {
        WiFi_Mgr.setWifiEnabled(true);
    }
    WiFi_Mgr.startScan();

    //Set an alarm to fire after N seconds to release wake lock & shut off WiFi if no connection is available.
    // ... 
}

Registre pour les émissions WiFi

//Setup WiFi connection status receiver
IntentFilter WiFiFilters = new IntentFilter();
WiFiFilters.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
WiFiFilters.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
this.registerReceiver(WiFiReceiver, WiFiFilters);

et les gestionnaires D'événements WiFi

private final BroadcastReceiver WiFiReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if(WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
            NetworkInfo netInfo = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
            // This is the magic bullet. The connection usually establishes after 
            // the scan results callback so release the wake lock here.
            if(netInfo.isConnected()) {
                mWakeLock.release();
            }
        }
        //Search the scan results for saved WiFi APs.
        else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action))
        {
            boolean foundMatch = false;
            if (!IsWiFiConnected()) {
                Map<String, Integer> savedNetworks = new HashMap<String, Integer>();
                for (WifiConfiguration config : WiFi_Mgr.getConfiguredNetworks()) {
                    String escapedSSID = config.SSID.replaceAll("\"", "");
                    savedNetworks.put(escapedSSID, config.networkId);
                }
                List<ScanResult> scanResults = WiFi_Mgr.getScanResults();
                for (ScanResult ap : scanResults) {
                    Integer networkId = savedNetworks.get(ap.SSID);
                    if (networkId != null) {
                        savedNetworks.remove(ap.SSID);
                        WiFi_Mgr.enableNetwork(networkId, false);
                        foundMatch = true;
                    }
                }
            }
            if(foundMatch) {
                WiFi_Mgr.reassociate();
                WiFi_Mgr.reconnect();
            }
            if (IsWiFiConnected())
                mWakeLock.release();
        }
    }
};

vous devrez déclarer les variables nécessaires (par exemple, mWakeLock est un wakelock partiel, Non-référence compté; WiFi_Mgr est une instance de WifiManager; etc...).

1
répondu pmont 2014-04-25 20:41:29

a fait un second tour dans le secteur. Bien que la solution ci-dessus ait fonctionné pour tous nos appareils qualifiés, il y avait trop d'appels qui auraient pu être inutiles. De plus, nous avons un nouvel appareil pour lequel la solution n'a pas fonctionné. Voici une bien meilleure solution:

à chaque intervalle ce code est appelé

NetworkInfo wifiInfo = _androidConnectivityMgr.GetNetworkInfo(ConnectivityType.Wifi);
if (!wifiInfo.IsConnectedOrConnecting)
{
    // Need to make sure the CPU does not go to sleep before the following async calls are finished
    _wifiScanWakeLock.Acquire();

    // Do not wait for the OS to initiate a reconnect to a Wi-Fi router
    _wifiManager.StartScan();
}
  • _wifiScanWakeLock est seulement partielle, de la non-référence compté WakeLock, l'Éliminer OnDestroy

lorsque le balayage Wi-Fi est terminé

private void OnWifiScanResultsReceived(Intent result)
{
    NetworkInfo wifiInfo = _androidConnectivityMgr.GetNetworkInfo(ConnectivityType.Wifi);
    if (!wifiInfo.IsConnectedOrConnecting)
    {
        Dictionary<string, int> savedNetworks = new Dictionary<string, int>();
        foreach (WifiConfiguration config in _wifiManager.ConfiguredNetworks)
        {
            string escapedSsid = Regex.Replace(config.Ssid, "^\"|\"$", String.Empty);
            savedNetworks[escapedSsid] = config.NetworkId;
        }

        foreach (ScanResult ap in _wifiManager.ScanResults)
        {
            int networkId;
            if (savedNetworks.TryGetValue(ap.Ssid, out networkId))
            {
                savedNetworks.Remove(ap.Ssid);
                _wifiManager.EnableNetwork(networkId, false);
            }
        }
    }
    _wifiScanWakeLock.Release();
}
  • BSSID pour la configuration Wific est toujours nul et ne peut pas être utilisé que pour comparer uniquement à la BSSID du ScanResult
  • c'est le code de base, évidemment vous auriez à vous soucier du cas de deux SSIDs identiques et d'autres optimisations
0
répondu Alex 2014-03-18 16:19:11