PageRenderTime 505ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/app/src/main/java/org/fdroid/fdroid/net/WifiStateChangeService.java

https://gitlab.com/khairulazlan-abdhalim/fdroidclient
Java | 259 lines | 209 code | 26 blank | 24 comment | 63 complexity | 217364743d4851b6e220a4a258c60605 MD5 | raw file
  1. package org.fdroid.fdroid.net;
  2. import android.app.IntentService;
  3. import android.content.Context;
  4. import android.content.Intent;
  5. import android.net.DhcpInfo;
  6. import android.net.NetworkInfo;
  7. import android.net.wifi.WifiInfo;
  8. import android.net.wifi.WifiManager;
  9. import android.support.v4.content.LocalBroadcastManager;
  10. import android.text.TextUtils;
  11. import android.util.Log;
  12. import org.apache.commons.net.util.SubnetUtils;
  13. import org.fdroid.fdroid.FDroidApp;
  14. import org.fdroid.fdroid.Preferences;
  15. import org.fdroid.fdroid.Utils;
  16. import org.fdroid.fdroid.data.Repo;
  17. import org.fdroid.fdroid.localrepo.LocalRepoKeyStore;
  18. import org.fdroid.fdroid.localrepo.LocalRepoManager;
  19. import java.net.Inet6Address;
  20. import java.net.InetAddress;
  21. import java.net.InterfaceAddress;
  22. import java.net.NetworkInterface;
  23. import java.net.SocketException;
  24. import java.security.cert.Certificate;
  25. import java.util.Enumeration;
  26. import java.util.Locale;
  27. /**
  28. * Handle state changes to the device's wifi, storing the required bits.
  29. * The {@link Intent} that starts it either has no extras included,
  30. * which is how it can be triggered by code, or it came in from the system
  31. * via {@link org.fdroid.fdroid.receiver.WifiStateChangeReceiver}, in
  32. * which case an instance of {@link NetworkInfo} is included.
  33. *
  34. * The work is done in a {@link Thread} so that new incoming {@code Intents}
  35. * are not blocked by processing. A new {@code Intent} immediately nullifies
  36. * the current state because it means that something about the wifi has
  37. * changed. Having the {@code Thread} also makes it easy to kill work
  38. * that is in progress.
  39. */
  40. public class WifiStateChangeService extends IntentService {
  41. private static final String TAG = "WifiStateChangeService";
  42. public static final String BROADCAST = "org.fdroid.fdroid.action.WIFI_CHANGE";
  43. private WifiManager wifiManager;
  44. private static WifiInfoThread wifiInfoThread;
  45. public WifiStateChangeService() {
  46. super("WifiStateChangeService");
  47. }
  48. @Override
  49. protected void onHandleIntent(Intent intent) {
  50. android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_LOWEST);
  51. if (intent == null) {
  52. Utils.debugLog(TAG, "received null Intent, ignoring");
  53. return;
  54. }
  55. Utils.debugLog(TAG, "WiFi change service started, clearing info about wifi state until we have figured it out again.");
  56. NetworkInfo ni = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
  57. wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
  58. int wifiState = wifiManager.getWifiState();
  59. if (ni == null || ni.isConnected()) {
  60. Utils.debugLog(TAG, "ni == " + ni + " wifiState == " + printWifiState(wifiState));
  61. if (wifiState == WifiManager.WIFI_STATE_ENABLED
  62. || wifiState == WifiManager.WIFI_STATE_DISABLING // might be switching to hotspot
  63. || wifiState == WifiManager.WIFI_STATE_DISABLED // might be hotspot
  64. || wifiState == WifiManager.WIFI_STATE_UNKNOWN) { // might be hotspot
  65. if (wifiInfoThread != null) {
  66. wifiInfoThread.interrupt();
  67. }
  68. wifiInfoThread = new WifiInfoThread();
  69. wifiInfoThread.start();
  70. }
  71. }
  72. }
  73. public class WifiInfoThread extends Thread {
  74. private static final String TAG = "WifiInfoThread";
  75. @Override
  76. public void run() {
  77. android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_LOWEST);
  78. try {
  79. FDroidApp.initWifiSettings();
  80. Utils.debugLog(TAG, "Checking wifi state (in background thread).");
  81. WifiInfo wifiInfo = null;
  82. int wifiState = wifiManager.getWifiState();
  83. while (FDroidApp.ipAddressString == null) {
  84. if (isInterrupted()) { // can be canceled by a change via WifiStateChangeReceiver
  85. return;
  86. }
  87. if (wifiState == WifiManager.WIFI_STATE_ENABLED) {
  88. wifiInfo = wifiManager.getConnectionInfo();
  89. FDroidApp.ipAddressString = formatIpAddress(wifiInfo.getIpAddress());
  90. DhcpInfo dhcpInfo = wifiManager.getDhcpInfo();
  91. if (dhcpInfo != null) {
  92. String netmask = formatIpAddress(dhcpInfo.netmask);
  93. if (!TextUtils.isEmpty(FDroidApp.ipAddressString) && netmask != null) {
  94. FDroidApp.subnetInfo = new SubnetUtils(FDroidApp.ipAddressString, netmask).getInfo();
  95. }
  96. }
  97. } else if (wifiState == WifiManager.WIFI_STATE_DISABLED
  98. || wifiState == WifiManager.WIFI_STATE_DISABLING) {
  99. // try once to see if its a hotspot
  100. setIpInfoFromNetworkInterface();
  101. if (FDroidApp.ipAddressString == null) {
  102. return;
  103. }
  104. } else { // a hotspot can be active during WIFI_STATE_UNKNOWN
  105. setIpInfoFromNetworkInterface();
  106. }
  107. if (FDroidApp.ipAddressString == null) {
  108. Thread.sleep(1000);
  109. Utils.debugLog(TAG, "waiting for an IP address...");
  110. }
  111. }
  112. if (isInterrupted()) { // can be canceled by a change via WifiStateChangeReceiver
  113. return;
  114. }
  115. if (wifiInfo != null) {
  116. String ssid = wifiInfo.getSSID();
  117. Utils.debugLog(TAG, "Have wifi info, connected to " + ssid);
  118. if (ssid != null) {
  119. FDroidApp.ssid = ssid.replaceAll("^\"(.*)\"$", "$1");
  120. }
  121. String bssid = wifiInfo.getBSSID();
  122. if (bssid != null) {
  123. FDroidApp.bssid = bssid;
  124. }
  125. }
  126. String scheme;
  127. if (Preferences.get().isLocalRepoHttpsEnabled()) {
  128. scheme = "https";
  129. } else {
  130. scheme = "http";
  131. }
  132. Repo repo = new Repo();
  133. repo.name = Preferences.get().getLocalRepoName();
  134. repo.address = String.format(Locale.ENGLISH, "%s://%s:%d/fdroid/repo",
  135. scheme, FDroidApp.ipAddressString, FDroidApp.port);
  136. if (isInterrupted()) { // can be canceled by a change via WifiStateChangeReceiver
  137. return;
  138. }
  139. Context context = WifiStateChangeService.this.getApplicationContext();
  140. LocalRepoManager lrm = LocalRepoManager.get(context);
  141. lrm.writeIndexPage(Utils.getSharingUri(FDroidApp.repo).toString());
  142. if (isInterrupted()) { // can be canceled by a change via WifiStateChangeReceiver
  143. return;
  144. }
  145. // the fingerprint for the local repo's signing key
  146. LocalRepoKeyStore localRepoKeyStore = LocalRepoKeyStore.get(context);
  147. Certificate localCert = localRepoKeyStore.getCertificate();
  148. repo.fingerprint = Utils.calcFingerprint(localCert);
  149. FDroidApp.repo = repo;
  150. /*
  151. * Once the IP address is known we need to generate a self
  152. * signed certificate to use for HTTPS that has a CN field set
  153. * to the ipAddressString. This must be run in the background
  154. * because if this is the first time the singleton is run, it
  155. * can take a while to instantiate.
  156. */
  157. if (Preferences.get().isLocalRepoHttpsEnabled()) {
  158. localRepoKeyStore.setupHTTPSCertificate();
  159. }
  160. } catch (LocalRepoKeyStore.InitException e) {
  161. Log.e(TAG, "Unable to configure a fingerprint or HTTPS for the local repo", e);
  162. } catch (InterruptedException e) {
  163. Utils.debugLog(TAG, "interrupted");
  164. return;
  165. }
  166. Intent intent = new Intent(BROADCAST);
  167. LocalBroadcastManager.getInstance(WifiStateChangeService.this).sendBroadcast(intent);
  168. }
  169. }
  170. private void setIpInfoFromNetworkInterface() {
  171. try {
  172. Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
  173. if (networkInterfaces == null) {
  174. return;
  175. }
  176. while (networkInterfaces.hasMoreElements()) {
  177. NetworkInterface netIf = networkInterfaces.nextElement();
  178. for (Enumeration<InetAddress> inetAddresses = netIf.getInetAddresses(); inetAddresses.hasMoreElements();) {
  179. InetAddress inetAddress = inetAddresses.nextElement();
  180. if (inetAddress.isLoopbackAddress() || inetAddress instanceof Inet6Address) {
  181. continue;
  182. }
  183. if (netIf.getDisplayName().contains("wlan0")
  184. || netIf.getDisplayName().contains("eth0")
  185. || netIf.getDisplayName().contains("ap0")) {
  186. FDroidApp.ipAddressString = inetAddress.getHostAddress();
  187. for (InterfaceAddress address : netIf.getInterfaceAddresses()) {
  188. short networkPrefixLength = address.getNetworkPrefixLength();
  189. if (networkPrefixLength > 32) {
  190. // something is giving a "/64" netmask, IPv6?
  191. // java.lang.IllegalArgumentException: Value [64] not in range [0,32]
  192. continue;
  193. }
  194. if (inetAddress.equals(address.getAddress()) && !TextUtils.isEmpty(FDroidApp.ipAddressString)) {
  195. String cidr = String.format(Locale.ENGLISH, "%s/%d",
  196. FDroidApp.ipAddressString, networkPrefixLength);
  197. FDroidApp.subnetInfo = new SubnetUtils(cidr).getInfo();
  198. break;
  199. }
  200. }
  201. }
  202. }
  203. }
  204. } catch (SocketException e) {
  205. Log.e(TAG, "Could not get ip address", e);
  206. }
  207. }
  208. static String formatIpAddress(int ipAddress) {
  209. if (ipAddress == 0) {
  210. return null;
  211. }
  212. return String.format(Locale.ENGLISH, "%d.%d.%d.%d",
  213. ipAddress & 0xff,
  214. ipAddress >> 8 & 0xff,
  215. ipAddress >> 16 & 0xff,
  216. ipAddress >> 24 & 0xff);
  217. }
  218. private String printWifiState(int wifiState) {
  219. switch (wifiState) {
  220. case WifiManager.WIFI_STATE_DISABLED:
  221. return "WIFI_STATE_DISABLED";
  222. case WifiManager.WIFI_STATE_DISABLING:
  223. return "WIFI_STATE_DISABLING";
  224. case WifiManager.WIFI_STATE_ENABLING:
  225. return "WIFI_STATE_ENABLING";
  226. case WifiManager.WIFI_STATE_ENABLED:
  227. return "WIFI_STATE_ENABLED";
  228. case WifiManager.WIFI_STATE_UNKNOWN:
  229. return "WIFI_STATE_UNKNOWN";
  230. }
  231. return null;
  232. }
  233. }