BobStore/F-Droid/src/org/fdroid/fdroid/net/WifiStateChangeService.java
Daniel Martí 9c86655af0 Make some declaration accesses weaker
As suggested by Android Studio.
2016-02-28 14:19:02 +00:00

261 lines
11 KiB
Java

package org.fdroid.fdroid.net;
import android.annotation.TargetApi;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.net.DhcpInfo;
import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.IBinder;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
import org.apache.commons.net.util.SubnetUtils;
import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.localrepo.LocalRepoKeyStore;
import org.fdroid.fdroid.localrepo.LocalRepoManager;
import org.fdroid.fdroid.localrepo.SwapService;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.security.cert.Certificate;
import java.util.Enumeration;
import java.util.Locale;
public class WifiStateChangeService extends Service {
private static final String TAG = "WifiStateChangeService";
public static final String BROADCAST = "org.fdroid.fdroid.action.WIFI_CHANGE";
private WifiManager wifiManager;
private static WaitForWifiAsyncTask asyncTask;
private int wifiState;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Utils.debugLog(TAG, "WiFi change service started, clearing info about wifi state until we have figured it out again.");
FDroidApp.initWifiSettings();
NetworkInfo ni = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
wifiState = wifiManager.getWifiState();
if (ni == null || ni.isConnected()) {
/* started on app start or from WifiStateChangeReceiver,
NetworkInfo is only passed via WifiStateChangeReceiver */
Utils.debugLog(TAG, "ni == " + ni + " wifiState == " + printWifiState(wifiState));
if (wifiState == WifiManager.WIFI_STATE_ENABLED
|| wifiState == WifiManager.WIFI_STATE_DISABLING // might be switching to hotspot
|| wifiState == WifiManager.WIFI_STATE_DISABLED // might be hotspot
|| wifiState == WifiManager.WIFI_STATE_UNKNOWN) { // might be hotspot
if (asyncTask != null) {
asyncTask.cancel(true);
}
asyncTask = new WaitForWifiAsyncTask();
asyncTask.execute();
}
}
return START_NOT_STICKY;
}
public class WaitForWifiAsyncTask extends AsyncTask<Void, Void, Void> {
private static final String TAG = "WaitForWifiAsyncTask";
@Override
protected Void doInBackground(Void... params) {
try {
Utils.debugLog(TAG, "Checking wifi state (in background thread).");
WifiInfo wifiInfo = null;
wifiState = wifiManager.getWifiState();
while (FDroidApp.ipAddressString == null) {
if (isCancelled()) { // can be canceled by a change via WifiStateChangeReceiver
return null;
}
if (wifiState == WifiManager.WIFI_STATE_ENABLED) {
wifiInfo = wifiManager.getConnectionInfo();
FDroidApp.ipAddressString = formatIpAddress(wifiInfo.getIpAddress());
DhcpInfo dhcpInfo = wifiManager.getDhcpInfo();
if (dhcpInfo != null) {
String netmask = formatIpAddress(dhcpInfo.netmask);
if (!TextUtils.isEmpty(FDroidApp.ipAddressString) && netmask != null) {
FDroidApp.subnetInfo = new SubnetUtils(FDroidApp.ipAddressString, netmask).getInfo();
}
}
} else if (wifiState == WifiManager.WIFI_STATE_DISABLED
|| wifiState == WifiManager.WIFI_STATE_DISABLING) {
// try once to see if its a hotspot
setIpInfoFromNetworkInterface();
if (FDroidApp.ipAddressString == null) {
return null;
}
} else { // a hotspot can be active during WIFI_STATE_UNKNOWN
setIpInfoFromNetworkInterface();
}
if (FDroidApp.ipAddressString == null) {
Thread.sleep(1000);
Utils.debugLog(TAG, "waiting for an IP address...");
}
}
if (isCancelled()) { // can be canceled by a change via WifiStateChangeReceiver
return null;
}
if (wifiInfo != null) {
String ssid = wifiInfo.getSSID();
Utils.debugLog(TAG, "Have wifi info, connected to " + ssid);
if (ssid != null) {
FDroidApp.ssid = ssid.replaceAll("^\"(.*)\"$", "$1");
}
String bssid = wifiInfo.getBSSID();
if (bssid != null) {
FDroidApp.bssid = bssid;
}
}
// TODO: Can this be moved to the swap service instead?
String scheme;
if (Preferences.get().isLocalRepoHttpsEnabled()) {
scheme = "https";
} else {
scheme = "http";
}
FDroidApp.REPO.name = Preferences.get().getLocalRepoName();
FDroidApp.REPO.address = String.format(Locale.ENGLISH, "%s://%s:%d/fdroid/repo",
scheme, FDroidApp.ipAddressString, FDroidApp.port);
if (isCancelled()) { // can be canceled by a change via WifiStateChangeReceiver
return null;
}
Context context = WifiStateChangeService.this.getApplicationContext();
LocalRepoManager lrm = LocalRepoManager.get(context);
lrm.writeIndexPage(Utils.getSharingUri(FDroidApp.REPO).toString());
if (isCancelled()) { // can be canceled by a change via WifiStateChangeReceiver
return null;
}
// the fingerprint for the local repo's signing key
LocalRepoKeyStore localRepoKeyStore = LocalRepoKeyStore.get(context);
Certificate localCert = localRepoKeyStore.getCertificate();
FDroidApp.REPO.fingerprint = Utils.calcFingerprint(localCert);
/*
* Once the IP address is known we need to generate a self
* signed certificate to use for HTTPS that has a CN field set
* to the ipAddressString. This must be run in the background
* because if this is the first time the singleton is run, it
* can take a while to instantiate.
*/
if (Preferences.get().isLocalRepoHttpsEnabled()) {
localRepoKeyStore.setupHTTPSCertificate();
}
} catch (LocalRepoKeyStore.InitException | InterruptedException e) {
Log.e(TAG, "Unable to configure a fingerprint or HTTPS for the local repo", e);
}
return null;
}
@Override
protected void onPostExecute(Void result) {
Intent intent = new Intent(BROADCAST);
LocalBroadcastManager.getInstance(WifiStateChangeService.this).sendBroadcast(intent);
WifiStateChangeService.this.stopSelf();
Intent swapService = new Intent(WifiStateChangeService.this, SwapService.class);
getApplicationContext().bindService(swapService, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
((SwapService.Binder) service).getService().stopWifiIfEnabled(true);
getApplicationContext().unbindService(this);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}, BIND_AUTO_CREATE);
}
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@TargetApi(9)
private void setIpInfoFromNetworkInterface() {
try {
for (Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces(); networkInterfaces.hasMoreElements();) {
NetworkInterface netIf = networkInterfaces.nextElement();
for (Enumeration<InetAddress> inetAddresses = netIf.getInetAddresses(); inetAddresses.hasMoreElements();) {
InetAddress inetAddress = inetAddresses.nextElement();
if (inetAddress.isLoopbackAddress() || inetAddress instanceof Inet6Address) {
continue;
}
if (netIf.getDisplayName().contains("wlan0")
|| netIf.getDisplayName().contains("eth0")
|| netIf.getDisplayName().contains("ap0")) {
FDroidApp.ipAddressString = inetAddress.getHostAddress();
if (Build.VERSION.SDK_INT < 9) {
return;
}
// the following methods were not added until android-9/Gingerbread
for (InterfaceAddress address : netIf.getInterfaceAddresses()) {
if (inetAddress.equals(address.getAddress()) && !TextUtils.isEmpty(FDroidApp.ipAddressString)) {
String cidr = String.format(Locale.ENGLISH, "%s/%d",
FDroidApp.ipAddressString, address.getNetworkPrefixLength());
FDroidApp.subnetInfo = (new SubnetUtils(cidr)).getInfo();
break;
}
}
}
}
}
} catch (SocketException e) {
Log.e(TAG, "Could not get ip address", e);
}
}
private String formatIpAddress(int ipAddress) {
if (ipAddress == 0) {
return null;
}
return String.format(Locale.ENGLISH, "%d.%d.%d.%d",
ipAddress & 0xff,
ipAddress >> 8 & 0xff,
ipAddress >> 16 & 0xff,
ipAddress >> 24 & 0xff);
}
private String printWifiState(int wifiState) {
switch (wifiState) {
case WifiManager.WIFI_STATE_DISABLED:
return "WIFI_STATE_DISABLED";
case WifiManager.WIFI_STATE_DISABLING:
return "WIFI_STATE_DISABLING";
case WifiManager.WIFI_STATE_ENABLING:
return "WIFI_STATE_ENABLING";
case WifiManager.WIFI_STATE_ENABLED:
return "WIFI_STATE_ENABLED";
case WifiManager.WIFI_STATE_UNKNOWN:
return "WIFI_STATE_UNKNOWN";
}
return null;
}
}