aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/fil
diff options
context:
space:
mode:
authorFil <fil.bergamo@riseup.net>2018-07-04 19:14:17 +0200
committerFil <fil.bergamo@riseup.net>2018-07-04 19:29:53 +0200
commit121a4bec96d2f9b842c6d393d2b9c6356a9a4405 (patch)
treeda1c557520d670def2f755492e3afd62ce891cf6 /app/src/fil
parenta6b34d6c75109b831976bc872a5a0a47a08c1664 (diff)
downloadRepWifiApp-121a4bec96d2f9b842c6d393d2b9c6356a9a4405.tar.gz
RepWifiApp-121a4bec96d2f9b842c6d393d2b9c6356a9a4405.tar.bz2
RepWifiApp-121a4bec96d2f9b842c6d393d2b9c6356a9a4405.zip
Add full integration with the Connectivity Frameworkv0.9-beta
Fixes Issue #1867 "Download app not working". Uses proxy classes to hook into the application framework, solving almost any problem with missing connectivity features, creating a connection that is fully managed by the framework itself. Introduces a background service that performs all backend operations. Cleans up code design, refining structure of classes and entities.
Diffstat (limited to 'app/src/fil')
-rw-r--r--app/src/fil/libre/repwifiapp/ActivityLauncher.java41
-rw-r--r--app/src/fil/libre/repwifiapp/Commons.java295
-rw-r--r--app/src/fil/libre/repwifiapp/Prefs.java67
-rw-r--r--app/src/fil/libre/repwifiapp/RepWifiIntentReceiver.java41
-rw-r--r--app/src/fil/libre/repwifiapp/Utils.java (renamed from app/src/fil/libre/repwifiapp/helpers/Utils.java)245
-rw-r--r--app/src/fil/libre/repwifiapp/activities/ConnectionBoundActivity.java282
-rw-r--r--app/src/fil/libre/repwifiapp/activities/CreditsActivity.java6
-rw-r--r--app/src/fil/libre/repwifiapp/activities/InputPasswordActivity.java33
-rw-r--r--app/src/fil/libre/repwifiapp/activities/InputSsidActivity.java19
-rw-r--r--app/src/fil/libre/repwifiapp/activities/Ipv4SettingsActivity.java38
-rw-r--r--app/src/fil/libre/repwifiapp/activities/LongTaskActivity.java217
-rw-r--r--app/src/fil/libre/repwifiapp/activities/MainActivity.java545
-rw-r--r--app/src/fil/libre/repwifiapp/activities/MenuEnabledActivity.java13
-rw-r--r--app/src/fil/libre/repwifiapp/activities/NetworkDetailsActivity.java33
-rw-r--r--app/src/fil/libre/repwifiapp/activities/SelectNetworkActivity.java23
-rw-r--r--app/src/fil/libre/repwifiapp/activities/SettingsActivity.java126
-rw-r--r--app/src/fil/libre/repwifiapp/activities/ShowStatusActivity.java253
-rw-r--r--app/src/fil/libre/repwifiapp/activities/VpnAndConnectionBoundActivity.java435
-rw-r--r--app/src/fil/libre/repwifiapp/activities/VpnSettingsActivity.java227
-rw-r--r--app/src/fil/libre/repwifiapp/fwproxies/ConnectivityManagerProxy.java98
-rw-r--r--app/src/fil/libre/repwifiapp/fwproxies/FrameworkProxy.java126
-rw-r--r--app/src/fil/libre/repwifiapp/fwproxies/LinkAddressProxy.java61
-rw-r--r--app/src/fil/libre/repwifiapp/fwproxies/LinkPropertiesProxy.java90
-rw-r--r--app/src/fil/libre/repwifiapp/fwproxies/NetworkCapabilitiesProxy.java90
-rw-r--r--app/src/fil/libre/repwifiapp/fwproxies/NetworkInfoProxy.java102
-rw-r--r--app/src/fil/libre/repwifiapp/fwproxies/RepWifiNetworkAgent.java581
-rw-r--r--app/src/fil/libre/repwifiapp/fwproxies/RouteInfoProxy.java59
-rw-r--r--app/src/fil/libre/repwifiapp/helpers/ConnectionStatus.java109
-rw-r--r--app/src/fil/libre/repwifiapp/helpers/Engine6p0.java271
-rw-r--r--app/src/fil/libre/repwifiapp/helpers/Logger.java160
-rw-r--r--app/src/fil/libre/repwifiapp/helpers/OpenVpnManager.java237
-rw-r--r--app/src/fil/libre/repwifiapp/helpers/RootCommand.java81
-rw-r--r--app/src/fil/libre/repwifiapp/helpers/ShellCommand.java107
-rw-r--r--app/src/fil/libre/repwifiapp/network/AccessPointInfo.java (renamed from app/src/fil/libre/repwifiapp/helpers/AccessPointInfo.java)94
-rw-r--r--app/src/fil/libre/repwifiapp/network/ConnectionResult.java95
-rw-r--r--app/src/fil/libre/repwifiapp/network/ConnectionStatus.java298
-rw-r--r--app/src/fil/libre/repwifiapp/network/DhcpSettings.java (renamed from app/src/fil/libre/repwifiapp/helpers/DhcpSettings.java)153
-rw-r--r--app/src/fil/libre/repwifiapp/network/Engine.java (renamed from app/src/fil/libre/repwifiapp/helpers/Engine.java)92
-rw-r--r--app/src/fil/libre/repwifiapp/network/Engine6p0.java315
-rw-r--r--app/src/fil/libre/repwifiapp/network/IEngine.java (renamed from app/src/fil/libre/repwifiapp/helpers/IEngine.java)12
-rw-r--r--app/src/fil/libre/repwifiapp/network/NetworkButton.java (renamed from app/src/fil/libre/repwifiapp/helpers/NetworkButton.java)4
-rw-r--r--app/src/fil/libre/repwifiapp/network/NetworkManager.java (renamed from app/src/fil/libre/repwifiapp/helpers/NetworkManager.java)114
-rw-r--r--app/src/fil/libre/repwifiapp/network/WpaCli.java (renamed from app/src/fil/libre/repwifiapp/helpers/WpaCli.java)161
-rw-r--r--app/src/fil/libre/repwifiapp/network/WpaSupplicant.java (renamed from app/src/fil/libre/repwifiapp/helpers/WpaSupplicant.java)40
-rw-r--r--app/src/fil/libre/repwifiapp/service/Channel.java148
-rw-r--r--app/src/fil/libre/repwifiapp/service/ConnectionManagementService.java720
-rw-r--r--app/src/fil/libre/repwifiapp/service/StatusManager.java139
47 files changed, 5555 insertions, 1941 deletions
diff --git a/app/src/fil/libre/repwifiapp/ActivityLauncher.java b/app/src/fil/libre/repwifiapp/ActivityLauncher.java
index 5e9c31d..d5d2b15 100644
--- a/app/src/fil/libre/repwifiapp/ActivityLauncher.java
+++ b/app/src/fil/libre/repwifiapp/ActivityLauncher.java
@@ -1,5 +1,5 @@
//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
//
// This file is part of RepWifiApp.
//
@@ -31,9 +31,9 @@ import fil.libre.repwifiapp.activities.NetworkDetailsActivity;
import fil.libre.repwifiapp.activities.SelectNetworkActivity;
import fil.libre.repwifiapp.activities.ShowStatusActivity;
import fil.libre.repwifiapp.activities.VpnSettingsActivity;
-import fil.libre.repwifiapp.helpers.AccessPointInfo;
-import fil.libre.repwifiapp.helpers.ConnectionStatus;
-import fil.libre.repwifiapp.helpers.NetworkManager;
+import fil.libre.repwifiapp.network.AccessPointInfo;
+import fil.libre.repwifiapp.network.ConnectionStatus;
+import fil.libre.repwifiapp.network.NetworkManager;
public class ActivityLauncher {
@@ -42,9 +42,12 @@ public class ActivityLauncher {
public static final String EXTRA_APINFO_ARR = "ExAPInfoArr";
public static final String EXTRA_CONSTATUS = "ExConSts";
public static final String EXTRA_BOOLEAN = "ExBool";
+ public static final String EXTRA_INTEGER = "ExInt";
public static final String EXTRA_REQCODE = "ExReqCode";
public static final String EXTRA_RESCAN = "ExRescan";
public static final String EXTRA_DELETE = "ExDelete";
+ public static final String EXTRA_MESSAGE = "ExMsg";
+ public static final String EXTRA_CONNRES = "ExConnRes";
public class RequestCode {
@@ -61,7 +64,8 @@ public class ActivityLauncher {
public static final int CONNECT_HIDDEN = 10;
public static final int USB_ATTACHED = 11;
public static final int USB_DETACHED = 12;
- public static final int VPN_PERMISSION = 13;
+ public static final int VPN_PERMISSION_CONN = 13;
+ public static final int VPN_PERMISSION_LIST = 14;
}
@@ -82,7 +86,7 @@ public class ActivityLauncher {
}
public void launchLongTaskActivityConnect(AccessPointInfo info) {
-
+
Intent intent = new Intent(currentContext, LongTaskActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.putExtra(ActivityLauncher.EXTRA_REQCODE, RequestCode.CONNECT);
@@ -99,11 +103,20 @@ public class ActivityLauncher {
}
public void launchPasswordActivity(AccessPointInfo info) {
+ launchPasswordActivity(info, null);
+ }
+
+ public void launchPasswordActivity(AccessPointInfo info, String message) {
Intent intent = new Intent();
// intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.setClass(currentContext, InputPasswordActivity.class);
intent.putExtra(EXTRA_APINFO, info);
+
+ if (message != null) {
+ intent.putExtra(EXTRA_MESSAGE, message);
+ }
+
currentContext.startActivityForResult(intent, RequestCode.PASS_INPUT);
}
@@ -121,11 +134,12 @@ public class ActivityLauncher {
boolean fromStorage) {
if (fromStorage) {
- NetworkManager manager = new NetworkManager(Commons.getNetworkStorageFile());
- nets = manager.getKnownNetworks();
+
+ nets = NetworkManager.getKnownNetworks();
if (nets == null || nets.length == 0) {
- Toast toast = Toast.makeText(currentContext, Commons.msgNoSavedNetwork, Toast.LENGTH_LONG);
+ Toast toast = Toast.makeText(currentContext, currentContext.getResources()
+ .getString(R.string.msg_no_saved_network), Toast.LENGTH_LONG);
toast.show();
return;
}
@@ -153,7 +167,7 @@ public class ActivityLauncher {
currentContext.startActivityForResult(intent, RequestCode.DETAILS_SHOW);
}
-
+
public void launchIpSettingsActivity(AccessPointInfo info) {
Intent intent = new Intent(currentContext, Ipv4SettingsActivity.class);
@@ -162,14 +176,14 @@ public class ActivityLauncher {
currentContext.startActivity(intent);
}
-
+
public void launchVpnSettingsActivity(AccessPointInfo info) {
-
+
Intent intent = new Intent(currentContext, VpnSettingsActivity.class);
// intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.putExtra(EXTRA_APINFO, info);
currentContext.startActivity(intent);
-
+
}
public void launchInputSsidActivity() {
@@ -180,6 +194,5 @@ public class ActivityLauncher {
currentContext.startActivityForResult(intent, RequestCode.CONNECT_HIDDEN);
}
-
}
diff --git a/app/src/fil/libre/repwifiapp/Commons.java b/app/src/fil/libre/repwifiapp/Commons.java
index 1603a76..0347982 100644
--- a/app/src/fil/libre/repwifiapp/Commons.java
+++ b/app/src/fil/libre/repwifiapp/Commons.java
@@ -1,5 +1,5 @@
//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
//
// This file is part of RepWifiApp.
//
@@ -20,64 +20,26 @@
package fil.libre.repwifiapp;
-import java.io.File;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.Locale;
-import android.annotation.SuppressLint;
-import android.app.AlertDialog;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Service;
import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
-import android.os.Environment;
-import android.preference.PreferenceManager;
-import fil.libre.repwifiapp.activities.MainActivity;
-import fil.libre.repwifiapp.helpers.ConnectionStatus;
-import fil.libre.repwifiapp.helpers.Engine;
-import fil.libre.repwifiapp.helpers.Engine6p0;
-import fil.libre.repwifiapp.helpers.IEngine;
-import fil.libre.repwifiapp.helpers.NetworkManager;
-import fil.libre.repwifiapp.helpers.Utils;
-import fil.libre.repwifiapp.helpers.WpaCli;
-import fil.libre.repwifiapp.helpers.WpaSupplicant;
+import fil.libre.repwifiapp.network.NetworkManager;
public abstract class Commons {
- private static Context currentContext;
-
- public static Context getContext() {
- return currentContext;
- }
-
// ------------- Environment Constants -----------------
public static final int EXCOD_ROOT_DISABLED = 255;
public static final int EXCOD_ROOT_DENIED = 1;
public static final int WAIT_ON_USB_ATTACHED = 1500;
public static final int WAIT_FOR_GATEWAY = 4000;
+ public static final int WAIT_FOR_DHCPCD = 30;
public static final String BSSID_NOT_AVAILABLE = "[BSSID-NOT-AVAILABLE]";
public static final String v6p0 = "6.0";
// ---------------------------------------------
- // ------------- Shared Engines -----------------------
- public static IEngine connectionEngine = null;
- public static NetworkManager storage = null;
- // ----------------------------------------------------
-
- // ------------- Shared Resources ---------------------
- public static int colorThemeDark;
- public static int colorThemeLight;
- public static int colorBlack;
- public static String dns1Default = "";
- public static String dns2Default = "";
private static String APP_DATA_FOLDER;
- public static String msgNoSavedNetwork;
+ public static void init(Context context) {
+ APP_DATA_FOLDER = context.getExternalFilesDir(null).getAbsolutePath();
+ NetworkManager.init(getNetworkStorageFile());
+ }
public static String getNetworkStorageFile() {
if (APP_DATA_FOLDER == null) {
@@ -86,249 +48,14 @@ public abstract class Commons {
return APP_DATA_FOLDER + "/repwifi_storage.conf";
}
}
-
- @SuppressLint("SimpleDateFormat")
- public static String getLogDumpFile() {
- File f = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
- if (f == null || !f.exists()) {
- return null;
- }
-
- String basefolder;
- try {
- basefolder = f.getCanonicalPath();
- } catch (Exception e) {
- Utils.logError("Exception while resolving canonical path for log dump file.", e);
+ public static String getExitCodeTempFile() {
+ if (APP_DATA_FOLDER == null) {
return null;
- }
-
- DateFormat dateFormat = new SimpleDateFormat("yyyyMMddhhmmss", Locale.getDefault());
- String ts = dateFormat.format(Calendar.getInstance().getTime());
- return basefolder + "/repwifi_log_dump." + ts + ".log";
- }
-
- // --------------------------------------------------------
-
- private static final int NOTIFICATION_ID = 1;
-
- public static void updateNotification(Context context) {
-
- ConnectionStatus status = WpaCli.getConnectionStatus();
-
- Notification.Builder builder = new Notification.Builder(context);
-
- Intent intent = new Intent(context, MainActivity.class);
- PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
- builder.setContentIntent(pendingIntent);
-
- int iconId = R.drawable.ic_stat_discon;
- String msg = "RepWifi";
- if (status != null) {
- if (status.isConnected()) {
- iconId = R.drawable.ic_stat_repwifi;
- msg += " - " + status.SSID;
- } else {
- msg += " - " + status.status;
- }
-
- }
-
- builder.setSmallIcon(iconId);
-
- builder.setContentTitle(msg);
- builder.setContentText(currentContext.getString(R.string.msg_touch_open));
-
- Notification n = builder.build();
- n.flags |= Notification.FLAG_NO_CLEAR;
-
- NotificationManager notificationManager = (NotificationManager) context
- .getSystemService(Service.NOTIFICATION_SERVICE);
- notificationManager.notify(NOTIFICATION_ID, n);
-
- }
-
- public static void showMessage(String msg) {
- showMessage(msg, currentContext);
- }
-
- public static void showMessage(String msg, Context context) {
-
- AlertDialog.Builder dlgAlert = new AlertDialog.Builder(context,
- R.style.Theme_RepWifiDialogTheme);
- dlgAlert.setMessage(msg);
- dlgAlert.setPositiveButton(currentContext.getString(android.R.string.ok),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int whichButton) {
- return;
- }
- });
-
- dlgAlert.setCancelable(false);
- AlertDialog diag = dlgAlert.create();
-
- diag.show();
-
- }
-
- public static void resetSettingsDefault(Context context, boolean silent) {
-
- if (silent) {
- Editor e = getSettings().edit();
- e.clear();
- e.commit();
- } else {
-
- String msg = context.getString(R.string.confirm_reset_settings);
- AlertDialog.Builder dlgAlert = new AlertDialog.Builder(context,
- R.style.Theme_RepWifiDialogTheme);
- dlgAlert.setMessage(msg);
- dlgAlert.setPositiveButton(context.getString(android.R.string.ok),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int whichButton) {
- resetSettingsDefault(null, true);
- return;
- }
- });
- dlgAlert.setNegativeButton(context.getString(android.R.string.cancel),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int whichButton) {
- return;
- }
- });
-
- dlgAlert.setCancelable(false);
- AlertDialog diag = dlgAlert.create();
-
- diag.show();
-
- return;
- }
-
- }
-
- public static void killBackEnd(Context context, boolean silent) {
-
- if (silent) {
-
- Engine.killDhcpcd();
- WpaSupplicant.kill();
-
} else {
-
- String msg = context.getString(R.string.confirm_kill_backend);
- AlertDialog.Builder dlgAlert = new AlertDialog.Builder(context,
- R.style.Theme_RepWifiDialogTheme);
- dlgAlert.setMessage(msg);
- dlgAlert.setPositiveButton(context.getString(android.R.string.ok),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int whichButton) {
- killBackEnd(null, true);
- return;
- }
- });
- dlgAlert.setNegativeButton(context.getString(android.R.string.cancel),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int whichButton) {
- return;
- }
- });
-
- dlgAlert.setCancelable(false);
- AlertDialog diag = dlgAlert.create();
-
- diag.show();
- return;
-
- }
-
- }
-
- public static int getLogPriority() {
-
- SharedPreferences sets = getSettings();
- return Integer.parseInt(sets.getString("debug_priority", "3"));
-
- }
-
- public static boolean isProgbarEnabled() {
- return getSettings().getBoolean("enable_progbar", true);
- }
-
- public static boolean isAutoConnectEnabled() {
- return getSettings().getBoolean("enable_autoconnect", false);
- }
-
- public static String[] getDnss() {
-
- String dns1 = getSettings().getString("dns1", dns1Default);
- String dns2 = getSettings().getString("dns2", dns2Default);
-
- if (dns1 == null || dns1.isEmpty()) {
- return null;
- }
-
- return new String[] { dns1, dns2 };
-
- }
-
- public static SharedPreferences getSettings() {
-
- return PreferenceManager.getDefaultSharedPreferences(currentContext);
-
- }
-
- // ----------------------------------------------------
-
- // ----------- Initialization methods ---------------------------
- public static boolean init(Context context) {
-
- currentContext = context;
-
- try {
-
- colorThemeDark = currentContext.getResources().getColor(R.color.ThemeDark);
- colorThemeLight = currentContext.getResources().getColor(R.color.ThemeLight);
- msgNoSavedNetwork = currentContext.getResources().getString(
- R.string.msg_no_saved_network);
- colorBlack = currentContext.getResources().getColor(R.color.black);
- APP_DATA_FOLDER = currentContext.getExternalFilesDir(null).getAbsolutePath();
- dns1Default = currentContext.getResources().getString(R.string.dns1_default);
- dns2Default = currentContext.getResources().getString(R.string.dns2_default);
-
- initEngine();
- initNetworkStorage();
-
- return true;
-
- } catch (Exception e) {
- Utils.logError("Error initializing common resources.", e);
- return false;
+ return APP_DATA_FOLDER + "/tmp_excode";
}
}
- private static void initEngine() throws Exception {
-
- connectionEngine = new Engine6p0();
-
- String vers = android.os.Build.VERSION.RELEASE;
-
- if (!vers.startsWith(Commons.v6p0)) {
- showMessage(currentContext.getString(R.string.msg_os_unsupported));
- }
-
- }
-
- private static void initNetworkStorage() throws Exception {
-
- Commons.storage = new NetworkManager(getNetworkStorageFile());
-
- }
- // --------------------------------------------------------------
-
+
}
diff --git a/app/src/fil/libre/repwifiapp/Prefs.java b/app/src/fil/libre/repwifiapp/Prefs.java
new file mode 100644
index 0000000..ef13d5a
--- /dev/null
+++ b/app/src/fil/libre/repwifiapp/Prefs.java
@@ -0,0 +1,67 @@
+//
+// Copyright 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+//
+// This file is part of RepWifiApp.
+//
+// RepWifiApp 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.
+//
+// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
+//
+// ********************************************************************
+package fil.libre.repwifiapp;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+public class Prefs {
+
+ public static final String PREF_MONITOR_NET_STATE = "monitor_connection";
+ public static final String PREF_LOG_LEVEL = "debug_priority";
+ public static final String PREF_DNS_1 = "dns1";
+ public static final String PREF_DNS_2 = "dns2";
+ public static final String PREF_AUTOSTART = "enable_autostart";
+ public static final String PREF_PROGBAR = "enable_progbar";
+ public static final String PREF_AUTOCONNECT = "enable_autoconnect";
+
+ private static final String PREFS_FILENAME = "fil.libre.repwifiapp_preferences";
+
+ public static int getLogPriority(Context c) {
+ return Integer.parseInt(getPrefs(c).getString(PREF_LOG_LEVEL, "3"));
+ }
+
+ public static boolean isNetworkStateMonitoringEnabled(Context c) {
+ return getPrefs(c).getBoolean(PREF_MONITOR_NET_STATE, false);
+ }
+
+ public static int getInt(Context c, String key, int defaultVal) {
+ return getPrefs(c).getInt(key, defaultVal);
+ }
+
+ public static boolean getBoolean(Context c, String key, boolean defaultVal) {
+ return getPrefs(c).getBoolean(key, defaultVal);
+ }
+
+ public static String getString(Context c, String key, String defaultVal) {
+ return getPrefs(c).getString(key, defaultVal);
+ }
+
+ public static void commit(Context c) {
+ getPrefs(c).edit().commit();
+ }
+
+ private static SharedPreferences getPrefs(Context c) {
+ SharedPreferences prefs = c
+ .getSharedPreferences(PREFS_FILENAME, Context.MODE_MULTI_PROCESS);
+ return prefs;
+ }
+
+}
diff --git a/app/src/fil/libre/repwifiapp/RepWifiIntentReceiver.java b/app/src/fil/libre/repwifiapp/RepWifiIntentReceiver.java
index ba26bb3..6ee419a 100644
--- a/app/src/fil/libre/repwifiapp/RepWifiIntentReceiver.java
+++ b/app/src/fil/libre/repwifiapp/RepWifiIntentReceiver.java
@@ -1,26 +1,46 @@
+//
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+//
+// This file is part of RepWifiApp.
+//
+// RepWifiApp 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.
+//
+// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
+//
+// ********************************************************************
+
package fil.libre.repwifiapp;
-import fil.libre.repwifiapp.ActivityLauncher.RequestCode;
-import fil.libre.repwifiapp.activities.MainActivity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
+import fil.libre.repwifiapp.ActivityLauncher.RequestCode;
+import fil.libre.repwifiapp.activities.MainActivity;
+import fil.libre.repwifiapp.helpers.Logger;
+import fil.libre.repwifiapp.service.ConnectionManagementService;
public class RepWifiIntentReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context
- .getApplicationContext());
- if (!prefs.getBoolean("enable_autostart", false)) {
+ if (!Prefs.getBoolean(context, Prefs.PREF_AUTOSTART, false)) {
return;
}
String a = intent.getAction();
if (a.equals(Intent.ACTION_BOOT_COMPLETED) || a.equals(Intent.ACTION_REBOOT)) {
+ Logger.logDebug("Starting on boot.", 100);
+ startConnectionManagementService(context);
launchRepWifiMainActivity(context, RequestCode.NONE);
}
@@ -37,4 +57,11 @@ public class RepWifiIntentReceiver extends BroadcastReceiver {
context.startActivity(intent);
}
+
+ private void startConnectionManagementService(Context context) {
+ Intent startIntent = new Intent(context, ConnectionManagementService.class);
+ startIntent.setAction(ConnectionManagementService.ACTION_VOID);
+ context.startService(startIntent);
+ }
+
}
diff --git a/app/src/fil/libre/repwifiapp/helpers/Utils.java b/app/src/fil/libre/repwifiapp/Utils.java
index ad10c1a..28db6a8 100644
--- a/app/src/fil/libre/repwifiapp/helpers/Utils.java
+++ b/app/src/fil/libre/repwifiapp/Utils.java
@@ -1,5 +1,5 @@
//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
//
// This file is part of RepWifiApp.
//
@@ -18,51 +18,34 @@
//
// ********************************************************************
-package fil.libre.repwifiapp.helpers;
+package fil.libre.repwifiapp;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import fil.libre.repwifiapp.helpers.Logger;
+import fil.libre.repwifiapp.network.Engine;
+import fil.libre.repwifiapp.network.WpaSupplicant;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
+import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
-import fil.libre.repwifiapp.Commons;
-import android.util.Log;
+import java.util.Scanner;
public class Utils {
private static final long MILLIS_IN_DAY = 86400000;
- public static final String APP_NAME = "RepWifi";
-
private static Exception _lastException = null;
public static Exception getLastException() {
return _lastException;
}
-
- public static void logError(String msg, Exception e) {
- Log.e(APP_NAME, msg, e);
- }
-
- public static void logError(String msg) {
- Log.e(APP_NAME, msg);
- }
-
- public static void logDebug(String msg) {
- logDebug(msg, 0);
- }
-
- public static void logDebug(String msg, int level) {
-
- if (level < Commons.getLogPriority()) {
- return;
- }
-
- Log.d(APP_NAME, msg);
- }
-
+
public static boolean writeFileLines(String filePath, String[] lines, boolean overwrite) {
if (lines == null) {
@@ -113,7 +96,7 @@ public class Utils {
try {
writer.close();
} catch (IOException e) {
- logError("error while closing filewriter", e);
+ Logger.logError("error while closing filewriter", e);
}
}
@@ -129,7 +112,7 @@ public class Utils {
File f = new File(filePath);
if (!f.exists()) {
- logError("File doesn't exist: " + filePath);
+ Logger.logError("File doesn't exist: " + filePath);
return null;
}
@@ -153,7 +136,7 @@ public class Utils {
return lines.toArray(ar);
} catch (Exception e) {
- logError("Error while reading file " + filePath, e);
+ Logger.logError("Error while reading file " + filePath, e);
return null;
} finally {
@@ -162,28 +145,28 @@ public class Utils {
bufr.close();
}
} catch (IOException ex) {
- logError("error while closing filereader", ex);
+ Logger.logError("error while closing filereader", ex);
}
try {
if (fr != null) {
fr.close();
}
} catch (IOException exc) {
- logError("error while closing filereader", exc);
+ Logger.logError("error while closing filereader", exc);
}
}
}
- public static String readFile(String filePath){
-
+ public static String readFile(String filePath) {
+
if (filePath == null) {
return null;
}
File f = new File(filePath);
if (!f.exists()) {
- logError("File doesn't exist: " + filePath);
+ Logger.logError("File doesn't exist: " + filePath);
return null;
}
@@ -206,7 +189,7 @@ public class Utils {
return sb.toString();
} catch (Exception e) {
- logError("Error while reading file " + filePath, e);
+ Logger.logError("Error while reading file " + filePath, e);
return null;
} finally {
@@ -215,66 +198,56 @@ public class Utils {
bufr.close();
}
} catch (IOException ex) {
- logError("error while closing filereader", ex);
+ Logger.logError("error while closing filereader", ex);
}
try {
if (fr != null) {
fr.close();
}
} catch (IOException exc) {
- logError("error while closing filereader", exc);
+ Logger.logError("error while closing filereader", exc);
}
}
-
- }
-
- public static long daysToMilliseconds(int days) {
- return (days * MILLIS_IN_DAY);
- }
- public static long millisecondsToDays(long milliseconds) {
- return (milliseconds / MILLIS_IN_DAY);
}
- public static boolean dumpLogcatToFile(String filePath) {
-
- if (filePath == null) {
- return false;
+ /* private static String flagExists = "EXISTS";
+ private static String flagNotExists = "NOT-EXISTS";
+ public static boolean fileExistsRoot(String filePath) throws Exception{
+
+ String cmd = "if [ -e \""+ filePath +"\" ]; then echo \"" + flagExists + "\"; else echo \""+ flagNotExists + "\";fi";
+
+ RootCommand su = new RootCommand(cmd);
+ if (su.execute() !=0){
+ throw new Exception("RepWifi.Utils Failed to check for file existence!");
}
-
- try {
-
- String cmd1 = "logcat -d | grep " + APP_NAME + ">" + filePath;
- String cmd2 = "logcat -d | grep " + Commons.getContext().getPackageName() + ">>"
- + filePath;
- String SEP_LOG = "\n\n---------- [REPWIFI_LOG_SEPARATOR] ----------\n\n";
-
- RootCommand c1 = new RootCommand(cmd1);
- RootCommand c2 = new RootCommand(cmd2);
-
- if (c1.execute() != 0) {
- return false;
- }
-
- if (!writeFile(filePath, SEP_LOG, false)) {
- return false;
- }
-
- if (c2.execute() != 0) {
- return false;
- }
-
+
+ if (su._cmdOut.trim().equals(flagExists)){
return true;
-
- } catch (Exception e) {
- logError("Exception during log dump.", e);
+ } else if (su._cmdOut.trim().equals(flagNotExists)){
return false;
+ } else {
+ throw new Exception("RepWifi.Utils received unknown flag while checking for file existence!");
}
+ }*/
+
+ /* public static boolean createDirectoryRoot(String dirPath){
+
+ String cmd = "mkdir " + dirPath;
+ return RootCommand.executeRootCmd(cmd);
+
+ }
+ */
+ public static long daysToMilliseconds(int days) {
+ return (days * MILLIS_IN_DAY);
+ }
+ public static long millisecondsToDays(long milliseconds) {
+ return (milliseconds / MILLIS_IN_DAY);
}
public static String netmaskIntToString(int mask) {
-
+
if (mask < 8 || mask > 32) {
return null;
}
@@ -307,11 +280,15 @@ public class Utils {
public static int netmaskStringToInt(String mask) {
if (mask == null) {
+ Logger.logError("netmaskStringToInt received null mask");
return -1;
}
+ mask = mask.trim();
+
String[] octs = mask.split("\\.");
if (octs.length != 4) {
+ Logger.logError("netmaskStringToInt invalid input string: " + mask);
return -1;
}
@@ -320,16 +297,18 @@ public class Utils {
for (String o : octs) {
int intval = 0;
-
+
try {
intval = Integer.parseInt(o, 10);
} catch (NumberFormatException e) {
+ Logger.logError("netmaskStringToInt", e);
return -1;
- }
+ }
String b = Integer.toBinaryString(intval);
if (b.length() != 8 && b.contains("1")) {
- //invalid mask! has ones after a zero
+ // invalid mask! has ones after a zero
+ Logger.logError("netmaskStringToInt invalid mask! has ones after a zero");
return -1;
}
for (int i = 0; i < b.length(); i++) {
@@ -338,18 +317,114 @@ public class Utils {
} else if (prevIsZero) {
// invalid mask
+ Logger.logError("netmaskStringToInt invalid mask");
return -1;
-
+
} else {
intmask += 1;
}
-
+
}
-
+
}
-
+
return intmask;
}
+ public static String rawResourceAsString(Context context, int resId) {
+
+ InputStream s = null;
+ Scanner scan = null;
+
+ try {
+
+ s = context.getResources().openRawResource(resId);
+ scan = new Scanner(s, "UTF-8").useDelimiter("\\A");
+ return scan.next();
+
+ } catch (Exception e) {
+ Logger.logError("Exception while reading raw resource as string", e);
+ return null;
+
+ } finally {
+
+ try {
+ if (scan != null) {
+ scan.close();
+ }
+ } catch (Exception e2) {
+ }
+
+ try {
+ if (s != null) {
+ s.close();
+ }
+ } catch (Exception e2) {
+ }
+
+ }
+
+ }
+
+ public static void showMessage(String msg, Context context) {
+
+ AlertDialog.Builder dlgAlert = new AlertDialog.Builder(context,
+ R.style.Theme_RepWifiDialogTheme);
+ dlgAlert.setMessage(msg);
+ dlgAlert.setPositiveButton(context.getString(android.R.string.ok),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ return;
+ }
+ });
+
+ dlgAlert.setCancelable(false);
+ AlertDialog diag = dlgAlert.create();
+
+ diag.show();
+
+ }
+
+ public static void killBackEnd(Context context, boolean silent) {
+
+ if (silent) {
+
+ Engine.killDhcpcd();
+ WpaSupplicant.kill();
+
+ } else {
+
+ String msg = context.getString(R.string.confirm_kill_backend);
+ AlertDialog.Builder dlgAlert = new AlertDialog.Builder(context,
+ R.style.Theme_RepWifiDialogTheme);
+ dlgAlert.setMessage(msg);
+ dlgAlert.setPositiveButton(context.getString(android.R.string.ok),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ killBackEnd(null, true);
+ return;
+ }
+ });
+ dlgAlert.setNegativeButton(context.getString(android.R.string.cancel),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ return;
+ }
+ });
+
+ dlgAlert.setCancelable(false);
+ AlertDialog diag = dlgAlert.create();
+
+ diag.show();
+ return;
+
+ }
+
+ }
+
+
}
diff --git a/app/src/fil/libre/repwifiapp/activities/ConnectionBoundActivity.java b/app/src/fil/libre/repwifiapp/activities/ConnectionBoundActivity.java
new file mode 100644
index 0000000..4996024
--- /dev/null
+++ b/app/src/fil/libre/repwifiapp/activities/ConnectionBoundActivity.java
@@ -0,0 +1,282 @@
+//
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+//
+// This file is part of RepWifiApp.
+//
+// RepWifiApp 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.
+//
+// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
+//
+// ********************************************************************
+
+package fil.libre.repwifiapp.activities;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.preference.PreferenceManager;
+import fil.libre.repwifiapp.Prefs;
+import fil.libre.repwifiapp.helpers.Logger;
+import fil.libre.repwifiapp.network.AccessPointInfo;
+import fil.libre.repwifiapp.network.ConnectionResult;
+import fil.libre.repwifiapp.network.ConnectionStatus;
+import fil.libre.repwifiapp.service.Channel;
+import fil.libre.repwifiapp.service.ConnectionManagementService;
+
+/**
+ * Provides a base class for all activities that need to bind to the
+ * ConnectionManagementService.
+ */
+public class ConnectionBoundActivity extends MenuEnabledActivity implements
+ OnSharedPreferenceChangeListener {
+
+ private IBinder svcBinder = null;
+ private Channel channel = null;
+ private ServiceConnection svcConn = null;
+
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ startConnectionService();
+ registerPreferenceChangeListener();
+ }
+
+ protected boolean isServiceBound() {
+ return (channel != null);
+ }
+
+ private void startConnectionService() {
+
+ Intent startIntent = new Intent(this, ConnectionManagementService.class);
+ startIntent.setAction(ConnectionManagementService.ACTION_VOID);
+ startService(startIntent);
+
+ if (channel != null) {
+ Logger.logDebug("ConnectionManagementService already bound");
+ return;
+ }
+
+ try {
+
+ if (svcConn == null) {
+
+ svcConn = new ServiceConnection() {
+
+ public void onServiceConnected(ComponentName className,
+ android.os.IBinder service) {
+ ConnectionBoundActivity.this.initChannel(service);
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ ConnectionBoundActivity.this.channel = null;
+ ConnectionBoundActivity.this.svcBinder = null;
+ }
+
+ };
+
+ }
+
+ Intent intentGetService = new Intent(this, ConnectionManagementService.class);
+ if (!this.bindService(intentGetService, svcConn, Context.BIND_AUTO_CREATE)) {
+ Logger.logError("Failed to bind to ConnectionManagementService (bindService returned false)");
+ return;
+ }
+
+ } catch (Exception ex) {
+ Logger.logError("Exception while bounding to inner connectivity management service.",
+ ex);
+ }
+
+ }
+
+ private void initChannel(IBinder service) {
+ ConnectionBoundActivity.this.svcBinder = service;
+ ConnectionBoundActivity.this.channel = new Channel(this, new Messenger(service),
+ new Messenger(new ResponseHandler()));
+ ConnectionBoundActivity.this.onManagementServiceConnected();
+ }
+
+ protected void onManagementServiceConnected() {
+ sendCmdPrefChanged(Prefs.PREF_MONITOR_NET_STATE);
+ }
+
+ protected boolean sendCmdStartConnect(AccessPointInfo network) {
+ if (channel == null) {
+ return false;
+ }
+ return channel.sendMsg(ConnectionManagementService.CMD_START_CONNECT, network,
+ Channel.PAYLOAD_APINFO);
+ }
+
+ protected boolean sendCmdAbortConnection() {
+ return sendMsgIfChannelConnected(ConnectionManagementService.CMD_ABORT_CONNECTION);
+ }
+
+ protected boolean sendCmdDisconnect() {
+ return sendMsgIfChannelConnected(ConnectionManagementService.CMD_DISCONNECT);
+ }
+
+ protected boolean sendCmdGetAvailableNetworks() {
+ return sendMsgIfChannelConnected(ConnectionManagementService.CMD_GET_AVAILABLE_NETWORKS);
+ }
+
+ protected boolean sendCmdStartMonitoringConnectionStatus() {
+ return sendMsgIfChannelConnected(ConnectionManagementService.CMD_START_MONITOR_CONNECTION_STATUS);
+ }
+
+ protected boolean sendCmdStopMonitoringConnectionStatus() {
+ return sendMsgIfChannelConnected(ConnectionManagementService.CMD_STOP_MONITOR_CONNECTION_STATUS);
+ }
+
+ protected boolean requestStatusUpdate() {
+ return sendMsgIfChannelConnected(ConnectionManagementService.CMD_STATUS_UPDATE);
+ }
+
+ protected boolean sendCmdAutoconnect() {
+ return sendMsgIfChannelConnected(ConnectionManagementService.CMD_AUTOCONNECT);
+ }
+
+ private boolean sendCmdPrefChanged(String prefname){
+ if (channel == null){
+ return false;
+ }
+ Bundle b = new Bundle();
+ b.putString(Channel.PAYLOAD_PREFKEY, prefname);
+ return channel.sendMsg(ConnectionManagementService.CMD_PREF_CHANGED, b, 0);
+ }
+
+ private void sendCmdUnbind() {
+ sendMsgIfChannelConnected(ConnectionManagementService.CMD_CLIENT_UNBINDING);
+ }
+
+ private boolean sendMsgIfChannelConnected(int what) {
+ return sendMsgIfChannelConnected(what, 0);
+ }
+
+ private boolean sendMsgIfChannelConnected(int what, int arg1) {
+ if (channel == null) {
+ return false;
+ }
+ return channel.sendMsg(what, null, arg1);
+ }
+
+ protected void onMsgStatusChange(ConnectionStatus status) {
+ }
+
+ protected void onMsgConnectionResult(ConnectionResult connres) {
+ }
+
+ protected void onMsgAvailableNetworks(AccessPointInfo[] infos) {
+ }
+
+ protected void onMsgAutoconnectReport(AccessPointInfo[] infos) {
+ }
+
+ protected void onMsgDisconnectReport(ConnectionStatus status) {
+ }
+
+ private class ResponseHandler extends Handler {
+
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+
+ case ConnectionManagementService.MSG_STATUS_CHANGE:
+ Logger.logDebug("Received status update from the management service");
+ ConnectionStatus c = channel.getConnectionStatusPayload(msg);
+ onMsgStatusChange(c);
+ break;
+
+ case ConnectionManagementService.MSG_CONNECTION_RESULT:
+ Logger.logDebug("Received connection result from management service");
+ ConnectionResult r = channel.getConnectionResultPayload(msg);
+ onMsgConnectionResult(r);
+ break;
+
+ case ConnectionManagementService.MSG_AVAILABLE_NETWORKS:
+ Logger.logDebug("Received available networks from management service");
+ onMsgAvailableNetworks(channel.getApinfoArrayPayload(msg));
+ break;
+
+ case ConnectionManagementService.MSG_AUTOCONNECT_REPORT:
+ Logger.logDebug("Received autoconnect report from management service");
+ onMsgAutoconnectReport(channel.getApinfoArrayPayload(msg));
+ break;
+
+ case ConnectionManagementService.MSG_DISCONNECT_REPORT:
+ Logger.logDebug("Received disconnect report from management service");
+ onMsgDisconnectReport(channel.getConnectionStatusPayload(msg));
+ break;
+
+ default:
+ Logger.logError("Received response from connection management service with unknown what: "
+ + msg.what);
+ }
+ }
+
+ }
+
+ private void registerPreferenceChangeListener() {
+
+ SharedPreferences prefs = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext());
+ prefs.registerOnSharedPreferenceChangeListener(this);
+
+ }
+
+ private void unregisterPreferenceChangeListener() {
+ SharedPreferences prefs = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext());
+ prefs.unregisterOnSharedPreferenceChangeListener(this);
+ }
+
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+
+ if (sharedPreferences == null) {
+ return;
+ }
+
+ sharedPreferences.edit().commit();
+ Prefs.commit(getApplicationContext());
+ sendCmdPrefChanged(key);
+
+ }
+
+ protected void unbindFromService() {
+ sendCmdUnbind();
+ if (svcBinder != null && svcConn != null && channel != null) {
+ unbindService(svcConn);
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ }
+
+ @Override
+ public void onDestroy() {
+ unregisterPreferenceChangeListener();
+ unbindFromService();
+ super.onDestroy();
+ }
+}
diff --git a/app/src/fil/libre/repwifiapp/activities/CreditsActivity.java b/app/src/fil/libre/repwifiapp/activities/CreditsActivity.java
index 80c15e6..cc520a9 100644
--- a/app/src/fil/libre/repwifiapp/activities/CreditsActivity.java
+++ b/app/src/fil/libre/repwifiapp/activities/CreditsActivity.java
@@ -1,5 +1,5 @@
//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
//
// This file is part of RepWifiApp.
//
@@ -20,14 +20,14 @@
package fil.libre.repwifiapp.activities;
-import fil.libre.repwifiapp.R;
-import android.os.Bundle;
import android.app.Activity;
+import android.os.Bundle;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.text.method.ScrollingMovementMethod;
import android.view.Menu;
import android.widget.TextView;
+import fil.libre.repwifiapp.R;
public class CreditsActivity extends Activity {
diff --git a/app/src/fil/libre/repwifiapp/activities/InputPasswordActivity.java b/app/src/fil/libre/repwifiapp/activities/InputPasswordActivity.java
index 32922c6..7578ec3 100644
--- a/app/src/fil/libre/repwifiapp/activities/InputPasswordActivity.java
+++ b/app/src/fil/libre/repwifiapp/activities/InputPasswordActivity.java
@@ -1,5 +1,5 @@
//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
//
// This file is part of RepWifiApp.
//
@@ -17,13 +17,11 @@
// along with RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
//
// ********************************************************************
+//
+// ********************************************************************
package fil.libre.repwifiapp.activities;
-import fil.libre.repwifiapp.ActivityLauncher;
-import fil.libre.repwifiapp.Commons;
-import fil.libre.repwifiapp.R;
-import fil.libre.repwifiapp.helpers.AccessPointInfo;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
@@ -35,10 +33,15 @@ import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
import android.widget.TextView;
+import fil.libre.repwifiapp.ActivityLauncher;
+import fil.libre.repwifiapp.R;
+import fil.libre.repwifiapp.Utils;
+import fil.libre.repwifiapp.network.AccessPointInfo;
public class InputPasswordActivity extends Activity implements OnCheckedChangeListener {
AccessPointInfo apinfo = null;
+ String message = null;
@Override
public void onCreate(Bundle icicle) {
@@ -54,11 +57,27 @@ public class InputPasswordActivity extends Activity implements OnCheckedChangeLi
TextView v = (TextView) findViewById(R.id.txt_insert_pass);
// get the network to set password to:
- this.apinfo = (AccessPointInfo) getIntent().getSerializableExtra(
+ this.apinfo = (AccessPointInfo) getIntent().getParcelableExtra(
ActivityLauncher.EXTRA_APINFO);
+
+ if (getIntent().hasExtra(ActivityLauncher.EXTRA_MESSAGE)){
+ this.message = getIntent().getStringExtra(ActivityLauncher.EXTRA_MESSAGE);
+ }
+
v.append(" " + apinfo.getSsid());
}
+
+ @Override
+ public void onStart(){
+ super.onStart();
+
+ if (this.message != null){
+ Utils.showMessage(message, this);
+ this.message = null;
+ }
+
+ }
@Override
public boolean onCreateOptionsMenu(Menu menu) {
@@ -81,7 +100,7 @@ public class InputPasswordActivity extends Activity implements OnCheckedChangeLi
String pass = txpass.getText().toString();
if (pass.length() == 0) {
- Commons.showMessage(getString(R.string.msg_password_empty), this);
+ Utils.showMessage(getString(R.string.msg_password_empty), this);
}
this.apinfo.setPassword(pass);
diff --git a/app/src/fil/libre/repwifiapp/activities/InputSsidActivity.java b/app/src/fil/libre/repwifiapp/activities/InputSsidActivity.java
index 8b7c60c..8ff1100 100644
--- a/app/src/fil/libre/repwifiapp/activities/InputSsidActivity.java
+++ b/app/src/fil/libre/repwifiapp/activities/InputSsidActivity.java
@@ -1,5 +1,5 @@
//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
//
// This file is part of RepWifiApp.
//
@@ -17,20 +17,20 @@
// along with RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
//
// ********************************************************************
-
package fil.libre.repwifiapp.activities;
-import fil.libre.repwifiapp.ActivityLauncher;
-import fil.libre.repwifiapp.ActivityLauncher.RequestCode;
-import fil.libre.repwifiapp.Commons;
-import fil.libre.repwifiapp.R;
-import fil.libre.repwifiapp.helpers.AccessPointInfo;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
+import fil.libre.repwifiapp.ActivityLauncher;
+import fil.libre.repwifiapp.ActivityLauncher.RequestCode;
+import fil.libre.repwifiapp.Commons;
+import fil.libre.repwifiapp.R;
+import fil.libre.repwifiapp.Utils;
+import fil.libre.repwifiapp.network.AccessPointInfo;
public class InputSsidActivity extends Activity {
@@ -66,8 +66,7 @@ public class InputSsidActivity extends Activity {
if (intent.hasExtra(ActivityLauncher.EXTRA_APINFO)) {
Bundle xtras = intent.getExtras();
- AccessPointInfo i = (AccessPointInfo) xtras
- .getSerializable(ActivityLauncher.EXTRA_APINFO);
+ AccessPointInfo i = (AccessPointInfo) xtras.getParcelable(ActivityLauncher.EXTRA_APINFO);
returnAccessPointInfo(i);
}
@@ -85,7 +84,7 @@ public class InputSsidActivity extends Activity {
String ssid = txssid.getText().toString();
if (ssid.length() == 0) {
- Commons.showMessage(getString(R.string.msg_network_name_empty), this);
+ Utils.showMessage(getString(R.string.msg_network_name_empty), this);
return;
}
diff --git a/app/src/fil/libre/repwifiapp/activities/Ipv4SettingsActivity.java b/app/src/fil/libre/repwifiapp/activities/Ipv4SettingsActivity.java
index 1f15252..eba62ac 100644
--- a/app/src/fil/libre/repwifiapp/activities/Ipv4SettingsActivity.java
+++ b/app/src/fil/libre/repwifiapp/activities/Ipv4SettingsActivity.java
@@ -1,5 +1,5 @@
//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
//
// This file is part of RepWifiApp.
//
@@ -20,21 +20,22 @@
package fil.libre.repwifiapp.activities;
-import fil.libre.repwifiapp.ActivityLauncher;
-import fil.libre.repwifiapp.Commons;
-import fil.libre.repwifiapp.R;
-import fil.libre.repwifiapp.helpers.AccessPointInfo;
-import fil.libre.repwifiapp.helpers.DhcpSettings;
-import android.nfc.FormatException;
-import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
+import android.nfc.FormatException;
+import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
-import android.widget.EditText;
import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.EditText;
+import fil.libre.repwifiapp.ActivityLauncher;
+import fil.libre.repwifiapp.R;
+import fil.libre.repwifiapp.Utils;
+import fil.libre.repwifiapp.network.AccessPointInfo;
+import fil.libre.repwifiapp.network.DhcpSettings;
+import fil.libre.repwifiapp.network.NetworkManager;
public class Ipv4SettingsActivity extends Activity implements OnCheckedChangeListener {
@@ -65,15 +66,14 @@ public class Ipv4SettingsActivity extends Activity implements OnCheckedChangeLis
return;
}
- this.currentNetwork = (AccessPointInfo) intent.getExtras().getSerializable(
- ActivityLauncher.EXTRA_APINFO);
+ this.currentNetwork = (AccessPointInfo) intent.getExtras().getParcelable(ActivityLauncher.EXTRA_APINFO);
if (this.currentNetwork == null) {
this.setResult(RESULT_CANCELED);
this.finish();
return;
}
- this.currentNetwork = Commons.storage.getSavedNetwork(currentNetwork);
+ this.currentNetwork = NetworkManager.getSavedNetwork(currentNetwork);
loadNetwork();
@@ -88,7 +88,7 @@ public class Ipv4SettingsActivity extends Activity implements OnCheckedChangeLis
private void loadNetwork() {
- setTitle(this.currentNetwork.getSsid());
+ setTitle(getString(R.string.text_ipv4_settings) + " " + this.currentNetwork.getSsid());
currentSettings = this.currentNetwork.getDhcpConfiguration();
loadSettings();
@@ -147,17 +147,17 @@ public class Ipv4SettingsActivity extends Activity implements OnCheckedChangeLis
String gw = txtGw.getText().toString();
if (!DhcpSettings.isValidAddress(ip)) {
- Commons.showMessage(getString(R.string.msg_invalid_ip),this);
+ Utils.showMessage(getString(R.string.msg_invalid_ip),this);
return;
}
if (!DhcpSettings.isValidMaks(mask)) {
- Commons.showMessage(getString(R.string.msg_invalid_netmask),this);
+ Utils.showMessage(getString(R.string.msg_invalid_netmask),this);
return;
}
if (!DhcpSettings.isValidAddress(gw)) {
- Commons.showMessage(getString(R.string.msg_invalid_gateway),this);
+ Utils.showMessage(getString(R.string.msg_invalid_gateway),this);
return;
}
@@ -166,10 +166,10 @@ public class Ipv4SettingsActivity extends Activity implements OnCheckedChangeLis
}
currentNetwork.setDhcpConfiguration(currentSettings);
- if (Commons.storage.save(currentNetwork)) {
- Commons.showMessage(getString(R.string.msg_network_saved),this);
+ if (NetworkManager.save(currentNetwork)) {
+ Utils.showMessage(getString(R.string.msg_network_saved),this);
} else {
- Commons.showMessage(getString(R.string.msg_network_save_fail),this);
+ Utils.showMessage(getString(R.string.msg_network_save_fail),this);
}
}
diff --git a/app/src/fil/libre/repwifiapp/activities/LongTaskActivity.java b/app/src/fil/libre/repwifiapp/activities/LongTaskActivity.java
index a5292d2..940d547 100644
--- a/app/src/fil/libre/repwifiapp/activities/LongTaskActivity.java
+++ b/app/src/fil/libre/repwifiapp/activities/LongTaskActivity.java
@@ -1,5 +1,5 @@
//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
//
// This file is part of RepWifiApp.
//
@@ -18,83 +18,47 @@
//
// ********************************************************************
-
package fil.libre.repwifiapp.activities;
-import fil.libre.repwifiapp.ActivityLauncher;
-import fil.libre.repwifiapp.Commons;
-import fil.libre.repwifiapp.R;
-import fil.libre.repwifiapp.helpers.AccessPointInfo;
-import fil.libre.repwifiapp.helpers.ConnectionStatus;
-import fil.libre.repwifiapp.helpers.Utils;
-import android.os.AsyncTask;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
-import android.app.Activity;
-import android.content.Intent;
-
-public class LongTaskActivity extends Activity {
-
- private class Task extends AsyncTask<Object, Object, Object> {
-
- private int REQ_CODE;
-
- public Task(int reqCode, Object input) {
- this.REQ_CODE = reqCode;
- }
-
- @Override
- protected Object doInBackground(Object... params) {
-
- Object ret = null;
-
- switch (this.REQ_CODE) {
-
- case ActivityLauncher.RequestCode.CONNECT:
-
- ret = Commons.connectionEngine.connect((AccessPointInfo) params[0]);
- break;
-
- case ActivityLauncher.RequestCode.NETWORKS_GET:
-
- ret = Commons.connectionEngine.getAvailableNetworks();
- break;
-
- case ActivityLauncher.RequestCode.STATUS_GET:
-
- ret = Commons.connectionEngine.getConnectionStatus();
- break;
-
- default:
-
- break;
-
- }
-
- return ret;
-
- }
-
- @Override
- protected void onPostExecute(Object result) {
- taskCompleted(result, this.REQ_CODE);
- }
+import android.widget.Toast;
+import fil.libre.repwifiapp.ActivityLauncher;
+import fil.libre.repwifiapp.ActivityLauncher.RequestCode;
+import fil.libre.repwifiapp.Prefs;
+import fil.libre.repwifiapp.R;
+import fil.libre.repwifiapp.helpers.Logger;
+import fil.libre.repwifiapp.network.AccessPointInfo;
+import fil.libre.repwifiapp.network.ConnectionResult;
- }
+public class LongTaskActivity extends ConnectionBoundActivity {
private AccessPointInfo currentNetwork = null;
+ // private Task currentTask = null;
+
+ private int currentReqCode = -1;
+ private boolean isCancelled = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_long_task);
-
- toggleProgbar(Commons.isProgbarEnabled());
- startTask();
-
+ toggleProgbar(isProgbarEnabled());
+
+ }
+
+ @Override
+ public void onStart(){
+ super.onStart();
+ if (isServiceBound()){
+ startTask();
+ }
}
@Override
@@ -102,6 +66,21 @@ public class LongTaskActivity extends Activity {
return true;
}
+ @Override
+ protected void onManagementServiceConnected() {
+ startTask();
+ }
+
+ @Override
+ protected void onMsgConnectionResult(ConnectionResult connres) {
+ taskCompleted(connres, currentReqCode);
+ }
+
+ @Override
+ protected void onMsgAvailableNetworks(AccessPointInfo[] infos) {
+ taskCompleted(infos, currentReqCode);
+ }
+
private void startTask() {
// retrieve the request code:
@@ -112,40 +91,56 @@ public class LongTaskActivity extends Activity {
}
Object input = null;
- int reqCode = intent.getExtras().getInt(ActivityLauncher.EXTRA_REQCODE);
-
- switch (reqCode) {
+ currentReqCode = intent.getExtras().getInt(ActivityLauncher.EXTRA_REQCODE);
+
+ switch (currentReqCode) {
case ActivityLauncher.RequestCode.CONNECT:
// Extract AccessPointInfo
- input = intent.getExtras().getSerializable(ActivityLauncher.EXTRA_APINFO);
+ input = intent.getExtras().getParcelable(ActivityLauncher.EXTRA_APINFO);
currentNetwork = (AccessPointInfo) input;
setTitle(getString(R.string.msg_connecting_to) + " " + currentNetwork.getSsid() + "...");
- setMessage(getString(R.string.msg_connecting_to) + " " + currentNetwork.getSsid() + "...");
+ setMessage(getString(R.string.msg_connecting_to) + " " + currentNetwork.getSsid()
+ + "...");
+
+ sendCmdStartConnect(currentNetwork);
+
break;
case ActivityLauncher.RequestCode.NETWORKS_GET:
setTitle(getString(R.string.title_scanning));
setMessage(getString(R.string.msg_scanning_for_nets));
-
- case ActivityLauncher.RequestCode.STATUS_GET:
+
+ sendCmdGetAvailableNetworks();
+
+ break;
+
+/* case ActivityLauncher.RequestCode.STATUS_GET:
setTitle(getString(R.string.msg_checking_status));
setMessage(getString(R.string.msg_checking_status));
-
+ break;*/
+
default:
setTitle(getString(R.string.msg_please_wait));
setMessage(getString(R.string.msg_please_wait));
break;
}
- Task task = new Task(reqCode, input);
- task.execute(input);
+
+/* currentTask = new Task(reqCode, input);
+ currentTask.execute(input);*/
}
private void taskCompleted(Object result, int reqCode) {
- Utils.logDebug("Finished long task reqCode: " + reqCode, 1);
+ Logger.logDebug("Finished long task reqCode: " + reqCode);
+
+ if (isCancelled){
+ Logger.logDebug("Received result but activity has been canceled");
+ this.setResult(RESULT_CANCELED);
+ finish();
+ }
// Return to caller:
Intent intent = this.getIntent();
@@ -154,7 +149,7 @@ public class LongTaskActivity extends Activity {
case ActivityLauncher.RequestCode.CONNECT:
- intent.putExtra(ActivityLauncher.EXTRA_BOOLEAN, (Boolean) result);
+ intent.putExtra(ActivityLauncher.EXTRA_CONNRES, (ConnectionResult)result);
intent.putExtra(ActivityLauncher.EXTRA_APINFO, this.currentNetwork);
break;
@@ -162,15 +157,15 @@ public class LongTaskActivity extends Activity {
intent.putExtra(ActivityLauncher.EXTRA_APINFO_ARR, (AccessPointInfo[]) result);
break;
-
+/*
case ActivityLauncher.RequestCode.STATUS_GET:
intent.putExtra(ActivityLauncher.EXTRA_CONSTATUS, (ConnectionStatus) result);
- break;
+ break;*/
default:
- Utils.logDebug("Task terminating in null: ", 1);
+ Logger.logDebug("Task terminating in null: ", 1);
break;
}
@@ -199,7 +194,71 @@ public class LongTaskActivity extends Activity {
@Override
public void onBackPressed() {
- // suppress back button
+ promtpCancelJob();
+ }
+
+ private void promtpCancelJob() {
+
+ String msg = getString(R.string.msg_confirm_stop_job);
+ AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this,
+ R.style.Theme_RepWifiDialogTheme);
+ dlgAlert.setMessage(msg);
+ dlgAlert.setPositiveButton(getString(android.R.string.ok),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ confirmCancelJob();
+ return;
+ }
+ });
+ dlgAlert.setNegativeButton(getString(android.R.string.cancel),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ return;
+ }
+ });
+
+ dlgAlert.setCancelable(false);
+ AlertDialog diag = dlgAlert.create();
+
+ diag.show();
+
+ }
+
+ private boolean cancelConnection() {
+ return sendCmdAbortConnection();
+ }
+
+ private void confirmCancelJob() {
+ /*
+ * if (currentTask != null){
+ * if (currentReqCode == RequestCode.CONNECT){
+ * Commons.connectionEngine.abortConnection();
+ * }
+ *
+ * currentTask.cancel(true);
+ * }
+ */
+
+ if (currentReqCode == RequestCode.CONNECT) {
+ if (cancelConnection()){
+
+ super.onBackPressed();
+ } else {
+ Toast.makeText(getApplicationContext(), getString(R.string.msg_error_generic), Toast.LENGTH_LONG).show();
+ }
+
+ } else {
+ isCancelled = true;
+ super.onBackPressed();
+ }
+
+
+ }
+
+ private boolean isProgbarEnabled() {
+ return Prefs.getBoolean(getApplicationContext(), Prefs.PREF_PROGBAR, true);
}
}
diff --git a/app/src/fil/libre/repwifiapp/activities/MainActivity.java b/app/src/fil/libre/repwifiapp/activities/MainActivity.java
index 7a22416..14f33e9 100644
--- a/app/src/fil/libre/repwifiapp/activities/MainActivity.java
+++ b/app/src/fil/libre/repwifiapp/activities/MainActivity.java
@@ -1,5 +1,5 @@
//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
//
// This file is part of RepWifiApp.
//
@@ -20,64 +20,80 @@
package fil.libre.repwifiapp.activities;
-import java.net.SocketException;
-import fil.libre.repwifiapp.ActivityLauncher;
-import fil.libre.repwifiapp.Commons;
-import fil.libre.repwifiapp.R;
-import fil.libre.repwifiapp.ActivityLauncher.RequestCode;
-import fil.libre.repwifiapp.helpers.AccessPointInfo;
-import fil.libre.repwifiapp.helpers.ConnectionStatus;
-import fil.libre.repwifiapp.helpers.NetworkManager;
-import fil.libre.repwifiapp.helpers.OpenVpnManager;
-import fil.libre.repwifiapp.helpers.RootCommand;
-import fil.libre.repwifiapp.helpers.Utils;
-import fil.libre.repwifiapp.helpers.WpaSupplicant;
-import android.os.Bundle;
+import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.AssetManager;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.usb.UsbManager;
+import android.os.Bundle;
+import android.os.Parcelable;
import android.util.Log;
import android.view.View;
+import android.widget.Button;
import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.RelativeLayout.LayoutParams;
+import android.widget.TextView;
import android.widget.Toast;
+import fil.libre.repwifiapp.ActivityLauncher;
+import fil.libre.repwifiapp.ActivityLauncher.RequestCode;
+import fil.libre.repwifiapp.Commons;
+import fil.libre.repwifiapp.Prefs;
+import fil.libre.repwifiapp.R;
+import fil.libre.repwifiapp.Utils;
+import fil.libre.repwifiapp.helpers.Logger;
+import fil.libre.repwifiapp.helpers.RootCommand;
+import fil.libre.repwifiapp.network.AccessPointInfo;
+import fil.libre.repwifiapp.network.ConnectionResult;
+import fil.libre.repwifiapp.network.ConnectionStatus;
+import fil.libre.repwifiapp.network.Engine;
+import fil.libre.repwifiapp.network.NetworkManager;
+import fil.libre.repwifiapp.network.WpaCli;
+import fil.libre.repwifiapp.network.WpaSupplicant;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.SocketException;
-public class MainActivity extends MenuEnabledActivity {
+public class MainActivity extends VpnAndConnectionBoundActivity{
private ActivityLauncher launcher = new ActivityLauncher(this);
private BroadcastReceiver detachReceiver;
-
+ private ConnectionStatus status = null;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
- if (!Commons.init(this)) {
- Utils.logDebug("Failed to initialize Commons. Aborting.");
- finish();
- return;
- }
+ Commons.init(getApplicationContext());
- if (! Commons.storage.updateStorageVersion()){
- Utils.logError("Failed to convert storage file to new version!");
+ if (!NetworkManager.updateStorageVersion()) {
+ Logger.logError("Failed to convert storage file to new version!");
}
-
- setImage();
+
+ toggleStatusAppearance(false);
setUsbDeviceMonitor();
setVersionOnTitle();
-
- OpenVpnManager.initialize(this);
+
+ if (isSystemApp()) {
+ setTitle(getTitle() + " (sysapp)");
+ }
}
@Override
public void onStart() {
super.onStart();
- Utils.logDebug("Main onStart()");
-
- Commons.updateNotification(this);
+ Logger.logDebug("Main onStart()");
if (handleIntent()) {
// app called for a specific intent.
@@ -88,14 +104,17 @@ public class MainActivity extends MenuEnabledActivity {
checkConditions();
- ConnectionStatus status = Commons.connectionEngine.getConnectionStatus();
- if (status != null && status.isConnected()) {
- Utils.logDebug("Main about to launch status activity...");
- launcher.launchStatusActivity(status);
+ if (isServiceBound()) {
+ requestStatusUpdate();
}
- Utils.logDebug("Main onStart() returning.");
+ Logger.logDebug("Main onStart() returning.");
+
+ }
+ @Override
+ public void onSaveInstanceState(Bundle savedInstanceState) {
+ super.onSaveInstanceState(savedInstanceState);
}
private boolean handleIntent() {
@@ -126,53 +145,62 @@ public class MainActivity extends MenuEnabledActivity {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
- Utils.logDebug("Main onActivityResult(): ", 1);
+ Logger.logDebug("Main onActivityResult(): ");
if (intent == null) {
return;
}
- if (resultCode != RESULT_OK) {
+ if (resultCode != RESULT_OK && requestCode != RequestCode.VPN_PERMISSION_CONN) {
return;
}
AccessPointInfo i = null;
if (intent.hasExtra(ActivityLauncher.EXTRA_APINFO)) {
Bundle xtras = intent.getExtras();
- i = (AccessPointInfo) xtras.getSerializable(ActivityLauncher.EXTRA_APINFO);
+ i = (AccessPointInfo) xtras.getParcelable(ActivityLauncher.EXTRA_APINFO);
}
switch (requestCode) {
case RequestCode.PASS_INPUT:
+ Logger.logDebug("ReqCode: PASS_INPUT");
handleResultSetPass(i);
break;
case RequestCode.SELECT_CONN:
+ Logger.logDebug("ReqCode: SELECT_CONN");
boolean rescan = intent.getExtras().getBoolean(ActivityLauncher.EXTRA_RESCAN);
handleResultSelect(i, rescan);
break;
case RequestCode.CONNECT:
- boolean conres = intent.getExtras().getBoolean(ActivityLauncher.EXTRA_BOOLEAN);
+ Logger.logDebug("ReqCode: CONNECT");
+ ConnectionResult conres = (ConnectionResult) intent.getExtras().getParcelable(
+ ActivityLauncher.EXTRA_CONNRES);
handleFinishedConnecting(conres, i);
break;
-
case RequestCode.NETWORKS_GET:
- AccessPointInfo[] nets = (AccessPointInfo[]) intent.getExtras().getSerializable(
+ Logger.logDebug("ReqCode: NETWORKS_GET");
+ Parcelable[] p = intent.getExtras().getParcelableArray(
ActivityLauncher.EXTRA_APINFO_ARR);
+ AccessPointInfo[] nets = AccessPointInfo.fromParcellableArray(p);
launcher.launchSelectActivity(nets, true, false);
+ break;
case RequestCode.STATUS_SHOW:
+ Logger.logDebug("ReqCode: STATUS_SHOW");
// do nothing
break;
case RequestCode.SELECT_DETAILS:
+ Logger.logDebug("ReqCode: SELECT_DETAILS");
launcher.launchDetailsActivity(i);
break;
case RequestCode.DETAILS_SHOW:
+ Logger.logDebug("ReqCode: DETAILS_SHOW");
boolean del = intent.getExtras().getBoolean(ActivityLauncher.EXTRA_DELETE);
if (del) {
deleteNetwork(i);
@@ -180,13 +208,14 @@ public class MainActivity extends MenuEnabledActivity {
break;
case RequestCode.CONNECT_HIDDEN:
+ Logger.logDebug("ReqCode: CONNECT_HIDDEN");
if (i != null) {
+ Logger.logDebug("NetworkInfo NOT null, handling result");
handleResultSelect(i, false);
}
break;
-
default:
break;
@@ -207,22 +236,118 @@ public class MainActivity extends MenuEnabledActivity {
setTitle(getTitle() + " - v." + vers);
} catch (Exception e) {
- Utils.logError("Error while setting version on MainActivity's title.", e);
+ Logger.logError("Error while setting version on MainActivity's title.", e);
}
}
- private void setImage() {
+ private void showStatus(ConnectionStatus status) {
+
+ Logger.logDebug("MainActivity.showStatus()");
- ImageView img = (ImageView) findViewById(R.id.img_logo);
+ String msg = "";
+ this.status = status;
try {
- Drawable d = Drawable.createFromStream(getAssets().open("repwifi-logo-0.png"), null);
- img.setImageDrawable(d);
+
+ if (status == null) {
+ msg = getString(R.string.text_status) + ": [NULL]";
+ toggleStatusAppearance(false);
+
+ } else if (this.status.isConnected()) {
+ Logger.logDebug("showStatus isConnected,showing buttons");
+ msg = getString(R.string.msg_connected_to) + " " + status.SSID;
+ toggleStatusAppearance(true);
+
+ } else {
+ Logger.logDebug("showStatus status Else");
+ msg = getString(R.string.msg_disconnected);
+ toggleStatusAppearance(false);
+ }
+
} catch (Exception e) {
- Utils.logError("Error while loading logo image", e);
+ Logger.logError("Exception on showStatus", e);
+ msg = "[ERORR]";
}
+ TextView view = (TextView) findViewById(R.id.txt_status);
+ view.setText(msg);
+
+ }
+
+ private void toggleStatusAppearance(boolean connected) {
+
+ try {
+ Button b = (Button) findViewById(R.id.btn_disconnect);
+ Button i = (Button) findViewById(R.id.btn_info);
+ ImageView img = (ImageView) findViewById(R.id.img_logo);
+
+ LayoutParams lp = (LayoutParams) img.getLayoutParams();
+
+ b.setEnabled(connected);
+ i.setEnabled(connected);
+
+ if (connected) {
+ b.setVisibility(View.VISIBLE);
+ i.setVisibility(View.VISIBLE);
+ lp.removeRule(RelativeLayout.CENTER_HORIZONTAL);
+
+ } else {
+ b.setVisibility(View.INVISIBLE);
+ i.setVisibility(View.INVISIBLE);
+ lp.addRule(RelativeLayout.CENTER_HORIZONTAL);
+ }
+
+ setLogoDrawable(img, connected);
+ img.setLayoutParams(lp);
+
+ } catch (Exception e) {
+ Logger.logError("Error while setting status appearance", e);
+
+ }
+
+ }
+
+ BitmapDrawable logoConn;
+ BitmapDrawable logoDisc;
+
+ public void setLogoDrawable(ImageView img, boolean connected) {
+
+ img.setImageDrawable(null);
+ System.gc();
+
+ try {
+
+ String res = "repwifi-logo-0-small.png";
+ BitmapDrawable logo = logoConn;
+ if (!connected) {
+ logo = logoDisc;
+ res = "repwifi-logo-1-small.png";
+ }
+
+ AssetManager am = getAssets();
+ InputStream s = am.open(res);
+
+ if (logo == null) {
+ logo = (BitmapDrawable) Drawable.createFromStream(s, res);
+ }
+ img.setImageDrawable(logo);
+
+ s.close();
+ } catch (IOException e) {
+ }
+
+ }
+
+ public void onBtnDisconnectClick(View v) {
+
+ disconnectVpn();
+ sendCmdDisconnect();
+
+ }
+
+ public void onBtnInfoClick(View v) {
+ launcher.launchStatusActivity(this.status);
}
private void setUsbDeviceMonitor() {
@@ -245,9 +370,17 @@ public class MainActivity extends MenuEnabledActivity {
registerReceiver(detachReceiver, filt2);
}
- private boolean checkConditions() {
+ private boolean checkConditions(boolean serviceStarted) {
+ // second chance to have the service bound:
+ boolean conds = (checkRootEnabled() && checkIsSystemApp() && checkInterface(true));
+ if (serviceStarted) {
+ conds = conds && isServiceBound();
+ }
+ return conds;
+ }
- return (checkRootEnabled() && checkInterface(true));
+ private boolean checkConditions() {
+ return checkConditions(false);
}
private boolean checkInterface(boolean alert) {
@@ -256,16 +389,16 @@ public class MainActivity extends MenuEnabledActivity {
String msg = "";
try {
- res = Commons.connectionEngine.isInterfaceAvailable(WpaSupplicant.INTERFACE_NAME);
+ res = Engine.isInterfaceAvailable(WpaSupplicant.INTERFACE_NAME);
} catch (SocketException e) {
- Utils.logError("SocketException during isInterfaceAvailable()", e);
+ Logger.logError("SocketException during isInterfaceAvailable()", e);
msg = "Error while retrieving interface list!";
res = false;
}
if (res == false && alert) {
msg = getResources().getString(R.string.msg_interface_not_found);
- Commons.showMessage(msg, this);
+ Utils.showMessage(msg, this);
}
return res;
@@ -283,7 +416,7 @@ public class MainActivity extends MenuEnabledActivity {
try {
excode = su.testRootAccess();
} catch (Exception e) {
- Utils.logError("Error while trying to get first Super User access.", e);
+ Logger.logError("Error while trying to get first Super User access.", e);
excode = -1;
result = false;
}
@@ -310,7 +443,7 @@ public class MainActivity extends MenuEnabledActivity {
}
if (!result) {
- Commons.showMessage(msg, this);
+ Utils.showMessage(msg, this);
}
return result;
@@ -328,7 +461,7 @@ public class MainActivity extends MenuEnabledActivity {
if (i.needsPassword()) {
// try to fetch network's configuration from storage
- AccessPointInfo fromStorage = Commons.storage.getSavedNetwork(i);
+ AccessPointInfo fromStorage = NetworkManager.getSavedNetwork(i);
if (fromStorage == null) {
launcher.launchPasswordActivity(i);
@@ -341,6 +474,8 @@ public class MainActivity extends MenuEnabledActivity {
}
+ // disconnect any vpn before connecting to a new network
+ disconnectVpn();
launcher.launchLongTaskActivityConnect(i);
}
@@ -350,44 +485,70 @@ public class MainActivity extends MenuEnabledActivity {
launcher.launchLongTaskActivityConnect(i);
}
- private void handleFinishedConnecting(boolean connectionResult, AccessPointInfo info) {
+ private void handleFinishedConnecting(ConnectionResult connectionResult, AccessPointInfo info) {
- if (connectionResult && info.needsPassword()) {
+ Logger.logDebug("handleFinishedConnecting");
- ConnectionStatus status = Commons.connectionEngine.getConnectionStatus();
- if (status != null) {
- // update APinfo with the right BSSID
- info.setBssid(status.BSSID);
- }
+ String toastText = null;
+ int res = connectionResult.getResult();
+ ConnectionStatus status = connectionResult.getStatus();
- // Save network
- if (Commons.storage.save(info)) {
- Toast toast2 = Toast.makeText(getApplicationContext(),
- getString(R.string.msg_network_saved), Toast.LENGTH_LONG);
- toast2.show();
+ switch (res) {
- } else {
- Toast toast2 = Toast.makeText(getApplicationContext(),
- getString(R.string.msg_network_save_fail), Toast.LENGTH_LONG);
- toast2.show();
- }
+ case ConnectionResult.CONN_OK:
- // show status
- launcher.launchStatusActivity(status);
+ Logger.logDebug("About to launch VPN on successful connection result.");
+ beginConnectVpn(status.getNetworkDetails());
+
+
+ break;
- } else {
- // alert that connection failed
- Toast toast = Toast.makeText(getApplicationContext(),
- getString(R.string.msg_connect_fail), Toast.LENGTH_LONG);
+ case ConnectionResult.CONN_GW_FAIL:
+ Logger.logDebug("Result code CONN_GW_FAIL");
+ toastText = getString(R.string.msg_gw_failed);
+ break;
+
+ case ConnectionResult.CONN_TIMEOUT:
+ // probable wrong password:
+ handleConnectionTimeout(info);
+ break;
+
+ default:
+ Logger.logDebug("Result code: " + res);
+ toastText = getString(R.string.msg_connect_fail);
+ break;
+
+ }
+
+ if (toastText != null) {
+ Toast toast = Toast.makeText(getApplicationContext(), toastText, Toast.LENGTH_LONG);
toast.show();
}
+
+ showStatus(status);
+
+ }
+
+ private void handleConnectionTimeout(AccessPointInfo info) {
+ // reset wpa_supplicant's state
+ WpaCli.disconnect();
+ if (!WpaCli.terminateSupplicant()) {
+ WpaSupplicant.kill();
+ }
+
+ if (info.needsPassword()) {
+ // prompt user for password retry:
+ launcher.launchPasswordActivity(info, getString(R.string.msg_connection_timeout));
+ } else {
+ Utils.showMessage(getString(R.string.msg_connection_timeout_nopass), this);
+ }
+
}
private void deleteNetwork(AccessPointInfo info) {
- NetworkManager manager = new NetworkManager(Commons.getNetworkStorageFile());
String msg = "";
- if (manager.remove(info)) {
+ if (NetworkManager.remove(info)) {
msg = getString(R.string.msg_netinfo_deleted);
} else {
msg = getString(R.string.msg_netinfo_delete_fail);
@@ -401,10 +562,11 @@ public class MainActivity extends MenuEnabledActivity {
private void handleUsbEvent(boolean detached) {
if (detached && !checkInterface(false)) {
- // device disconnected, update the status bar:
- Commons.updateNotification(this);
+ // device disconnected:
+ // clear back-end state and update status.
+ disconnectVpn();
- } else if (Commons.isAutoConnectEnabled()) {
+ } else if (isAutoConnectEnabled()) {
try {
@@ -417,7 +579,7 @@ public class MainActivity extends MenuEnabledActivity {
msWaited += 100;
if (checkInterface(false)) {
- autoConnect();
+ sendCmdAutoconnect();
return;
}
}
@@ -429,56 +591,213 @@ public class MainActivity extends MenuEnabledActivity {
}
- }
+ requestStatusUpdate();
- private void autoConnect() {
+ }
- try {
+ private boolean isAutoConnectEnabled() {
+ return Prefs.getBoolean(getApplicationContext(), Prefs.PREF_AUTOCONNECT, false);
+ }
- AccessPointInfo[] nets = Commons.connectionEngine.getAvailableNetworks();
- if (nets == null || nets.length == 0) {
- return;
- }
+ private void doScan() {
+ if (checkConditions(true)) {
+ launcher.launchLongTaskActivityScan();
+ }
+ }
- for (AccessPointInfo i : nets) {
+ public void btnScanClick(View v) {
+ doScan();
+ }
- if (Commons.storage.isKnown(i)) {
- launcher.launchLongTaskActivityConnect(i);
- return;
- }
+ public void btnHiddenClick(View v) {
+ if (checkConditions(true)) {
+ launcher.launchInputSsidActivity();
+ }
+ }
- }
+ public void btnManageClick(View v) {
+ launcher.launchSelectActivity(null, false, true);
+ }
- // if no network is known, shows available networks to the user.
- launcher.launchSelectActivity(nets, true, false);
+ private boolean checkIsSystemApp() {
- } catch (Exception e) {
- Utils.logError("Error while autoconnecting", e);
- Commons.showMessage(getString(R.string.msg_autoconnect_error), this);
+ if (isSystemApp()) {
+ return true;
+ } else {
+ promtpMakeSystemApp();
+ return false;
}
+ }
+
+ private void promtpMakeSystemApp() {
+
+ String msg = getString(R.string.msg_make_system_app);
+ AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this,
+ R.style.Theme_RepWifiDialogTheme);
+ dlgAlert.setMessage(msg);
+ dlgAlert.setPositiveButton(getString(android.R.string.ok),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ confirmMakeSystemApp();
+ return;
+ }
+ });
+ dlgAlert.setNegativeButton(getString(android.R.string.cancel),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ return;
+ }
+ });
+
+ dlgAlert.setCancelable(false);
+ AlertDialog diag = dlgAlert.create();
+
+ diag.show();
}
- private void doScan() {
- if (checkConditions()) {
- launcher.launchLongTaskActivityScan();
+ private void confirmMakeSystemApp() {
+ String msg = getString(R.string.msg_confirm_make_system_app);
+ AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this,
+ R.style.Theme_RepWifiDialogTheme);
+ dlgAlert.setMessage(msg);
+ dlgAlert.setPositiveButton(getString(android.R.string.yes),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ makeSystemApp();
+ return;
+ }
+ });
+ dlgAlert.setNegativeButton(getString(android.R.string.no),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ return;
+ }
+ });
+
+ dlgAlert.setCancelable(false);
+ AlertDialog diag = dlgAlert.create();
+
+ diag.show();
+ }
+
+ private boolean isSystemApp() {
+
+ PackageManager pm = getPackageManager();
+ boolean isSystem = false;
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(getPackageName(), 0);
+
+ isSystem = ((ai.flags & ApplicationInfo.FLAG_SYSTEM) == 1);
+
+ } catch (NameNotFoundException e) {
+ // always succeeds, as we use our own package
}
+ return isSystem;
+
}
- public void btnScanClick(View v) {
- doScan();
+ public static final String SYS_FOLDER = "/system";
+ public static final String SYS_APP_FOLDER = "/system/priv-app";
+ private static final String SYSAPP_SCRIPT_FNAME = "make-system-app.sh";
+
+ private boolean makeSystemApp() {
+
+ PackageManager pm = getPackageManager();
+ String pkgName = getPackageName();
+ File currentSourceDir = null;
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(pkgName, 0);
+ currentSourceDir = new File(ai.sourceDir);
+
+ } catch (NameNotFoundException e) {
+ }// always succeeds, as we use our own package
+
+ String parentFolder = currentSourceDir.getParentFile().getName();
+ if (!parentFolder.contains(pkgName)) {
+ // use the plain package name as a parent folder for the apk
+ parentFolder = pkgName;
+ }
+
+ String targetDir = SYS_APP_FOLDER + "/" + parentFolder;
+
+ // get own data directory:
+ File ownDir = getFilesDir();
+ File scriptFile = new File(ownDir, SYSAPP_SCRIPT_FNAME);
+ String conts = Utils.rawResourceAsString(getApplicationContext(), R.raw.make_system_app);
+
+ if (conts == null) {
+ Logger.logError("Error while opening script from raw resources.");
+ return false;
+ }
+
+ if (!Utils.writeFile(scriptFile.getAbsolutePath(), conts, true)) {
+ Logger.logError("Failed to write script contents to file.");
+ return false;
+ }
+
+ Logger.logDebug("Starting script to make myself a system app...");
+
+ String cmd = "bash \"" + scriptFile.getAbsolutePath() + "\"" + " \"" + targetDir + "\""
+ + " \"" + currentSourceDir.getAbsolutePath() + "\"" + " \"" + SYS_FOLDER
+ + "\"";
+
+ return RootCommand.executeRootCmd(cmd);
+
}
+
+
+ @Override
+ protected void onMsgDisconnectReport(ConnectionStatus status) {
+ String msg = "";
- public void btnHiddenClick(View v) {
+ if (status != null && !status.isConnected()) {
+ msg = getString(R.string.msg_disconnected);
+ } else {
+ msg = getString(R.string.msg_disconnect_fail);
+ }
- if (checkConditions()) {
- launcher.launchInputSsidActivity();
+ Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
+ showStatus(status);
+
+ }
+
+ @Override
+ protected void onMsgAutoconnectReport(AccessPointInfo[] infos) {
+ if (infos != null && infos.length > 0) {
+ launcher.launchSelectActivity(infos, true, false);
}
+ }
+ @Override
+ protected void onMsgStatusChange(ConnectionStatus status) {
+ Logger.logDebug("Received status update from service.");
+ showStatus(status);
}
- public void btnManageClick(View v) {
- launcher.launchSelectActivity(null, false, true);
+ @Override
+ protected void onManagementServiceConnected() {
+ super.onManagementServiceConnected();
+ requestStatusUpdate();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ }
+
+ @Override
+ public void onDestroy() {
+
+ if (detachReceiver != null) {
+ unregisterReceiver(detachReceiver);
+ }
+
+ super.onDestroy();
}
}
diff --git a/app/src/fil/libre/repwifiapp/activities/MenuEnabledActivity.java b/app/src/fil/libre/repwifiapp/activities/MenuEnabledActivity.java
index f5c8516..55afd5a 100644
--- a/app/src/fil/libre/repwifiapp/activities/MenuEnabledActivity.java
+++ b/app/src/fil/libre/repwifiapp/activities/MenuEnabledActivity.java
@@ -1,5 +1,5 @@
//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
//
// This file is part of RepWifiApp.
//
@@ -20,10 +20,6 @@
package fil.libre.repwifiapp.activities;
-import java.lang.reflect.Field;
-import fil.libre.repwifiapp.Commons;
-import fil.libre.repwifiapp.R;
-import fil.libre.repwifiapp.helpers.RootCommand;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
@@ -31,6 +27,10 @@ import android.content.Intent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewConfiguration;
+import fil.libre.repwifiapp.R;
+import fil.libre.repwifiapp.Utils;
+import fil.libre.repwifiapp.helpers.RootCommand;
+import java.lang.reflect.Field;
public class MenuEnabledActivity extends Activity {
@@ -95,8 +95,7 @@ public class MenuEnabledActivity extends Activity {
if (silent) {
- Commons.connectionEngine.disconnect();
- Commons.killBackEnd(this, true);
+ Utils.killBackEnd(this, true);
super.onDestroy();
RootCommand.executeRootCmd("am force-stop " + getPackageName());
diff --git a/app/src/fil/libre/repwifiapp/activities/NetworkDetailsActivity.java b/app/src/fil/libre/repwifiapp/activities/NetworkDetailsActivity.java
index f2a8944..7b12efc 100644
--- a/app/src/fil/libre/repwifiapp/activities/NetworkDetailsActivity.java
+++ b/app/src/fil/libre/repwifiapp/activities/NetworkDetailsActivity.java
@@ -1,5 +1,5 @@
//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
//
// This file is part of RepWifiApp.
//
@@ -20,23 +20,23 @@
package fil.libre.repwifiapp.activities;
-import java.util.Date;
-import fil.libre.repwifiapp.ActivityLauncher;
-import fil.libre.repwifiapp.Commons;
-import fil.libre.repwifiapp.R;
-import fil.libre.repwifiapp.helpers.AccessPointInfo;
-import android.os.Bundle;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
+import android.os.Bundle;
import android.text.format.DateFormat;
import android.view.Menu;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
-import android.widget.TextView;
import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.TextView;
+import fil.libre.repwifiapp.ActivityLauncher;
+import fil.libre.repwifiapp.R;
+import fil.libre.repwifiapp.network.AccessPointInfo;
+import fil.libre.repwifiapp.network.NetworkManager;
+import java.util.Date;
public class NetworkDetailsActivity extends Activity implements OnCheckedChangeListener {
@@ -57,7 +57,7 @@ public class NetworkDetailsActivity extends Activity implements OnCheckedChangeL
return;
}
- this.currentNetwork = (AccessPointInfo) intent.getExtras().getSerializable(
+ this.currentNetwork = (AccessPointInfo) intent.getExtras().getParcelable(
ActivityLauncher.EXTRA_APINFO);
if (this.currentNetwork == null) {
this.setResult(RESULT_CANCELED);
@@ -72,9 +72,9 @@ public class NetworkDetailsActivity extends Activity implements OnCheckedChangeL
@Override
protected void onStart() {
super.onStart();
- currentNetwork = Commons.storage.getSavedNetwork(currentNetwork);
+ currentNetwork = NetworkManager.getSavedNetwork(currentNetwork);
};
-
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
@@ -100,21 +100,22 @@ public class NetworkDetailsActivity extends Activity implements OnCheckedChangeL
}
if (showPassword) {
- v.append("\n\n" + getString(R.string.text_password) + ":\n" + this.currentNetwork.getPassword());
+ v.append("\n\n" + getString(R.string.text_password) + ":\n"
+ + this.currentNetwork.getPassword());
} else {
v.append("\n\n\n");
}
}
- public void btnIpSettingsClick(View v){
+ public void btnIpSettingsClick(View v) {
new ActivityLauncher(this).launchIpSettingsActivity(currentNetwork);
}
-
- public void btnVpnSettingsClick(View v){
+
+ public void btnVpnSettingsClick(View v) {
new ActivityLauncher(this).launchVpnSettingsActivity(currentNetwork);
}
-
+
public void btnDeleteClick(View v) {
String msg = getResources().getString(R.string.msg_confirm_delete_network);
diff --git a/app/src/fil/libre/repwifiapp/activities/SelectNetworkActivity.java b/app/src/fil/libre/repwifiapp/activities/SelectNetworkActivity.java
index 51baed8..f1e1a96 100644
--- a/app/src/fil/libre/repwifiapp/activities/SelectNetworkActivity.java
+++ b/app/src/fil/libre/repwifiapp/activities/SelectNetworkActivity.java
@@ -1,5 +1,5 @@
//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
//
// This file is part of RepWifiApp.
//
@@ -20,15 +20,9 @@
package fil.libre.repwifiapp.activities;
-import fil.libre.repwifiapp.ActivityLauncher;
-import fil.libre.repwifiapp.Commons;
-import fil.libre.repwifiapp.R;
-import fil.libre.repwifiapp.helpers.AccessPointInfo;
-import fil.libre.repwifiapp.helpers.NetworkButton;
-import fil.libre.repwifiapp.helpers.Utils;
-import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
+import android.os.Bundle;
import android.view.Gravity;
import android.view.Menu;
import android.view.View;
@@ -38,6 +32,11 @@ import android.widget.Button;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
+import fil.libre.repwifiapp.ActivityLauncher;
+import fil.libre.repwifiapp.R;
+import fil.libre.repwifiapp.helpers.Logger;
+import fil.libre.repwifiapp.network.AccessPointInfo;
+import fil.libre.repwifiapp.network.NetworkButton;
public class SelectNetworkActivity extends Activity implements OnClickListener {
@@ -75,8 +74,8 @@ public class SelectNetworkActivity extends Activity implements OnClickListener {
finish();
return;
}
- AccessPointInfo[] nets = (AccessPointInfo[]) intent.getExtras().getSerializable(
- ActivityLauncher.EXTRA_APINFO_ARR);
+ AccessPointInfo[] nets = AccessPointInfo.fromParcellableArray(intent.getExtras().getParcelableArray(
+ ActivityLauncher.EXTRA_APINFO_ARR));
if (nets == null) {
this.setResult(RESULT_CANCELED);
finish();
@@ -134,7 +133,7 @@ public class SelectNetworkActivity extends Activity implements OnClickListener {
private void showNetworksForConnection(AccessPointInfo[] info) {
if (info == null) {
- Utils.logError("Unable to retrieve network list!");
+ Logger.logError("Unable to retrieve network list!");
writeOut(getString(R.string.msg_network_list_fail));
return;
}
@@ -203,7 +202,7 @@ public class SelectNetworkActivity extends Activity implements OnClickListener {
LayoutParams.WRAP_CONTENT);
button.setLayoutParams(params);
button.setBackground(getResources().getDrawable(R.drawable.repwifi_button));
- button.setTextColor(Commons.colorThemeLight);
+ button.setTextColor(getResources().getColor(R.color.ThemeLight));
button.setTextSize(20);
button.setPadding(25, 10, 25, 10);
button.setGravity(Gravity.CENTER_HORIZONTAL);
diff --git a/app/src/fil/libre/repwifiapp/activities/SettingsActivity.java b/app/src/fil/libre/repwifiapp/activities/SettingsActivity.java
index 36e0e1f..c50524a 100644
--- a/app/src/fil/libre/repwifiapp/activities/SettingsActivity.java
+++ b/app/src/fil/libre/repwifiapp/activities/SettingsActivity.java
@@ -1,5 +1,5 @@
//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
//
// This file is part of RepWifiApp.
//
@@ -18,19 +18,28 @@
//
// ********************************************************************
-
package fil.libre.repwifiapp.activities;
-import org.apache.http.conn.util.InetAddressUtils;
-import java.util.List;
-import fil.libre.repwifiapp.Commons;
-import fil.libre.repwifiapp.R;
-import fil.libre.repwifiapp.helpers.Utils;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.SharedPreferences.Editor;
import android.os.Bundle;
+import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
+import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;
+import android.preference.PreferenceManager;
+import fil.libre.repwifiapp.Prefs;
+import fil.libre.repwifiapp.R;
+import fil.libre.repwifiapp.R.string;
+import fil.libre.repwifiapp.Utils;
+import fil.libre.repwifiapp.helpers.Logger;
+import fil.libre.repwifiapp.service.ConnectionManagementService;
+import org.apache.http.conn.util.InetAddressUtils;
+import java.util.List;
public class SettingsActivity extends PreferenceActivity {
@@ -38,6 +47,7 @@ public class SettingsActivity extends PreferenceActivity {
protected void onCreate(Bundle savedInstanceState) {
setTheme(R.style.Theme_SettingsTheme);
super.onCreate(savedInstanceState);
+
}
@Override
@@ -54,7 +64,7 @@ public class SettingsActivity extends PreferenceActivity {
setConfirmKillBackend();
setDumpFileClick();
-
+ setDebugPriorityChangeListener();
}
private void setConfirmKillBackend() {
@@ -64,7 +74,7 @@ public class SettingsActivity extends PreferenceActivity {
@Override
public boolean onPreferenceClick(Preference p) {
- Commons.killBackEnd(getActivity(), false);
+ Utils.killBackEnd(getActivity(), false);
return true;
}
});
@@ -77,18 +87,38 @@ public class SettingsActivity extends PreferenceActivity {
@Override
public boolean onPreferenceClick(Preference p) {
- if (Utils.dumpLogcatToFile(Commons.getLogDumpFile())) {
- Commons.showMessage(getString(R.string.msg_log_saved) + ": \n" + Commons.getLogDumpFile(),
- getActivity());
+ if (Logger.dumpLogcatToFile(getActivity())) {
+ Utils.showMessage(
+ getString(R.string.msg_log_saved) + ": \n"
+ + Logger.getLogDumpFile(), getActivity());
} else {
- Commons.showMessage(getString(R.string.msg_log_save_fail), getActivity());
+ Utils.showMessage(getString(R.string.msg_log_save_fail), getActivity());
}
return true;
}
});
}
+ /**
+ * Monitors changes in the "log priority" setting, and reflects them
+ * onto the Logger class.
+ */
+ private void setDebugPriorityChangeListener() {
+
+ ListPreference logPrioPref = (ListPreference) getPreferenceScreen().findPreference(
+ "debug_priority");
+
+ logPrioPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ Logger.setLogPriority(Integer.parseInt((String) newValue));
+ return true;
+ }
+ });
+ }
+
}
public static class GeneralSettingFragment extends PreferenceFragment {
@@ -98,26 +128,37 @@ public class SettingsActivity extends PreferenceActivity {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.general_settings);
- setValidationListener("dns1");
- setValidationListener("dns2");
+ setIpFormatValidationListener(Prefs.PREF_DNS_1);
+ setIpFormatValidationListener(Prefs.PREF_DNS_2);
setConfirmRestore();
+
+ formatSummaryMonitoryConnection();
+
+ }
+
+ private void formatSummaryMonitoryConnection() {
+
+ CheckBoxPreference chkMonitor = (CheckBoxPreference) findPreference("monitor_connection");
+ chkMonitor.setSummary(getString(string.summary_monitor_connection_state).replace(
+ ConnectionManagementService.PLACEHOLDER_CHECK_STATUS_INTERVAL,
+ String.valueOf(ConnectionManagementService.CHECK_STATUS_INTERVAL_SECS)));
}
- private void setValidationListener(String prefName) {
- EditTextPreference edit_Pref = (EditTextPreference) getPreferenceScreen()
+ private void setIpFormatValidationListener(String prefName) {
+ EditTextPreference editPref = (EditTextPreference) getPreferenceScreen()
.findPreference(prefName);
- edit_Pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ editPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
- // put validation here..
+
if (((String) newValue).isEmpty()
|| InetAddressUtils.isIPv4Address((String) newValue)) {
return true;
} else {
- Commons.showMessage(getString(R.string.msg_error_ip_format), getActivity());
+ Utils.showMessage(getString(R.string.msg_error_ip_format), getActivity());
return false;
}
}
@@ -131,12 +172,55 @@ public class SettingsActivity extends PreferenceActivity {
@Override
public boolean onPreferenceClick(Preference p) {
- Commons.resetSettingsDefault(getActivity(), false);
+ resetSettingsDefault(false);
return true;
}
});
}
+
+ public void resetSettingsDefault(boolean silent) {
+
+ Context context = getActivity();
+
+ if (silent) {
+
+ Editor e = PreferenceManager.getDefaultSharedPreferences(context).edit();
+ e.clear();
+ e.commit();
+
+ } else {
+
+ String msg = context.getString(R.string.confirm_reset_settings);
+ AlertDialog.Builder dlgAlert = new AlertDialog.Builder(context,
+ R.style.Theme_RepWifiDialogTheme);
+ dlgAlert.setMessage(msg);
+ dlgAlert.setPositiveButton(context.getString(android.R.string.ok),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ resetSettingsDefault(true);
+ return;
+ }
+ });
+ dlgAlert.setNegativeButton(context.getString(android.R.string.cancel),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ return;
+ }
+ });
+
+ dlgAlert.setCancelable(false);
+ AlertDialog diag = dlgAlert.create();
+
+ diag.show();
+
+ return;
+ }
+
+ }
+
}
}
diff --git a/app/src/fil/libre/repwifiapp/activities/ShowStatusActivity.java b/app/src/fil/libre/repwifiapp/activities/ShowStatusActivity.java
index 21bfc4c..7076556 100644
--- a/app/src/fil/libre/repwifiapp/activities/ShowStatusActivity.java
+++ b/app/src/fil/libre/repwifiapp/activities/ShowStatusActivity.java
@@ -1,5 +1,5 @@
//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
//
// This file is part of RepWifiApp.
//
@@ -20,274 +20,67 @@
package fil.libre.repwifiapp.activities;
-import fil.libre.repwifiapp.ActivityLauncher;
-import fil.libre.repwifiapp.Commons;
-import fil.libre.repwifiapp.R;
-import fil.libre.repwifiapp.ActivityLauncher.RequestCode;
-import fil.libre.repwifiapp.helpers.AccessPointInfo;
-import fil.libre.repwifiapp.helpers.ConnectionStatus;
-import fil.libre.repwifiapp.helpers.OpenVpnManager;
-import fil.libre.repwifiapp.helpers.Utils;
-import android.content.Intent;
import android.os.Bundle;
-import android.os.RemoteException;
import android.view.View;
-import android.widget.Button;
import android.widget.TextView;
-import android.widget.Toast;
+import fil.libre.repwifiapp.ActivityLauncher;
+import fil.libre.repwifiapp.R;
+import fil.libre.repwifiapp.helpers.Logger;
+import fil.libre.repwifiapp.network.ConnectionStatus;
public class ShowStatusActivity extends MenuEnabledActivity {
private ConnectionStatus status;
- // private AccessPointInfo info;
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_status);
if (getIntent().hasExtra(ActivityLauncher.EXTRA_CONSTATUS)) {
- this.status = (ConnectionStatus) getIntent().getSerializableExtra(
+ this.status = (ConnectionStatus) getIntent().getParcelableExtra(
ActivityLauncher.EXTRA_CONSTATUS);
}
try {
- showStatus(false);
+ showStatus();
} catch (Exception e) {
- Utils.logError("Exception on showStatus", e);
+ Logger.logError("Exception on showStatus", e);
}
}
- @Override
- public void onRestart() {
- super.onRestart();
- try {
- showStatus(true);
- } catch (Exception e) {
- Utils.logError("Exception on showStatus", e);
- }
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent intent) {
-
- switch (requestCode) {
+ private void showStatus() {
- case RequestCode.VPN_PERMISSION:
-
- if (resultCode == RESULT_OK) {
- endLaunchVpn(true);
- } else {
- endLaunchVpn(false);
- }
+ if (this.status != null) {
- break;
+ String txt = getPrintableConnectionStatus(status);
- default:
-
- break;
+ TextView t = (TextView) findViewById(R.id.txt_status);
+ t.setText(txt);
}
}
- private void setMessage(String msg) {
- TextView view = (TextView) findViewById(R.id.txt_status);
- view.setText(msg);
+ public void onBtnMainClick(View v) {
+ finish();
}
- private void showStatus(boolean refresh) throws Exception {
-
- if (refresh || status == null) {
- this.status = Commons.connectionEngine.getConnectionStatus();
- }
+ public String getPrintableConnectionStatus(ConnectionStatus status) {
if (status == null) {
- this.finish();
-
- } else if (this.status.isConnected()) {
- Utils.logDebug("StatusActivity isConnected,showing buttons");
- setMessage(getString(R.string.msg_connected_to) + " " + status.SSID + "\n\n"
- + getString(R.string.text_ip_address) + ": " + status.IP + "\n");
- toggleBtnDisconnect(true);
- beginLauncVpn();
-
- } else {
- Utils.logDebug("StatusActivity status Else");
- setMessage(getString(R.string.text_status) + ":\n" + status.status);
- toggleBtnDisconnect(false);
+ return "[NULL]";
}
- Commons.updateNotification(this);
+ String fmat = "%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %s\n";
- }
-
- private void toggleBtnDisconnect(boolean enable) {
-
- Button b = (Button) findViewById(R.id.btn_disconnect);
- Button bk = (Button) findViewById(R.id.btn_back);
- b.setEnabled(enable);
- bk.setEnabled(!enable);
-
- if (enable) {
- b.setVisibility(View.VISIBLE);
- bk.setVisibility(View.INVISIBLE);
- } else {
- b.setVisibility(View.INVISIBLE);
- bk.setVisibility(View.VISIBLE);
- }
+ return String.format(fmat, getString(R.string.text_ip_address), status.IP,
+ getString(R.string.text_subnet_mask), status.subnetMask,
+ getString(R.string.text_gateway), status.gateway,
+ getString(R.string.text_broadcast_address), status.broadcastAddress,
+ getString(R.string.text_hardware_address), status.hwAddress);
}
- public void onBtnDisconnectClick(View v) {
-
- disconnectVpn();
-
- boolean res = Commons.connectionEngine.disconnect();
- String msg = "";
- if (res) {
- msg = getString(R.string.msg_disconnected);
- } else {
- msg = getString(R.string.msg_disconnect_fail);
- }
-
- Toast toast = Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT);
- toast.show();
-
- try {
- showStatus(true);
- } catch (Exception e) {
- Utils.logError("Exception on showStatus", e);
- }
-
- }
-
- private void disconnectVpn() {
-
- if (! OpenVpnManager.isVpnConnected()){
- return;
- }
-
- if (OpenVpnManager.disconnect()) {
- Toast t = Toast.makeText(this, getString(R.string.msg_vpn_disconnect),
- Toast.LENGTH_LONG);
- t.show();
-
- } else {
- Commons.showMessage(getString(R.string.msg_vpn_disconnect_error), this);
- }
-
- }
-
- private void beginLauncVpn() throws Exception {
-
- if (OpenVpnManager.isVpnConnected()){
- // already connected;
- return;
- }
-
- if (!OpenVpnManager.isExternalAppInstalled(this)) {
- Utils.logDebug("External VPN app not installed.");
- return;
- }
-
- if (getVpnNameIfAny() == null){
- Utils.logDebug("No vpn profile set. Exiting beginLaunchVpn()");
- return;
- }
-
- // first, we make sure we have permission to use the vpn service.
- Intent pi;
-
- try {
- pi = OpenVpnManager.askApiPermissionsGetIntent();
- } catch (RemoteException e) {
- Utils.logError("Exception while asking for VPN permission", e);
- Toast t = Toast.makeText(getApplicationContext(), getString(R.string.msg_vpn_connect_error), Toast.LENGTH_LONG);
- t.show();
- return;
-
- }
-
- if (pi == null){
- // no need to ask for permission
- Utils.logDebug("No need for vpn permission: going to endLaunchVpn.");
- endLaunchVpn(true);
-
- } else{
- // launch the intent to ask permission
- Utils.logDebug("Need to ask for vpn permission. Starting intent..");
- startActivityForResult(pi, ActivityLauncher.RequestCode.VPN_PERMISSION);
- }
-
- }
-
- private void endLaunchVpn(boolean permissionGranted) {
-
- try {
-
- if (!permissionGranted) {
- // warn user that permission must be granted
- Utils.logDebug("User rejected vpn permission.");
- String msg = getString(R.string.msg_vpn_no_permission).replace(
- OpenVpnManager.PLACEHOLDER_APPNAME, OpenVpnManager.APP_COMMON_NAME);
- Commons.showMessage(msg, this);
- return;
- }
-
- String profname = getVpnNameIfAny();
-
- // check if profile exists
- String profUuid = OpenVpnManager.getUuidFromName(profname);
- if (profUuid == null){
- // warn user that selected profile doesn't exist
- Commons.showMessage(getString(R.string.msg_vpn_wrong_profile), this);
- return;
- }
-
- if (OpenVpnManager.startVpn(profUuid)){
- Toast t = Toast.makeText(this, getString(R.string.msg_vpn_launched), Toast.LENGTH_LONG);
- t.show();
- } else {
- Commons.showMessage(getString(R.string.msg_vpn_connect_error),this);
- }
-
- } catch (Exception e) {
- Utils.logError("Exception while endLaunchVpn", e);
-
- }
-
- }
-
- private String getVpnNameIfAny(){
-
- if (status == null) {
- return null;
- }
-
- AccessPointInfo i = status.getNetworkDetails();
- if (i == null) {
- return null;
- }
-
- String profname = i.getVpnProfileName();
- if (profname == null || profname.isEmpty()) {
- return null;
- } else {
- return profname;
- }
-
- }
-
- public void onBtnMainClick(View v) {
- finish();
- }
-
- @Override
- public void onBackPressed() {
- moveTaskToBack(true);
- }
-
}
diff --git a/app/src/fil/libre/repwifiapp/activities/VpnAndConnectionBoundActivity.java b/app/src/fil/libre/repwifiapp/activities/VpnAndConnectionBoundActivity.java
new file mode 100644
index 0000000..4acf28a
--- /dev/null
+++ b/app/src/fil/libre/repwifiapp/activities/VpnAndConnectionBoundActivity.java
@@ -0,0 +1,435 @@
+//
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+//
+// This file is part of RepWifiApp.
+// This file is based upon the example file included in
+// de.blinkt.openvpn package by Arne Schwabe.
+//
+// RepWifiApp 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.
+//
+// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
+//
+// ********************************************************************
+
+package fil.libre.repwifiapp.activities;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.widget.Toast;
+import de.blinkt.openvpn.api.APIVpnProfile;
+import de.blinkt.openvpn.api.IOpenVPNAPIService;
+import fil.libre.repwifiapp.ActivityLauncher;
+import fil.libre.repwifiapp.R;
+import fil.libre.repwifiapp.Utils;
+import fil.libre.repwifiapp.helpers.Logger;
+import fil.libre.repwifiapp.network.AccessPointInfo;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class VpnAndConnectionBoundActivity extends ConnectionBoundActivity {
+
+ public static final String SERVICE_PACKAGE_NAME = "de.blinkt.openvpn";
+ public static final String APP_COMMON_NAME = "OpenVPN for Android";
+ public static final String PLACEHOLDER_APPNAME = "[VPN_EXT_APP]";
+
+ private static final int ACTION_NONE = 0;
+ private static final int ACTION_GET_PROFILES = 1;
+ private static final int ACTION_CONNECT = 2;
+
+ protected IOpenVPNAPIService _vpnSvc;
+
+ private AccessPointInfo _lastInfo;
+ private int _lastAction = ACTION_NONE;
+
+ private int lastRequestCode = ActivityLauncher.RequestCode.NONE;
+ private boolean permissionAsked = false;
+
+ private ServiceConnection _svcConnection;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ bindVpnService(ACTION_NONE);
+ }
+
+ private void bindVpnService(int action) {
+
+ if (!isExternalAppInstalled()) {
+ Logger.logDebug("External VPN app is not installed. Skipping vpn service binding.");
+ return;
+ }
+
+ _lastAction = action;
+
+ this._svcConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ _vpnSvc = IOpenVPNAPIService.Stub.asInterface(service);
+ onVpnServiceConnected();
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ _vpnSvc = null;
+ onVpnServiceDisconnected();
+ }
+ };
+
+ Intent intentGetService = new Intent(IOpenVPNAPIService.class.getName());
+ intentGetService.setPackage(SERVICE_PACKAGE_NAME);
+ if (!bindService(intentGetService, _svcConnection, Context.BIND_AUTO_CREATE)) {
+ Logger.logError("FAILED to bind to OpenVPN service!");
+ }
+
+ }
+
+ private void unbindVpnService() {
+ if (_svcConnection != null && _vpnSvc != null) {
+ unbindService(_svcConnection);
+ }
+ }
+
+ protected void onVpnServiceConnected() {
+
+ switch (_lastAction) {
+ case ACTION_NONE:
+ return;
+
+ case ACTION_GET_PROFILES:
+ beginGetExistingVpnProfiles();
+ break;
+
+ case ACTION_CONNECT:
+ beginConnectVpn(_lastInfo);
+ break;
+
+ default:
+ break;
+ }
+
+ }
+
+ protected void onVpnServiceDisconnected() {
+ }
+
+ protected void onVpnProfilesAvailable(List<String> vpnProfiles) {
+ }
+
+ protected void onVpnPermissionDenied(){
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent intent) {
+
+ if (requestCode != ActivityLauncher.RequestCode.VPN_PERMISSION_CONN
+ && requestCode != ActivityLauncher.RequestCode.VPN_PERMISSION_LIST) {
+ // activity result is unrelated to our code.
+ return;
+
+ } else if (resultCode != Activity.RESULT_OK) {
+ // warn user that permission is needed to manage VPN
+
+ Logger.logDebug("User rejected vpn permission.");
+
+ String msg = getString(R.string.msg_vpn_no_permission).replace(
+ VpnAndConnectionBoundActivity.PLACEHOLDER_APPNAME,
+ VpnAndConnectionBoundActivity.APP_COMMON_NAME);
+ Utils.showMessage(msg, this);
+
+ onVpnPermissionDenied();
+ return;
+ }
+
+ switch (lastRequestCode) {
+
+ case ActivityLauncher.RequestCode.VPN_PERMISSION_CONN:
+
+ endConnectVpn();
+ break;
+
+ case ActivityLauncher.RequestCode.VPN_PERMISSION_LIST:
+
+ endGetExistingVpnProfiles();
+ break;
+
+ default:
+ break;
+ }
+
+ }
+
+ protected void close() {
+ unbindVpnService();
+ }
+
+ protected boolean isExternalAppInstalled() {
+
+ try {
+
+ ApplicationInfo i;
+ i = getPackageManager().getApplicationInfo(SERVICE_PACKAGE_NAME, 0);
+ return (i != null);
+
+ } catch (NameNotFoundException e) {
+ return false;
+ }
+
+ }
+
+ private boolean doStartVpn(String profileUuid) {
+
+ if (profileUuid == null) {
+ Logger.logError("Invoked startVpn with null uuid");
+ return false;
+ }
+
+ if (_vpnSvc == null) {
+ Logger.logError("Invoked startVpn but inner service is null.");
+ return false;
+ }
+
+ try {
+
+ _vpnSvc.startProfile(profileUuid);
+ return true;
+
+ } catch (RemoteException e) {
+ Logger.logError("Exception while starting vpn.", e);
+ return false;
+ }
+
+ }
+
+ private String getUuidFromName(String profileName) {
+ if (_vpnSvc == null) {
+ Logger.logError("Called getUuidFromName but inner service is null!");
+ return null;
+ }
+
+ try {
+ List<APIVpnProfile> list = _vpnSvc.getProfiles();
+
+ for (APIVpnProfile vp : list) {
+ if (vp.mName.equals(profileName)) {
+ return vp.mUUID;
+ }
+ }
+
+ return null;
+
+ } catch (RemoteException e) {
+ Logger.logError("Exception while retrieving profiles from vpn service.", e);
+ return null;
+ }
+ }
+
+ /***
+ *
+ * @return Returns 0 if no permission is needed,
+ * 1 if permission is needed and it was asked successfully,
+ * a negative number on error.
+ */
+ private int askPermissionIfNeeded(int requestCode) {
+
+ Logger.logDebug("Called OpenVpnManager.askPermissionIfNeeded()");
+
+ if (_vpnSvc == null) {
+ Logger.logError("Internal vpn service is null, but not supposed to be. Aborting.");
+ return -1;
+ }
+
+ Intent pi;
+
+ try {
+ pi = _vpnSvc.prepare(getPackageName());
+ } catch (RemoteException e) {
+ Logger.logError("Exception while asking for VPN permission", e);
+ Toast t = Toast.makeText(getApplicationContext(),
+ getString(R.string.msg_vpn_service_error), Toast.LENGTH_LONG);
+
+ t.show();
+
+ String msgerr = getString(R.string.msg_vpn_error_manual_open).replace(
+ PLACEHOLDER_APPNAME, APP_COMMON_NAME);
+ Utils.showMessage(msgerr, getApplicationContext());
+
+ return -1;
+
+ }
+
+ if (pi == null) {
+ // no need to ask for permission
+ Logger.logDebug("No need for vpn permission.");
+ return 0;
+
+ } else if (!permissionAsked) {
+ // launch the intent to ask permission
+ Logger.logDebug("Need to ask for vpn permission. Starting intent..");
+ permissionAsked = true;
+ startActivityForResult(pi, requestCode);
+ return 1;
+
+ } else {
+ return 1;
+
+ }
+
+ }
+
+ private String getVpnNameIfAny(AccessPointInfo i) {
+
+ if (i == null) {
+ return null;
+ }
+
+ String profname = i.getVpnProfileName();
+ if (profname == null || profname.isEmpty()) {
+ return null;
+ } else {
+ return profname;
+ }
+
+ }
+
+ protected void beginConnectVpn(AccessPointInfo info) {
+
+ if (!isExternalAppInstalled()) {
+ return;
+ }
+
+ if (getVpnNameIfAny(info) == null) {
+ Logger.logDebug("No vpn profile set. Exiting beginConnectVpn()");
+ return;
+ }
+
+ _lastInfo = info;
+
+ if (_vpnSvc == null) {
+ bindVpnService(ACTION_CONNECT);
+ return;
+ }
+
+ // make sure we have permission to use the vpn service.
+ if (askPermissionIfNeeded(ActivityLauncher.RequestCode.VPN_PERMISSION_CONN) == 0) {
+ // no need for permission
+ Logger.logDebug("Going to endConnectVpn.");
+ endConnectVpn();
+ }
+
+ }
+
+ private void endConnectVpn() {
+
+ try {
+
+ if (_lastInfo == null) {
+ Logger.logError("Called endConnectVpn, but last AccessPointInfo is null.");
+ return;
+ }
+
+ String profname = getVpnNameIfAny(_lastInfo);
+
+ // check if profile exists
+ String profUuid = getUuidFromName(profname);
+ if (profUuid == null) {
+ // warn user that selected profile doesn't exist
+ Utils.showMessage(getString(R.string.msg_vpn_wrong_profile),
+ getApplicationContext());
+ return;
+ }
+
+ if (doStartVpn(profUuid)) {
+ Toast t = Toast.makeText(getApplicationContext(),
+ getString(R.string.msg_vpn_launched), Toast.LENGTH_LONG);
+ t.show();
+ } else {
+ Utils.showMessage(getString(R.string.msg_vpn_connect_error),
+ getApplicationContext());
+ }
+
+ } catch (Exception e) {
+ Logger.logError("Exception while endConnectVpn", e);
+
+ }
+
+ }
+
+ protected void beginGetExistingVpnProfiles() {
+
+ if (!isExternalAppInstalled()) {
+ return;
+ }
+
+ if (_vpnSvc == null) {
+ bindVpnService(ACTION_GET_PROFILES);
+ return;
+ }
+
+ // we make sure we have permission to use the vpn service.
+ if (askPermissionIfNeeded(ActivityLauncher.RequestCode.VPN_PERMISSION_LIST) == 0) {
+ // no need for permission
+ endGetExistingVpnProfiles();
+ }
+
+ }
+
+ private void endGetExistingVpnProfiles() {
+
+ try {
+ List<APIVpnProfile> list = _vpnSvc.getProfiles();
+
+ List<String> ret = new ArrayList<String>();
+ for (APIVpnProfile vp : list) {
+ ret.add(vp.mName);
+ }
+
+ onVpnProfilesAvailable(ret);
+
+ } catch (RemoteException e) {
+ Logger.logError("Exception while retrieving profiles from vpn service.", e);
+ }
+
+ }
+
+
+
+ protected boolean disconnectVpn() {
+
+ if (_vpnSvc == null) {
+ Logger.logDebug("Attempted to disconnect from VPN, but inner service is null");
+ return true;
+ }
+
+ try {
+ _vpnSvc.disconnect();
+ return true;
+ } catch (Exception e) {
+ Logger.logError("Exception while disconnecting from vpn.", e);
+ return false;
+ }
+
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ this.close();
+ }
+
+}
diff --git a/app/src/fil/libre/repwifiapp/activities/VpnSettingsActivity.java b/app/src/fil/libre/repwifiapp/activities/VpnSettingsActivity.java
index b9dab93..3c134d7 100644
--- a/app/src/fil/libre/repwifiapp/activities/VpnSettingsActivity.java
+++ b/app/src/fil/libre/repwifiapp/activities/VpnSettingsActivity.java
@@ -1,5 +1,5 @@
//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
//
// This file is part of RepWifiApp.
//
@@ -20,37 +20,35 @@
package fil.libre.repwifiapp.activities;
-import java.util.List;
-import fil.libre.repwifiapp.ActivityLauncher;
-import fil.libre.repwifiapp.Commons;
-import fil.libre.repwifiapp.R;
-import fil.libre.repwifiapp.ActivityLauncher.RequestCode;
-import fil.libre.repwifiapp.helpers.AccessPointInfo;
-import fil.libre.repwifiapp.helpers.OpenVpnManager;
-import fil.libre.repwifiapp.helpers.Utils;
-import android.os.Bundle;
-import android.app.Activity;
import android.content.Intent;
+import android.os.Bundle;
+import android.view.Gravity;
import android.view.Menu;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;
+import fil.libre.repwifiapp.ActivityLauncher;
+import fil.libre.repwifiapp.R;
+import fil.libre.repwifiapp.Utils;
+import fil.libre.repwifiapp.network.AccessPointInfo;
+import fil.libre.repwifiapp.network.NetworkManager;
+import java.util.List;
-public class VpnSettingsActivity extends Activity {
+public class VpnSettingsActivity extends VpnAndConnectionBoundActivity {
private AccessPointInfo currentNetwork;
private Spinner spinProfile;
private TextView summaryView;
-
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_vpn_settings);
-
+
String title = getString(R.string.title_activity_vpn_settings);
-
+
Intent intent = getIntent();
if (!intent.hasExtra(ActivityLauncher.EXTRA_APINFO)) {
this.setResult(RESULT_CANCELED);
@@ -58,144 +56,129 @@ public class VpnSettingsActivity extends Activity {
return;
}
- this.currentNetwork = (AccessPointInfo) intent.getExtras().getSerializable(
- ActivityLauncher.EXTRA_APINFO);
- if (this.currentNetwork == null) {
- this.setResult(RESULT_CANCELED);
- this.finish();
- return;
- }
-
- this.currentNetwork = Commons.storage.getSavedNetwork(currentNetwork);
- this.spinProfile = (Spinner)findViewById(R.id.spin_vpn_profile);
- this.summaryView = (TextView)findViewById(R.id.lbl_vpn_settings);
- String summary = getString(R.string.summary_vpn_settings).replace(OpenVpnManager.PLACEHOLDER_APPNAME, OpenVpnManager.APP_COMMON_NAME);
+ this.spinProfile = (Spinner) findViewById(R.id.spin_vpn_profile);
+ this.summaryView = (TextView) findViewById(R.id.lbl_vpn_settings);
+ String summary = getString(R.string.summary_vpn_settings).replace(PLACEHOLDER_APPNAME,
+ APP_COMMON_NAME);
summaryView.setText(summary);
- title += " " + currentNetwork.getSsid();
- this.setTitle(title);
-
- if (!checkExternalApp()){
- toggleSettingsEnabled(false);
- } else {
- initVpnManager();
- }
+ this.currentNetwork = (AccessPointInfo) intent.getExtras().getParcelable(
+ ActivityLauncher.EXTRA_APINFO);
+
+ this.currentNetwork = NetworkManager.getSavedNetwork(currentNetwork);
+ if (this.currentNetwork != null) {
+ title += " " + this.currentNetwork.getSsid();
+ this.setTitle(title);
+ toggleSettingsEnabled(true);
+ }
}
-
@Override
- public void onActivityResult(int requestCode, int resultCode, Intent intent) {
-
- switch (requestCode) {
-
- case RequestCode.VPN_PERMISSION:
-
- if (resultCode != RESULT_OK) {
- toggleSettingsEnabled(false);
- Utils.logDebug("User rejected vpn permission.");
- String msg = getString(R.string.msg_vpn_no_permission).replace(
- OpenVpnManager.PLACEHOLDER_APPNAME, OpenVpnManager.APP_COMMON_NAME);
- Commons.showMessage(msg, this);
- return;
- }
-
- break;
-
- default:
-
- break;
-
+ public void onStart() {
+
+ super.onStart();
+ if (!checkExternalApp() || currentNetwork == null) {
+ toggleSettingsEnabled(false);
+ } else {
+ beginGetExistingVpnProfiles();
}
}
-
@Override
public boolean onCreateOptionsMenu(Menu menu) {
return true;
}
- private void initVpnManager(){
-
- try {
-
- Intent intentAllow = OpenVpnManager.askApiPermissionsGetIntent();
- if (intentAllow != null){
- startActivityForResult(intentAllow, ActivityLauncher.RequestCode.VPN_PERMISSION);
- }
-
- List<String> profiles = OpenVpnManager.getExistingProfiles();
- if (profiles.size() == 0){
- String msg = getString(R.string.msg_vpn_no_profile).replace(OpenVpnManager.PLACEHOLDER_APPNAME, OpenVpnManager.APP_COMMON_NAME);
- Commons.showMessage(msg, this);
- toggleSettingsEnabled(false);
- return;
- }
- Spinner spin = (Spinner)findViewById(R.id.spin_vpn_profile);
-
- ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_spinner_dropdown_item,profiles);
- adapter.insert("",0);
- spin.setAdapter(adapter);
- spin.setSelection(adapter.getPosition(currentNetwork.getVpnProfileName()));
-
- } catch (Exception e) {
- Utils.logError("Exception while creating openvpnmanager",e);
- Commons.showMessage(getString(R.string.msg_vpn_connect_error));
- toggleSettingsEnabled(false);
- }
-
- }
-
- private boolean checkExternalApp(){
-
- if (! OpenVpnManager.isExternalAppInstalled(this)){
- String msg = getString(R.string.text_vpn_package_missing).replace(OpenVpnManager.PLACEHOLDER_APPNAME, OpenVpnManager.APP_COMMON_NAME);
- Commons.showMessage(msg, this);
+ private boolean checkExternalApp() {
+
+ if (!isExternalAppInstalled()) {
+ String msg = getString(R.string.text_vpn_package_missing).replace(PLACEHOLDER_APPNAME,
+ APP_COMMON_NAME);
+ Utils.showMessage(msg, this);
toggleSettingsEnabled(false);
return false;
} else {
return true;
}
-
+
}
-
- private void toggleSettingsEnabled(boolean enabled){
-
+
+ private void toggleSettingsEnabled(boolean enabled) {
+
spinProfile.setEnabled(enabled);
-
- Button b = (Button)findViewById(R.id.btn_save_vpn_settings);
+
+ Button b = (Button) findViewById(R.id.btn_save_vpn_settings);
b.setEnabled(enabled);
-
+
}
-
- public void btnSaveClick(View v){
-
- String vpnProf = (String)spinProfile.getSelectedItem();
-
- /*if (! vpnProf.isEmpty()){
- // check if profile name exists
- if( OpenVpnManager.getUuidFromName(vpnProf) == null){
- Commons.showMessage(getString(R.string.msg_vpn_wrong_profile), this);
- return;
- }
-
- }*/
-
+
+ public void btnSaveClick(View v) {
+
+ String vpnProf = (String) spinProfile.getSelectedItem();
+
// save profile
currentNetwork.setVpnProfileName(vpnProf);
- Commons.storage.save(currentNetwork);
-
+ NetworkManager.save(currentNetwork);
+
terminate();
-
+
}
-
- public void btnBackClick(View v){
+
+ public void btnBackClick(View v) {
terminate();
}
-
- private void terminate(){
+
+ private void terminate() {
finish();
}
+
+ @Override
+ protected void onVpnProfilesAvailable(List<String> profiles) {
+
+ if (profiles.size() == 0) {
+ String msg = getString(R.string.msg_vpn_no_profile).replace(PLACEHOLDER_APPNAME,
+ APP_COMMON_NAME);
+ Utils.showMessage(msg, this);
+ toggleSettingsEnabled(false);
+ return;
+ }
+ Spinner spin = (Spinner) findViewById(R.id.spin_vpn_profile);
+
+ ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
+ android.R.layout.simple_spinner_dropdown_item, profiles){
+ @Override
+ public View getView(int position, View convertView, android.view.ViewGroup parent)
+ {
+ return getStyledView(super.getView(position, convertView,parent));
+ }
+
+ private View getStyledView(View view)
+ {
+ TextView textView = (TextView)view.findViewById(android.R.id.text1);
+ textView.setGravity(Gravity.CENTER);
+ textView.setTextColor(R.color.ThemeLight);
+ return view;
+ }
+
+ };
+ adapter.insert(AccessPointInfo.DUMMY_VPN_PROFILE, 0);
+ spin.setAdapter(adapter);
+
+ String pn = currentNetwork.getVpnProfileName();
+ int pos = adapter.getPosition(pn);
+ if (pos < 0) {
+ spin.setSelection(adapter.getPosition(AccessPointInfo.DUMMY_VPN_PROFILE));
+ } else {
+ spin.setSelection(pos);
+ }
+
+ }
+ @Override
+ protected void onVpnPermissionDenied(){
+ }
+
+
}
diff --git a/app/src/fil/libre/repwifiapp/fwproxies/ConnectivityManagerProxy.java b/app/src/fil/libre/repwifiapp/fwproxies/ConnectivityManagerProxy.java
new file mode 100644
index 0000000..f2e7de7
--- /dev/null
+++ b/app/src/fil/libre/repwifiapp/fwproxies/ConnectivityManagerProxy.java
@@ -0,0 +1,98 @@
+//
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+//
+// This file is part of RepWifiApp.
+//
+// RepWifiApp 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.
+//
+// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
+//
+// ********************************************************************
+//
+// This file is derivative work, inspired by the original class definition:
+// "android.net.ConnectivityManager.java" as found in version 6.0 of the Android Operating System.
+// Following is the original copyright notice:
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package fil.libre.repwifiapp.fwproxies;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Messenger;
+
+public class ConnectivityManagerProxy extends FrameworkProxy {
+
+ private Context currentContext;
+
+ public ConnectivityManagerProxy(Context context) {
+ currentContext = context;
+ init();
+ }
+
+ @Override
+ protected String getInnerClassName() {
+ return ConnectivityManager.class.getCanonicalName();
+ }
+
+
+ public int registerNetworkAgent(Messenger msgr, NetworkInfoProxy ni,
+ LinkPropertiesProxy lp, NetworkCapabilitiesProxy nc, int score){
+
+ Class<?>[] types = getTypesArray(Messenger.class,
+ NetworkInfo.class,
+ getClassFromName(LinkPropertiesProxy.getStaticInnerClassName()),
+ getClassFromName(NetworkCapabilitiesProxy.getStaticInnerClassName()),
+ int.class,
+ getClassFromName("android.net.NetworkMisc"));
+
+ return (Integer)invokeMethodGetResult("registerNetworkAgent",types, msgr, ni.getNetworkInfo(), lp, nc, score, null);
+
+ }
+
+/* public boolean isWifiConnected(){
+
+ init();
+ ConnectivityManager cm = (ConnectivityManager)inner;
+
+ NetworkInfo i = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+ if (i == null){
+ return false;
+ } else {
+ return i.isConnected();
+ }
+
+ }*/
+
+ private void init(){
+
+ if (this.inner == null){
+ this.inner = currentContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ }
+
+ }
+
+
+}
diff --git a/app/src/fil/libre/repwifiapp/fwproxies/FrameworkProxy.java b/app/src/fil/libre/repwifiapp/fwproxies/FrameworkProxy.java
new file mode 100644
index 0000000..a468a1c
--- /dev/null
+++ b/app/src/fil/libre/repwifiapp/fwproxies/FrameworkProxy.java
@@ -0,0 +1,126 @@
+//
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+//
+// This file is part of RepWifiApp.
+//
+// RepWifiApp 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.
+//
+// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
+//
+// ********************************************************************
+
+package fil.libre.repwifiapp.fwproxies;
+
+import fil.libre.repwifiapp.helpers.Logger;
+import java.lang.reflect.Constructor;
+
+/**
+ * Provides a base class for creating "proxy" classes that wrap up classes from
+ * the Android Application Framework via reflection, exposing them outside the
+ * framework itself.
+ */
+public abstract class FrameworkProxy {
+
+ protected Object inner;
+
+ protected abstract String getInnerClassName();
+
+ protected static String getStaticInnerClassName() {
+ return "Object";
+ }
+
+ protected static Class<?> getClassFromName(String className) {
+
+ try {
+ return Class.forName(className);
+ } catch (ClassNotFoundException e) {
+ Logger.logError(null, e);
+ return null;
+ }
+
+ }
+
+ protected Class<?> getInnerClass() {
+ return getClassFromName(getInnerClassName());
+ }
+
+ protected Class<?>[] getTypesArray(Class<?>... args) {
+ Class<?>[] types = new Class<?>[args.length];
+ for (int i = 0; i < args.length; i++) {
+ types[i] = args[i];
+ }
+ return types;
+ }
+
+ protected Object createInnerObject(Class<?> argType, Object arg) {
+ return createInnerObject(getTypesArray(argType), arg);
+ }
+
+ protected Object createInnerObject(Class<?>[] argumentTypes, Object... args) {
+ try {
+ Class<?> cls = getClassFromName(getInnerClassName());
+ Constructor<?> c = cls.getConstructor(argumentTypes);
+ this.inner = c.newInstance(getRealArgs(args));
+ return this.inner;
+ } catch (Exception e) {
+ Logger.logError("Exception while creating inner object via reflection.", e);
+ return null;
+ }
+ }
+
+ protected Object invokeMethodGetResult(String methodName, Class<?> argumentType, Object arg) {
+ return invokeMethodGetResult(methodName, getTypesArray(argumentType), arg);
+ }
+
+ protected Object invokeMethodGetResult(String methodName, Class<?>[] argumentTypes,
+ Object... args) {
+ try {
+ return getClassFromName(getInnerClassName()).getMethod(methodName, argumentTypes)
+ .invoke(inner, getRealArgs(args));
+ } catch (Exception e) {
+ Logger.logError("Exception while invoking method via reflection.", e);
+ return null;
+ }
+ }
+
+ protected void invokeMethod(String methodName, Class<?> argumentType, Object arg) {
+ invokeMethod(methodName, getTypesArray(argumentType), arg);
+ }
+
+ protected void invokeMethod(String methodName, Class<?>[] argumentTypes, Object... args) {
+ try {
+ getClassFromName(getInnerClassName()).getMethod(methodName, argumentTypes).invoke(
+ inner, getRealArgs(args));
+ } catch (Exception e) {
+ Logger.logError("Exception while invoking method via reflection.", e);
+ }
+ }
+
+ private Object[] getRealArgs(Object[] args) {
+
+ if (args == null || args.length == 0) {
+ return new Object[] {};
+ }
+
+ // if the object is just a proxy, use the inner object as an argument to
+ // the call.
+ for (int i = 0; i < args.length; i++) {
+ if (args[i] instanceof FrameworkProxy) {
+ args[i] = ((FrameworkProxy) args[i]).inner;
+ }
+ }
+
+ return args;
+
+ }
+
+} \ No newline at end of file
diff --git a/app/src/fil/libre/repwifiapp/fwproxies/LinkAddressProxy.java b/app/src/fil/libre/repwifiapp/fwproxies/LinkAddressProxy.java
new file mode 100644
index 0000000..5fcd211
--- /dev/null
+++ b/app/src/fil/libre/repwifiapp/fwproxies/LinkAddressProxy.java
@@ -0,0 +1,61 @@
+//
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+//
+// This file is part of RepWifiApp.
+//
+// RepWifiApp 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.
+//
+// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
+//
+// ********************************************************************
+//
+// This file is derivative work, inspired by the original class definition:
+// "android.net.LinkAddress.java" as found in version 6.0 of the Android Operating System.
+// Following is the original copyright notice:
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package fil.libre.repwifiapp.fwproxies;
+
+import java.net.InetAddress;
+
+
+public class LinkAddressProxy extends FrameworkProxy {
+
+ @Override
+ protected String getInnerClassName() {
+ return "android.net.LinkAddress";
+ }
+
+ public LinkAddressProxy(String ipAndMask){
+ createInnerObject(String.class, ipAndMask);
+ }
+
+ public LinkAddressProxy(InetAddress address, int mask){
+ createInnerObject(getTypesArray(InetAddress.class, int.class), address, mask);
+ }
+
+
+}
diff --git a/app/src/fil/libre/repwifiapp/fwproxies/LinkPropertiesProxy.java b/app/src/fil/libre/repwifiapp/fwproxies/LinkPropertiesProxy.java
new file mode 100644
index 0000000..4bfeeaa
--- /dev/null
+++ b/app/src/fil/libre/repwifiapp/fwproxies/LinkPropertiesProxy.java
@@ -0,0 +1,90 @@
+//
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+//
+// This file is part of RepWifiApp.
+//
+// RepWifiApp 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.
+//
+// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
+//
+// ********************************************************************
+//
+// This file is derivative work, inspired by the original class definition:
+// "android.net.LinkProperties.java" as found in version 6.0 of the Android Operating System.
+// Following is the original copyright notice:
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package fil.libre.repwifiapp.fwproxies;
+
+import java.net.InetAddress;
+
+
+public class LinkPropertiesProxy extends FrameworkProxy{
+
+ @Override
+ protected String getInnerClassName(){
+ return "android.net.LinkProperties";
+ }
+
+ public LinkPropertiesProxy(){
+ createInnerObject(null);
+ }
+
+ public LinkPropertiesProxy(LinkPropertiesProxy lp){
+ createInnerObject(getInnerClass(), lp.inner);
+ }
+
+ public void setInterfaceName(String iface) {
+ invokeMethod("setInterfaceName", String.class , iface);
+ }
+
+ public boolean addRoute(RouteInfoProxy route) {
+ if (route == null){
+ return false;
+ }
+ return (Boolean)invokeMethodGetResult("addRoute", route.getInnerClass(), route);
+ }
+
+ public boolean addLinkAddress(LinkAddressProxy address) {
+ if (address == null){
+ return false;
+ }
+ return (Boolean)invokeMethodGetResult("addLinkAddress",address.getInnerClass(), address);
+ }
+
+ public boolean addDnsServer(InetAddress dnsServer) {
+ if (dnsServer == null){
+ return false;
+ }
+ return (Boolean)invokeMethodGetResult("addDnsServer", InetAddress.class, dnsServer);
+ }
+
+ protected static String getStaticInnerClassName(){
+ return "android.net.LinkProperties";
+ }
+
+
+}
diff --git a/app/src/fil/libre/repwifiapp/fwproxies/NetworkCapabilitiesProxy.java b/app/src/fil/libre/repwifiapp/fwproxies/NetworkCapabilitiesProxy.java
new file mode 100644
index 0000000..f66bebd
--- /dev/null
+++ b/app/src/fil/libre/repwifiapp/fwproxies/NetworkCapabilitiesProxy.java
@@ -0,0 +1,90 @@
+//
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+//
+// This file is part of RepWifiApp.
+//
+// RepWifiApp 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.
+//
+// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
+//
+// ********************************************************************
+//
+// This file is derivative work, inspired by the original class definition:
+// "android.net.NetworkCapabilities.java" as found in version 6.0 of the Android Operating System.
+// Following is the original copyright notice:
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package fil.libre.repwifiapp.fwproxies;
+
+
+public class NetworkCapabilitiesProxy extends FrameworkProxy{
+
+ public static final int NET_CAPABILITY_MMS = 0;
+ public static final int NET_CAPABILITY_SUPL = 1;
+ public static final int NET_CAPABILITY_DUN = 2;
+ public static final int NET_CAPABILITY_FOTA = 3;
+ public static final int NET_CAPABILITY_IMS = 4;
+ public static final int NET_CAPABILITY_CBS = 5;
+ public static final int NET_CAPABILITY_WIFI_P2P = 6;
+ public static final int NET_CAPABILITY_IA = 7;
+ public static final int NET_CAPABILITY_RCS = 8;
+ public static final int NET_CAPABILITY_XCAP = 9;
+ public static final int NET_CAPABILITY_EIMS = 10;
+ public static final int NET_CAPABILITY_NOT_METERED = 11;
+ public static final int NET_CAPABILITY_INTERNET = 12;
+ public static final int NET_CAPABILITY_NOT_RESTRICTED = 13;
+ public static final int NET_CAPABILITY_TRUSTED = 14;
+ public static final int NET_CAPABILITY_NOT_VPN = 15;
+ public static final int NET_CAPABILITY_VALIDATED = 16;
+ public static final int NET_CAPABILITY_CAPTIVE_PORTAL = 17;
+
+ public NetworkCapabilitiesProxy(){
+ createInnerObject(null);
+ }
+
+ public NetworkCapabilitiesProxy(NetworkCapabilitiesProxy nc){
+ createInnerObject(getInnerClass(), nc.inner);
+ }
+
+ public void addCapability(int capability) {
+ invokeMethod("addCapability", new Class<?>[]{int.class}, capability);
+ }
+
+ public void removeCapability(int capability) {
+ invokeMethod("removeCapability", new Class<?>[]{int.class}, capability);
+ }
+
+ @Override
+ protected String getInnerClassName() {
+ return "android.net.NetworkCapabilities";
+ }
+
+ protected static String getStaticInnerClassName(){
+ return "android.net.NetworkCapabilities";
+ }
+
+}
+
diff --git a/app/src/fil/libre/repwifiapp/fwproxies/NetworkInfoProxy.java b/app/src/fil/libre/repwifiapp/fwproxies/NetworkInfoProxy.java
new file mode 100644
index 0000000..0537451
--- /dev/null
+++ b/app/src/fil/libre/repwifiapp/fwproxies/NetworkInfoProxy.java
@@ -0,0 +1,102 @@
+//
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+//
+// This file is part of RepWifiApp.
+//
+// RepWifiApp 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.
+//
+// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
+//
+// ********************************************************************
+//
+// This file is derivative work of the original class definition:
+// "android.net.NetworkInfo" as found in version 6.0 of the Android Operating System.
+// Following is the original copyright notice:
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package fil.libre.repwifiapp.fwproxies;
+
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+
+public class NetworkInfoProxy extends FrameworkProxy {
+
+ @Override
+ protected String getInnerClassName() {
+ return "android.net.NetworkInfo";
+ }
+
+ public NetworkInfoProxy(int type, int subtype, String typeName, String subtypeName){
+ createInnerObject(new Class<?>[]{int.class, int.class, String.class, String.class}, type, subtype, typeName, subtypeName);
+ }
+
+ public NetworkInfoProxy(NetworkInfo source){
+ createInnerObject(new Class<?>[]{NetworkInfo.class}, source);
+ }
+
+ public static NetworkInfoProxy getForWifi(){
+ return new NetworkInfoProxy(ConnectivityManager.TYPE_WIFI,0, "WIFI", "");
+ }
+
+ public NetworkInfo getNetworkInfo(){
+ if (inner == null){
+ return null;
+ }
+ return (NetworkInfo)inner;
+ }
+
+ public void setType(int type) {
+ invokeMethod("setType", new Class<?>[]{int.class}, type);
+ }
+
+ public void setSubtype(int subtype, String subtypeName) {
+ invokeMethod("setSubType", new Class<?>[]{int.class, String.class}, subtype, subtypeName);
+ }
+
+ public void setIsAvailable(boolean isAvailable) {
+ invokeMethod("setIsAvailable", new Class<?>[]{boolean.class}, isAvailable);
+ }
+
+ public void setFailover(boolean isFailover) {
+ invokeMethod("setFailover", new Class<?>[]{boolean.class}, isFailover);
+ }
+
+
+ public void setRoaming(boolean isRoaming) {
+ invokeMethod("setRoaming", new Class<?>[]{boolean.class}, isRoaming);
+ }
+
+ public void setDetailedState(DetailedState detailedState, String reason, String extraInfo) {
+ invokeMethod("setDetailedState", new Class<?>[]{DetailedState.class, String.class, String.class}, detailedState, reason, extraInfo);
+ }
+
+ public void setExtraInfo(String extraInfo) {
+ invokeMethod("setExtraInfo", new Class<?>[]{String.class}, extraInfo);
+ }
+
+
+}
diff --git a/app/src/fil/libre/repwifiapp/fwproxies/RepWifiNetworkAgent.java b/app/src/fil/libre/repwifiapp/fwproxies/RepWifiNetworkAgent.java
new file mode 100644
index 0000000..bab5c5d
--- /dev/null
+++ b/app/src/fil/libre/repwifiapp/fwproxies/RepWifiNetworkAgent.java
@@ -0,0 +1,581 @@
+//
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+//
+// This file is part of RepWifiApp.
+//
+// RepWifiApp 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.
+//
+// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
+//
+// ********************************************************************
+//
+// This file is derivative work, inspired by the original class definition:
+// "com.android.server.wifi.WifiStateMachine$WifiNetworkAgent.java"
+// as found in version 6.0 of the Android Operating System.
+// Following is the original copyright notice:
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package fil.libre.repwifiapp.fwproxies;
+
+import android.content.Context;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.util.Log;
+import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class RepWifiNetworkAgent extends Handler {
+
+ public final int netId;
+
+ private Messenger myMessenger = null;
+
+ private volatile AsyncChannelProxy mAsyncChannel;
+ private final String LOG_TAG;
+ private static final boolean DBG = true;
+ private static final boolean VDBG = true;
+ private final Context mContext;
+ private final ArrayList<Message> mPreConnectedQueue = new ArrayList<Message>();
+ private volatile long mLastBwRefreshTime = 0;
+ private static final long BW_REFRESH_MIN_WIN_MS = 500;
+ private boolean mPollLceScheduled = false;
+ private AtomicBoolean mPollLcePending = new AtomicBoolean(false);
+
+ /* as in com.android.internal.util.Protocol.BASE_NETWORK_AGENT; */
+ private static final int BASE = 0x00081000;
+
+ /**
+ * Sent by ConnectivityService to the NetworkAgent to inform it of
+ * suspected connectivity problems on its network. The NetworkAgent
+ * should take steps to verify and correct connectivity.
+ */
+ public static final int CMD_SUSPECT_BAD = BASE;
+
+ /**
+ * Sent by the NetworkAgent (note the EVENT vs CMD prefix) to
+ * ConnectivityService to pass the current NetworkInfo (connection state).
+ * Sent when the NetworkInfo changes, mainly due to change of state.
+ * obj = NetworkInfo
+ */
+ public static final int EVENT_NETWORK_INFO_CHANGED = BASE + 1;
+
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to pass the current
+ * NetworkCapabilties.
+ * obj = NetworkCapabilities
+ */
+ public static final int EVENT_NETWORK_CAPABILITIES_CHANGED = BASE + 2;
+
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to pass the current
+ * NetworkProperties.
+ * obj = NetworkProperties
+ */
+ public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 3;
+
+ public static final int WIFI_BASE_SCORE = 60;
+
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to pass the current
+ * network score.
+ * obj = network score Integer
+ */
+ public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 4;
+
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to add new UID ranges
+ * to be forced into this Network. For VPNs only.
+ * obj = UidRange[] to forward
+ */
+ public static final int EVENT_UID_RANGES_ADDED = BASE + 5;
+
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to remove UID ranges
+ * from being forced into this Network. For VPNs only.
+ * obj = UidRange[] to stop forwarding
+ */
+ public static final int EVENT_UID_RANGES_REMOVED = BASE + 6;
+
+ /**
+ * Sent by ConnectivityService to the NetworkAgent to inform the agent of
+ * the
+ * networks status - whether we could use the network or could not, due to
+ * either a bad network configuration (no internet link) or captive portal.
+ *
+ * arg1 = either {@code VALID_NETWORK} or {@code INVALID_NETWORK}
+ */
+ public static final int CMD_REPORT_NETWORK_STATUS = BASE + 7;
+
+ public static final int VALID_NETWORK = 1;
+ public static final int INVALID_NETWORK = 2;
+
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to indicate this network
+ * was
+ * explicitly selected. This should be sent before the NetworkInfo is marked
+ * CONNECTED so it can be given special treatment at that time.
+ *
+ * obj = boolean indicating whether to use this network even if unvalidated
+ */
+ public static final int EVENT_SET_EXPLICITLY_SELECTED = BASE + 8;
+
+ /**
+ * Sent by ConnectivityService to the NetworkAgent to inform the agent of
+ * whether the network should in the future be used even if not validated.
+ * This decision is made by the user, but it is the network transport's
+ * responsibility to remember it.
+ *
+ * arg1 = 1 if true, 0 if false
+ */
+ public static final int CMD_SAVE_ACCEPT_UNVALIDATED = BASE + 9;
+
+ /**
+ * Sent by ConnectivityService to the NetworkAgent to inform the agent to
+ * pull
+ * the underlying network connection for updated bandwidth information.
+ */
+ public static final int CMD_REQUEST_BANDWIDTH_UPDATE = BASE + 10;
+
+ /**
+ * Sent by ConnectivityService to the NetworkAgent to request that the
+ * specified packet be sent
+ * periodically on the given interval.
+ *
+ * arg1 = the slot number of the keepalive to start
+ * arg2 = interval in seconds
+ * obj = KeepalivePacketData object describing the data to be sent
+ *
+ * Also used internally by ConnectivityService / KeepaliveTracker, with
+ * different semantics.
+ */
+ public static final int CMD_START_PACKET_KEEPALIVE = BASE + 11;
+
+ /**
+ * Requests that the specified keepalive packet be stopped.
+ *
+ * arg1 = slot number of the keepalive to stop.
+ *
+ * Also used internally by ConnectivityService / KeepaliveTracker, with
+ * different semantics.
+ */
+ public static final int CMD_STOP_PACKET_KEEPALIVE = BASE + 12;
+
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to provide status on a
+ * packet keepalive
+ * request. This may either be the reply to a CMD_START_PACKET_KEEPALIVE, or
+ * an asynchronous
+ * error notification.
+ *
+ * This is also sent by KeepaliveTracker to the app's
+ * ConnectivityManager.PacketKeepalive to
+ * so that the app's PacketKeepaliveCallback methods can be called.
+ *
+ * arg1 = slot number of the keepalive
+ * arg2 = error code
+ */
+ public static final int EVENT_PACKET_KEEPALIVE = BASE + 13;
+
+ /**
+ * Sent by ConnectivityService to inform this network transport of signal
+ * strength thresholds
+ * that when crossed should trigger a system wakeup and a
+ * NetworkCapabilities update.
+ *
+ * obj = int[] describing signal strength thresholds.
+ */
+ public static final int CMD_SET_SIGNAL_STRENGTH_THRESHOLDS = BASE + 14;
+
+ /**
+ * Sent by ConnectivityService to the NeworkAgent to inform the agent to
+ * avoid
+ * automatically reconnecting to this network (e.g. via autojoin). Happens
+ * when user selects "No" option on the "Stay connected?" dialog box.
+ */
+ public static final int CMD_PREVENT_AUTOMATIC_RECONNECT = BASE + 15;
+
+ public RepWifiNetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
+ NetworkCapabilitiesProxy nc, LinkPropertiesProxy lp, int score) {
+
+ super(looper);
+
+ LOG_TAG = logTag;
+ mContext = context;
+ if (ni == null || nc == null || lp == null) {
+ throw new IllegalArgumentException();
+ }
+
+ if (VDBG)
+ log("Registering NetworkAgent");
+
+ ConnectivityManagerProxy cm = new ConnectivityManagerProxy(mContext);
+
+ myMessenger = new Messenger(this);
+ netId = cm.registerNetworkAgent(myMessenger, new NetworkInfoProxy(ni),
+ new LinkPropertiesProxy(lp), new NetworkCapabilitiesProxy(nc), score);
+
+ }
+
+ public boolean isChannellConnected() {
+ return (mAsyncChannel != null);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+
+ switch (msg.what) {
+ case AsyncChannelProxy.CMD_CHANNEL_FULL_CONNECTION: {
+ if (mAsyncChannel != null) {
+ log("Received new connection while already connected!");
+ } else {
+ if (VDBG)
+ log("NetworkAgent fully connected");
+ AsyncChannelProxy ac = new AsyncChannelProxy();
+ ac.connected(null, this, msg.replyTo);
+ ac.replyToMessage(msg, AsyncChannelProxy.CMD_CHANNEL_FULLY_CONNECTED,
+ AsyncChannelProxy.STATUS_SUCCESSFUL);
+ synchronized (mPreConnectedQueue) {
+ mAsyncChannel = ac;
+ for (Message m : mPreConnectedQueue) {
+ ac.sendMessage(m);
+ }
+ mPreConnectedQueue.clear();
+ }
+ }
+ break;
+ }
+ case AsyncChannelProxy.CMD_CHANNEL_DISCONNECT: {
+ if (VDBG)
+ log("CMD_CHANNEL_DISCONNECT");
+ if (mAsyncChannel != null)
+ mAsyncChannel.disconnect();
+ break;
+ }
+ case AsyncChannelProxy.CMD_CHANNEL_DISCONNECTED: {
+ if (DBG)
+ log("NetworkAgent channel lost");
+ // let the client know CS is done with us.
+
+ synchronized (mPreConnectedQueue) {
+ mAsyncChannel = null;
+ }
+ break;
+ }
+ case CMD_SUSPECT_BAD: {
+ log("Unhandled Message " + msg);
+ break;
+ }
+ case CMD_REQUEST_BANDWIDTH_UPDATE: {
+ long currentTimeMs = System.currentTimeMillis();
+ if (VDBG) {
+ log("CMD_REQUEST_BANDWIDTH_UPDATE request received.");
+ }
+ if (currentTimeMs >= (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) {
+ mPollLceScheduled = false;
+ if (mPollLcePending.getAndSet(true) == false) {
+
+ shouldCallUninimplementedMethod("pollLceData()");
+
+ }
+ } else {
+ // deliver the request at a later time rather than discard it
+ // completely.
+ if (!mPollLceScheduled) {
+ long waitTime = mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS - currentTimeMs + 1;
+ mPollLceScheduled = sendEmptyMessageDelayed(CMD_REQUEST_BANDWIDTH_UPDATE,
+ waitTime);
+ }
+ }
+ break;
+ }
+ case CMD_REPORT_NETWORK_STATUS: {
+ if (VDBG) {
+ log("CMD_REPORT_NETWORK_STATUS("
+ + (msg.arg1 == VALID_NETWORK ? "VALID)" : "INVALID)"));
+ }
+ shouldCallUninimplementedMethod("shounetworkStatus(msg.arg1)");
+ break;
+ }
+ case CMD_SAVE_ACCEPT_UNVALIDATED: {
+ shouldCallUninimplementedMethod("saveAcceptUnvalidated(msg.arg1 != 0)");
+
+ break;
+ }
+ case CMD_START_PACKET_KEEPALIVE: {
+ shouldCallUninimplementedMethod("startPacketKeepalive(msg)");
+ break;
+ }
+ case CMD_STOP_PACKET_KEEPALIVE: {
+ shouldCallUninimplementedMethod("stopPacketKeepalive(msg)");
+
+ break;
+ }
+
+ case CMD_SET_SIGNAL_STRENGTH_THRESHOLDS: {
+ ArrayList<Integer> thresholds = ((Bundle) msg.obj).getIntegerArrayList("thresholds");
+ int[] intThresholds = new int[(thresholds != null) ? thresholds.size() : 0];
+ for (int i = 0; i < intThresholds.length; i++) {
+ intThresholds[i] = thresholds.get(i);
+ }
+ shouldCallUninimplementedMethod("setSignalStrengthThresholds(intThresholds)");
+ break;
+ }
+ case CMD_PREVENT_AUTOMATIC_RECONNECT: {
+ shouldCallUninimplementedMethod("preventAutomaticReconnect()");
+ break;
+ }
+ default: {
+ String rep = "";
+ if (msg.replyTo != null) {
+ rep = msg.replyTo.toString();
+ }
+ log("Received unhandled message: what = " + msg.what + " replyTo: " + rep);
+ }
+ }
+ }
+
+ private void queueOrSendMessage(int what, Object obj) {
+ queueOrSendMessage(what, 0, 0, obj);
+ }
+
+ /*
+ * private void queueOrSendMessage(int what, int arg1, int arg2) {
+ * queueOrSendMessage(what, arg1, arg2, null);
+ * }
+ */
+
+ private void queueOrSendMessage(int what, int arg1, int arg2, Object obj) {
+ if (VDBG)
+ log("Send or queue message; what=" + what);
+ Message msg = Message.obtain();
+ msg.what = what;
+ msg.arg1 = arg1;
+ msg.arg2 = arg2;
+ msg.obj = obj;
+ msg.replyTo = this.myMessenger;
+
+ queueOrSendMessage(msg);
+ }
+
+ private void queueOrSendMessage(Message msg) {
+ synchronized (mPreConnectedQueue) {
+ if (mAsyncChannel != null) {
+ if (VDBG)
+ log("Actually sending message " + msg);
+ mAsyncChannel.sendMessage(msg);
+ } else {
+ mPreConnectedQueue.add(msg);
+ }
+ }
+ }
+
+ /**
+ * Called by the bearer code when it has new LinkProperties data.
+ */
+ public void sendLinkProperties(LinkPropertiesProxy linkProperties) {
+ queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED,
+ new LinkPropertiesProxy(linkProperties).inner);
+ }
+
+ /**
+ * Called by the bearer code when it has new NetworkInfo data.
+ */
+ public void sendNetworkInfo(NetworkInfo networkInfo) {
+ queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED,
+ new NetworkInfoProxy(networkInfo).getNetworkInfo());
+ }
+
+ /**
+ * Called by the bearer code when it has new NetworkCapabilities data.
+ */
+ public void sendNetworkCapabilities(NetworkCapabilitiesProxy networkCapabilities) {
+ mPollLcePending.set(false);
+ mLastBwRefreshTime = System.currentTimeMillis();
+ queueOrSendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED, new NetworkCapabilitiesProxy(
+ networkCapabilities).inner);
+ }
+
+ /**
+ * Called by the bearer code when it has a new score for this network.
+ */
+ public void sendNetworkScore(int score) {
+ if (score < 0) {
+ throw new IllegalArgumentException("Score must be >= 0");
+ }
+ queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, Integer.valueOf(score));
+ }
+
+ protected void log(String s) {
+ Log.d(LOG_TAG, "NetworkAgent: " + s);
+ }
+
+ private void shouldCallUninimplementedMethod(String methodName) {
+ String msg = "[WRN] Should be calling " + methodName
+ + " but the method is not implemented by the proxy..";
+ Log.w(LOG_TAG, msg);
+ }
+
+ /*
+ * A proxy for package com.android.internal.util.AsyncChannel;
+ * Mererly replicates constants and functions needed by the NetworkAgent to
+ * communicate with the other side of the Handler
+ */
+ private static class AsyncChannelProxy extends FrameworkProxy {
+
+ // as in com.android.internal.util.Protocol.BASE_SYSTEM_ASYNC_CHANNEL;
+ private static final int BASE = 0x00011000;
+
+ /** Successful status always 0, !0 is an unsuccessful status */
+ public static final int STATUS_SUCCESSFUL = 0;
+
+ /**
+ * Command typically sent when after receiving the
+ * CMD_CHANNEL_HALF_CONNECTED.
+ * This is used to initiate a long term connection with the destination
+ * and
+ * typically the destination will reply with
+ * CMD_CHANNEL_FULLY_CONNECTED.
+ *
+ * msg.replyTo = srcMessenger.
+ */
+ public static final int CMD_CHANNEL_FULL_CONNECTION = BASE + 1;
+
+ /**
+ * Command typically sent after the destination receives a
+ * CMD_CHANNEL_FULL_CONNECTION.
+ * This signifies the acceptance or rejection of the channel by the
+ * sender.
+ *
+ * msg.arg1 == 0 : Accept connection
+ * : All other values signify the destination rejected the connection
+ * and {@link AsyncChannel#disconnect} would typically be called.
+ */
+ public static final int CMD_CHANNEL_FULLY_CONNECTED = BASE + 2;
+
+ /**
+ * Command sent when one side or the other wishes to disconnect. The
+ * sender
+ * may or may not be able to receive a reply depending upon the protocol
+ * and
+ * the state of the connection. The receiver should call
+ * {@link AsyncChannel#disconnect} to close its side of the channel and
+ * it will receive a CMD_CHANNEL_DISCONNECTED
+ * when the channel is closed.
+ *
+ * msg.replyTo = messenger that is disconnecting
+ */
+ public static final int CMD_CHANNEL_DISCONNECT = BASE + 3;
+
+ /**
+ * Command sent when the channel becomes disconnected. This is sent when
+ * the
+ * channel is forcibly disconnected by the system or as a reply to
+ * CMD_CHANNEL_DISCONNECT.
+ *
+ * msg.arg1 == 0 : STATUS_SUCCESSFUL
+ * 1 : STATUS_BINDING_UNSUCCESSFUL
+ * 2 : STATUS_SEND_UNSUCCESSFUL
+ * : All other values signify failure and the channel state is
+ * indeterminate
+ * msg.obj == the AsyncChannel
+ * msg.replyTo = messenger disconnecting or null if it was never
+ * connected.
+ */
+ public static final int CMD_CHANNEL_DISCONNECTED = BASE + 4;
+
+ /*
+ * Following is a block of unused system constants, kept here for future
+ * reference
+ *//**
+ * Command sent when the channel is half connected. Half connected
+ * means that the channel can be used to send commends to the
+ * destination
+ * but the destination is unaware that the channel exists. The first
+ * command sent to the destination is typically
+ * CMD_CHANNEL_FULL_CONNECTION if
+ * it is desired to establish a long term connection, but any command
+ * maybe
+ * sent.
+ *
+ * msg.arg1 == 0 : STATUS_SUCCESSFUL
+ * 1 : STATUS_BINDING_UNSUCCESSFUL
+ * msg.obj == the AsyncChannel
+ * msg.replyTo == dstMessenger if successful
+ */
+ /*
+ * public static final int CMD_CHANNEL_HALF_CONNECTED = BASE + 0;
+ *
+ * private static final int CMD_TO_STRING_COUNT =
+ * CMD_CHANNEL_DISCONNECTED - BASE + 1;
+ *//** Error attempting to bind on a connect */
+ /*
+ * public static final int STATUS_BINDING_UNSUCCESSFUL = 1;
+ *//** Error attempting to send a message */
+ /*
+ * public static final int STATUS_SEND_UNSUCCESSFUL = 2;
+ *//** CMD_FULLY_CONNECTED refused because a connection already exists */
+ /*
+ * public static final int
+ * STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED = 3;
+ *//** Error indicating abnormal termination of destination messenger */
+ /*
+ * public static final int STATUS_REMOTE_DISCONNECTION = 4;
+ */
+
+ public AsyncChannelProxy() {
+ createInnerObject(null);
+ }
+
+ @Override
+ protected String getInnerClassName() {
+ return "com.android.internal.util.AsyncChannel";
+ }
+
+ public void connected(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
+ invokeMethod("connected", getTypesArray(Context.class, Handler.class, Messenger.class),
+ srcContext, srcHandler, dstMessenger);
+ }
+
+ public void disconnect() {
+ invokeMethod("disconnect", null);
+ }
+
+ public void sendMessage(Message msg) {
+ invokeMethod("sendMessage", Message.class, msg);
+ }
+
+ public void replyToMessage(Message srcMsg, int what, int arg1) {
+ invokeMethod("replyToMessage", getTypesArray(Message.class, int.class, int.class),
+ srcMsg, what, arg1);
+ }
+
+ }
+
+}
diff --git a/app/src/fil/libre/repwifiapp/fwproxies/RouteInfoProxy.java b/app/src/fil/libre/repwifiapp/fwproxies/RouteInfoProxy.java
new file mode 100644
index 0000000..ad2e9f6
--- /dev/null
+++ b/app/src/fil/libre/repwifiapp/fwproxies/RouteInfoProxy.java
@@ -0,0 +1,59 @@
+//
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+//
+// This file is part of RepWifiApp.
+//
+// RepWifiApp 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.
+//
+// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
+//
+// ********************************************************************
+//
+// This file is derivative work, inspired by the original class definition:
+// "android.net.RouteInfo.java" as found in version 6.0 of the Android Operating System.
+// Following is the original copyright notice:
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package fil.libre.repwifiapp.fwproxies;
+
+import java.net.InetAddress;
+
+public class RouteInfoProxy extends FrameworkProxy{
+
+ @Override
+ protected String getInnerClassName() {
+ return "android.net.RouteInfo";
+ }
+
+ public RouteInfoProxy(InetAddress gateway, String ifaceName) {
+ Class<?>[] sig = new Class<?>[]{getClassFromName("android.net.IpPrefix"),
+ InetAddress.class, String.class};
+ createInnerObject(sig, null, gateway, ifaceName);
+ }
+
+
+
+}
diff --git a/app/src/fil/libre/repwifiapp/helpers/ConnectionStatus.java b/app/src/fil/libre/repwifiapp/helpers/ConnectionStatus.java
deleted file mode 100644
index e271dbe..0000000
--- a/app/src/fil/libre/repwifiapp/helpers/ConnectionStatus.java
+++ /dev/null
@@ -1,109 +0,0 @@
-//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
-//
-// This file is part of RepWifiApp.
-//
-// RepWifiApp 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.
-//
-// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
-//
-// ********************************************************************
-
-package fil.libre.repwifiapp.helpers;
-
-import fil.libre.repwifiapp.Commons;
-import java.io.Serializable;
-
-public class ConnectionStatus implements Serializable {
-
- /**
- *
- */
- private static final long serialVersionUID = 1L;
- public static final String STATUS_CONNECTED = "COMPLETED";
- public static final String STATUS_INACTIVE = "INACTIVE";
- public static final String STATUS_DISCONNECTED = "DISCONNECTED";
- public static final String STATUS_UNDEFINED = "UNDEFINED";
-
- public String status;
- public String SSID;
- public String BSSID;
- public String IP;
-
- private static final String F_SEP = "=";
- private static final String KeyStatus = "wpa_state";
- private static final String KeySSID = "ssid";
- private static final String KeyBSSID = "bssid";
- private static final String KeyIP = "ip_address";
-
- public static ConnectionStatus parseWpaCliOutput(String wpaCliOutput) {
-
- if (wpaCliOutput == null) {
- return null;
- }
-
- if (wpaCliOutput.trim().length() == 0) {
- return null;
- }
-
- String[] lines = wpaCliOutput.split("\n");
-
- ConnectionStatus s = new ConnectionStatus();
- for (String line : lines) {
-
- if (line.trim().equals("")) {
- continue;
- }
-
- String[] fields = line.split(F_SEP);
- if (fields.length < 2) {
- continue;
- }
-
- String key = fields[0];
- String val = fields[1];
-
- if (key.equals(KeyBSSID)) {
- s.BSSID = val;
- } else if (key.equals(KeySSID)) {
- s.SSID = val;
- } else if (key.equals(KeyStatus)) {
- s.status = val;
- } else if (key.equals(KeyIP)) {
- s.IP = val;
- }
-
- }
-
- return s;
-
- }
-
- public boolean isConnected() {
-
- if (this.status == null) {
- return false;
- }
-
- if (this.status.equals(STATUS_CONNECTED)) {
- return true;
- } else {
- return false;
- }
- }
-
- public AccessPointInfo getNetworkDetails(){
- AccessPointInfo i = new AccessPointInfo(SSID, BSSID, "","", "");
- return Commons.storage.getSavedNetwork(i);
- }
-
-}
diff --git a/app/src/fil/libre/repwifiapp/helpers/Engine6p0.java b/app/src/fil/libre/repwifiapp/helpers/Engine6p0.java
deleted file mode 100644
index 3eff6c8..0000000
--- a/app/src/fil/libre/repwifiapp/helpers/Engine6p0.java
+++ /dev/null
@@ -1,271 +0,0 @@
-//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
-//
-// This file is part of RepWifiApp.
-//
-// RepWifiApp 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.
-//
-// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
-//
-// ********************************************************************
-
-package fil.libre.repwifiapp.helpers;
-
-import org.apache.http.conn.util.InetAddressUtils;
-import fil.libre.repwifiapp.Commons;
-
-public class Engine6p0 extends Engine {
-
- @Override
- public boolean connect(AccessPointInfo info) {
-
- WpaSupplicant.kill();
-
- if (info == null) {
- Utils.logDebug("Engine's connect() received a null AccessPointInfo");
- return false;
- }
-
- // clear any previously set network
- if (!destroyNetwork()) {
- Utils.logDebug("Unable to ndc destroy network");
- return false;
- }
-
- // clear interface's ip
- if (!clearAddrs()) {
- Utils.logDebug("Unable to ndc clearaddrs");
- return false;
- }
-
- // bring up interface
- if (!interfaceUp()) {
- Utils.logDebug("Unable to bring up interface.");
- return false;
- }
-
- // launch wpa_supplicant specifying our custom configuration and the
- // socket file
- if (!WpaSupplicant.start()) {
- Utils.logDebug("Unable to run wpa start");
- return false;
- }
-
- // create new network and get network id
- String netID = WpaCli.createNetworkGetId();
- if (netID == null) {
- Utils.logDebug("Unable to fetch network id");
- return false;
- }
-
- // set network SSID
- if (!WpaCli.setNetworkSSID(info.getSsid(), netID)) {
- Utils.logDebug("Failed to set network ssid");
- return false;
- }
-
- if (info.isHidden() && !WpaCli.setNetworkScanSSID(netID)) {
- Utils.logDebug("Failed to set scan_ssid 1 for hidden network.");
- return false;
- }
-
- // set password (if any)
- if (!WpaCli.setNetworkPSK(info, netID)) {
- Utils.logDebug("Failed to set network psk");
- return false;
- }
-
- // select the network we just created
- if (!WpaCli.selectNetwork(netID)) {
- Utils.logDebug("Unable to wpa_cli select network");
- return false;
- }
-
- // enable the newtork
- if (!WpaCli.enableNetwork(netID)) {
- Utils.logDebug("Unable to wpa_cli enable_newtork");
- return false;
- }
-
- // kill previous dhchcd instances
- if (!RootCommand.executeRootCmd("killall -SIGINT dhcpcd")){
- Utils.logError("Unable to kill previous instances of dhcpcd");
- }
-
- // get DHCP
- Utils.logDebug("Attempt to run dhcpcd..");
- if (!runDhcpcd(info.getDhcpConfiguration())) {
- Utils.logDebug("Failed to run dhcpcd");
- return false;
- }
-
- // try to fetch gateway
- String gw = getGateWayTimeout(Commons.WAIT_FOR_GATEWAY);
- if (gw == null || !InetAddressUtils.isIPv4Address(gw)) {
- // failed to get gateway
- Utils.logDebug("Failed to get gateway");
- return false;
- }
-
- if (!RootCommand.executeRootCmd("ndc network create 1")) {
- Utils.logDebug("Failed to wpa_cli network create 1 ");
- return false;
- }
-
- if (!RootCommand.executeRootCmd("ndc network interface add 1 "
- + WpaSupplicant.INTERFACE_NAME)) {
- Utils.logDebug("Failed to add interface.");
- return false;
- }
-
- // set route to gateway for all traffic
- if (!RootCommand.executeRootCmd("ndc network route add 1 " + WpaSupplicant.INTERFACE_NAME
- + " 0.0.0.0/0 " + gw)) {
- Utils.logDebug("Failed to add route to gateway");
- return false;
- }
-
- if (!setDns(Commons.getDnss(), gw)) {
- Utils.logDebug("Failed to set DNS");
- return false;
- }
-
- // use network
- if (!RootCommand.executeRootCmd("ndc network default set 1")) {
- Utils.logDebug("Failed to set network as default");
- return false;
- }
-
- return true;
-
- }
-
- private boolean destroyNetwork() {
- // needs root (tested)
- return RootCommand.executeRootCmd("ndc network destroy 1");
- }
-
- private boolean setDns(String[] dnss, String gateway) {
-
- if (dnss == null || dnss.length == 0) {
- // the DNS setting has been left blank
- // try to use the gateway as dns
-
- if (gateway == null || gateway.length() == 0) {
- // no possible DNS.
- return false;
- }
-
- dnss = new String[] { gateway, gateway };
-
- }
-
- if (!InetAddressUtils.isIPv4Address(dnss[0])) {
- // invalid ip can't proceed.
- return false;
- }
-
- String cmd = "ndc resolver setnetdns 1 " + dnss[0];
-
- if (dnss.length > 1 && InetAddressUtils.isIPv4Address(dnss[1])) {
- cmd += " " + dnss[1];
- } else {
- cmd += " " + dnss[0];
- }
-
- return RootCommand.executeRootCmd(cmd);
- }
-
- private String getGateWayTimeout(int timeoutMillis) {
-
- String gw = getGateway();
- if (gw != null && !gw.trim().isEmpty()) {
- return gw;
- }
-
- Utils.logDebug("Gateway not available.. going into wait loop..");
-
- // gateway not (yet) available
- // waits for a maximum of timeoutMillis milliseconds
- // to let the interface being registered.
- int msWaited = 0;
- while (msWaited < timeoutMillis) {
-
- try {
- Thread.sleep(100);
- } catch (Exception e) {
- return null;
- }
- msWaited += 100;
-
- gw = getGateway();
- if (gw != null && !gw.trim().isEmpty()) {
- Utils.logDebug("Gateway found after wait loop!");
- return gw;
- }
- }
-
- // unable to get gateway
- Utils.logError("Gateway not found after wait loop.");
- return null;
-
- }
-
- private String getGateway() {
-
- try {
-
- // doesn't need root (tested)
- ShellCommand cmd = new ShellCommand("ip route show dev " + WpaSupplicant.INTERFACE_NAME);
- if (cmd.execute() != 0) {
- Utils.logDebug("command failed show route");
- return null;
- }
-
- // read command output
- String out = cmd.getOutput();
- if (out == null) {
- return null;
- }
-
- String[] lines = out.split("\n");
-
- for (String l : lines) {
-
- if (l.contains("default via")) {
-
- String[] f = l.split(" ");
- if (f.length > 2) {
-
- // found route's address:
- return f[2];
-
- }
- }
- }
-
- return null;
-
- } catch (Exception e) {
- Utils.logError("Error while trying to fetch route", e);
- return null;
- }
-
- }
-
- private boolean clearAddrs() {
- // needs root (tested)
- return RootCommand.executeRootCmd("ndc interface clearaddrs "
- + WpaSupplicant.INTERFACE_NAME);
- }
-
-} \ No newline at end of file
diff --git a/app/src/fil/libre/repwifiapp/helpers/Logger.java b/app/src/fil/libre/repwifiapp/helpers/Logger.java
new file mode 100644
index 0000000..2641571
--- /dev/null
+++ b/app/src/fil/libre/repwifiapp/helpers/Logger.java
@@ -0,0 +1,160 @@
+//
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+//
+// This file is part of RepWifiApp.
+//
+// RepWifiApp 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.
+//
+// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
+//
+// ********************************************************************
+
+package fil.libre.repwifiapp.helpers;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.os.Environment;
+import android.util.Log;
+import fil.libre.repwifiapp.Utils;
+import fil.libre.repwifiapp.service.ConnectionManagementService;
+import java.io.File;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Locale;
+
+public abstract class Logger {
+
+ public static String APP_NAME = "RepWifi";
+
+ private static int logPriority = 3;
+
+ public static void logError(String msg, Exception e) {
+ Log.e(APP_NAME, msg, e);
+ }
+
+ public static void logError(String msg) {
+ Log.e(APP_NAME, msg);
+ }
+
+ public static void logDebug(String msg) {
+ logDebug(msg, 0);
+ }
+
+ public static void logDebug(String msg, int level) {
+
+ if (level < logPriority) {
+ return;
+ }
+
+ Log.d(APP_NAME, msg);
+ }
+
+
+ @SuppressLint("SimpleDateFormat")
+ public static String getLogDumpFile() {
+
+ File f = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
+ if (f == null || !f.exists()) {
+ return null;
+ }
+
+
+ String basefolder;
+ try {
+ basefolder = f.getCanonicalPath();
+ } catch (Exception e) {
+ logError("Exception while resolving canonical path for log dump file.", e);
+ return null;
+ }
+
+ DateFormat dateFormat = new SimpleDateFormat("yyyyMMddhhmmss", Locale.getDefault());
+ String ts = dateFormat.format(Calendar.getInstance().getTime());
+ return basefolder + "/repwifi_log_dump." + ts + ".log";
+ }
+
+ public static boolean dumpLogcatToFile(Context appcontext){
+ return dumpLogcatToFile(getLogDumpFile(),appcontext);
+ }
+
+ public static void setLogPriority(int priority){
+
+ if (priority < 0){
+ priority = 0;
+ }
+
+ logPriority = priority;
+
+ }
+
+ public static boolean dumpLogcatToFile(String filePath, Context appcontext) {
+
+ if (filePath == null) {
+ return false;
+ }
+
+ try {
+
+ String cmd1 = "logcat -d | grep " + APP_NAME + ">" + filePath;
+ String cmd2 = "logcat -d | grep -A10 -B10 " + appcontext.getPackageName()
+ + ">>" + filePath;
+ String cmd3 = "logcat -d | grep " + ConnectionManagementService.LOG_TAG_NETWORKAGENT
+ + ">>" + filePath;
+ String cmd4 = "logcat -d | grep " + ConnectionManagementService.LOG_TAG_SERVICE + ">>"
+ + filePath;
+
+ String SEP_LOG = "\n\n---------- [REPWIFI_LOG_SEPARATOR] ----------\n\n";
+
+ RootCommand c1 = new RootCommand(cmd1);
+ RootCommand c2 = new RootCommand(cmd2);
+ RootCommand c3 = new RootCommand(cmd3);
+ RootCommand c4 = new RootCommand(cmd4);
+
+ if (c1.execute() != 0) {
+ return false;
+ }
+
+ if (!Utils.writeFile(filePath, SEP_LOG, false)) {
+ return false;
+ }
+
+ if (c2.execute() != 0) {
+ return false;
+ }
+
+ if (!Utils.writeFile(filePath, SEP_LOG, false)) {
+ return false;
+ }
+
+ if (c3.execute() != 0) {
+ return false;
+ }
+
+ if (c4.execute() != 0) {
+ return false;
+ }
+
+ RootCommand.executeRootCmd("chmod 666 " + filePath);
+
+ return true;
+
+ } catch (Exception e) {
+ logError("Exception during log dump.", e);
+ return false;
+ }
+
+ }
+
+
+
+
+}
diff --git a/app/src/fil/libre/repwifiapp/helpers/OpenVpnManager.java b/app/src/fil/libre/repwifiapp/helpers/OpenVpnManager.java
deleted file mode 100644
index 5de2501..0000000
--- a/app/src/fil/libre/repwifiapp/helpers/OpenVpnManager.java
+++ /dev/null
@@ -1,237 +0,0 @@
-//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
-//
-// This file is part of RepWifiApp.
-// This file is based upon the example file included in
-// de.blinkt.openvpn package by Arne Schwabe.
-//
-// RepWifiApp 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.
-//
-// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
-//
-// ********************************************************************
-
-package fil.libre.repwifiapp.helpers;
-
-import android.app.Activity;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.IBinder;
-import android.os.RemoteException;
-import java.util.ArrayList;
-import java.util.List;
-import de.blinkt.openvpn.api.APIVpnProfile;
-import de.blinkt.openvpn.api.IOpenVPNAPIService;
-
-public class OpenVpnManager {
-
- public static final String SERVICE_PACKAGE_NAME = "de.blinkt.openvpn";
- public static final String APP_COMMON_NAME = "OpenVPN for Android";
- public static final String PLACEHOLDER_APPNAME = "[VPN_EXT_APP]";
-
- private static boolean VpnIsConnected;
- private static OpenVpnManager _currentInstance;
-
- protected IOpenVPNAPIService _vpnSvc;
-
- private Activity _caller;
- private ServiceConnection _svcConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- _vpnSvc = IOpenVPNAPIService.Stub.asInterface(service);
- }
-
- public void onServiceDisconnected(ComponentName className) {
- _vpnSvc = null;
- }
- };
-
- private OpenVpnManager(Activity c) throws Exception {
- this._caller = c;
- bindService();
- }
-
- private void bindService() throws Exception {
-
- Intent intentGetService = new Intent(IOpenVPNAPIService.class.getName());
- intentGetService.setPackage(SERVICE_PACKAGE_NAME);
- if (!_caller.bindService(intentGetService, _svcConnection, Context.BIND_AUTO_CREATE)) {
- throw new Exception("FAILED to bind to OpenVPN service!");
- }
-
- }
-
- private void unbindService() {
- _caller.unbindService(_svcConnection);
- }
-
- private Intent askApiPermissionsGetIntentInternal() throws RemoteException {
- return _vpnSvc.prepare(_caller.getPackageName());
- }
-
- private boolean startVpnInternal(String profileUuid) {
-
- if (profileUuid == null) {
- Utils.logError("Invoked startVpn with null uuid");
- return false;
- }
-
- if (_vpnSvc == null) {
- Utils.logError("Invoked startVpn but inner service is null.");
- return false;
- }
-
- try {
-
- _vpnSvc.startProfile(profileUuid);
- VpnIsConnected = true;
- return true;
-
- } catch (RemoteException e) {
- Utils.logError("Exception while starting vpn.", e);
- return false;
- }
- }
-
- private boolean disconnectInternal() {
-
- if (_vpnSvc == null) {
- Utils.logDebug("Attempted to disconnect from VPN, but inner service is null");
- VpnIsConnected = false;
- return true;
- }
-
- try {
- _vpnSvc.disconnect();
- VpnIsConnected = false;
- return true;
- } catch (RemoteException e) {
- Utils.logError("Exception while disconnecting from vpn.", e);
- return false;
- }
- }
-
- private List<String> getExistingProfilesInternal(){
-
- try {
- List<APIVpnProfile> list = _vpnSvc.getProfiles();
-
- List<String> ret = new ArrayList<String>();
- for (APIVpnProfile vp : list) {
- ret.add(vp.mName);
- }
-
- return ret;
-
- } catch (RemoteException e) {
- Utils.logError("Exception while retrieving profiles from vpn service.", e);
- return null;
- }
-
- }
-
- private String getUuidFromNameInternal(String profileName) {
-
- if (_vpnSvc == null) {
- Utils.logError("Called getUuidFromName but inner service is null!");
- return null;
- }
-
- try {
- List<APIVpnProfile> list = _vpnSvc.getProfiles();
-
- for (APIVpnProfile vp : list) {
- if (vp.mName.equals(profileName)) {
- return vp.mUUID;
- }
- }
-
- return null;
-
- } catch (RemoteException e) {
- Utils.logError("Exception while retrieving profiles from vpn service.", e);
- return null;
- }
- }
-
- public void close() {
- if (_vpnSvc != null) {
- unbindService();
-
- }
-
- }
-
- public static void initialize(Activity caller){
-
- if (_currentInstance != null){
- return;
- }
-
- try {
- _currentInstance = new OpenVpnManager(caller);
- } catch (Exception e) {
- Utils.logError("Exception while initializing vpn manager.",e);
- }
- }
-
- public static boolean isExternalAppInstalled(Activity caller) {
-
- try {
-
- ApplicationInfo i;
- i = caller.getPackageManager().getApplicationInfo(SERVICE_PACKAGE_NAME, 0);
- return (i != null);
-
- } catch (NameNotFoundException e) {
- return false;
- }
-
- }
-
- public static boolean startVpn(String profileUuid){
- if (_currentInstance == null){
- return false;
- }
- return _currentInstance.startVpnInternal(profileUuid);
- }
-
- public static boolean disconnect(){
- if (_currentInstance == null){
- return false;
- }
- return _currentInstance.disconnectInternal();
- }
-
- public static boolean isVpnConnected(){
- return VpnIsConnected;
- }
-
- public static String getUuidFromName(String profileName){
- if (_currentInstance == null){
- return null;
- }
- return _currentInstance.getUuidFromNameInternal(profileName);
- }
-
- public static List<String> getExistingProfiles(){
- return _currentInstance.getExistingProfilesInternal();
- }
-
- public static Intent askApiPermissionsGetIntent() throws RemoteException{
- return _currentInstance.askApiPermissionsGetIntentInternal();
- }
-
-}
diff --git a/app/src/fil/libre/repwifiapp/helpers/RootCommand.java b/app/src/fil/libre/repwifiapp/helpers/RootCommand.java
index bd859c8..61dfa2d 100644
--- a/app/src/fil/libre/repwifiapp/helpers/RootCommand.java
+++ b/app/src/fil/libre/repwifiapp/helpers/RootCommand.java
@@ -1,5 +1,5 @@
//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
//
// This file is part of RepWifiApp.
//
@@ -20,40 +20,61 @@
package fil.libre.repwifiapp.helpers;
+import fil.libre.repwifiapp.Commons;
+import fil.libre.repwifiapp.Utils;
import java.io.DataOutputStream;
import java.io.InputStream;
+import java.util.concurrent.TimeoutException;
public class RootCommand extends ShellCommand {
- // protected static final String CMD_WRAPPING = "export TEMPOUT=\"$(%s)\";echo \"$TEMPOUT\";exit $?";
- protected static final String CMD_WRAPPING = "TEMPOUT=\"$(%s)\";ec=$?;echo \"$TEMPOUT\";exit $ec";
+ protected static final String CMD_WRAPPING = "TEMPOUT=\"$(%s)\";ec=$?;echo \"$TEMPOUT\";echo $ec > \"%s\";exit $ec";
+ //protected static final String CMD_WRAPPING = "TEMPOUT=\"$(%s)\";ec=$?;echo \"$TEMPOUT\";exit $ec";
public RootCommand(String commandText) {
super(commandText);
this._cmdTxt = commandText;
}
+
+ public static boolean executeRootCmd(String cmd){
+ // won't return
+ try {
+ return executeRootCmd(cmd, -1);
+ } catch (TimeoutException e){
+ // will never throw timeout exception as it won't use asynchronous wait.
+ return false;
+ }
+ }
+
+ public static boolean executeRootCmd(String cmd, long timeoutMillis) throws TimeoutException{
- public static boolean executeRootCmd(String cmd) {
-
try {
RootCommand c = new RootCommand(cmd);
- if (c.execute() == 0) {
+ if (c.execute(timeoutMillis) == 0) {
return true;
} else {
return false;
}
- } catch (Exception e) {
- Utils.logError("Error executing \"" + cmd + "\"", e);
+ }
+ catch (TimeoutException te){
+ throw te;
+ }
+ catch (Exception e) {
+ Logger.logError("Error executing \"" + cmd + "\"", e);
return false;
}
}
@Override
public int execute() throws Exception {
-
+ return execute(-1);
+ }
+
+ public int execute(long timeoutMillis) throws Exception{
+
if (this._cmdTxt == null) {
return EXITCODE_INVALID_INPUT;
}
@@ -64,9 +85,9 @@ public class RootCommand extends ShellCommand {
InputStream os = su.getInputStream();
InputStream es = su.getErrorStream();
- Utils.logDebug("SU:EXEC: " + this._cmdTxt);
+ Logger.logDebug("SU:EXEC: " + this._cmdTxt);
- String wrappedCmd = String.format(CMD_WRAPPING, this._cmdTxt);
+ String wrappedCmd = String.format(CMD_WRAPPING, this._cmdTxt, Commons.getExitCodeTempFile());
stdin.writeBytes(wrappedCmd + "\n");
stdin.flush();
@@ -76,7 +97,16 @@ public class RootCommand extends ShellCommand {
sb.append(getStringFromStream(es));
sb.append(getStringFromStream(os));
- int res = su.waitFor();
+ int res;
+
+ if (timeoutMillis <= MIN_TIMEOUT_MILLIS){
+ res = su.waitFor();
+
+ } else {
+ Logger.logDebug("Executing command with " + timeoutMillis + "ms timeout.");
+ ProcessTimeout w = new ProcessTimeout();
+ res = w.waitFor(su, timeoutMillis);
+ }
// re-read the output, in case it was empty when first tried
sb.append(getStringFromStream(es));
@@ -84,10 +114,16 @@ public class RootCommand extends ShellCommand {
this._cmdOut = sb.toString();
- Utils.logDebug("OUT: " + getOutput());
+ Logger.logDebug("OUT: " + getOutput());
+ if (res == 0){
+ // could be su's own exit code hiding the original one:
+ res = readLastExitCodeFromFile();
+ }
+
+ Logger.logDebug("ExitCode: " + res);
return res;
-
+
}
public int testRootAccess() throws Exception {
@@ -96,16 +132,27 @@ public class RootCommand extends ShellCommand {
DataOutputStream stdin = new DataOutputStream(su.getOutputStream());
- Utils.logDebug("Testing root access: executing simple \"su\"");
+ Logger.logDebug("Testing root access: executing simple \"su\"");
stdin.writeBytes("exit\n");
stdin.flush();
int res = su.waitFor();
- Utils.logDebug("Simple \"su\" exitcode: " + res);
+ Logger.logDebug("Simple \"su\" exitcode: " + res);
return res;
}
-
+
+ private int readLastExitCodeFromFile(){
+
+ String strec = Utils.readFile(Commons.getExitCodeTempFile()).trim();
+ try{
+ return Integer.parseInt(strec);
+ }catch(NumberFormatException e){
+ Logger.logError("NumberFormatException while parsing contents of ExitCodeTempFile: " + strec);
+ return EXITCODE_PARSING_ERROR;
+ }
+ }
+
}
diff --git a/app/src/fil/libre/repwifiapp/helpers/ShellCommand.java b/app/src/fil/libre/repwifiapp/helpers/ShellCommand.java
index f232ac7..d6b7553 100644
--- a/app/src/fil/libre/repwifiapp/helpers/ShellCommand.java
+++ b/app/src/fil/libre/repwifiapp/helpers/ShellCommand.java
@@ -1,11 +1,34 @@
+//
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+//
+// This file is part of RepWifiApp.
+//
+// RepWifiApp 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.
+//
+// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
+//
+// ********************************************************************
+
package fil.libre.repwifiapp.helpers;
import java.io.IOException;
import java.io.InputStream;
+import java.util.concurrent.TimeoutException;
public class ShellCommand {
public static final int EXITCODE_INVALID_INPUT = -9;
+ public static final int EXITCODE_PARSING_ERROR = 999;
+ public static final int MIN_TIMEOUT_MILLIS = 100;
protected String _cmdOut = "";
protected String _cmdTxt = "";
@@ -20,7 +43,7 @@ public class ShellCommand {
return EXITCODE_INVALID_INPUT;
}
- Utils.logDebug("EXEC: " + this._cmdTxt);
+ Logger.logDebug("EXEC: " + this._cmdTxt);
Process cmd = Runtime.getRuntime().exec(this._cmdTxt);
@@ -41,32 +64,16 @@ public class ShellCommand {
this._cmdOut = sb.toString();
- Utils.logDebug("EXITCODE: " + res);
- Utils.logDebug("OUT: " + getOutput());
+ Logger.logDebug("EXITCODE: " + res);
+ Logger.logDebug("OUT: " + getOutput());
return res;
}
protected String getStringFromStream(InputStream s) throws IOException {
-
java.util.Scanner sc = new java.util.Scanner(s,"UTF-8").useDelimiter("\\A");
return sc.hasNext() ? sc.next() : "";
-
- /*
-
- StringBuilder sb = new StringBuilder();
- while ((s.available() > 0)) {
- int b = s.read();
- if (b >= 0) {
- sb.append((char) b);
- } else {
- break;
- }
- }
-
- return sb.toString();*/
-
}
public String getOutput() {
@@ -74,5 +81,67 @@ public class ShellCommand {
return this._cmdOut;
}
+
+ protected class ProcessTimeout{
+
+ private Process _proc;
+ private int _exitCode;
+ private boolean _hasExited = false;
+ private InterruptedException e = null;
+
+ public int waitFor(Process p, long timeoutMillis) throws InterruptedException, TimeoutException{
+
+ // waits for a maximum of timeoutMillis milliseconds
+ // for the process to exit.
+ int msWaited = 0;
+ this._proc = p;
+ startWaitingForProcess();
+ while (msWaited < timeoutMillis) {
+
+ if (e != null){
+ // exception from the underlying thread;
+ throw e;
+ }
+
+ if (! _hasExited){
+ Thread.sleep(MIN_TIMEOUT_MILLIS);
+ msWaited += MIN_TIMEOUT_MILLIS;
+
+ } else {
+ return _exitCode;
+ }
+ }
+
+ throw new TimeoutException("Timeout elapsed while waiting for inner process to finish its execution.");
+
+ }
+
+ private void startWaitingForProcess(){
+
+ new Thread(new Runnable() {
+ public void run(){
+ try {
+ int exitCode = _proc.waitFor();
+ setExitCode(exitCode);
+ } catch (InterruptedException e) {
+ setException(e);
+ }
+ }
+ }).start();
+
+ }
+
+ private void setExitCode(int ec){
+ _hasExited = true;
+ _exitCode = ec;
+ }
+
+ private void setException(InterruptedException e){
+ this.e = e;
+ }
+
+ }
+
}
+
diff --git a/app/src/fil/libre/repwifiapp/helpers/AccessPointInfo.java b/app/src/fil/libre/repwifiapp/network/AccessPointInfo.java
index eee569d..862a852 100644
--- a/app/src/fil/libre/repwifiapp/helpers/AccessPointInfo.java
+++ b/app/src/fil/libre/repwifiapp/network/AccessPointInfo.java
@@ -1,5 +1,5 @@
//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
//
// This file is part of RepWifiApp.
//
@@ -18,19 +18,23 @@
//
// ********************************************************************
-package fil.libre.repwifiapp.helpers;
+package fil.libre.repwifiapp.network;
+import android.os.Parcel;
+import android.os.Parcelable;
+import fil.libre.repwifiapp.Utils;
+import fil.libre.repwifiapp.helpers.Logger;
import org.json.JSONException;
import org.json.JSONObject;
-import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
-public class AccessPointInfo implements Serializable {
+public class AccessPointInfo implements Parcelable {
- private static final long serialVersionUID = 2L;
+ public static final String DUMMY_VPN_PROFILE = "--------";
+
private static final int MAX_SSID_LENGTH = 32;
protected static final String SCAN_FILE_HDR = "bssid / frequency / signal level / flags / ssid";
@@ -117,6 +121,11 @@ public class AccessPointInfo implements Serializable {
}
public void setVpnProfileName(String profileName){
+
+ if (profileName != null && profileName.equals(DUMMY_VPN_PROFILE)){
+ profileName = "";
+ }
+
_vpnProfileName = profileName;
}
@@ -227,7 +236,7 @@ public class AccessPointInfo implements Serializable {
return info;
} catch (Exception e) {
- Utils.logError("Error while parsing line: " + line, e);
+ Logger.logError("Error while parsing line: " + line, e);
return null;
}
@@ -241,7 +250,7 @@ public class AccessPointInfo implements Serializable {
return null;
}
- Utils.logDebug("AccesPointInfo trying to parse file scan content:\n"
+ Logger.logDebug("AccesPointInfo trying to parse file scan content:\n"
+ scanResultContent);
String[] lines = scanResultContent.split("\n");
@@ -265,7 +274,7 @@ public class AccessPointInfo implements Serializable {
// try to parse line into network info
AccessPointInfo info = AccessPointInfo.parseLine(l);
if (info == null) {
- Utils.logError("Failed to parse line into AccessPointInfo: " + l);
+ Logger.logError("Failed to parse line into AccessPointInfo: " + l);
continue;
}
@@ -280,10 +289,23 @@ public class AccessPointInfo implements Serializable {
return a;
} catch (Exception e) {
- Utils.logError("Error while parsing scan results in class AccessPointInfo", e);
+ Logger.logError("Error while parsing scan results in class AccessPointInfo", e);
+ return null;
+ }
+
+ }
+
+ public static AccessPointInfo[] fromParcellableArray(Parcelable[] p){
+ if (p == null) {
return null;
}
+ AccessPointInfo[] infos = new AccessPointInfo[p.length];
+ for (int j = 0; j < infos.length; j++) {
+ infos[j] = (AccessPointInfo) p[j];
+ }
+
+ return infos;
}
public JSONObject toJson(){
@@ -311,7 +333,7 @@ public class AccessPointInfo implements Serializable {
return j;
} catch (JSONException e) {
- Utils.logError("Exception while converting AccessPointInfo to JSON.", e);
+ Logger.logError("Exception while converting AccessPointInfo to JSON.", e);
return null;
}
@@ -346,7 +368,7 @@ public class AccessPointInfo implements Serializable {
return info;
} catch (JSONException e) {
- Utils.logError("Exception while parsing json object to AccessPointInfo", e);
+ Logger.logError("Exception while parsing json object to AccessPointInfo", e);
return null;
}
@@ -367,4 +389,54 @@ public class AccessPointInfo implements Serializable {
}
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public AccessPointInfo(Parcel in){
+
+ this._ssid = in.readString();
+ this._bssid = in.readString();
+ this._auth = in.readString();
+ this._level = in.readString();
+ this._freq = in.readString();
+ this._password = in.readString();
+ this._vpnProfileName = in.readString();
+ this._isHidden = in.readInt() == 1 ? true : false;
+ this._lastTimeUsed = in.readLong();
+ this._dhcpsets = in.readParcelable(DhcpSettings.class.getClassLoader());
+
+ }
+
+ public static final Parcelable.Creator<AccessPointInfo> CREATOR = new Parcelable.Creator<AccessPointInfo>() {
+ public AccessPointInfo createFromParcel(Parcel in) {
+ return new AccessPointInfo(in);
+ }
+
+ @Override
+ public AccessPointInfo[] newArray(int size) {
+ return new AccessPointInfo[size];
+ }
+
+
+
+ };
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+
+ dest.writeString(_ssid);
+ dest.writeString(_bssid);
+ dest.writeString(_auth);
+ dest.writeString(_level);
+ dest.writeString(_freq);
+ dest.writeString(_password);
+ dest.writeString(_vpnProfileName);
+ dest.writeInt(_isHidden ? 1 : 0);
+ dest.writeLong(_lastTimeUsed);
+ dest.writeParcelable(_dhcpsets, flags);
+
+ }
+
}
diff --git a/app/src/fil/libre/repwifiapp/network/ConnectionResult.java b/app/src/fil/libre/repwifiapp/network/ConnectionResult.java
new file mode 100644
index 0000000..32ae771
--- /dev/null
+++ b/app/src/fil/libre/repwifiapp/network/ConnectionResult.java
@@ -0,0 +1,95 @@
+//
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+//
+// This file is part of RepWifiApp.
+//
+// RepWifiApp 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.
+//
+// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
+//
+// ********************************************************************
+
+package fil.libre.repwifiapp.network;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class ConnectionResult implements Parcelable {
+
+ public static final int CONN_OK = 0;
+ public static final int CONN_FAILED = 1;
+ public static final int CONN_TIMEOUT = 2;
+ // public static final int CONN_DNS_FAIL = 3; // Removed as dns is now set
+ // by the framework
+ public static final int CONN_GW_FAIL = 4;
+ public static final int CONN_ABORTED = 5;
+ public static final int CONN_NOTIFY_FAILED = 6;
+ public static final int NO_RESULT = -1;
+
+ private ConnectionStatus _status = null;
+ private int _result = NO_RESULT;
+
+ public ConnectionResult(int result) {
+ this._result = result;
+ this._status = null;
+ }
+
+ public ConnectionStatus getStatus() {
+ return _status;
+ }
+
+ public int getResult() {
+ return _result;
+ }
+
+ public void setStatus(ConnectionStatus status) {
+
+ // keep coherence between the result code and the status
+ if (status != null && status.isConnected() && this._result == CONN_OK) {
+ this._status = status;
+ } else if (this._result == CONN_OK) {
+ this._result = CONN_FAILED;
+ this._status = null;
+ } else {
+ this._status = null;
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public ConnectionResult(Parcel in) {
+ this._result = in.readInt();
+ this._status = in.readParcelable(ConnectionStatus.class.getClassLoader());
+ }
+
+ public static final Parcelable.Creator<ConnectionResult> CREATOR = new Parcelable.Creator<ConnectionResult>() {
+ public ConnectionResult createFromParcel(Parcel in) {
+ return new ConnectionResult(in);
+ }
+
+ @Override
+ public ConnectionResult[] newArray(int size) {
+ return new ConnectionResult[size];
+ }
+
+ };
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(this._result);
+ dest.writeParcelable(this._status, flags);
+ }
+
+}
diff --git a/app/src/fil/libre/repwifiapp/network/ConnectionStatus.java b/app/src/fil/libre/repwifiapp/network/ConnectionStatus.java
new file mode 100644
index 0000000..5fac0a1
--- /dev/null
+++ b/app/src/fil/libre/repwifiapp/network/ConnectionStatus.java
@@ -0,0 +1,298 @@
+//
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+//
+// This file is part of RepWifiApp.
+//
+// RepWifiApp 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.
+//
+// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
+//
+// ********************************************************************
+
+package fil.libre.repwifiapp.network;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import fil.libre.repwifiapp.Utils;
+import fil.libre.repwifiapp.helpers.Logger;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+public class ConnectionStatus implements Parcelable {
+
+ public static final String STATUS_CONNECTED = "COMPLETED";
+ public static final String STATUS_INACTIVE = "INACTIVE";
+ public static final String STATUS_DISCONNECTED = "DISCONNECTED";
+ public static final String STATUS_UNDEFINED = "UNDEFINED";
+
+ public String wpaStatus = null;
+ public String SSID = null;
+ public String BSSID = null;
+ public String IP = null;
+ public String gateway = null;
+ public String subnetMask = null;
+ public String hwAddress = null;
+ public String broadcastAddress = null;
+
+ private static final String F_SEP = "=";
+ private static final String KeyStatus = "wpa_state";
+ private static final String KeySSID = "ssid";
+ private static final String KeyBSSID = "bssid";
+ private static final String KeyIP = "ip_address";
+
+ private static final String IFCONFIG_BCAST = "Bcast";
+ private static final String IFCONFIG_MASK = "Mask";
+ private static final String IFCONFIG_HWADDR = "HWaddr";
+ private static final String IFCONFIG_KVALSEP = ":";
+ private static final String IFCONFIG_FSEP = " ";
+
+ public ConnectionStatus() {
+ }
+
+ public static ConnectionStatus parseWpaCliOutput(String wpaCliOutput) {
+
+ if (wpaCliOutput == null) {
+ return null;
+ }
+
+ if (wpaCliOutput.trim().length() == 0) {
+ return null;
+ }
+
+ String[] lines = wpaCliOutput.split("\n");
+
+ ConnectionStatus s = new ConnectionStatus();
+ for (String line : lines) {
+
+ if (line.trim().equals("")) {
+ continue;
+ }
+
+ String[] fields = line.split(F_SEP);
+ if (fields.length < 2) {
+ continue;
+ }
+
+ String key = fields[0];
+ String val = fields[1];
+
+ if (key.equals(KeyBSSID)) {
+ s.BSSID = val;
+ } else if (key.equals(KeySSID)) {
+ s.SSID = val;
+ } else if (key.equals(KeyStatus)) {
+ s.wpaStatus = val;
+ } else if (key.equals(KeyIP)) {
+ s.IP = val;
+ }
+
+ }
+
+ return s;
+
+ }
+
+ public static ConnectionStatus getDummyDisconnected() {
+ ConnectionStatus s = new ConnectionStatus();
+ s.wpaStatus = STATUS_DISCONNECTED;
+ return s;
+ }
+
+ public boolean parseIfconfigOutput(String ifconfigOutput) {
+
+ if (ifconfigOutput == null) {
+ return false;
+ }
+
+ String[] lines = ifconfigOutput.split("\n");
+ if (lines.length < 2) {
+ return false;
+ }
+
+ String[] fields1 = lines[0].split(IFCONFIG_FSEP);
+ String[] fields2 = lines[1].split(IFCONFIG_FSEP);
+
+ for (String f : fields1) {
+ // first line uses a single blank space as key-val separator
+ String[] splt = f.split(" ");
+ if (splt.length == 2 && splt[0].equals(IFCONFIG_HWADDR)) {
+ this.hwAddress = splt[1];
+ }
+ }
+
+ for (String f : fields2) {
+ String[] splt = f.split(IFCONFIG_KVALSEP);
+ if (splt.length == 2) {
+
+ String key = splt[0];
+ String val = splt[1];
+
+ if (key.equals(IFCONFIG_MASK)) {
+
+ this.subnetMask = val.trim();
+
+ } else if (key.equals(IFCONFIG_BCAST)) {
+
+ this.broadcastAddress = val;
+
+ } else if (key.equals(IFCONFIG_HWADDR)) {
+
+ this.hwAddress = val;
+
+ }
+
+ }
+ }
+
+ return true;
+
+ }
+
+ public boolean isConnected() {
+
+ if (this.wpaStatus == null) {
+ return false;
+ }
+
+ if (this.wpaStatus.equals(STATUS_CONNECTED)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /***
+ * @return Returns a string representation of the current IP address and
+ * subnet mask (e.g. "192.168.1.123/24"); returns null if either the
+ * address or the mask is null.
+ */
+ public String addressAndMaskToString() {
+
+ if (this.IP == null || this.subnetMask == null) {
+ return null;
+ }
+
+ int mask = Utils.netmaskStringToInt(this.subnetMask);
+ return this.IP + "/" + mask;
+
+ }
+
+ public InetAddress getInetAddress() {
+ if (this.IP == null) {
+ return null;
+ }
+
+ try {
+ return InetAddress.getByName(this.IP);
+ } catch (UnknownHostException e) {
+ Logger.logError("Exception while parsing InetAddress from string", e);
+ return null;
+ }
+
+ }
+
+ public int getSubnetMaskInt() {
+ return Utils.netmaskStringToInt(this.subnetMask);
+ }
+
+ public InetAddress getGatewayInetAddress() {
+
+ if (this.gateway == null) {
+ return null;
+ }
+
+ try {
+ return InetAddress.getByName(this.gateway);
+ } catch (Exception e) {
+ Logger.logError("Exception while parsing gateway's InetAddress from string.", e);
+ return null;
+ }
+
+ }
+
+ public AccessPointInfo getNetworkDetails() {
+ AccessPointInfo i = new AccessPointInfo(SSID, BSSID, "", "", "");
+ return NetworkManager.getSavedNetwork(i);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public ConnectionStatus(Parcel in) {
+
+ this.SSID = in.readString();
+ this.BSSID = in.readString();
+ this.IP = in.readString();
+ this.subnetMask = in.readString();
+ this.gateway = in.readString();
+ this.hwAddress = in.readString();
+ this.broadcastAddress = in.readString();
+ this.wpaStatus = in.readString();
+
+ }
+
+ public static final Parcelable.Creator<ConnectionStatus> CREATOR = new Parcelable.Creator<ConnectionStatus>() {
+ public ConnectionStatus createFromParcel(Parcel in) {
+ return new ConnectionStatus(in);
+ }
+
+ public ConnectionStatus[] newArray(int size) {
+ return new ConnectionStatus[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+
+ dest.writeString(this.SSID);
+ dest.writeString(this.BSSID);
+ dest.writeString(this.IP);
+ dest.writeString(this.subnetMask);
+ dest.writeString(this.gateway);
+ dest.writeString(this.hwAddress);
+ dest.writeString(this.broadcastAddress);
+ dest.writeString(this.wpaStatus);
+
+ }
+
+ public boolean equals(ConnectionStatus status) {
+
+ if (status == null) {
+ return false;
+ }
+
+ if (status.isConnected() != this.isConnected()) {
+ return false;
+ }
+
+ return fieldEquals(this.IP, status.IP) && fieldEquals(this.BSSID, status.BSSID)
+ && fieldEquals(this.SSID, status.SSID)
+ && fieldEquals(this.subnetMask, status.subnetMask)
+ && fieldEquals(this.gateway, status.gateway)
+ && fieldEquals(this.hwAddress, status.hwAddress);
+
+ }
+
+ private boolean fieldEquals(String myField, String extField) {
+ return myField == null ? extField == null : myField.equals(extField);
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "WPAsts: %s; \nIP: %s; \nMask: %s; \nGway: %s; \nBcast: %s; \nHWaddr: %s ",
+ wpaStatus, IP, subnetMask, gateway, broadcastAddress, hwAddress);
+ }
+
+}
diff --git a/app/src/fil/libre/repwifiapp/helpers/DhcpSettings.java b/app/src/fil/libre/repwifiapp/network/DhcpSettings.java
index c0587a3..65524d5 100644
--- a/app/src/fil/libre/repwifiapp/helpers/DhcpSettings.java
+++ b/app/src/fil/libre/repwifiapp/network/DhcpSettings.java
@@ -1,5 +1,5 @@
//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
//
// This file is part of RepWifiApp.
//
@@ -18,17 +18,19 @@
//
// ********************************************************************
-package fil.libre.repwifiapp.helpers;
+package fil.libre.repwifiapp.network;
+import android.nfc.FormatException;
+import android.os.Parcel;
+import android.os.Parcelable;
+import fil.libre.repwifiapp.Utils;
+import fil.libre.repwifiapp.helpers.Logger;
import org.apache.http.conn.util.InetAddressUtils;
import org.json.JSONException;
import org.json.JSONObject;
-import java.io.Serializable;
-import android.nfc.FormatException;
-public class DhcpSettings implements Serializable {
+public class DhcpSettings implements Parcelable {
- private static final long serialVersionUID = 1L;
public boolean useDhcp;
private String _staticIP;
private int _mask;
@@ -37,7 +39,7 @@ public class DhcpSettings implements Serializable {
private static final String JSONKEY_DHCP = "DHCP";
private static final String JSONKEY_STATIC_IP = "StaticIP";
private static final String JSONKEY_GW = "Gateway";
-
+
public DhcpSettings(boolean useDhcp, String staticIP, String subnetMask, String defaultGateway)
throws FormatException {
this(useDhcp, staticIP, Utils.netmaskStringToInt(subnetMask), defaultGateway);
@@ -62,12 +64,12 @@ public class DhcpSettings implements Serializable {
}
- private DhcpSettings(){
+ private DhcpSettings() {
// inner use
}
-
- public static DhcpSettings parseSavedSettings(String staticIPwithMask, String defaultGateway){
-
+
+ public static DhcpSettings parseSavedSettings(String staticIPwithMask, String defaultGateway) {
+
try {
String[] ipm = staticIPwithMask.split("/");
@@ -75,24 +77,24 @@ public class DhcpSettings implements Serializable {
int mask = Integer.parseInt(ipm[1]);
return new DhcpSettings(false, ip, mask, defaultGateway);
-
} catch (Exception e) {
- Utils.logError("Exception while parsing DhcpSettings for saved network. Reverting to dhcp.", e);
+ Logger.logError("Exception while parsing DhcpSettings for saved network. Reverting to dhcp.",
+ e);
return null;
}
-
+
}
-
- public static DhcpSettings getDefault(){
+
+ public static DhcpSettings getDefault() {
try {
return new DhcpSettings(true, null, 24, null);
} catch (FormatException e) {
- //no format exception can happen.
+ // no format exception can happen.
return null;
}
}
-
+
private boolean validateParams(String ip, String gateway, int mask) {
if (isValidAddress(ip) && isValidAddress(gateway) && isValidMask(mask)) {
@@ -102,26 +104,26 @@ public class DhcpSettings implements Serializable {
}
}
-
- public static boolean isValidAddress(String ipAddress){
+
+ public static boolean isValidAddress(String ipAddress) {
return InetAddressUtils.isIPv4Address(ipAddress);
}
-
- public static boolean isValidMaks(String mask){
+
+ public static boolean isValidMaks(String mask) {
int m = Utils.netmaskStringToInt(mask);
return isValidMask(m);
}
- public static boolean isValidMask(int mask){
- if (mask >= 8 && mask <= 32){
+ public static boolean isValidMask(int mask) {
+ if (mask >= 8 && mask <= 32) {
return true;
- }else{
+ } else {
return false;
}
}
-
+
public String getStaticIP() {
- if (_staticIP == null){
+ if (_staticIP == null) {
return "";
}
return _staticIP;
@@ -134,72 +136,107 @@ public class DhcpSettings implements Serializable {
public int getSubnetMaskInt() {
return _mask;
}
-
- public String getSubnetMaskString(){
+
+ public String getSubnetMaskString() {
String v = Utils.netmaskIntToString(_mask);
- if (v == null){
+ if (v == null) {
return "";
}
return v;
}
-
+
public String getDefaultGateway() {
- if (_defGw == null){
+ if (_defGw == null) {
return "";
}
return _defGw;
}
- public JSONObject toJson(){
-
+ public JSONObject toJson() {
+
JSONObject j = new JSONObject();
-
+
try {
j.put(JSONKEY_DHCP, useDhcp);
j.put(JSONKEY_GW, getDefaultGateway());
j.put(JSONKEY_STATIC_IP, getStaticIPwithMask());
-
+
return j;
-
+
} catch (JSONException e) {
- Utils.logError("Exception while converting DhcpSettings to JSON.", e);
+ Logger.logError("Exception while converting DhcpSettings to JSON.", e);
return null;
}
-
-
+
}
-
- public static DhcpSettings fromJsonObject(JSONObject json){
-
- if (json == null){
+
+ public static DhcpSettings fromJsonObject(JSONObject json) {
+
+ if (json == null) {
return null;
}
-
+
DhcpSettings sets = new DhcpSettings();
-
+
try {
-
+
sets.useDhcp = json.getBoolean(JSONKEY_DHCP);
-
- if (json.has(JSONKEY_GW) && ! json.isNull(JSONKEY_GW)){
+
+ if (json.has(JSONKEY_GW) && !json.isNull(JSONKEY_GW)) {
sets._defGw = json.getString(JSONKEY_GW);
}
-
- if (json.has(JSONKEY_STATIC_IP) && !json.isNull(JSONKEY_STATIC_IP)){
-
+
+ if (json.has(JSONKEY_STATIC_IP) && !json.isNull(JSONKEY_STATIC_IP)) {
+
String[] splt = json.getString(JSONKEY_STATIC_IP).split("/");
sets._staticIP = splt[0];
sets._mask = Integer.parseInt(splt[1]);
-
+
}
-
+
return sets;
-
+
} catch (Exception e) {
- Utils.logError("Exception while parsing json object to DhcpSettings", e);
+ Logger.logError("Exception while parsing json object to DhcpSettings", e);
return null;
}
-
+
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
}
-
+
+ public DhcpSettings(Parcel in) {
+
+ this.useDhcp = in.readInt() == 1 ? true : false;
+ this._staticIP = in.readString();
+ this._mask = in.readInt();
+ this._defGw = in.readString();
+
+ }
+
+ public static final Parcelable.Creator<DhcpSettings> CREATOR = new Parcelable.Creator<DhcpSettings>() {
+ public DhcpSettings createFromParcel(Parcel in) {
+ return new DhcpSettings(in);
+ }
+
+ @Override
+ public DhcpSettings[] newArray(int size) {
+ return new DhcpSettings[size];
+ }
+
+ };
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+
+ dest.writeInt(useDhcp ? 1 : 0);
+ dest.writeString(_staticIP);
+ dest.writeInt(_mask);
+ dest.writeString(_defGw);
+
+ }
+
}
diff --git a/app/src/fil/libre/repwifiapp/helpers/Engine.java b/app/src/fil/libre/repwifiapp/network/Engine.java
index 225d993..34de124 100644
--- a/app/src/fil/libre/repwifiapp/helpers/Engine.java
+++ b/app/src/fil/libre/repwifiapp/network/Engine.java
@@ -1,5 +1,5 @@
//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
//
// This file is part of RepWifiApp.
//
@@ -18,50 +18,87 @@
//
// ********************************************************************
-package fil.libre.repwifiapp.helpers;
+package fil.libre.repwifiapp.network;
+import fil.libre.repwifiapp.Commons;
+import fil.libre.repwifiapp.helpers.Logger;
+import fil.libre.repwifiapp.helpers.RootCommand;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
public abstract class Engine implements IEngine {
+
+ public static final String PIDFILE_DHCPCD = "/data/misc/dhcp/dhcpcd-wlan0.pid";
+
@Override
public AccessPointInfo[] getAvailableNetworks() {
- Utils.logDebug("getAvailableNetworks():");
+ Logger.logDebug("getAvailableNetworks():");
if (!WpaSupplicant.start()) {
- Utils.logError("Failed starting wpa_supplicant");
+ Logger.logError("Failed starting wpa_supplicant");
return null;
}
if (!WpaCli.scanNetworks()) {
- Utils.logError("failed scanning networks");
+ Logger.logError("failed scanning networks");
return null;
}
String scanRes = WpaCli.getScanResults();
if (scanRes == null) {
- Utils.logError("failed getting scan results");
+ Logger.logError("failed getting scan results");
return null;
}
AccessPointInfo[] a = AccessPointInfo.parseScanResult(scanRes);
if (a == null) {
- Utils.logError("Unable to parse scan file into AccessPointInfo array");
+ Logger.logError("Unable to parse scan file into AccessPointInfo array");
return a;
}
- Utils.logDebug("# of APs found: " + a.length);
+ Logger.logDebug("# of APs found: " + a.length);
return a;
}
+
+ public ConnectionStatus getConnectionStatus() {
+
+ ConnectionStatus s = WpaCli.getConnectionStatus();
+ if (s == null){
+ return null;
+ }
+
+ String ifcfg = getIfconfigString();
+ s.parseIfconfigOutput(ifcfg);
+ return s;
+ }
+
+ private String getIfconfigString(){
+
+ // needs root for accessing encapsulation and hardware address (tested).
+ RootCommand cmd = new RootCommand("ifconfig " + WpaSupplicant.INTERFACE_NAME);
+
+ try {
+ if (cmd.execute() != 0){
+ Logger.logError("FAILED to run ifconfig to obtain interface info.");
+ return null;
+ }
+ } catch (Exception e) {
+ Logger.logError("Exception while running ifconfig.", e);
+ return null;
+ }
+
+ return cmd.getOutput();
+
+ }
@Override
- public abstract boolean connect(AccessPointInfo info);
+ public abstract int connect(AccessPointInfo info);
@Override
public boolean disconnect() {
@@ -70,16 +107,7 @@ public abstract class Engine implements IEngine {
}
- /***
- * returns null if unable to determine connection status for any reason.
- */
- @Override
- public ConnectionStatus getConnectionStatus() {
- return WpaCli.getConnectionStatus();
- }
-
- @Override
- public boolean isInterfaceAvailable(String ifaceName) throws SocketException {
+ public static boolean isInterfaceAvailable(String ifaceName) throws SocketException {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
@@ -93,35 +121,42 @@ public abstract class Engine implements IEngine {
}
- public boolean runDhcpcd() {
+ public int runDhcpcd() throws Exception{
// needs root
// option -w avoids dhcpcd forking to background,
// in order to keep control over its exit code, and be able to wait for it.
// option -A avoids ARP IP checking, we use it to save some seconds in the connection process.
- return RootCommand.executeRootCmd("dhcpcd -w -A " + WpaSupplicant.INTERFACE_NAME);
-
+ // option -t <timeout> sets time out for obtaining a lease.
+ String cmdText = "dhcpcd -w -A -t " + Commons.WAIT_FOR_DHCPCD + " " + WpaSupplicant.INTERFACE_NAME;
+ RootCommand su = new RootCommand(cmdText);
+ return su.execute();
}
- public boolean runDhcpcd(DhcpSettings dhcpConfig) {
+ public int runDhcpcd(DhcpSettings dhcpConfig) throws Exception{
if (dhcpConfig == null || dhcpConfig.useDhcp){
- Utils.logDebug("running dhchpc without dhcp settings, reverting to dhcp");
+ Logger.logDebug("running dhchpc without dhcp settings, reverting to dhcp");
return runDhcpcd();
}
- Utils.logDebug("Running dhcpcd with custom ip settings");
- String cmdMask = "dhcpcd -w -A -S ip_address=%s -S routers=%s %s";
+ Logger.logDebug("Running dhcpcd with custom ip settings");
+ String cmdMask = "dhcpcd -w -A -S ip_address=%s -S routers=%s -t %d %s";
String cmd = String.format(cmdMask,
dhcpConfig.getStaticIPwithMask(),
dhcpConfig.getDefaultGateway(),
+ Commons.WAIT_FOR_DHCPCD,
WpaSupplicant.INTERFACE_NAME);
- return RootCommand.executeRootCmd(cmd);
+ RootCommand su = new RootCommand(cmd);
+ return su.execute();
}
public static boolean killDhcpcd(){
- return RootCommand.executeRootCmd("killall -SIGINT dhcpcd");
+ if (! RootCommand.executeRootCmd("killall -SIGINT dhcpcd")){
+ return false;
+ }
+ return RootCommand.executeRootCmd("rm " + PIDFILE_DHCPCD);
}
public boolean interfaceUp() {
@@ -129,4 +164,5 @@ public abstract class Engine implements IEngine {
return RootCommand.executeRootCmd("ifconfig " + WpaSupplicant.INTERFACE_NAME + " up");
}
+
}
diff --git a/app/src/fil/libre/repwifiapp/network/Engine6p0.java b/app/src/fil/libre/repwifiapp/network/Engine6p0.java
new file mode 100644
index 0000000..c6475e2
--- /dev/null
+++ b/app/src/fil/libre/repwifiapp/network/Engine6p0.java
@@ -0,0 +1,315 @@
+//
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+//
+// This file is part of RepWifiApp.
+//
+// RepWifiApp 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.
+//
+// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
+//
+// ********************************************************************
+
+package fil.libre.repwifiapp.network;
+
+import fil.libre.repwifiapp.Commons;
+import fil.libre.repwifiapp.helpers.Logger;
+import fil.libre.repwifiapp.helpers.RootCommand;
+import fil.libre.repwifiapp.helpers.ShellCommand;
+import org.apache.http.conn.util.InetAddressUtils;
+
+public class Engine6p0 extends Engine {
+
+ private Object abortFlagSync = new Object();
+ private boolean abortConnectionSignaled = false;
+
+ @Override
+ public int connect(AccessPointInfo info) {
+
+ WpaSupplicant.kill();
+
+
+ if (info == null) {
+ Logger.logDebug("Engine's connect() received a null AccessPointInfo");
+ return ConnectionResult.CONN_FAILED;
+ }
+
+ if (!checkAbortSignal())
+ return ConnectionResult.CONN_ABORTED;
+
+ // clear any previously set network
+ if (!destroyNetwork()) {
+ Logger.logDebug("Unable to ndc destroy network");
+ return ConnectionResult.CONN_FAILED;
+ }
+
+ if (!checkAbortSignal())
+ return ConnectionResult.CONN_ABORTED;
+
+ // clear interface's ip
+ if (!clearAddrs()) {
+ Logger.logDebug("Unable to ndc clearaddrs");
+ return ConnectionResult.CONN_FAILED;
+ }
+
+ if (!checkAbortSignal())
+ return ConnectionResult.CONN_ABORTED;
+
+ // bring up interface
+ if (!interfaceUp()) {
+ Logger.logDebug("Unable to bring up interface.");
+ return ConnectionResult.CONN_FAILED;
+ }
+
+ if (!checkAbortSignal())
+ return ConnectionResult.CONN_ABORTED;
+
+ // launch wpa_supplicant specifying our custom configuration and the
+ // socket file
+ if (!WpaSupplicant.start()) {
+ Logger.logDebug("Unable to run wpa start");
+ return ConnectionResult.CONN_FAILED;
+ }
+
+ if (!checkAbortSignal())
+ return ConnectionResult.CONN_ABORTED;
+
+ // create new network and get network id
+ String netID = WpaCli.createNetworkGetId();
+ if (netID == null) {
+ Logger.logDebug("Unable to fetch network id");
+ return ConnectionResult.CONN_FAILED;
+ }
+
+ if (!checkAbortSignal())
+ return ConnectionResult.CONN_ABORTED;
+
+ // set network SSID
+ if (!WpaCli.setNetworkSSID(info.getSsid(), netID)) {
+ Logger.logDebug("Failed to set network ssid");
+ return ConnectionResult.CONN_FAILED;
+ }
+
+ if (!checkAbortSignal())
+ return ConnectionResult.CONN_ABORTED;
+
+ if (info.isHidden() && !WpaCli.setNetworkScanSSID(netID)) {
+ Logger.logDebug("Failed to set scan_ssid 1 for hidden network.");
+ return ConnectionResult.CONN_FAILED;
+ }
+
+ if (!checkAbortSignal())
+ return ConnectionResult.CONN_ABORTED;
+
+ // set password (if any)
+ if (!WpaCli.setNetworkPSK(info, netID)) {
+ Logger.logDebug("Failed to set network psk");
+ return ConnectionResult.CONN_FAILED;
+ }
+
+ if (!checkAbortSignal())
+ return ConnectionResult.CONN_ABORTED;
+
+ // select the network we just created
+ if (!WpaCli.selectNetwork(netID)) {
+ Logger.logDebug("Unable to wpa_cli select network");
+ return ConnectionResult.CONN_FAILED;
+ }
+
+ if (!checkAbortSignal())
+ return ConnectionResult.CONN_ABORTED;
+
+ // enable the newtork
+ if (!WpaCli.enableNetwork(netID)) {
+ Logger.logDebug("Unable to wpa_cli enable_newtork");
+ return ConnectionResult.CONN_FAILED;
+ }
+
+ if (!checkAbortSignal())
+ return ConnectionResult.CONN_ABORTED;
+
+ // kill previous dhchcd instances
+ if (!killDhcpcd()) {
+ Logger.logError("Unable to kill previous instances of dhcpcd");
+ }
+
+ if (!checkAbortSignal())
+ return ConnectionResult.CONN_ABORTED;
+
+ // get DHCP
+ Logger.logDebug("Attempt to run dhcpcd..");
+ try {
+
+ int retcode = runDhcpcd(info.getDhcpConfiguration());
+ if (retcode == 1) {
+ Logger.logDebug("Dhcpcd exited on timeout exceeded.");
+ return ConnectionResult.CONN_TIMEOUT;
+
+ } else if (retcode != 0) {
+ Logger.logDebug("Dhcpcd exited with unknown code: " + retcode);
+ return ConnectionResult.CONN_FAILED;
+
+ }
+ } catch (Exception e) {
+ Logger.logError("Exception while executing dhcpcd: ", e);
+ return ConnectionResult.CONN_FAILED;
+ }
+
+ if (!checkAbortSignal())
+ return ConnectionResult.CONN_ABORTED;
+
+ // try to fetch gateway
+ String gw = getGateWayTimeout(Commons.WAIT_FOR_GATEWAY);
+ if (gw == null || !InetAddressUtils.isIPv4Address(gw)) {
+ // failed to get gateway
+ Logger.logDebug("Failed to get gateway");
+ return ConnectionResult.CONN_GW_FAIL;
+ }
+
+ /*
+ * Calls to ndc to set gateway and dns were removed 2018-04-09
+ * Starting from 2018-04-20, RepWifi registers itself as a NetworkAgent.
+ * When we register, all the following steps are performed by
+ * ConnectivityService itself:
+ * - creating a new network
+ * - setting the gateway
+ * - setting DNS
+ * There is no reason to perform those actions on our side via the command line;
+ * it would conflict with ConnectivityService's calls to "ndc"
+ */
+
+ return ConnectionResult.CONN_OK;
+
+ }
+
+ public void abortConnection() {
+ synchronized (abortFlagSync) {
+ this.abortConnectionSignaled = true;
+ }
+ }
+
+ @Override
+ public ConnectionStatus getConnectionStatus() {
+ ConnectionStatus s = super.getConnectionStatus();
+ if (s == null) {
+ return null;
+ }
+
+ s.gateway = this.getGateway();
+
+ return s;
+ }
+
+ private boolean checkAbortSignal() {
+
+ synchronized (abortFlagSync) {
+ if (abortConnectionSignaled) {
+ abortConnectionSignaled = false;
+ Logger.logDebug("Engine received abort connection signal. Aborting connection...");
+ killDhcpcd();
+ disconnect();
+ return false;
+ } else {
+ return true;
+ }
+ }
+ }
+
+ private boolean destroyNetwork() {
+
+ // needs root (tested)
+ return RootCommand.executeRootCmd("ndc network destroy 1");
+ }
+
+ private String getGateWayTimeout(int timeoutMillis) {
+
+ String gw = getGateway();
+ if (gw != null && !gw.trim().isEmpty()) {
+ return gw;
+ }
+
+ Logger.logDebug("Gateway not available.. going into wait loop..");
+
+ // gateway not (yet) available
+ // waits for a maximum of timeoutMillis milliseconds
+ // to let the interface being registered.
+ int msWaited = 0;
+ while (msWaited < timeoutMillis) {
+
+ try {
+ Thread.sleep(100);
+ } catch (Exception e) {
+ return null;
+ }
+ msWaited += 100;
+
+ gw = getGateway();
+ if (gw != null && !gw.trim().isEmpty()) {
+ Logger.logDebug("Gateway found after wait loop!");
+ return gw;
+ }
+ }
+
+ // unable to get gateway
+ Logger.logError("Gateway not found after wait loop.");
+ return null;
+
+ }
+
+ private String getGateway() {
+
+ try {
+
+ // doesn't need root (tested)
+ ShellCommand cmd = new ShellCommand("ip route show dev " + WpaSupplicant.INTERFACE_NAME);
+ if (cmd.execute() != 0) {
+ Logger.logDebug("command failed show route");
+ return null;
+ }
+
+ // read command output
+ String out = cmd.getOutput();
+ if (out == null) {
+ return null;
+ }
+
+ String[] lines = out.split("\n");
+
+ for (String l : lines) {
+
+ if (l.contains("default via")) {
+
+ String[] f = l.split(" ");
+ if (f.length > 2) {
+
+ // found route's address:
+ return f[2];
+
+ }
+ }
+ }
+
+ return null;
+
+ } catch (Exception e) {
+ Logger.logError("Error while trying to fetch route", e);
+ return null;
+ }
+
+ }
+
+ private boolean clearAddrs() {
+ // needs root (tested)
+ return RootCommand.executeRootCmd("ndc interface clearaddrs "
+ + WpaSupplicant.INTERFACE_NAME);
+ }
+
+} \ No newline at end of file
diff --git a/app/src/fil/libre/repwifiapp/helpers/IEngine.java b/app/src/fil/libre/repwifiapp/network/IEngine.java
index 26ebddb..d598550 100644
--- a/app/src/fil/libre/repwifiapp/helpers/IEngine.java
+++ b/app/src/fil/libre/repwifiapp/network/IEngine.java
@@ -1,5 +1,5 @@
//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
//
// This file is part of RepWifiApp.
//
@@ -18,20 +18,20 @@
//
// ********************************************************************
-package fil.libre.repwifiapp.helpers;
-
-import java.net.SocketException;
+package fil.libre.repwifiapp.network;
public interface IEngine {
public AccessPointInfo[] getAvailableNetworks();
- public boolean connect(AccessPointInfo info);
+ public int connect(AccessPointInfo info);
public boolean disconnect();
public ConnectionStatus getConnectionStatus();
- public boolean isInterfaceAvailable(String ifaceName) throws SocketException;
+ // public boolean isInterfaceAvailable(String ifaceName) throws SocketException;
+ public void abortConnection();
+
}
diff --git a/app/src/fil/libre/repwifiapp/helpers/NetworkButton.java b/app/src/fil/libre/repwifiapp/network/NetworkButton.java
index 7d0bdf9..49c816c 100644
--- a/app/src/fil/libre/repwifiapp/helpers/NetworkButton.java
+++ b/app/src/fil/libre/repwifiapp/network/NetworkButton.java
@@ -1,5 +1,5 @@
//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
//
// This file is part of RepWifiApp.
//
@@ -18,7 +18,7 @@
//
// ********************************************************************
-package fil.libre.repwifiapp.helpers;
+package fil.libre.repwifiapp.network;
import android.content.Context;
import android.widget.Button;
diff --git a/app/src/fil/libre/repwifiapp/helpers/NetworkManager.java b/app/src/fil/libre/repwifiapp/network/NetworkManager.java
index f78b4a5..4d5635d 100644
--- a/app/src/fil/libre/repwifiapp/helpers/NetworkManager.java
+++ b/app/src/fil/libre/repwifiapp/network/NetworkManager.java
@@ -1,43 +1,45 @@
//
-// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
//
// This file is part of RepWifiApp.
//
// RepWifiApp is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
+// it under the terms of the GNU General public static License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// RepWifiApp 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.
+// GNU General public static License for more details.
//
-// You should have received a copy of the GNU General Public License
+// You should have received a copy of the GNU General public static License
// along with RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
//
// ********************************************************************
-package fil.libre.repwifiapp.helpers;
+package fil.libre.repwifiapp.network;
+import fil.libre.repwifiapp.Utils;
+import fil.libre.repwifiapp.helpers.Logger;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.File;
import java.util.ArrayList;
-public class NetworkManager {
+public abstract class NetworkManager {
- private static final String VERSION_NOTE = "RepWifiStorageVersion: 2.0\n";
+ private static final String VERSION_NOTE = "RepWifiStorageVersion: 2.0";
private static final String F_SEP = "\t";
private static final int NET_MAX_AGE = 1095; // Expressed in days
- private String _knownNetworksFile = null;
+ private static String knownNetworksFile = null;
- public NetworkManager(String networksFilePath) {
- this._knownNetworksFile = networksFilePath;
+ public static void init(String storageFilePath){
+ knownNetworksFile = storageFilePath;
}
-
- private AccessPointInfo getSavedInfo(AccessPointInfo i) {
+
+ private static AccessPointInfo getSavedInfo(AccessPointInfo i) {
if (i == null) {
return null;
@@ -87,7 +89,7 @@ public class NetworkManager {
}
- private boolean saveOrRemove(AccessPointInfo info, boolean save) {
+ private static boolean saveOrRemove(AccessPointInfo info, boolean save) {
AccessPointInfo[] existingNets = getKnownNetworks();
@@ -153,7 +155,7 @@ public class NetworkManager {
}
- private AccessPointInfo getFromStringOld(String savedString) {
+ private static AccessPointInfo getFromStringOld(String savedString) {
if (savedString == null || savedString.trim().equals("")) {
return null;
@@ -194,7 +196,7 @@ public class NetworkManager {
lastusedmillis = Long.parseLong(lastUsed);
} catch (NumberFormatException e) {
// invalid format
- Utils.logError("Invalid time format in network manager \"" + lastUsed
+ Logger.logError("Invalid time format in network manager \"" + lastUsed
+ "\". Network BSSID: " + bssid, e);
}
@@ -216,7 +218,7 @@ public class NetworkManager {
}
- private boolean saveList(AccessPointInfo[] list) {
+ private static boolean saveList(AccessPointInfo[] list) {
try {
@@ -238,63 +240,53 @@ public class NetworkManager {
sb.append(jarr.toString(2));
- return Utils.writeFile(_knownNetworksFile, sb.toString(), true);
+ return Utils.writeFile(knownNetworksFile, sb.toString(), true);
} catch (Exception e) {
- Utils.logError("Exception while saving AccessPointInfo array to JSON-formatted file.",
+ Logger.logError("Exception while saving AccessPointInfo array to JSON-formatted file.",
e);
return false;
}
- /*
- * if (list == null) { return false; }
- *
- * String[] lines = new String[list.length];
- *
- * for (int i = 0; i < list.length; i++) {
- *
- * String storeString = InfoToString(list[i]); if (storeString == null)
- * { return false; } lines[i] = storeString;
- *
- * }
- *
- * return Utils.writeFileLines(this._knownNetworksFile, lines, true);
- */
-
}
- public boolean updateStorageVersion() {
+ public static boolean updateStorageVersion() {
- String[] lines = Utils.readFileLines(_knownNetworksFile);
-
- if (lines == null){
- return false;
- }
-
- if (lines.length == 0) {
- return true;
- }
+ try{
+ String[] lines = Utils.readFileLines(knownNetworksFile);
+
+ if (lines == null){
+ return false;
+ }
+
+ if (lines.length == 0) {
+ return true;
+ }
- if (lines[0].trim().equals(VERSION_NOTE)) {
- return true;
+ if (lines[0].trim().equals(VERSION_NOTE)) {
+ return true;
- } else {
+ } else {
- AccessPointInfo[] infos = getKnownNetworksOld();
- if (infos == null || infos.length == 0) {
- return true;
- }
- return saveList(infos);
+ AccessPointInfo[] infos = getKnownNetworksOld();
+ if (infos == null || infos.length == 0) {
+ return true;
+ }
+ return saveList(infos);
+ }
+ }catch (Exception e){
+ Logger.logError("Exception while trying to update network storage version",e);
+ return false;
}
}
- public AccessPointInfo[] getKnownNetworks() {
+ public static AccessPointInfo[] getKnownNetworks() {
try {
- String fconts = Utils.readFile(_knownNetworksFile);
+ String fconts = Utils.readFile(knownNetworksFile);
if (fconts == null) {
return null;
}
@@ -325,22 +317,22 @@ public class NetworkManager {
return list.toArray(arr);
} catch (Exception e) {
- Utils.logError("Exception while parsing JSON content from storage file.", e);
+ Logger.logError("Exception while parsing JSON content from storage file.", e);
return null;
}
}
- public AccessPointInfo[] getKnownNetworksOld() {
+ public static AccessPointInfo[] getKnownNetworksOld() {
ArrayList<AccessPointInfo> list = new ArrayList<AccessPointInfo>();
- File f = new File(this._knownNetworksFile);
+ File f = new File(knownNetworksFile);
if (!f.exists()) {
return null;
}
- String[] lines = Utils.readFileLines(_knownNetworksFile);
+ String[] lines = Utils.readFileLines(knownNetworksFile);
if (lines == null || lines.length == 0) {
return null;
}
@@ -361,7 +353,7 @@ public class NetworkManager {
}
- public boolean isKnown(AccessPointInfo info) {
+ public static boolean isKnown(AccessPointInfo info) {
AccessPointInfo i = getSavedInfo(info);
if (i == null) {
@@ -372,15 +364,15 @@ public class NetworkManager {
}
- public boolean save(AccessPointInfo info) {
+ public static boolean save(AccessPointInfo info) {
return saveOrRemove(info, true);
}
- public boolean remove(AccessPointInfo info) {
+ public static boolean remove(AccessPointInfo info) {
return saveOrRemove(info, false);
}
- public AccessPointInfo getSavedNetwork(AccessPointInfo i) {
+ public static AccessPointInfo getSavedNetwork(AccessPointInfo i) {
return getSavedInfo(i);
}
diff --git a/app/src/fil/libre/repwifiapp/helpers/WpaCli.java b/app/src/fil/libre/repwifiapp/network/WpaCli.java
index 8bd6561..10f6b7c 100644
--- a/app/src/fil/libre/repwifiapp/helpers/WpaCli.java
+++ b/app/src/fil/libre/repwifiapp/network/WpaCli.java
@@ -1,8 +1,32 @@
-package fil.libre.repwifiapp.helpers;
+//
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+//
+// This file is part of RepWifiApp.
+//
+// RepWifiApp 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.
+//
+// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
+//
+// ********************************************************************
+
+
+package fil.libre.repwifiapp.network;
+
+import fil.libre.repwifiapp.helpers.Logger;
+import fil.libre.repwifiapp.helpers.RootCommand;
public abstract class WpaCli {
- private static final int SCAN_WAIT_INTERVAL = 3;
+ private static final int SCAN_WAIT_INTERVAL = 4;
private static final String BASE_COMMAND = "wpa_cli -p" + WpaSupplicant.SOCKET_DIR + " -P"
+ WpaSupplicant.PID_FILE + " -i" + WpaSupplicant.INTERFACE_NAME;
@@ -23,7 +47,7 @@ public abstract class WpaCli {
}
} catch (Exception e) {
- Utils.logError("Error while creating network", e);
+ Logger.logError("Error while creating network", e);
return null;
}
@@ -34,21 +58,11 @@ public abstract class WpaCli {
try {
// needs root (wpa_cli)
- RootCommand su = new RootCommand(BASE_COMMAND + " set_network " + networkID
- + " ssid '\"" + ssid + "\"'");
- if (su.execute() == 0) {
- String out = su.getOutput();
- if (out != null && out.trim().replace("\n", "").equals("OK")) {
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
+ return executeCmd(BASE_COMMAND + " set_network " + networkID + " ssid '\"" + ssid
+ + "\"'");
} catch (Exception e) {
- Utils.logError("Error while setting network SSID", e);
+ Logger.logError("Error while setting network SSID", e);
return false;
}
@@ -68,20 +82,10 @@ public abstract class WpaCli {
cmdSetPass = BASE_COMMAND + " set_network " + networkID + " key_mgmt NONE";
}
- RootCommand su = new RootCommand(cmdSetPass);
- if (su.execute() == 0) {
- String out = su.getOutput();
- if (out != null && out.trim().replace("\n", "").equals("OK")) {
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
+ return executeCmd(cmdSetPass);
} catch (Exception e) {
- Utils.logError("Error while setting network PSK", e);
+ Logger.logError("Error while setting network PSK", e);
return false;
}
@@ -92,21 +96,10 @@ public abstract class WpaCli {
try {
// needs root (wpa_cli)
- RootCommand su = new RootCommand(BASE_COMMAND + " set_network " + networkID
- + " scan_ssid 1");
- if (su.execute() == 0) {
- String out = su.getOutput();
- if (out != null && out.trim().replace("\n", "").equals("OK")) {
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
+ return executeCmd(BASE_COMMAND + " set_network " + networkID + " scan_ssid 1");
} catch (Exception e) {
- Utils.logError("Error while setting network SSID", e);
+ Logger.logError("Error while setting network SSID", e);
return false;
}
}
@@ -116,20 +109,10 @@ public abstract class WpaCli {
try {
// needs root (wpa_cli)
- RootCommand su = new RootCommand(BASE_COMMAND + " select_network " + networkID);
- if (su.execute() == 0) {
- String out = su.getOutput();
- if (out != null && out.trim().replace("\n", "").equals("OK")) {
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
+ return executeCmd(BASE_COMMAND + " select_network " + networkID);
} catch (Exception e) {
- Utils.logError("Error while selecting network", e);
+ Logger.logError("Error while selecting network", e);
return false;
}
@@ -140,21 +123,10 @@ public abstract class WpaCli {
try {
// needs root (wpa_cli)
-
- RootCommand su = new RootCommand(BASE_COMMAND + " enable_network " + networkID);
- if (su.execute() == 0) {
- String out = su.getOutput();
- if (out != null && out.trim().replace("\n", "").equals("OK")) {
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
+ return executeCmd(BASE_COMMAND + " enable_network " + networkID);
} catch (Exception e) {
- Utils.logError("Error while enabling network", e);
+ Logger.logError("Error while enabling network", e);
return false;
}
@@ -186,15 +158,10 @@ public abstract class WpaCli {
}
} catch (Exception e) {
- Utils.logError("Error while executing wpa_cli status", e);
+ Logger.logError("Error while executing wpa_cli status", e);
return null;
}
- /*
- * // needs root (for wpa_supplicant and wpa_cli) boolean res =
- * RootCommand.executeRootCmd(); if (!res) { return false; } return res;
- */
-
}
/***
@@ -202,11 +169,11 @@ public abstract class WpaCli {
*/
public static ConnectionStatus getConnectionStatus() {
- Utils.logDebug("called getConnecitonStatus()");
+ Logger.logDebug("called getConnecitonStatus()");
if (!WpaSupplicant.isRunning()) {
// wpa_supplicant is not running.
// unable to determine status.
- Utils.logDebug("wpa not running, cannot get connection status.");
+ Logger.logDebug("wpa not running, cannot get connection status.");
return null;
}
@@ -226,7 +193,7 @@ public abstract class WpaCli {
}
} catch (Exception e) {
- Utils.logError("Error while executing wpa_cli status", e);
+ Logger.logError("Error while executing wpa_cli status", e);
return null;
}
@@ -242,22 +209,44 @@ public abstract class WpaCli {
try {
- RootCommand su = new RootCommand(BASE_COMMAND + " disconnect");
- if (su.execute() == 0) {
- String out = su.getOutput();
- if (out != null && out.trim().replace("\n", "").equals("OK")) {
- return true;
- } else {
- return false;
- }
+ return executeCmd(BASE_COMMAND + " disconnect");
+
+ } catch (Exception e) {
+ Logger.logError("Error while enabling network", e);
+ return false;
+ }
+ }
+
+ public static boolean terminateSupplicant() {
+
+ if (!WpaSupplicant.isRunning()){
+ return true;
+ }
+ try {
+
+ return executeCmd(BASE_COMMAND + " terminate");
+
+ } catch (Exception e) {
+ Logger.logError("Error while enabling network", e);
+ return false;
+ }
+
+ }
+
+ private static boolean executeCmd(String cmdTxt) throws Exception {
+
+ RootCommand su = new RootCommand(cmdTxt);
+ if (su.execute() == 0) {
+ String out = su.getOutput();
+ if (out != null && out.trim().replace("\n", "").equals("OK")) {
+ return true;
} else {
return false;
}
-
- } catch (Exception e) {
- Utils.logError("Error while enabling network", e);
+ } else {
return false;
}
+
}
}
diff --git a/app/src/fil/libre/repwifiapp/helpers/WpaSupplicant.java b/app/src/fil/libre/repwifiapp/network/WpaSupplicant.java
index 1dfe449..d8079b9 100644
--- a/app/src/fil/libre/repwifiapp/helpers/WpaSupplicant.java
+++ b/app/src/fil/libre/repwifiapp/network/WpaSupplicant.java
@@ -1,4 +1,27 @@
-package fil.libre.repwifiapp.helpers;
+//
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+//
+// This file is part of RepWifiApp.
+//
+// RepWifiApp 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.
+//
+// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
+//
+// ********************************************************************
+
+package fil.libre.repwifiapp.network;
+
+import fil.libre.repwifiapp.helpers.Logger;
+import fil.libre.repwifiapp.helpers.RootCommand;
public abstract class WpaSupplicant {
@@ -6,7 +29,7 @@ public abstract class WpaSupplicant {
public static final String INTERFACE_NAME = "wlan0";
public static final String WORKDIR = "/data/misc/wifi";
public static final String PID_FILE = WORKDIR + "/pidfile";
- public static final String SOCKET_DIR = WORKDIR + "/sockets/";
+ public static final String SOCKET_DIR = WORKDIR + "/sockets";
public static final String SOFTAP_FILE = WORKDIR + "/softap.conf";
public static final String P2P_CONF = WORKDIR + "/p2p_supplicant.conf";
public static final String WPA_CONF = WORKDIR + "/wpa_supplicant.conf";
@@ -17,14 +40,17 @@ public abstract class WpaSupplicant {
+ " -I" + OVERLAY_FILE + " -e" + ENTROPY_FILE;
public static boolean start() {
-
- Utils.logDebug("startWpaSupplicant():");
-
+
+ Logger.logDebug("startWpaSupplicant():");
+ if (isRunning()){
+ return true;
+ }
+
// needs root (for wpa_supplicant)
if (RootCommand.executeRootCmd(BASE_COMMNAD)) {
return true;
} else {
- Utils.logDebug("Failed to start wpa");
+ Logger.logDebug("Failed to start wpa");
return false;
}
@@ -54,7 +80,7 @@ public abstract class WpaSupplicant {
}
} catch (Exception e) {
- Utils.logError("Exception during isWpaSupplicantRunning()", e);
+ Logger.logError("Exception during isWpaSupplicantRunning()", e);
retval = false;
}
diff --git a/app/src/fil/libre/repwifiapp/service/Channel.java b/app/src/fil/libre/repwifiapp/service/Channel.java
new file mode 100644
index 0000000..856c88d
--- /dev/null
+++ b/app/src/fil/libre/repwifiapp/service/Channel.java
@@ -0,0 +1,148 @@
+//
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+//
+// This file is part of RepWifiApp.
+//
+// RepWifiApp 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.
+//
+// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
+//
+// ********************************************************************
+
+package fil.libre.repwifiapp.service;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import fil.libre.repwifiapp.ActivityLauncher;
+import fil.libre.repwifiapp.helpers.Logger;
+import fil.libre.repwifiapp.network.AccessPointInfo;
+import fil.libre.repwifiapp.network.ConnectionResult;
+import fil.libre.repwifiapp.network.ConnectionStatus;
+
+public class Channel {
+
+ public static final String PAYLOAD_APINFO = ActivityLauncher.EXTRA_APINFO;
+ public static final String PAYLOAD_CONNSTATUS = ActivityLauncher.EXTRA_CONSTATUS;
+ public static final String PAYLOAD_CONNRES = ActivityLauncher.EXTRA_CONNRES;
+ public static String PAYLOAD_PREFKEY = "PrefKey";
+
+ private Messenger src;
+ private Messenger dest;
+ private Context context;
+
+ public Channel(Context context, Messenger destination, Messenger source) {
+ this.context = context;
+ this.dest = destination;
+ this.src = source;
+ }
+
+ public Channel(Context context, Messenger destination) {
+ this(context, destination, null);
+ }
+
+ public boolean sendMsg(int what) {
+ return sendMsg(what, (Parcelable) null, null);
+ }
+
+ public boolean sendMsg(int what, Parcelable payload, String payloadKey) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(payloadKey, payload);
+ return sendMsg(what, bundle, 0);
+ }
+
+ public boolean sendMsg(int what, Parcelable[] payload, String payloadKey) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelableArray(payloadKey, payload);
+ return sendMsg(what, bundle, 0);
+ }
+
+ public boolean sendMsg(int what, Bundle b, int arg1) {
+
+ if (dest == null){
+ Logger.logError("Tried to send message but destination messenger is null. What: " + what);
+ return false;
+ }
+
+ Message msg = Message.obtain();
+ msg.what = what;
+ msg.arg1 = arg1;
+
+ if (src != null) {
+ msg.replyTo = src;
+ }
+
+ if (b != null) {
+ msg.setData(b);
+ }
+
+ try {
+ dest.send(msg);
+ return true;
+ } catch (RemoteException e) {
+ Logger.logError("RemoteException while trying to send message: " + msg.toString()
+ + " to recipient: " + dest.toString(), e);
+ return false;
+ }
+ }
+
+ public ConnectionResult getConnectionResultPayload(Message msg){
+ return (ConnectionResult) getPayload(msg, PAYLOAD_CONNRES);
+ }
+
+ public ConnectionStatus getConnectionStatusPayload(Message msg){
+ return (ConnectionStatus) getPayload(msg, PAYLOAD_CONNSTATUS);
+ }
+
+ public AccessPointInfo getAccessPointInfoPayload(Message msg){
+ return (AccessPointInfo) getPayload(msg, PAYLOAD_APINFO);
+ }
+
+ public String getStringPayload(Message msg, String key){
+ Bundle b = msg.getData();
+ if (b == null) {
+ return null;
+ }
+ return b.getString(key);
+ }
+
+
+ private Parcelable getPayload(Message msg, String key) {
+
+ Bundle b = msg.getData();
+ if (b == null) {
+ return null;
+ }
+
+ b.setClassLoader(context.getClassLoader());
+ return b.getParcelable(key);
+
+ }
+
+
+ public AccessPointInfo[] getApinfoArrayPayload(Message msg) {
+
+ Bundle b = msg.getData();
+ if (b == null) {
+ return null;
+ }
+
+ b.setClassLoader(context.getClassLoader());
+ Parcelable[] p = b.getParcelableArray(PAYLOAD_APINFO);
+ return AccessPointInfo.fromParcellableArray(p);
+ }
+
+
+}
diff --git a/app/src/fil/libre/repwifiapp/service/ConnectionManagementService.java b/app/src/fil/libre/repwifiapp/service/ConnectionManagementService.java
new file mode 100644
index 0000000..2f70306
--- /dev/null
+++ b/app/src/fil/libre/repwifiapp/service/ConnectionManagementService.java
@@ -0,0 +1,720 @@
+//
+// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+//
+// This file is part of RepWifiApp.
+//
+// RepWifiApp 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.
+//
+// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
+//
+// ********************************************************************
+
+package fil.libre.repwifiapp.service;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Intent;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import fil.libre.repwifiapp.Commons;
+import fil.libre.repwifiapp.Prefs;
+import fil.libre.repwifiapp.R;
+import fil.libre.repwifiapp.Utils;
+import fil.libre.repwifiapp.activities.MainActivity;
+import fil.libre.repwifiapp.fwproxies.LinkAddressProxy;
+import fil.libre.repwifiapp.fwproxies.LinkPropertiesProxy;
+import fil.libre.repwifiapp.fwproxies.NetworkCapabilitiesProxy;
+import fil.libre.repwifiapp.fwproxies.NetworkInfoProxy;
+import fil.libre.repwifiapp.fwproxies.RepWifiNetworkAgent;
+import fil.libre.repwifiapp.fwproxies.RouteInfoProxy;
+import fil.libre.repwifiapp.helpers.Logger;
+import fil.libre.repwifiapp.network.AccessPointInfo;
+import fil.libre.repwifiapp.network.ConnectionResult;
+import fil.libre.repwifiapp.network.ConnectionStatus;
+import fil.libre.repwifiapp.network.Engine6p0;
+import fil.libre.repwifiapp.network.IEngine;
+import fil.libre.repwifiapp.network.NetworkManager;
+import fil.libre.repwifiapp.network.WpaCli;
+import fil.libre.repwifiapp.network.WpaSupplicant;
+import fil.libre.repwifiapp.service.StatusManager.ConnectionStatusChangeListener;
+import org.apache.http.conn.util.InetAddressUtils;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+
+public class ConnectionManagementService extends Service implements ConnectionStatusChangeListener {
+
+ public static final String LOG_TAG_SERVICE = "RepWifiConnectionManagementService";
+
+ public static final String ACTION_DOMAIN = "fil.libre.repwifiapp.ConnectionService";
+ public static final String ACTION_CONNECT = ACTION_DOMAIN + ".ACTION_CONNECT";
+ public static final String ACTION_DISCONNECT = ACTION_DOMAIN + ".ACTION_DISCONNECT";
+ public static final String ACTION_VOID = ACTION_DOMAIN + ".ACTION_VOID";
+
+ public static final int MSG_BASE = 0;
+ public static final int CMD_START_CONNECT = MSG_BASE + 1;
+ public static final int CMD_ABORT_CONNECTION = MSG_BASE + 2;
+ public static final int CMD_DISCONNECT = MSG_BASE + 3;
+ public static final int CMD_GET_AVAILABLE_NETWORKS = MSG_BASE + 4;
+ public static final int CMD_START_MONITOR_CONNECTION_STATUS = MSG_BASE + 5;
+ public static final int CMD_STOP_MONITOR_CONNECTION_STATUS = MSG_BASE + 6;
+ public static final int CMD_STATUS_UPDATE = MSG_BASE + 7;
+ public static final int CMD_AUTOCONNECT = MSG_BASE + 8;
+ public static final int CMD_CLIENT_UNBINDING = MSG_BASE + 9;
+
+ /**
+ * TODO:
+ * Remove this command when a better model for the application's settings is
+ * implemented using @ContentProvider.
+ * For now, the UI will use this command to signal the service's process
+ * when a change is detected in the @SharedPreferences.
+ * A @ContentProvider should be used instead, to properly share the
+ * application's settings between the UI process and the background service.
+ * This command should be removed especially if the service is ever made
+ * "exported", i.e. available to other applications, in order to prevent
+ * external apps from tampering with the inner state of the service.
+ */
+ public static final int CMD_PREF_CHANGED = MSG_BASE + 10;
+
+ public static final int MSG_STATUS_CHANGE = MSG_BASE + 1001;
+ public static final int MSG_CONNECTION_RESULT = MSG_BASE + 1002;
+ public static final int MSG_AVAILABLE_NETWORKS = MSG_BASE + 1003;
+ public static final int MSG_AUTOCONNECT_REPORT = MSG_BASE + 1004;
+ public static final int MSG_DISCONNECT_REPORT = MSG_BASE + 1005;
+
+ /**
+ * This message is returned to a calling client, upon reception of a Command
+ * which the caller is not allowed to request, the original command id is
+ * reported in "arg1" field of this reply message.
+ */
+ public static final int MSG_PERMISSION_DENIED = MSG_BASE + 1403;
+
+ public static final int CHECK_STATUS_INTERVAL_SECS = 15;
+ public static final String PLACEHOLDER_CHECK_STATUS_INTERVAL = "[CHK_STS_INTERVAL]";
+
+ public static final String LOG_TAG_NETWORKAGENT = "RepWifiNetworkAgent";
+
+ private RepWifiNetworkAgent currentNetworkAgent = null;
+ private IEngine eng = null;
+ private StatusManager smonitor;
+ private ArrayList<Channel> statusWatchers = new ArrayList<Channel>();
+
+ private final Messenger messenger = new Messenger(new Handler() {
+
+ @Override
+ public void handleMessage(Message msg) {
+
+ Channel channel = new Channel(ConnectionManagementService.this, msg.replyTo);
+
+ switch (msg.what) {
+ case CMD_START_CONNECT:
+
+ AccessPointInfo info = channel.getAccessPointInfoPayload(msg);
+ if (info == null) {
+ Logger.logError("Received connect message without valid AccessPointInfo.");
+ } else {
+ connect(info, channel);
+ }
+ break;
+
+ case CMD_ABORT_CONNECTION:
+ abortConnection();
+ break;
+
+ case CMD_DISCONNECT:
+ disconnect(channel);
+ break;
+
+ case CMD_GET_AVAILABLE_NETWORKS:
+ getAvailableNetworks(channel);
+ break;
+
+ case CMD_START_MONITOR_CONNECTION_STATUS:
+ startMonitoringNetworkStatus(channel);
+ break;
+
+ case CMD_STOP_MONITOR_CONNECTION_STATUS:
+ stopMonitoringNetworkStatus(channel);
+ break;
+
+ case CMD_STATUS_UPDATE:
+ getStatus(channel);
+ break;
+
+ case CMD_CLIENT_UNBINDING:
+ onClientUnbinding(channel);
+ break;
+
+ case CMD_PREF_CHANGED:
+ String prefKey = channel.getStringPayload(msg, Channel.PAYLOAD_PREFKEY);
+ onPreferenceChanged(prefKey);
+ break;
+
+ default:
+ Logger.logError("Received message with unknown what: " + msg.what);
+ }
+ }
+
+ });
+
+ public ConnectionManagementService() {
+ }
+
+ @Override
+ public void onCreate() {
+
+ Commons.init(getApplicationContext());
+ initEngine();
+
+ Logger.APP_NAME = LOG_TAG_SERVICE;
+ Logger.setLogPriority(Prefs.getLogPriority(getApplicationContext()));
+
+ // Reset wpa_supplicant when the service is first run
+ // This is needed in case the internal WifiManager is controlling the
+ // current instance of wpa_supplicant.
+ Utils.killBackEnd(getApplicationContext(), true);
+
+ smonitor = new StatusManager(eng, this);
+
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+
+ if (intent == null) {
+ Logger.logDebug("[WRN]Service started with null intent!");
+ return START_STICKY;
+ }
+
+ String a = intent.getAction();
+
+ if (a == null) {
+ Logger.logDebug("[WRN] Service started with null action");
+ return START_STICKY;
+ }
+
+ Logger.logDebug("Service started with action: " + a);
+
+ if (a == ACTION_CONNECT) {
+ handleActionConnect(intent.getExtras());
+
+ } else if (a == ACTION_DISCONNECT) {
+ handleActionDisconnect();
+
+ } else if (a == ACTION_VOID) {
+ // Just start the service and wait for bindings
+ Logger.logDebug("Started with void action.");
+ } else {
+ Logger.logError("Unknown action " + a);
+ }
+
+ getStatus();
+ return START_STICKY;
+
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ smonitor.unsetListener();
+ }
+
+ private void handleActionConnect(Bundle xtras) {
+
+ if (xtras == null || xtras.containsKey(Channel.PAYLOAD_APINFO)) {
+ Logger.logError("Requested action connect without AccespointInfo extra!");
+ return;
+ }
+
+ try {
+ AccessPointInfo info = (AccessPointInfo) xtras.get(Channel.PAYLOAD_APINFO);
+
+ connect(info);
+
+ } catch (Exception e) {
+ Logger.logError("Exception while extracting AccessPointInfo object from start intent's extras.",
+ e);
+ }
+
+ }
+
+ private void handleActionDisconnect() {
+ disconnect();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return this.messenger.getBinder();
+ }
+
+ private ConnectionResult connect(AccessPointInfo info, Channel callback) {
+
+ initEngine();
+
+ int result = eng.connect(info);
+ ConnectionResult connectionResult = new ConnectionResult(result);
+ ConnectionStatus status = null;
+
+ if (result == ConnectionResult.CONN_OK) {
+
+ Logger.logDebug("Result code CONN_OK");
+
+ status = smonitor.getConnectionStatus();
+ connectionResult.setStatus(status);
+
+ if (info.needsPassword()) {
+
+ if (status != null) {
+ // update APinfo with the right BSSID
+ info.setBssid(status.BSSID);
+ }
+
+ // Save network
+ if (NetworkManager.save(info)) {
+ Logger.logDebug("Network saved: " + status.SSID);
+
+ } else {
+ Logger.logError("FAILED saving network: " + status.SSID);
+
+ }
+
+ }
+
+ }
+
+ reportConnectionResult(connectionResult, callback);
+ return connectionResult;
+ }
+
+ public ConnectionResult connect(AccessPointInfo info) {
+ return connect(info, null);
+ }
+
+ /**
+ * Attempts to connect to any nearby known network if found.
+ *
+ * @return Returns null if a network is found and connected.
+ * Returns an empty array if no network is found.
+ * Returns an array of AccessPointInfo if no nearby network is
+ * known.
+ */
+ public AccessPointInfo[] autoConnect() {
+ return autoConnect(null);
+ }
+
+ private AccessPointInfo[] autoConnect(Channel callback) {
+ try {
+
+ AccessPointInfo[] nets = eng.getAvailableNetworks();
+ if (nets == null || nets.length == 0) {
+ nets = new AccessPointInfo[] {};
+ }
+
+ for (AccessPointInfo i : nets) {
+
+ if (NetworkManager.isKnown(i)) {
+ connect(i, null);
+ nets = null;
+ }
+
+ }
+
+ // if no network is known, return available networks:
+ if (callback != null) {
+ reportAutoconnectResult(nets, callback);
+ }
+ return nets;
+
+ } catch (Exception e) {
+ Logger.logError("Error while autoconnecting", e);
+ return null;
+ }
+ }
+
+ private ConnectionStatus getStatus(Channel callback) {
+
+ ConnectionStatus status = smonitor.getConnectionStatus();
+
+ if (callback != null) {
+ callback.sendMsg(MSG_STATUS_CHANGE, status, Channel.PAYLOAD_CONNSTATUS);
+ }
+ return status;
+
+ }
+
+ public ConnectionStatus getStatus() {
+ return getStatus(null);
+ }
+
+ public void abortConnection() {
+ initEngine();
+ eng.abortConnection();
+ }
+
+ public boolean disconnect() {
+ return disconnect(null);
+ }
+
+ private boolean disconnect(Channel callback) {
+
+ initEngine();
+ boolean res = eng.disconnect();
+ ConnectionStatus status = getStatus();
+
+ if (callback != null) {
+ callback.sendMsg(MSG_DISCONNECT_REPORT, status, Channel.PAYLOAD_CONNSTATUS);
+ }
+
+ return res;
+ }
+
+ private AccessPointInfo[] getAvailableNetworks(Channel callback) {
+ initEngine();
+ AccessPointInfo[] nets = eng.getAvailableNetworks();
+ if (callback != null) {
+ callback.sendMsg(MSG_AVAILABLE_NETWORKS, nets, Channel.PAYLOAD_APINFO);
+ }
+ return nets;
+ }
+
+ public AccessPointInfo[] getAvailableNetworks() {
+ return getAvailableNetworks(null);
+ }
+
+ private void initEngine() {
+ if (eng == null) {
+ eng = new Engine6p0();
+ }
+ }
+
+ private void reportConnectionResult(ConnectionResult result, Channel callback) {
+ callback.sendMsg(MSG_CONNECTION_RESULT, result, Channel.PAYLOAD_CONNRES);
+ }
+
+ private void reportAutoconnectResult(AccessPointInfo[] infos, Channel callback) {
+ callback.sendMsg(MSG_AUTOCONNECT_REPORT, infos, Channel.PAYLOAD_APINFO);
+ }
+
+ private static final int NOTIFICATION_ID = 1;
+
+ @Override
+ public void onConnectionStatusChange(ConnectionStatus status) {
+
+ Logger.logDebug("Received connection status changed");
+ notifyWifiState(status);
+ updateNotification(status);
+ reportNetworkStatus(status);
+
+ }
+
+ private void onClientUnbinding(Channel client) {
+
+ if (client == null) {
+ return;
+ }
+
+ Logger.logDebug("Processing client unbinding.. ");
+ stopMonitoringNetworkStatus(client);
+
+ }
+
+ private boolean notifyWifiState(ConnectionStatus status) {
+
+ try {
+
+ if (status == null) {
+ Logger.logDebug("Received null ConnectionStatus; using dummy status disconnected.");
+ status = ConnectionStatus.getDummyDisconnected();
+ }
+
+ Logger.logDebug("Notifying wifi state with status object: " + status.toString());
+
+ NetworkInfoProxy ni = NetworkInfoProxy.getForWifi();
+ NetworkCapabilitiesProxy nc = new NetworkCapabilitiesProxy();
+ LinkPropertiesProxy lp = new LinkPropertiesProxy();
+ lp.setInterfaceName(WpaSupplicant.INTERFACE_NAME);
+
+ if (status.isConnected()) {
+
+ ni.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
+ ni.setIsAvailable(true);
+
+ if (!lp.addLinkAddress(new LinkAddressProxy(status.getInetAddress(), status
+ .getSubnetMaskInt()))) {
+ Logger.logError("Failed to add LinkAddress to LinkProperties.");
+ return false;
+ }
+
+ if (!lp.addRoute(new RouteInfoProxy(status.getGatewayInetAddress(),
+ WpaSupplicant.INTERFACE_NAME))) {
+ Logger.logError("Failed to add route to linkProperties");
+ return false;
+ }
+
+ InetAddress[] dnss = getUseableDnss(status.gateway);
+
+ if (dnss == null || dnss.length == 0) {
+ Logger.logError("Received null or empty dns array");
+ return false;
+ }
+
+ for (InetAddress d : dnss) {
+ if (d != null && !lp.addDnsServer(d)) {
+ Logger.logError("Failed to add dns to LinkProperties.");
+ return false;
+ }
+ }
+
+ nc.addCapability(NetworkCapabilitiesProxy.NET_CAPABILITY_NOT_METERED);
+ nc.addCapability(NetworkCapabilitiesProxy.NET_CAPABILITY_INTERNET);
+
+ if (!agentIsAvailable()) {
+ Logger.logDebug("Willing to communicate netwtork connection, but no NetworkAgent available. Creating new NetworkAgent..");
+ createNetworkAgent(ni, lp, nc, 100);
+ }
+
+ } else if (agentIsAvailable()) {
+ ni.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
+ ni.setIsAvailable(true);
+
+ } else {
+ // status is "disconnected" and we have no active communication
+ // channel with the ConnectivityService.
+ // no need to establish a new channel just to communicate
+ // disconnection:
+ // ConnectivityService should know we're disconnected already.
+ return true;
+ }
+
+ Logger.logDebug("About to call NetworkAgent.sendNetworkIngfo() connected="
+ + status.isConnected());
+
+ currentNetworkAgent.sendNetworkInfo(ni.getNetworkInfo());
+ Logger.logDebug("Called NetworkAgent.sendNetworkIngfo()..");
+
+ return true;
+
+ } catch (Exception e) {
+ Logger.logError("FAIL registerNetworkAgent", e);
+ return false;
+ }
+
+ }
+
+ private boolean agentIsAvailable() {
+ return (currentNetworkAgent != null && currentNetworkAgent.isChannellConnected());
+ }
+
+ private int createNetworkAgent(NetworkInfoProxy ni, LinkPropertiesProxy lp,
+ NetworkCapabilitiesProxy nc, int score) {
+
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ Logger.logDebug("About to create new RepWifiNetworkAgent...");
+ try {
+
+ currentNetworkAgent = new RepWifiNetworkAgent(Looper.myLooper(),
+ getApplicationContext().getApplicationContext(), LOG_TAG_NETWORKAGENT,
+ ni.getNetworkInfo(), nc, lp, score);
+ Logger.logDebug("Created RepWifiNetworkAgent, netId: " + currentNetworkAgent.netId);
+ return currentNetworkAgent.netId;
+
+ } catch (Exception e) {
+ Logger.logError("Exception while creating RepWifiNetworkAgent", e);
+ return -1;
+ }
+
+ }
+
+ private String[] getConfiguredDnss() {
+
+ // no more default DNS, it's a stupid thing to do.
+ // instead, default to empty dns (using gateway as dns)
+ // it should be up to the user to chose their own dns explicitly.
+ String dns1 = Prefs.getString(getApplicationContext(),Prefs.PREF_DNS_1, "");
+ String dns2 = Prefs.getString(getApplicationContext(), Prefs.PREF_DNS_2, "");
+
+ if (dns1 == null || dns1.isEmpty()) {
+ return null;
+ }
+
+ return new String[] { dns1, dns2 };
+
+ }
+
+ private InetAddress[] getUseableDnss(String gateway) {
+
+ String[] dnss = getConfiguredDnss();
+
+ if (dnss == null || dnss.length == 0) {
+ // the DNS setting has been left blank
+ // try to use the gateway as dns
+
+ if (gateway == null || gateway.length() == 0) {
+ // no possible DNS.
+ return null;
+ }
+
+ dnss = new String[] { gateway, null };
+
+ }
+
+ InetAddress d1 = null;
+ InetAddress d2 = null;
+
+ if (InetAddressUtils.isIPv4Address(dnss[0])) {
+ try {
+
+ d1 = InetAddress.getByName(dnss[0]);
+
+ if (dnss[1] != null && InetAddressUtils.isIPv4Address(dnss[1])) {
+
+ d2 = InetAddress.getByName(dnss[1]);
+
+ }
+
+ } catch (UnknownHostException e) {
+ Logger.logError("Exception while parsing dns address!", e);
+ return null;
+ }
+
+ return new InetAddress[] { d1, d2 };
+
+ } else {
+ Logger.logError("Wrong dns1 format!");
+ return null;
+ }
+
+ }
+
+ private void updateNotification(ConnectionStatus status) {
+
+ if (status == null) {
+ status = WpaCli.getConnectionStatus();
+ }
+
+ Notification.Builder builder = new Notification.Builder(getApplicationContext());
+
+ Intent intent = new Intent(getApplicationContext(), MainActivity.class);
+ PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent,
+ 0);
+ builder.setContentIntent(pendingIntent);
+
+ int iconId = R.drawable.ic_stat_discon;
+ String msg = "RepWifi";
+ if (status != null) {
+ if (status.isConnected()) {
+ iconId = R.drawable.ic_stat_repwifi;
+ msg += " - " + status.SSID;
+ } else {
+ msg += " - " + status.wpaStatus;
+ }
+
+ }
+
+ builder.setSmallIcon(iconId);
+
+ builder.setContentTitle(msg);
+ builder.setContentText(getString(R.string.msg_touch_open));
+
+ Notification n = builder.build();
+ n.flags |= Notification.FLAG_NO_CLEAR;
+
+ NotificationManager notificationManager = (NotificationManager) getSystemService(Service.NOTIFICATION_SERVICE);
+ notificationManager.notify(NOTIFICATION_ID, n);
+
+ }
+
+ private void onPreferenceChanged(String prefName) {
+
+ if (prefName == null) {
+ Logger.logError("Received preference changed event, but prefName is null!");
+ return;
+ }
+
+ if (prefName.equals(Prefs.PREF_MONITOR_NET_STATE)) {
+ setMonitorNetworkStatus(Prefs.isNetworkStateMonitoringEnabled(getApplicationContext()));
+
+ } else if (prefName.equals(Prefs.PREF_LOG_LEVEL)) {
+ Logger.setLogPriority(Prefs.getLogPriority(getApplicationContext()));
+
+ }
+
+ }
+
+ private boolean monitoringExplicitlyEnabled = false;
+
+ private void setMonitorNetworkStatus(boolean enabled) {
+
+ monitoringExplicitlyEnabled = enabled;
+ if (enabled) {
+ startMonitoringNetworkStatus(null);
+
+ } else {
+ stopMonitoringNetworkStatus(null);
+
+ }
+ }
+
+ private void startMonitoringNetworkStatus(Channel watcher) {
+
+ synchronized (statusWatchers) {
+ if (watcher != null && !statusWatchers.contains(watcher)) {
+ Logger.logDebug("Added watcher for network status: " + watcher.toString());
+ statusWatchers.add(watcher);
+ }
+ }
+
+ smonitor.startPolling(CHECK_STATUS_INTERVAL_SECS * 1000);
+
+ }
+
+ private void stopMonitoringNetworkStatus(Channel watcher) {
+
+ synchronized (statusWatchers) {
+
+ if (watcher != null && statusWatchers.remove(watcher)) {
+ Logger.logDebug("Removed watcher for network status: " + watcher.toString());
+ }
+
+ if (statusWatchers.isEmpty() && !monitoringExplicitlyEnabled) {
+ if (smonitor != null) {
+ smonitor.stopPolling();
+ }
+ }
+ }
+
+ }
+
+ private void reportNetworkStatus(ConnectionStatus status) {
+
+ synchronized (statusWatchers) {
+
+ for (Channel m : statusWatchers) {
+ if (!m.sendMsg(MSG_STATUS_CHANGE, status, Channel.PAYLOAD_CONNSTATUS)) {
+ // remove recipient from watchers as it's not able to
+ // receive messages anymore
+ statusWatchers.remove(m);
+ }
+ }
+ }
+ }
+
+}
diff --git a/app/src/fil/libre/repwifiapp/service/StatusManager.java b/app/src/fil/libre/repwifiapp/service/StatusManager.java
new file mode 100644
index 0000000..1894a87
--- /dev/null
+++ b/app/src/fil/libre/repwifiapp/service/StatusManager.java
@@ -0,0 +1,139 @@
+//
+// Copyright 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net>
+//
+// This file is part of RepWifiApp.
+//
+// RepWifiApp 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.
+//
+// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>.
+//
+// ********************************************************************
+package fil.libre.repwifiapp.service;
+
+import android.os.Handler;
+import fil.libre.repwifiapp.helpers.Logger;
+import fil.libre.repwifiapp.network.ConnectionStatus;
+import fil.libre.repwifiapp.network.IEngine;
+
+public class StatusManager {
+
+ public interface ConnectionStatusChangeListener {
+
+ public void onConnectionStatusChange(ConnectionStatus status);
+
+ }
+
+ private Handler handler;
+ private Runnable runnable;
+ private boolean isStarted = false;
+ private int interval = 30000;
+ private ConnectionStatus currentStatus;
+
+ private IEngine eng = null;
+ private ConnectionStatusChangeListener listener = null;
+
+ public StatusManager(IEngine engine, ConnectionStatusChangeListener listener) {
+
+ this.listener = listener;
+ handler = new Handler();
+ currentStatus = ConnectionStatus.getDummyDisconnected();
+ eng = engine;
+
+ }
+
+ public ConnectionStatus getConnectionStatus() {
+ updateStatus();
+ return currentStatus;
+ }
+
+ private void updateStatus() {
+
+ synchronized (currentStatus) {
+
+ ConnectionStatus nextStatus = eng.getConnectionStatus();
+
+ if (nextStatus == null) {
+ nextStatus = ConnectionStatus.getDummyDisconnected();
+ }
+
+ if (! currentStatus.equals(nextStatus) && listener != null) {
+ listener.onConnectionStatusChange(nextStatus);
+ }
+
+ this.currentStatus = nextStatus;
+
+ }
+
+ }
+
+ public void startPolling(int intervalMs) {
+
+ synchronized (this) {
+
+ if (isStarted) {
+ Logger.logDebug("Called StatusMonitor.start() but monitor already started.. ignoring..");
+ return;
+ }
+
+ isStarted = true;
+
+ this.interval = intervalMs;
+
+ runnable = new Runnable() {
+ @Override
+ public void run() {
+
+ try {
+
+ Logger.logDebug("Polling enabled, connection status update triggered.");
+ updateStatus();
+
+ } catch (Exception e) {
+ Logger.logError("Exception while executing recurring network status update..",
+ e);
+ }
+
+ handler.postDelayed(this, interval);
+ }
+ };
+
+ Logger.logDebug("Starting recurring network status update every " + interval + " ms");
+ handler.postDelayed(runnable, interval);
+ }
+
+ }
+
+ public void stopPolling() {
+
+ synchronized (this) {
+
+ if (!isStarted) {
+ Logger.logDebug("Called StatusMonitor.stop() but already stopped.. ignoring..");
+ return;
+ }
+
+ try {
+ handler.removeCallbacks(runnable);
+ Logger.logDebug("Stopped recurring network status update.");
+ } finally {
+ isStarted = false;
+ }
+ }
+
+ }
+
+ public void unsetListener(){
+ stopPolling();
+ this.listener = null;
+ }
+
+}