/* * Copyright (C) 2020, University of the Basque Country (UPV/EHU) * Contact for licensing options: * * The original file was part of Open Source IMSDROID * Copyright (C) 2010-2011, Mamadou Diop. * Copyright (C) 2011, Doubango Telecom. * * * Contact: Mamadou Diop * * This file is part of Open Source Doubango Framework. * * This is free software: you can redistribute it and/or modify it under the terms of * the GNU General Public License as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * This is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.doubango.ngn.services.impl; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.AuthAlgorithm; import android.net.wifi.WifiConfiguration.GroupCipher; import android.net.wifi.WifiConfiguration.KeyMgmt; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiManager.WifiLock; import android.telephony.TelephonyManager; import android.util.Log; import android.widget.Toast; import org.doubango.ngn.BuildConfig; import org.doubango.ngn.NgnApplication; import org.doubango.ngn.NgnEngine; import org.doubango.ngn.model.NgnAccessPoint; import org.doubango.ngn.services.INgnNetworkService; import org.doubango.ngn.utils.NgnConfigurationEntry; import org.doubango.ngn.utils.NgnObservableList; import org.doubango.ngn.utils.NgnStringUtils; import org.doubango.utils.Utils; import java.io.IOException; import java.lang.reflect.Method; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.UnknownHostException; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /**@page NgnNetworkService_page Network Service * The network service is used to manage both WiFi and 3g/4g network connections. */ public class NgnNetworkService extends NgnBaseService implements INgnNetworkService { private static final String TAG = Utils.getTAG(NgnNetworkService.class.getCanonicalName()); private static final String OPENVPN_INTERFACE_NAME = "tun0"; @SuppressWarnings("unused") private static final String WLAN_INTERFACE_NAME = "wlan0"; private static final String USB_INTERFACE_NAME = "usb0"; private WifiManager mWifiManager; private WifiLock mWifiLock; private String mConnetedSSID; private boolean mAcquired; private boolean mStarted; private boolean mScanning; private final NgnObservableList mAccessPoints; private BroadcastReceiver mNetworkWatcher; private static int WifiManager_WIFI_MODE = WifiManager.WIFI_MODE_FULL; private static int ConnectivityManager_TYPE_ETHERNET = 0x00000009; private static int ConnectivityManager_TYPE_WIMAX = 0x00000006; private static Method NetworkInterface_isUp; static{ final int sdkVersion = NgnApplication.getSDKVersion(); if(sdkVersion >= 9){ try { WifiManager_WIFI_MODE = WifiManager.class.getDeclaredField("WIFI_MODE_FULL_HIGH_PERF").getInt(null); } catch (Exception e) { Log.e(TAG, e.toString()); } } if(sdkVersion >= 13){ try { ConnectivityManager_TYPE_ETHERNET = ConnectivityManager.class.getDeclaredField("TYPE_ETHERNET").getInt(null); } catch (Exception e) { Log.e(TAG, e.toString()); } } if(sdkVersion >= 8){ try { ConnectivityManager_TYPE_WIMAX = ConnectivityManager.class.getDeclaredField("TYPE_WIMAX").getInt(null); } catch (Exception e) { Log.e(TAG, e.toString()); } } // according to the documentation, NetworkInterface::isUp() is only defined starting API Level 9 but it's available on my GS1 (API Level 8) // this is why we don't test sdk version try{ NetworkInterface_isUp = NetworkInterface.class.getDeclaredMethod("isUp"); } catch (Exception e) { } } public static final int[] sWifiSignalValues = new int[] { 0, 1, 2, 3, 4 }; public static enum DNS_TYPE { DNS_1, DNS_2, DNS_3, DNS_4 } public NgnNetworkService() { super(); mAccessPoints = new NgnObservableList(true); } @Override public boolean start() { Log.d(TAG, "Starting..."); mWifiManager = (WifiManager) NgnApplication.getContext().getApplicationContext().getSystemService(Context.WIFI_SERVICE); if(mWifiManager == null){ Log.e(TAG, "WiFi manager is Null."); return false; } mStarted = true; return true; } @Override public boolean stop() { Log.d(TAG, "Stopping..."); if(!mStarted){ Log.w(TAG, "Not started..."); return false; } if(mNetworkWatcher != null){ NgnApplication.getContext().unregisterReceiver(mNetworkWatcher); mNetworkWatcher = null; } release(); mStarted = false; return true; } @Override public String getDnsServer(DNS_TYPE type) { String dns = null; switch (type) { case DNS_1: default: dns = "dns1"; break; case DNS_2: dns = "dns2"; break; case DNS_3: dns = "dns3"; break; case DNS_4: dns = "dns4"; break; } if (mWifiManager != null) { String[] dhcpInfos = mWifiManager.getDhcpInfo().toString().split(" "); int i = 0; while (i++ < dhcpInfos.length) { if (dhcpInfos[i - 1].equals(dns)) { return dhcpInfos[i]; } } } return null; } @Override public String getLocalIP(boolean ipv6) { final HashMap addressMap = new HashMap(); try { for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { final NetworkInterface intf = en.nextElement(); // http://code.google.com/p/imsdroid/issues/detail?id=398#c3 try{ if(NetworkInterface_isUp != null && !(Boolean)NetworkInterface_isUp.invoke(intf)){ Log.i(TAG, "interface=" + intf.getName() + " is not up"); continue; } } catch(Exception e){} for (Enumeration enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) { InetAddress inetAddress = enumIpAddr.nextElement(); Log.d(NgnNetworkService.TAG, inetAddress.getHostAddress().toString()); if(inetAddress.isLoopbackAddress() || ((inetAddress instanceof Inet6Address) && ((Inet6Address)inetAddress).isLinkLocalAddress())){ continue; } if (((inetAddress instanceof Inet4Address) && !ipv6) || ((inetAddress instanceof Inet6Address) && ipv6)) { addressMap.put(intf.getName(), inetAddress); } } } if(addressMap.size() > 0){ // openvpn address final InetAddress openvpn = addressMap.get(OPENVPN_INTERFACE_NAME); if(openvpn != null){ final String openvpnAddr = openvpn.getHostAddress().toString(); if(!NgnStringUtils.isNullOrEmpty(openvpnAddr)){ return openvpnAddr; } } final Iterator> it = addressMap.entrySet().iterator(); Map.Entry kvp; while (it.hasNext()) { kvp = it.next(); final InetAddress address = kvp.getValue(); if(kvp.getKey().equals(USB_INTERFACE_NAME)){ continue; } return address.getHostAddress(); } return addressMap.values().iterator().next().getHostAddress(); } } catch (SocketException ex) { Log.e(NgnNetworkService.TAG, ex.toString()); } // Hack try { java.net.Socket socket = new java.net.Socket(ipv6 ? "ipv6.google.com" : "google.com", 80); Log.d(NgnNetworkService.TAG, socket.getLocalAddress().getHostAddress()); return socket.getLocalAddress().getHostAddress(); } catch (UnknownHostException e) { Log.e(NgnNetworkService.TAG, e.toString()); } catch (IOException e) { Log.e(NgnNetworkService.TAG, e.toString()); } catch (Exception e){ Log.e(NgnNetworkService.TAG, e.toString()); } return null; } @Override public boolean isScanning(){ return mScanning; } @Override public boolean setNetworkEnabledAndRegister() { // TODO Auto-generated method stub return false; } @Override public boolean setNetworkEnabled(String SSID, boolean enabled, boolean force) { return setNetworkEnabled(getNetworkIdBySSID(SSID), enabled, force); } @Override public boolean setNetworkEnabled(int networkId, boolean enabled, boolean force){ Log.d(TAG, "setNetworkEnabled(" + enabled + ")"); if(mWifiManager == null){ Log.e(TAG, "WiFi manager is Null"); return false; } final boolean useWifi = NgnEngine.getInstance().getConfigurationService().getBoolean( NgnConfigurationEntry.NETWORK_USE_WIFI, NgnConfigurationEntry.DEFAULT_NETWORK_USE_WIFI); if (useWifi) { boolean ret = false; if ((force || !mWifiManager.isWifiEnabled()) && enabled) { Toast.makeText(NgnApplication.getContext(), "Trying to start WiFi", Toast.LENGTH_SHORT).show(); ret = mWifiManager.setWifiEnabled(true); if (ret && networkId>=0) { ret = mWifiManager.enableNetwork(networkId, true); } } else if ((force || mWifiManager.isWifiEnabled()) && !enabled) { Toast.makeText(NgnApplication.getContext(), "Trying to stop WiFi", Toast.LENGTH_SHORT).show(); ret = mWifiManager.setWifiEnabled(false); if (ret && networkId>=0) { ret = mWifiManager.disableNetwork(networkId); } } return ret; } else{ Log.w(TAG, "setNetworkEnabled() is called but WiFi not enabled"); } return false; } @Override public boolean forceConnectToNetwork() { // TODO Auto-generated method stub return false; } @Override public NgnObservableList getAccessPoints(){ return mAccessPoints; } @Override public int configure(NgnAccessPoint ap, String password, boolean bHex){ if(ap == null){ Log.e(TAG, "Null AccessPoint"); return -1; } else if(ap.isConfigured()){ Log.w(TAG, "AccessPoint already configured"); return -1; } else if(ap.getSR() == null){ Log.e(TAG, "Null SR"); } else if(mWifiManager == null){ Log.e(TAG, "Null WifiManager"); return -1; } final ScanResult sr = ap.getSR(); WifiConfiguration wConf = new WifiConfiguration(); //http://developer.android.com/reference/android/net/wifi/WifiConfiguration.html#SSID wConf.SSID = "\"" + sr.SSID + "\""; wConf.BSSID = sr.BSSID; wConf.priority = 40; String security = NgnAccessPoint.getScanResultSecurity(sr); if(security == NgnAccessPoint.AP_WEP){ wConf.wepKeys[0] = bHex ? password : NgnStringUtils.quote(password, "\"");//hex not quoted wConf.wepTxKeyIndex = 0; wConf.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN); wConf.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED); wConf.allowedKeyManagement.set(KeyMgmt.NONE); wConf.allowedGroupCiphers.set(GroupCipher.WEP40); wConf.allowedGroupCiphers.set(GroupCipher.WEP104); } else if(security == NgnAccessPoint.AP_WPA || security == NgnAccessPoint.AP_WPA2){ wConf.allowedProtocols.set(WifiConfiguration.Protocol.RSN); wConf.allowedProtocols.set(WifiConfiguration.Protocol.WPA); wConf.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); wConf.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); wConf.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP); wConf.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40); wConf.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104); wConf.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); wConf.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); wConf.preSharedKey = "\"".concat(BuildConfig.APP_NAME_PROPIETARY).concat("\""); } else if(security == NgnAccessPoint.AP_OPEN){ wConf.allowedKeyManagement.set(KeyMgmt.NONE); } return mWifiManager.addNetwork(wConf); } @Override public boolean scan(){ if(mWifiManager == null){ Log.e(TAG,"WiFi manager is Null"); return false; } Toast.makeText(NgnApplication.getContext(), "Network Scanning...", Toast.LENGTH_SHORT).show(); if(mNetworkWatcher == null){ IntentFilter intentNetWatcher = new IntentFilter(); intentNetWatcher.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); intentNetWatcher.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION); intentNetWatcher.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); intentNetWatcher.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); intentNetWatcher.addAction(WifiManager.RSSI_CHANGED_ACTION); mNetworkWatcher = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { handleNetworkEvent(context, intent); } }; NgnApplication.getContext().registerReceiver(mNetworkWatcher, intentNetWatcher); } mScanning = true; if(mWifiManager.setWifiEnabled(true)){ return mWifiManager.reassociate(); } return false; } @Override public boolean acquire() { if (mAcquired) { return true; } if(BuildConfig.DEBUG)Log.d(TAG, "acquireNetworkLock()"); boolean connected = false; NetworkInfo networkInfo = NgnApplication.getConnectivityManager().getActiveNetworkInfo(); if (networkInfo == null) { Log.e(NgnNetworkService.TAG, "Failed to get Network information"); return false; } int netType = networkInfo.getType(); int netSubType = networkInfo.getSubtype(); Log.d(NgnNetworkService.TAG, String.format("netType=%d and netSubType=%d", netType, netSubType)); boolean useWifi = NgnEngine.getInstance().getConfigurationService().getBoolean(NgnConfigurationEntry.NETWORK_USE_WIFI, NgnConfigurationEntry.DEFAULT_NETWORK_USE_WIFI); boolean use3G = NgnEngine.getInstance().getConfigurationService().getBoolean(NgnConfigurationEntry.NETWORK_USE_3G, NgnConfigurationEntry.DEFAULT_NETWORK_USE_3G); if (useWifi && (netType == ConnectivityManager.TYPE_WIFI) && mWifiLock == null) { if (mWifiManager != null && mWifiManager.isWifiEnabled()) { mWifiLock = mWifiManager.createWifiLock(NgnNetworkService.WifiManager_WIFI_MODE, NgnNetworkService.TAG); final WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); if (wifiInfo != null && mWifiLock != null) { final DetailedState detailedState = WifiInfo.getDetailedStateOf(wifiInfo.getSupplicantState()); if (detailedState == DetailedState.CONNECTED || detailedState == DetailedState.CONNECTING || detailedState == DetailedState.OBTAINING_IPADDR) { mWifiLock.acquire(); mConnetedSSID = wifiInfo.getSSID(); connected = true; } } } else { Log.d(NgnNetworkService.TAG, "WiFi not enabled"); } } else if (use3G && (netType == ConnectivityManager.TYPE_MOBILE || netType == ConnectivityManager_TYPE_WIMAX)) { if ((netSubType >= TelephonyManager.NETWORK_TYPE_UMTS) || // HACK (netSubType == TelephonyManager.NETWORK_TYPE_GPRS) || (netSubType == TelephonyManager.NETWORK_TYPE_EDGE)) { //Toast.makeText(WiPhone.getContext(), // "Using 2.5G (or later) network", Toast.LENGTH_SHORT) // .show(); connected = true; } } else if (netType == ConnectivityManager_TYPE_ETHERNET) { // Ethernet always accepted connected = true; } if (!connected) { Log.e(NgnNetworkService.TAG, "No active network"); return false; } mAcquired = true; return true; } @Override public boolean release() { try { if (mWifiLock != null) { if(mWifiLock.isHeld()){ Log.d(TAG, "releaseNetworkLock()"); mWifiLock.release(); } } }catch (Exception e){ Log.e(TAG,"Error in Lock Wifi:"+e.getMessage()); }finally { mWifiLock = null; } mAcquired = false; return true; } private int getNetworkIdBySSID(String SSID) { synchronized(mAccessPoints){ final NgnAccessPoint ap = getAccessPointBySSID(SSID); if(ap != null){ return ap.getNetworkId(); } return -1; } } @SuppressWarnings("unused") private WifiConfiguration getWifiConfBySSID(String SSID) { synchronized(mAccessPoints){ final NgnAccessPoint ap = getAccessPointBySSID(SSID); if(ap != null){ return ap.getConf(); } return null; } } private NgnAccessPoint getAccessPointBySSID(String SSID) { final List accessPoints = mAccessPoints.getList(); for (NgnAccessPoint ap : accessPoints) { String SSID1 = NgnStringUtils.unquote(ap.getSSID(), "\""); String SSID2 = NgnStringUtils.unquote(SSID, "\""); if (SSID1.equalsIgnoreCase(SSID2)) { return ap; } } return null; } private void loadConfiguredNetworks(){ synchronized(mAccessPoints){ mAccessPoints.clear(); final List confNetworks = mWifiManager.getConfiguredNetworks(); for (WifiConfiguration wifiConf : confNetworks) { NgnAccessPoint ap = new NgnAccessPoint(wifiConf); ap.setConnected(NgnStringUtils.equals(mConnetedSSID, ap.getSSID(), false)); mAccessPoints.add(ap); } } } private void handleNetworkEvent(Context context, Intent intent){ final String action = intent.getAction(); Log.d(TAG, "NetworkService::BroadcastReceiver(" + action + ")"); if(mWifiManager == null){ Log.e(TAG, "Invalid state"); return; } if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) { mScanning = true; // load() configured networks loadConfiguredNetworks(); // load() network results synchronized(mAccessPoints){ List scanResults = mWifiManager.getScanResults(); for(ScanResult sr : scanResults){ NgnAccessPoint ap = getAccessPointBySSID(sr.SSID); if(ap == null){ ap = new NgnAccessPoint(sr); mAccessPoints.add(ap); } } } updateConnectionState(); mScanning = false; } else if(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION.equals(action)){ final boolean connected = intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false); Log.d(TAG, "SUPPLICANT_CONNECTION_CHANGE_ACTION.CONNECTED="+connected); if(connected){ final WifiInfo wInfo = mWifiManager.getConnectionInfo(); if(wInfo != null){ if(!NgnStringUtils.equals(mConnetedSSID, wInfo.getSSID(), false)){ triggerSipRegistration(); } mConnetedSSID = wInfo.getSSID(); } } updateConnectionState(); // synchronized(mAccessPoints){ // final List aps = mAccessPoints.getList(); // for(AccessPoint ap : aps){ // ap.setConnected(connected && StringUtils.equals(mConnetedSSID, ap.getSSID(), false)); // } // } } else if(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)){ updateConnectionState(); // final SupplicantState newState = intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE); // if(newState != null){ // synchronized(mAccessPoints){ // final List aps = mAccessPoints.getList(); // final WifiInfo wInfo = mWifiManager.getConnectionInfo(); // if(wInfo != null){ // for(AccessPoint ap : aps){ // ap.setConnected((newState == SupplicantState.ASSOCIATED) && StringUtils.equals(wInfo.getSSID(), ap.getSSID(), false)); // } // } // } // } } else if(WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)){ updateConnectionState(); // final boolean connected = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN) // == WifiManager.WIFI_STATE_ENABLED; // synchronized(mAccessPoints){ // final List aps = mAccessPoints.getList(); // final WifiInfo wInfo = mWifiManager.getConnectionInfo(); // if(wInfo != null){ // for(AccessPoint ap : aps){ // ap.setConnected(connected && StringUtils.equals(wInfo.getSSID(), ap.getSSID(), false)); // } // } // } } else if(WifiManager.RSSI_CHANGED_ACTION.equals(action)){ final WifiInfo wInfo = mWifiManager.getConnectionInfo(); if(wInfo != null){ final NgnAccessPoint ap = getAccessPointBySSID(wInfo.getSSID()); if(ap != null){ final int newRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200); ap.setLevel(WifiManager.calculateSignalLevel(newRssi, sWifiSignalValues.length)); } } } } private void updateConnectionState(){ final WifiInfo wInfo = mWifiManager.getConnectionInfo(); boolean bAtLeastOneConnected = false; if(wInfo != null){ final DetailedState detailedState = WifiInfo .getDetailedStateOf(wInfo.getSupplicantState()); boolean isConnecting = detailedState == DetailedState.CONNECTED || detailedState == DetailedState.CONNECTING || detailedState == DetailedState.OBTAINING_IPADDR; synchronized(mAccessPoints){ final List aps = mAccessPoints.getList(); if(wInfo != null){ for(NgnAccessPoint ap : aps){ final boolean connected = isConnecting && NgnStringUtils.equals(wInfo.getSSID(), ap.getSSID(), false); ap.setConnected(connected); bAtLeastOneConnected |= connected; } } } } if(bAtLeastOneConnected || !NgnEngine.getInstance().getSipService().isRegistered()){ triggerSipRegistration(); } } private void triggerSipRegistration(){ // new Thread(new Runnable() { // @Override // public void run() { // Log.d(TAG, "Network connection chaged: restart the stack"); // final ISipService sipService = ServiceManager.getSipService(); // final ConnectionState registrationState = sipService.getRegistrationState(); // switch(registrationState){ // case NONE: // case TERMINATED: // sipService.register(null); // break; // case CONNECTING: // case TERMINATING: // case CONNECTED: // sipService.unRegister(); // sipService.register(null); // break; // } // } // }).start(); } @Override public boolean clearService(){ return true; } }