package com.thibaudperso.sonycamera.timelapse.control.io; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkInfo; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Handler; import java.util.ArrayList; import java.util.List; public class WifiHandler { public enum State {DISCONNECTED, SCANNING, CONNECTING, CONNECTED} private Context mContext; private WifiManager mWifiManager; private boolean wasWifiDisabled; private WifiInfo lastWifiConnected; private List<Listener> stateListeners; private State mState; public WifiHandler(Context context) { this.mContext = context; mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); wasWifiDisabled = mWifiManager.getWifiState() == WifiManager.WIFI_STATE_DISABLED; mWifiManager.setWifiEnabled(true); stateListeners = new ArrayList<>(); mState = State.DISCONNECTED; isConnected(); } public boolean isConnected() { WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); if (wifiInfo == null) { mState = State.DISCONNECTED; return false; } String ssid = parseSSID(wifiInfo.getSSID()); if(isSonyCameraSSID(ssid)) { bindProcessToWifiNetwork(); mState = State.CONNECTED; return true; } mState = State.DISCONNECTED; return false; } public void scanWifiConnections(final ScanListener listener) { if (isConnected() || mState == State.SCANNING || listener == null) { return; } mState = State.SCANNING; final Handler handler = new Handler(); mContext.registerReceiver( new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { context.unregisterReceiver(this); final List<WifiConfiguration> sonyCameraWifiConfiguration = new ArrayList<>(); final List<ScanResult> sonyCameraScanResults = new ArrayList<>(); for (ScanResult sr : mWifiManager.getScanResults()) { if (!isSonyCameraSSID(sr.SSID)) { continue; } sonyCameraScanResults.add(sr); WifiConfiguration wc = getWifiConfigurationFromSSID(sr.SSID); if (wc == null) { continue; } sonyCameraWifiConfiguration.add(wc); } mState = State.DISCONNECTED; handler.post(new Runnable() { @Override public void run() { listener.onWifiScanFinished(sonyCameraScanResults, sonyCameraWifiConfiguration); } }); } }, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)); mWifiManager.startScan(); } public void createIfNeededThenConnectToWifi(String networkSSID, String networkPassword) { int netId = -1; List<WifiConfiguration> list = mWifiManager.getConfiguredNetworks(); if (list != null) { for (WifiConfiguration i : list) { // In the case of network is already registered if (i.SSID != null && i.SSID.equals("\"" + networkSSID + "\"")) { // In case password changed since last time i.preSharedKey = "\"" + networkPassword + "\""; mWifiManager.saveConfiguration(); netId = i.networkId; break; } } } // In the case of network is not registered create it and join it if (netId == -1) { netId = createWPAWifi(networkSSID, networkPassword); } connectToNetworkId(netId); } public void connectToNetworkId(int netId) { List<WifiConfiguration> configuredNetworks = mWifiManager.getConfiguredNetworks(); for (WifiConfiguration wc : configuredNetworks) { if (wc.networkId != netId) { continue; } WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); if (wifiInfo != null) { String ssid = parseSSID(mWifiManager.getConnectionInfo().getSSID()); if (!isSonyCameraSSID(ssid)) { lastWifiConnected = mWifiManager.getConnectionInfo(); } } // Connect only if network id exist mWifiManager.enableNetwork(netId, true); for (Listener listener : stateListeners) { listener.onWifiConnecting(wc.SSID); } } } private int createWPAWifi(String networkSSID, String networkPassword) { WifiConfiguration wc = new WifiConfiguration(); wc.SSID = "\"" + networkSSID + "\""; wc.preSharedKey = "\"" + networkPassword + "\""; wc.hiddenSSID = true; wc.status = WifiConfiguration.Status.ENABLED; wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); wc.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); wc.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP); wc.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); wc.allowedProtocols.set(WifiConfiguration.Protocol.RSN); int netId = mWifiManager.addNetwork(wc); mWifiManager.saveConfiguration(); return netId; } private static boolean isSonyCameraSSID(String ssid) { return ssid != null && ssid.matches("^DIRECT-\\w{4}:.*$"); } private WifiConfiguration getWifiConfigurationFromSSID(String SSID) { List<WifiConfiguration> knownNetworks = mWifiManager.getConfiguredNetworks(); if (knownNetworks == null) { return null; } for (WifiConfiguration net : knownNetworks) { if (net.SSID != null && net.SSID.equals("\"" + SSID + "\"")) { return net; } } return null; } private boolean bindProcessToWifiNetwork() { // Workaround when there is a data connection more than the wifi one // http://stackoverflow.com/questions/33237074/request-over-wifi-on-android-m if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { ConnectivityManager connectivityManager = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); Network activeNetwork = connectivityManager.getActiveNetwork(); for (Network net : connectivityManager.getAllNetworks()) { if (!net.equals(activeNetwork)) { connectivityManager.bindProcessToNetwork(net); return true; } } return false; } return true; } public void addListener(Listener listener) { if (stateListeners.contains(listener)) { return; } stateListeners.add(listener); if (stateListeners.size() > 0) { final IntentFilter filters = new IntentFilter(); filters.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); mContext.registerReceiver(connectivityBroadcastReceiver, filters); } } public void removeListener(Listener listener) { stateListeners.remove(listener); if (stateListeners.size() == 0) { try { mContext.unregisterReceiver(connectivityBroadcastReceiver); } catch (IllegalArgumentException ignored) { } } } private BroadcastReceiver connectivityBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { NetworkInfo networkInfo = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); if (networkInfo == null || networkInfo.getType() != ConnectivityManager.TYPE_WIFI) { return; } NetworkInfo.State state = networkInfo.getState(); if (mState == State.CONNECTED && state == NetworkInfo.State.DISCONNECTED) { mState = State.DISCONNECTED; for (Listener listener : stateListeners) { listener.onWifiDisconnected(); } } else if (state == NetworkInfo.State.CONNECTED && mState != State.CONNECTED) { WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); String ssid; if (wifiInfo == null || !isSonyCameraSSID(ssid = parseSSID(wifiInfo.getSSID())) || !bindProcessToWifiNetwork()) { return; } mState = State.CONNECTED; for (Listener listener : stateListeners) { listener.onWifiConnected(ssid); } } } }; private String parseSSID(String ssid) { if (ssid != null && ssid.length() >= 2 && ssid.charAt(0) == '"' && ssid.charAt(ssid.length() - 1) == '"') { return ssid.substring(1, ssid.length() - 1); } return ssid; } public void disconnect() { mState = State.DISCONNECTED; //reconnect to last wifi if (lastWifiConnected != null && lastWifiConnected.getNetworkId() != -1) { mWifiManager.enableNetwork(lastWifiConnected.getNetworkId(), true); } else { //no previous wifi: just disconnect from the camera's mWifiManager.disconnect(); //disable wifi as well if it was disabled on app start if (wasWifiDisabled) { mWifiManager.setWifiEnabled(false); } } } public interface ScanListener { void onWifiScanFinished(List<ScanResult> sonyCameraScanResults, List<WifiConfiguration> knownSonyCameraConfigurations); } public interface Listener { void onWifiConnecting(String ssid); void onWifiConnected(String ssid); void onWifiDisconnected(); } }