diff options
author | Fil <fil.bergamo@riseup.net> | 2018-07-04 19:14:17 +0200 |
---|---|---|
committer | Fil <fil.bergamo@riseup.net> | 2018-07-04 19:29:53 +0200 |
commit | 121a4bec96d2f9b842c6d393d2b9c6356a9a4405 (patch) | |
tree | da1c557520d670def2f755492e3afd62ce891cf6 | |
parent | a6b34d6c75109b831976bc872a5a0a47a08c1664 (diff) | |
download | RepWifiApp-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.
75 files changed, 6639 insertions, 2359 deletions
@@ -1,3 +1,10 @@ +## [v0.9-beta] - 2018-07-04 +* FIXED Issue #1867 "Download app not working" (see below) +* NEW: creates a network connection that is fully integrated with android's connectivity framework +* NEW: all backend operations are now managed through a service +* cleaned up code design, refined structure of classes and entities +* massively restyled core logic: approaching version 1.0! + ## [v0.6] - 2017-12-17 * NEW: introduced static IP/gateway settings per network. * NEW: added OpenVPN support via interaction with de.blinkt.openvpn @@ -1,5 +1,5 @@ RepWifiApp is free software, released under GPLv3. -Copyright 2017 Filippo "Fil" Bergamo. +Copyright 2017, 2018 Filippo "Fil" Bergamo. All icons and images included in RepWifiApp are licensed under: Creative Commons Attribution-ShareAlike 3.0 Unported license @@ -17,19 +17,23 @@ It exposes basic functions to search for available netwroks and conenct to them. Thank GNUs: -- Tibi "tct" Turbureanu: +- Tiberiu (Technoethical): for having done the initial job of porting libre WiFi to Replicant 4.2. - Wolfgang Wiedmeyer: for porting libre WiFi to Replicant 6.0 and for helping with the scripts. +- Nicola Spanti (RyDroid): +for the French Translation. + To report on bugs, request features, or any help request, please refer to: -Replicant's Forum: "http://redmine.replicant.us/boards/9/topics/14079" +Replicant's Forum: "https://redmine.replicant.us/boards/9/topics/14079" +Replicant's Issue Tracker: "https://redmine.replicant.us/issues" -Compiled APKs are released in the same page linked above. -APKs are signed with this GnuPG key: +Official APKs are released through F-Droid "https://f-droid.org/" +Any unofficial or testing APKs are signed with this GnuPG key: Key ID: CD07CEAD Owner: Filippo Bergamo (AKA Fil) @@ -1,5 +1,18 @@ TODO -as of 2017-12-17 +as of 2018-07-03 -*) turn backend engine into a service -*) introduce support for EAP authentication (if possible at all) +*) Design a better model for preferences using ContentProvider + +*) Control wpa_supplicant directly using documented sockets and commands: + this should be the preferred way of communicating with wpa_supplicant, + allowing a fine-graned control over the authentication process + and giving access to asynchronous events about the connection status. + +*) SQLite database for storing network details: + drop the current JSON/text-file model and build a more consistent, + more secure way of storing data. + +*) Support wpa_supplicant in AP mode to create a Wi-Fi hotspot + +*) Support captive portals (e.g. "public" Wi-Fi systems or on-campus Wi-Fi): + (needs thorough research) diff --git a/app/AndroidManifest.xml b/app/AndroidManifest.xml index b967836..02b0b64 100644 --- a/app/AndroidManifest.xml +++ b/app/AndroidManifest.xml @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="fil.libre.repwifiapp" - android:versionCode="5" - android:versionName="0.6.2" > + android:versionCode="9" + android:versionName="0.9-beta" > <uses-sdk android:minSdkVersion="17" @@ -13,6 +13,7 @@ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> + <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" /> <application android:allowBackup="true" @@ -30,26 +31,23 @@ </intent-filter> </receiver> - <!-- <receiver android:name="fil.libre.repwifiapp.RepWifiIntentReceiver" android:description="@string/receiver_description" android:enabled="true" android:exported="false" - android:icon="@drawable/ic_launcher2" + android:icon="@drawable/ic_stat_repwifi" android:label="RepWifiIntentReceiver" > <intent-filter> <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> <action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" /> </intent-filter> </receiver> - --> - + <activity android:name="fil.libre.repwifiapp.activities.MainActivity" android:label="@string/app_name" - android:launchMode="singleTop" - android:screenOrientation="portrait" > + android:launchMode="singleTop" > <intent-filter> <action android:name="android.intent.action.MAIN" /> @@ -95,7 +93,17 @@ android:name="fil.libre.repwifiapp.activities.VpnSettingsActivity" android:label="@string/title_activity_vpn_settings" > </activity> - <service android:name="de.blinkt.openvpn.api.IOpenVPNAPIService"></service> + + <service android:name="de.blinkt.openvpn.api.IOpenVPNAPIService" > + </service> + <service + android:name="fil.libre.repwifiapp.service.ConnectionManagementService" + android:enabled="true" + android:exported="false" + android:process=":connsvc" > + </service> </application> + + </manifest> diff --git a/app/assets/repwifi-logo-0-small.png b/app/assets/repwifi-logo-0-small.png Binary files differnew file mode 100644 index 0000000..2e865d0 --- /dev/null +++ b/app/assets/repwifi-logo-0-small.png diff --git a/app/assets/repwifi-logo-1-small.png b/app/assets/repwifi-logo-1-small.png Binary files differnew file mode 100644 index 0000000..25276d1 --- /dev/null +++ b/app/assets/repwifi-logo-1-small.png diff --git a/app/assets/repwifi-logo-1.png b/app/assets/repwifi-logo-1.png Binary files differnew file mode 100644 index 0000000..511d3ab --- /dev/null +++ b/app/assets/repwifi-logo-1.png diff --git a/app/gen/fil/libre/repwifiapp/R.java b/app/gen/fil/libre/repwifiapp/R.java index 4874e0a..025ab02 100644 --- a/app/gen/fil/libre/repwifiapp/R.java +++ b/app/gen/fil/libre/repwifiapp/R.java @@ -9,19 +9,19 @@ package fil.libre.repwifiapp; public final class R { public static final class array { - public static final int debug_priority=0x7f050001; - public static final int debug_priority_names=0x7f050000; + public static final int debug_priority=0x7f060001; + public static final int debug_priority_names=0x7f060000; } public static final class attr { } public static final class color { - public static final int Anthracite=0x7f060006; - public static final int ThemeDark=0x7f060001; - public static final int ThemeDarkSecondary=0x7f060002; - public static final int ThemeLight=0x7f060000; - public static final int Transparent=0x7f060005; - public static final int White=0x7f060004; - public static final int black=0x7f060003; + public static final int Anthracite=0x7f070006; + public static final int ThemeDark=0x7f070001; + public static final int ThemeDarkSecondary=0x7f070002; + public static final int ThemeLight=0x7f070000; + public static final int Transparent=0x7f070005; + public static final int White=0x7f070004; + public static final int black=0x7f070003; } public static final class drawable { public static final int button_bg=0x7f020000; @@ -43,64 +43,69 @@ public final class R { public static final int rw_prog_1=0x7f020010; public static final int rw_prog_2=0x7f020011; public static final int rw_prog_3=0x7f020012; + public static final int top_frame=0x7f020013; } public static final class id { - public static final int btn_back=0x7f0a0016; - public static final int btn_delete=0x7f0a0023; - public static final int btn_disconnect=0x7f0a002a; - public static final int btn_hidden_ssid=0x7f0a001c; - public static final int btn_ip_settings=0x7f0a0021; - public static final int btn_manage_nets=0x7f0a001d; - public static final int btn_next_hidden_ssid=0x7f0a000c; - public static final int btn_rescan=0x7f0a0026; - public static final int btn_save_ip_settings=0x7f0a0015; - public static final int btn_save_vpn_settings=0x7f0a002d; - public static final int btn_scan=0x7f0a001b; - public static final int btn_select_hidden_ssid=0x7f0a0008; - public static final int btn_vpn_settings=0x7f0a0022; - public static final int chk_show_pass=0x7f0a0006; - public static final int chk_show_pass_details=0x7f0a0020; - public static final int chk_use_dhcp=0x7f0a000e; - public static final int img_logo=0x7f0a001e; - public static final int img_logo_fil=0x7f0a0001; - public static final int layout=0x7f0a0017; - public static final int layout_selnets=0x7f0a0024; - public static final int lbl_gateway=0x7f0a0013; - public static final int lbl_ip_settings=0x7f0a000d; - public static final int lbl_netmask=0x7f0a0011; - public static final int lbl_static_ip=0x7f0a000f; - public static final int lbl_vpn_settings=0x7f0a002b; - public static final int line=0x7f0a0009; - public static final int login_form=0x7f0a0003; - public static final int menu_btn_closeapp=0x7f0a0035; - public static final int menu_config=0x7f0a0036; - public static final int menu_credits=0x7f0a0037; - public static final int menu_settings=0x7f0a0034; - public static final int pref_autoconnect=0x7f0a0031; - public static final int pref_autostart=0x7f0a0033; - public static final int pref_debug_prio=0x7f0a002e; - public static final int pref_dns1=0x7f0a002f; - public static final int pref_dns2=0x7f0a0030; - public static final int pref_progbar=0x7f0a0032; - public static final int progbar=0x7f0a0018; - public static final int scrollview=0x7f0a0027; - public static final int sign_in_button=0x7f0a0007; - public static final int spin_vpn_profile=0x7f0a002c; - public static final int table_networks=0x7f0a0028; - public static final int txt_credits=0x7f0a0002; - public static final int txt_credits_title=0x7f0a0000; - public static final int txt_gateway=0x7f0a0014; - public static final int txt_insert_pass=0x7f0a0004; - public static final int txt_insert_ssid=0x7f0a000a; - public static final int txt_main=0x7f0a001a; - public static final int txt_msg=0x7f0a0019; - public static final int txt_net_details=0x7f0a001f; - public static final int txt_netmask=0x7f0a0012; - public static final int txt_password=0x7f0a0005; - public static final int txt_selnets=0x7f0a0025; - public static final int txt_ssid=0x7f0a000b; - public static final int txt_static_ip=0x7f0a0010; - public static final int txt_status=0x7f0a0029; + public static final int btn_back=0x7f0b0017; + public static final int btn_delete=0x7f0b0027; + public static final int btn_disconnect=0x7f0b001f; + public static final int btn_hidden_ssid=0x7f0b0021; + public static final int btn_info=0x7f0b001e; + public static final int btn_ip_settings=0x7f0b0025; + public static final int btn_manage_nets=0x7f0b0022; + public static final int btn_next_hidden_ssid=0x7f0b000c; + public static final int btn_rescan=0x7f0b002a; + public static final int btn_save_ip_settings=0x7f0b0016; + public static final int btn_save_vpn_settings=0x7f0b0030; + public static final int btn_scan=0x7f0b0020; + public static final int btn_select_hidden_ssid=0x7f0b0008; + public static final int btn_vpn_settings=0x7f0b0026; + public static final int button_frame=0x7f0b000e; + public static final int chk_show_pass=0x7f0b0006; + public static final int chk_show_pass_details=0x7f0b0024; + public static final int chk_use_dhcp=0x7f0b000f; + public static final int img_logo=0x7f0b001d; + public static final int img_logo_fil=0x7f0b0001; + public static final int layout=0x7f0b0018; + public static final int layout_selnets=0x7f0b0028; + public static final int layout_vpn_profile=0x7f0b002e; + public static final int lbl_gateway=0x7f0b0014; + public static final int lbl_netmask=0x7f0b0012; + public static final int lbl_static_ip=0x7f0b0010; + public static final int lbl_vpn_settings=0x7f0b002d; + public static final int left_frame=0x7f0b000d; + public static final int line=0x7f0b0009; + public static final int login_form=0x7f0b0003; + public static final int menu_btn_closeapp=0x7f0b0039; + public static final int menu_config=0x7f0b003a; + public static final int menu_credits=0x7f0b003b; + public static final int menu_settings=0x7f0b0038; + public static final int pref_autoconnect=0x7f0b0034; + public static final int pref_autostart=0x7f0b0037; + public static final int pref_debug_prio=0x7f0b0031; + public static final int pref_dns1=0x7f0b0032; + public static final int pref_dns2=0x7f0b0033; + public static final int pref_monitor_connection=0x7f0b0035; + public static final int pref_progbar=0x7f0b0036; + public static final int progbar=0x7f0b0019; + public static final int scrollview=0x7f0b002b; + public static final int sign_in_button=0x7f0b0007; + public static final int spin_vpn_profile=0x7f0b002f; + public static final int table_networks=0x7f0b002c; + public static final int top_frame=0x7f0b001b; + public static final int txt_credits=0x7f0b0002; + public static final int txt_credits_title=0x7f0b0000; + public static final int txt_gateway=0x7f0b0015; + public static final int txt_insert_pass=0x7f0b0004; + public static final int txt_insert_ssid=0x7f0b000a; + public static final int txt_msg=0x7f0b001a; + public static final int txt_net_details=0x7f0b0023; + public static final int txt_netmask=0x7f0b0013; + public static final int txt_password=0x7f0b0005; + public static final int txt_selnets=0x7f0b0029; + public static final int txt_ssid=0x7f0b000b; + public static final int txt_static_ip=0x7f0b0011; + public static final int txt_status=0x7f0b001c; } public static final class layout { public static final int activity_credits=0x7f030000; @@ -116,149 +121,18 @@ public final class R { public static final int activity_vpn_settings=0x7f03000a; } public static final class menu { - public static final int activity_input_ssid=0x7f090000; - public static final int activity_ipv4_settings=0x7f090001; - public static final int activity_main=0x7f090002; - public static final int activity_vpn_settings=0x7f090003; + public static final int activity_input_ssid=0x7f0a0000; + public static final int activity_ipv4_settings=0x7f0a0001; + public static final int activity_main=0x7f0a0002; + public static final int activity_vpn_settings=0x7f0a0003; + } + public static final class raw { + public static final int make_system_app=0x7f050000; } public static final class string { /** This file is part of RepWifiApp. - RepWifiApp is Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> - French translation is Copyright (C) 2017 Nicola Spanti - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see <https://www.gnu.org/licenses/>. - - */ - public static final int app_name=0x7f070000; - public static final int back_main=0x7f07000b; - public static final int button_text_next=0x7f070006; - public static final int confirm_exit_app=0x7f070025; - public static final int confirm_kill_backend=0x7f070026; - public static final int confirm_reset_settings=0x7f070024; - public static final int connect_hidden=0x7f07001c; - public static final int credit_text=0x7f070013; - public static final int delete=0x7f070010; - public static final int disconnect=0x7f07000c; - public static final int dns1_default=0x7f070021; - public static final int dns2_default=0x7f070022; - public static final int force_disconnect=0x7f07000d; - public static final int hello_world=0x7f070070; - public static final int input_ssid=0x7f07001d; - public static final int insert_nets_password=0x7f070007; - public static final int manage_networks=0x7f070011; - public static final int menu_closeapp=0x7f070002; - public static final int menu_config=0x7f070014; - public static final int menu_credits=0x7f070001; - public static final int menu_settings=0x7f07001f; - public static final int msg_autoconnect_error=0x7f070037; - public static final int msg_checking_status=0x7f070030; - public static final int msg_confirm_delete_network=0x7f070016; - public static final int msg_connect_fail=0x7f070034; - public static final int msg_connected_to=0x7f070041; - public static final int msg_connecting_to=0x7f07002d; - public static final int msg_disconnect_fail=0x7f070044; - public static final int msg_disconnected=0x7f070043; - public static final int msg_error_ip_format=0x7f070040; - public static final int msg_interface_not_found=0x7f070015; - public static final int msg_invalid_gateway=0x7f070066; - public static final int msg_invalid_ip=0x7f070064; - public static final int msg_invalid_netmask=0x7f070065; - public static final int msg_log_save_fail=0x7f07003f; - public static final int msg_log_saved=0x7f07003e; - public static final int msg_netinfo_delete_fail=0x7f070036; - public static final int msg_netinfo_deleted=0x7f070035; - public static final int msg_network_list_fail=0x7f07003a; - public static final int msg_network_name_empty=0x7f07002c; - public static final int msg_network_save_fail=0x7f070033; - public static final int msg_network_saved=0x7f070032; - public static final int msg_no_network=0x7f07003b; - public static final int msg_no_saved_network=0x7f070046; - public static final int msg_os_unsupported=0x7f070048; - public static final int msg_password_empty=0x7f07002a; - public static final int msg_please_wait=0x7f070031; - public static final int msg_root_denied=0x7f07001b; - public static final int msg_root_disabled=0x7f07001a; - public static final int msg_scanning_for_nets=0x7f07002f; - public static final int msg_select_network_connect=0x7f07003c; - public static final int msg_select_network_manage=0x7f07003d; - public static final int msg_touch_open=0x7f070047; - public static final int msg_vpn_connect_error=0x7f07006e; - public static final int msg_vpn_disconnect=0x7f070068; - public static final int msg_vpn_disconnect_error=0x7f070067; - public static final int msg_vpn_launched=0x7f070069; - public static final int msg_vpn_no_permission=0x7f07006d; - public static final int msg_vpn_no_profile=0x7f070073; - public static final int msg_vpn_wrong_profile=0x7f07006a; - public static final int no=0x7f070018; - public static final int receiver_description=0x7f070023; - public static final int rescan=0x7f070005; - public static final int retry=0x7f070019; - public static final int scan_networks=0x7f070003; - public static final int select_saved_net=0x7f070020; - public static final int show_password=0x7f070008; - public static final int summary_advanced_settings=0x7f07005a; - public static final int summary_autoconnect=0x7f070051; - public static final int summary_dns1=0x7f07004e; - public static final int summary_dns2=0x7f07004f; - public static final int summary_general_settings=0x7f07005b; - public static final int summary_kill_backend=0x7f070027; - public static final int summary_progbar=0x7f070053; - public static final int summary_restore_default=0x7f070057; - public static final int summary_start_at_boot=0x7f070055; - public static final int summary_vpn_settings=0x7f070072; - public static final int text_gateway=0x7f070060; - public static final int text_ip_address=0x7f070042; - public static final int text_ipv4_settings=0x7f07005d; - public static final int text_netinfo_last_used=0x7f070038; - public static final int text_password=0x7f070039; - public static final int text_presented_by=0x7f070028; - public static final int text_static_ip=0x7f07005e; - public static final int text_status=0x7f070045; - public static final int text_subnet_mask=0x7f07005f; - public static final int text_use_dhcp=0x7f07005c; - public static final int text_vpn_package_missing=0x7f07006c; - public static final int title_activity_connect=0x7f070009; - public static final int title_activity_credits=0x7f070012; - /** - This file is part of RepWifiApp. - RepWifiApp is Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> - French translation is Copyright (C) 2017 Nicola Spanti - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see <https://www.gnu.org/licenses/>. - - */ - public static final int title_activity_input_password=0x7f070074; - public static final int title_activity_input_ssid=0x7f07001e; - public static final int title_activity_ipv4_settings=0x7f070061; - public static final int title_activity_long_task=0x7f07000e; - public static final int title_activity_manage_networks=0x7f07000f; - public static final int title_activity_select_network=0x7f070004; - /** - This file is part of RepWifiApp. - RepWifiApp is Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> + RepWifiApp is Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> French translation is Copyright (C) 2017 Nicola Spanti This program is free software: you can redistribute it and/or modify @@ -275,28 +149,139 @@ public final class R { along with this program. If not, see <https://www.gnu.org/licenses/>. */ - public static final int title_activity_settings=0x7f070075; - public static final int title_activity_show_status=0x7f07000a; - public static final int title_activity_vpn_settings=0x7f07006f; - public static final int title_advanced_settings=0x7f070058; - public static final int title_autoconnect=0x7f070050; - public static final int title_dns_1=0x7f07004c; - public static final int title_dns_2=0x7f07004d; - public static final int title_dump_logs=0x7f07004a; - public static final int title_general_settings=0x7f070059; - public static final int title_input_password=0x7f070029; - public static final int title_input_ssid=0x7f07002b; - public static final int title_log_prio=0x7f070049; - public static final int title_progbar=0x7f070052; - public static final int title_reset_backend=0x7f07004b; - public static final int title_restore_default=0x7f070056; - public static final int title_scanning=0x7f07002e; - public static final int title_start_at_boot=0x7f070054; - public static final int title_vpn_settings=0x7f07006b; - public static final int txt_back=0x7f070063; - public static final int txt_save=0x7f070071; - public static final int txt_save_ip_settings=0x7f070062; - public static final int yes=0x7f070017; + public static final int app_name=0x7f080000; + public static final int back_main=0x7f08000b; + public static final int button_text_next=0x7f080006; + public static final int confirm_exit_app=0x7f080023; + public static final int confirm_kill_backend=0x7f080024; + public static final int confirm_reset_settings=0x7f080022; + public static final int connect_hidden=0x7f08001c; + public static final int credit_text=0x7f080013; + public static final int delete=0x7f080010; + public static final int disconnect=0x7f08000c; + public static final int force_disconnect=0x7f08000d; + public static final int hello_world=0x7f08006e; + public static final int input_ssid=0x7f08001d; + public static final int insert_nets_password=0x7f080007; + public static final int manage_networks=0x7f080011; + public static final int menu_closeapp=0x7f080002; + public static final int menu_config=0x7f080014; + public static final int menu_credits=0x7f080001; + public static final int menu_settings=0x7f08001f; + public static final int msg_autoconnect_error=0x7f080035; + public static final int msg_checking_status=0x7f08002e; + public static final int msg_chose_no_system_app=0x7f08007e; + public static final int msg_confirm_delete_network=0x7f080016; + public static final int msg_confirm_make_system_app=0x7f08007d; + public static final int msg_confirm_stop_job=0x7f080075; + public static final int msg_connect_fail=0x7f080032; + public static final int msg_connected_to=0x7f08003f; + public static final int msg_connecting_to=0x7f08002b; + public static final int msg_connection_timeout=0x7f080074; + public static final int msg_connection_timeout_nopass=0x7f08007b; + public static final int msg_disconnect_fail=0x7f080042; + public static final int msg_disconnected=0x7f080041; + public static final int msg_dns_fail=0x7f080072; + public static final int msg_error_generic=0x7f08007f; + public static final int msg_error_ip_format=0x7f08003e; + public static final int msg_fail_connservice=0x7f080080; + public static final int msg_gw_failed=0x7f080073; + public static final int msg_interface_not_found=0x7f080015; + public static final int msg_invalid_gateway=0x7f080064; + public static final int msg_invalid_ip=0x7f080062; + public static final int msg_invalid_netmask=0x7f080063; + public static final int msg_log_save_fail=0x7f08003d; + public static final int msg_log_saved=0x7f08003c; + public static final int msg_make_system_app=0x7f08007c; + public static final int msg_netinfo_delete_fail=0x7f080034; + public static final int msg_netinfo_deleted=0x7f080033; + public static final int msg_network_list_fail=0x7f080038; + public static final int msg_network_name_empty=0x7f08002a; + public static final int msg_network_save_fail=0x7f080031; + public static final int msg_network_saved=0x7f080030; + public static final int msg_no_network=0x7f080039; + public static final int msg_no_saved_network=0x7f080044; + public static final int msg_os_unsupported=0x7f080046; + public static final int msg_password_empty=0x7f080028; + public static final int msg_please_wait=0x7f08002f; + public static final int msg_root_denied=0x7f08001b; + public static final int msg_root_disabled=0x7f08001a; + public static final int msg_scanning_for_nets=0x7f08002d; + public static final int msg_select_network_connect=0x7f08003a; + public static final int msg_select_network_manage=0x7f08003b; + public static final int msg_touch_open=0x7f080045; + public static final int msg_vpn_connect_error=0x7f08006c; + public static final int msg_vpn_disconnect=0x7f080066; + public static final int msg_vpn_disconnect_error=0x7f080065; + public static final int msg_vpn_error_manual_open=0x7f080077; + public static final int msg_vpn_launched=0x7f080067; + public static final int msg_vpn_no_permission=0x7f08006b; + public static final int msg_vpn_no_profile=0x7f080071; + public static final int msg_vpn_service_error=0x7f080076; + public static final int msg_vpn_wrong_profile=0x7f080068; + public static final int no=0x7f080018; + public static final int receiver_description=0x7f080021; + public static final int rescan=0x7f080005; + public static final int retry=0x7f080019; + public static final int scan_networks=0x7f080003; + public static final int select_saved_net=0x7f080020; + public static final int show_password=0x7f080008; + public static final int summary_advanced_settings=0x7f080058; + public static final int summary_autoconnect=0x7f08004f; + public static final int summary_dns1=0x7f08004c; + public static final int summary_dns2=0x7f08004d; + public static final int summary_general_settings=0x7f080059; + public static final int summary_kill_backend=0x7f080025; + public static final int summary_monitor_connection_state=0x7f080081; + public static final int summary_progbar=0x7f080051; + public static final int summary_restore_default=0x7f080055; + public static final int summary_start_at_boot=0x7f080053; + public static final int summary_vpn_settings=0x7f080070; + public static final int text_broadcast_address=0x7f080078; + public static final int text_connection_info=0x7f08007a; + public static final int text_gateway=0x7f08005e; + public static final int text_hardware_address=0x7f080079; + public static final int text_ip_address=0x7f080040; + public static final int text_ipv4_settings=0x7f08005b; + public static final int text_netinfo_last_used=0x7f080036; + public static final int text_password=0x7f080037; + public static final int text_presented_by=0x7f080026; + public static final int text_static_ip=0x7f08005c; + public static final int text_status=0x7f080043; + public static final int text_subnet_mask=0x7f08005d; + public static final int text_use_dhcp=0x7f08005a; + public static final int text_vpn_package_missing=0x7f08006a; + public static final int title_activity_connect=0x7f080009; + public static final int title_activity_credits=0x7f080012; + public static final int title_activity_input_password=0x7f080083; + public static final int title_activity_input_ssid=0x7f08001e; + public static final int title_activity_ipv4_settings=0x7f08005f; + public static final int title_activity_long_task=0x7f08000e; + public static final int title_activity_manage_networks=0x7f08000f; + public static final int title_activity_select_network=0x7f080004; + public static final int title_activity_settings=0x7f080084; + public static final int title_activity_show_status=0x7f08000a; + public static final int title_activity_vpn_settings=0x7f08006d; + public static final int title_advanced_settings=0x7f080056; + public static final int title_autoconnect=0x7f08004e; + public static final int title_dns_1=0x7f08004a; + public static final int title_dns_2=0x7f08004b; + public static final int title_dump_logs=0x7f080048; + public static final int title_general_settings=0x7f080057; + public static final int title_input_password=0x7f080027; + public static final int title_input_ssid=0x7f080029; + public static final int title_log_prio=0x7f080047; + public static final int title_monitor_connection_state=0x7f080082; + public static final int title_progbar=0x7f080050; + public static final int title_reset_backend=0x7f080049; + public static final int title_restore_default=0x7f080054; + public static final int title_scanning=0x7f08002c; + public static final int title_start_at_boot=0x7f080052; + public static final int title_vpn_settings=0x7f080069; + public static final int txt_back=0x7f080061; + public static final int txt_save=0x7f08006f; + public static final int txt_save_ip_settings=0x7f080060; + public static final int yes=0x7f080017; } public static final class style { /** @@ -320,19 +305,19 @@ public final class R { API 14 theme customizations can go here. */ - public static final int AppBaseTheme=0x7f080000; + public static final int AppBaseTheme=0x7f090000; /** Application theme. */ - public static final int AppTheme=0x7f080001; - public static final int LoginFormContainer=0x7f080008; - public static final int RepWifi_BorderlessButton=0x7f080005; - public static final int RepWifi_ButtonBar_AlertDialog=0x7f080006; - public static final int TextAppearanceMenu=0x7f080002; + public static final int AppTheme=0x7f090001; + public static final int LoginFormContainer=0x7f090008; + public static final int RepWifi_BorderlessButton=0x7f090005; + public static final int RepWifi_ButtonBar_AlertDialog=0x7f090006; + public static final int TextAppearanceMenu=0x7f090002; /** dialog theme */ - public static final int Theme_RepWifiDialogTheme=0x7f080007; - public static final int Theme_SettingsTheme=0x7f080003; - public static final int listViewPrefs=0x7f080004; + public static final int Theme_RepWifiDialogTheme=0x7f090007; + public static final int Theme_SettingsTheme=0x7f090003; + public static final int listViewPrefs=0x7f090004; } public static final class xml { public static final int debug_settings=0x7f040000; diff --git a/app/lint.xml b/app/lint.xml new file mode 100644 index 0000000..ee0eead --- /dev/null +++ b/app/lint.xml @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="UTF-8"?> +<lint> +</lint>
\ No newline at end of file diff --git a/app/res/drawable/top_frame.xml b/app/res/drawable/top_frame.xml new file mode 100644 index 0000000..b4438e0 --- /dev/null +++ b/app/res/drawable/top_frame.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle" > + + <solid android:color="@color/black" /> + + <corners android:radius="3dp" /> + + <stroke + android:width="2dp" + android:color="@color/ThemeLight" /> + +</shape>
\ No newline at end of file diff --git a/app/res/layout-land/activity_input_ssid.xml b/app/res/layout-land/activity_input_ssid.xml new file mode 100644 index 0000000..9be39e6 --- /dev/null +++ b/app/res/layout-land/activity_input_ssid.xml @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="@color/black" + android:baselineAligned="false" + android:padding="15dp" > + + <RelativeLayout + android:id="@+id/left_frame" + android:layout_width="0dip" + android:layout_weight=".50" + android:layout_height="fill_parent" + android:background="@drawable/top_frame" + android:padding="5dp" + android:paddingRight="10dp" > + + <Button + android:id="@+id/btn_select_hidden_ssid" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_centerVertical="true" + android:background="@drawable/repwifi_button" + android:onClick="onBtnSelectClick" + android:paddingLeft="32dp" + android:paddingRight="32dp" + android:text="@string/select_saved_net" + android:textColor="@color/ThemeLight" /> + </RelativeLayout> + + <RelativeLayout + android:id="@+id/button_frame" + android:layout_toRightOf="@id/left_frame" + android:layout_width="0dip" + android:layout_weight=".50" + android:layout_height="fill_parent" + android:background="@drawable/top_frame" + android:padding="15dp" > + + <EditText + android:id="@+id/txt_ssid" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="30dp" + android:layout_centerInParent="true" + android:background="@drawable/button_bg" + android:inputType="text" + android:maxLines="1" + android:padding="5dp" + android:singleLine="true" + android:textColor="@color/ThemeLight" + android:textCursorDrawable="@null" + android:textSize="10pt" /> + + <TextView + android:id="@+id/txt_insert_ssid" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_above="@id/txt_ssid" + android:layout_centerHorizontal="true" + android:layout_marginBottom="10dp" + android:background="@color/black" + android:maxLines="1" + android:text="@string/input_ssid" + android:textColor="@color/ThemeLight" + android:textSize="9pt" /> + + <LinearLayout + style="@style/LoginFormContainer" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/txt_ssid" + android:background="@color/black" + android:orientation="vertical" > + + <Button + android:id="@+id/btn_next_hidden_ssid" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="right" + android:layout_marginTop="16dp" + android:background="@drawable/repwifi_button" + android:onClick="onBtnNextClick" + android:paddingLeft="32dp" + android:paddingRight="32dp" + android:text="@string/button_text_next" + android:textColor="@color/ThemeLight" /> + </LinearLayout> + </RelativeLayout> + +</LinearLayout>
\ No newline at end of file diff --git a/app/res/layout-land/activity_ipv4_settings.xml b/app/res/layout-land/activity_ipv4_settings.xml new file mode 100644 index 0000000..7bdb38b --- /dev/null +++ b/app/res/layout-land/activity_ipv4_settings.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:baselineAligned="false" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="@color/black" + android:padding="15dp" > + + + <RelativeLayout + android:id="@+id/left_frame" + android:layout_width="0dip" + android:layout_weight=".55" + android:layout_height="fill_parent" + android:background="@drawable/top_frame" + android:padding="5dp" + android:paddingRight="10dp" > + + <CheckBox + android:id="@+id/chk_use_dhcp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_marginBottom="5dp" + android:text="@string/text_use_dhcp" + android:textColor="@color/ThemeLight" + android:textSize="8pt" /> + + <TextView + android:id="@+id/lbl_static_ip" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/chk_use_dhcp" + android:layout_margin="5dp" + android:background="@color/black" + android:text="@string/text_ip_address" + android:textColor="@color/ThemeLight" + android:textSize="9pt" /> + + <EditText + android:id="@+id/txt_static_ip" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/chk_use_dhcp" + android:layout_marginBottom="8dp" + android:layout_marginRight="5dp" + android:layout_toRightOf="@id/lbl_static_ip" + android:background="@drawable/button_bg" + android:inputType="text" + android:padding="3dp" + android:textColor="@color/ThemeLight" + android:textSize="8pt" /> + + <TextView + android:id="@+id/lbl_netmask" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/txt_static_ip" + android:layout_margin="5dp" + android:background="@color/black" + android:text="@string/text_subnet_mask" + android:textColor="@color/ThemeLight" + android:textSize="9pt" /> + + <EditText + android:id="@+id/txt_netmask" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/txt_static_ip" + android:layout_marginBottom="8dp" + android:layout_marginRight="5dp" + android:layout_toRightOf="@id/lbl_netmask" + android:background="@drawable/button_bg" + android:inputType="text" + android:padding="3dp" + android:textColor="@color/ThemeLight" + android:textSize="8pt" /> + + <TextView + android:id="@+id/lbl_gateway" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/txt_netmask" + android:layout_margin="5dp" + android:background="@color/black" + android:text="@string/text_gateway" + android:textColor="@color/ThemeLight" + android:textSize="9pt" /> + + <EditText + android:id="@+id/txt_gateway" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/txt_netmask" + android:layout_marginBottom="8dp" + android:layout_marginRight="5dp" + android:layout_toRightOf="@id/lbl_gateway" + android:background="@drawable/button_bg" + android:inputType="text" + android:padding="3dp" + android:textColor="@color/ThemeLight" + android:textSize="8pt" /> + + </RelativeLayout> + + <RelativeLayout + android:id="@+id/button_frame" + android:layout_toRightOf="@id/left_frame" + android:layout_width="0dip" + android:layout_weight=".45" + android:layout_height="wrap_content" + android:background="@color/black" + android:padding="5dp" > + + <Button + android:id="@+id/btn_save_ip_settings" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/txt_gateway" + android:layout_centerHorizontal="true" + android:layout_marginBottom="10dp" + android:paddingLeft="5dp" + android:paddingRight="5dp" + android:background="@drawable/repwifi_button" + android:onClick="btnSaveClick" + android:text="@string/txt_save_ip_settings" + android:textColor="@color/ThemeLight" /> + + <Button + android:id="@+id/btn_back" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/btn_save_ip_settings" + android:layout_centerHorizontal="true" + android:layout_marginBottom="10dp" + android:paddingLeft="5dp" + android:paddingRight="5dp" + android:background="@drawable/repwifi_button" + android:onClick="btnBackClick" + android:text="@string/txt_back" + android:textColor="@color/ThemeLight" /> + + + </RelativeLayout> + + +</LinearLayout>
\ No newline at end of file diff --git a/app/res/layout-land/activity_main.xml b/app/res/layout-land/activity_main.xml new file mode 100644 index 0000000..84461c0 --- /dev/null +++ b/app/res/layout-land/activity_main.xml @@ -0,0 +1,128 @@ +<!-- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="@color/black" + android:padding="15dp" > --> + + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:baselineAligned="false" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="@color/black" + android:padding="15dp"> + + + <RelativeLayout + android:id="@+id/top_frame" + android:layout_width="0dip" + android:layout_weight=".55" + android:layout_height="fill_parent" + android:background="@drawable/top_frame" + android:padding="5dp" + android:paddingRight="10dp" > + + <TextView + android:id="@+id/txt_status" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:background="#80000000" + android:textColor="@color/ThemeLight" + android:textIsSelectable="true" + android:layout_marginTop="10dp" + android:layout_marginBottom="10dp" /> + + <ImageView + android:id="@+id/img_logo" + android:layout_below="@id/txt_status" + android:layout_width="80dp" + android:layout_height="80dp" + android:layout_margin="5dp" + android:paddingRight="5dp" + android:contentDescription="repwifi_logo" > + </ImageView> + + <Button + android:id="@+id/btn_info" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_marginTop="10dp" + android:layout_marginRight="5dp" + android:layout_toRightOf="@id/img_logo" + android:layout_below="@id/txt_status" + android:background="@drawable/repwifi_button" + android:onClick="onBtnInfoClick" + android:text="@string/text_connection_info" + android:textColor="@color/ThemeLight" /> + + <Button + android:id="@+id/btn_disconnect" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_marginTop="10dp" + android:layout_marginRight="5dp" + android:layout_below="@id/btn_info" + android:layout_toRightOf="@id/img_logo" + android:background="@drawable/repwifi_button" + android:onClick="onBtnDisconnectClick" + android:text="@string/disconnect" + android:textColor="@color/ThemeLight" /> + + </RelativeLayout> + + + <RelativeLayout + android:id="@+id/button_frame" + android:layout_toRightOf="@id/top_frame" + android:layout_width="0dip" + android:layout_weight=".45" + android:layout_height="wrap_content" + android:background="@color/black" + android:padding="5dp" > + + <Button + android:id="@+id/btn_scan" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_marginBottom="10dp" + android:background="@drawable/repwifi_button" + android:onClick="btnScanClick" + android:paddingLeft="5dp" + android:paddingRight="5dp" + android:text="@string/scan_networks" + android:textColor="@color/ThemeLight" /> + + <Button + android:id="@+id/btn_hidden_ssid" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/btn_scan" + android:layout_centerHorizontal="true" + android:layout_marginBottom="10dp" + android:paddingLeft="5dp" + android:paddingRight="5dp" + android:background="@drawable/repwifi_button" + android:onClick="btnHiddenClick" + android:text="@string/connect_hidden" + android:textColor="@color/ThemeLight" /> + + <Button + android:id="@+id/btn_manage_nets" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/btn_hidden_ssid" + android:layout_centerHorizontal="true" + android:layout_marginBottom="10dp" + android:background="@drawable/repwifi_button" + android:onClick="btnManageClick" + android:paddingLeft="5dp" + android:paddingRight="5dp" + android:text="@string/manage_networks" + android:textColor="@color/ThemeLight" /> + + </RelativeLayout> + + </LinearLayout>
\ No newline at end of file diff --git a/app/res/layout-land/activity_network_details.xml b/app/res/layout-land/activity_network_details.xml new file mode 100644 index 0000000..1b70f79 --- /dev/null +++ b/app/res/layout-land/activity_network_details.xml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:baselineAligned="false" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="@color/black" + android:padding="15dp" > + + + <RelativeLayout + android:id="@+id/left_frame" + android:layout_width="0dip" + android:layout_weight=".55" + android:layout_height="fill_parent" + android:background="@drawable/top_frame" + android:padding="5dp" + android:paddingRight="10dp" > + + <TextView + android:id="@+id/txt_net_details" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_margin="10dp" + android:background="@color/black" + android:textColor="@color/ThemeLight" + android:textIsSelectable="true" + android:textSize="7pt" /> + + <CheckBox + android:id="@+id/chk_show_pass_details" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/txt_net_details" + android:text="@string/show_password" + android:textColor="@color/ThemeLight" + android:textSize="10pt" /> + + </RelativeLayout> + + <RelativeLayout + android:id="@+id/button_frame" + android:layout_toRightOf="@id/left_frame" + android:layout_width="0dip" + android:layout_weight=".45" + android:layout_height="wrap_content" + android:background="@color/black" + android:padding="5dp" > + + <Button + android:id="@+id/btn_ip_settings" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/chk_show_pass_details" + android:layout_centerHorizontal="true" + android:layout_marginBottom="10dp" + android:background="@drawable/repwifi_button" + android:onClick="btnIpSettingsClick" + android:text="@string/text_ipv4_settings" + android:textColor="@color/ThemeLight" + android:paddingLeft="5dp" + android:paddingRight="5dp" /> + + <Button + android:id="@+id/btn_vpn_settings" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/btn_ip_settings" + android:layout_centerHorizontal="true" + android:layout_marginBottom="10dp" + android:paddingLeft="5dp" + android:paddingRight="5dp" + android:background="@drawable/repwifi_button" + android:onClick="btnVpnSettingsClick" + android:text="@string/title_activity_vpn_settings" + android:textColor="@color/ThemeLight" /> + + <Button + android:id="@+id/btn_delete" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/btn_vpn_settings" + android:layout_centerHorizontal="true" + android:layout_marginBottom="10dp" + android:paddingLeft="5dp" + android:paddingRight="5dp" + android:background="@drawable/repwifi_button" + android:onClick="btnDeleteClick" + android:text="@string/delete" + android:textColor="@color/ThemeLight" /> + + + </RelativeLayout> + + +</LinearLayout>
\ No newline at end of file diff --git a/app/res/layout-land/activity_vpn_settings.xml b/app/res/layout-land/activity_vpn_settings.xml new file mode 100644 index 0000000..ce6b521 --- /dev/null +++ b/app/res/layout-land/activity_vpn_settings.xml @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:baselineAligned="false" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="@color/black" + android:padding="15dp" > + + + <RelativeLayout + android:id="@+id/left_frame" + android:layout_width="0dip" + android:layout_weight=".55" + android:layout_height="fill_parent" + android:background="@drawable/top_frame" + android:padding="5dp" + android:paddingRight="10dp" > + + <TextView + android:id="@+id/lbl_vpn_settings" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_margin="10dp" + android:background="@color/black" + android:textColor="@color/ThemeLight" + android:textIsSelectable="false" + android:textSize="7pt" /> + + + + </RelativeLayout> + + <RelativeLayout + android:id="@+id/button_frame" + android:layout_toRightOf="@id/left_frame" + android:layout_width="0dip" + android:layout_weight=".45" + android:layout_height="wrap_content" + android:background="@color/black" + android:padding="5dp" > + + <RelativeLayout + android:id="@+id/layout_vpn_profile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/lbl_vpn_settings" + android:layout_margin="10dp" + android:background="@drawable/repwifi_button"> + android:textAlignment="center" + + <Spinner + android:id="@+id/spin_vpn_profile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:inputType="text" + android:textAlignment="center" + android:gravity="center" + android:textSize="10pt" + android:popupBackground="@color/ThemeLight" /> + + </RelativeLayout> + + + <Button + android:id="@+id/btn_save_vpn_settings" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/layout_vpn_profile" + android:layout_centerHorizontal="true" + android:layout_marginTop="25dp" + android:background="@drawable/repwifi_button" + android:onClick="btnSaveClick" + android:text="@string/txt_save" + android:textColor="@color/ThemeLight" /> + + <Button + android:id="@+id/btn_back" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/btn_save_vpn_settings" + android:layout_centerHorizontal="true" + android:layout_marginTop="15dp" + android:background="@drawable/repwifi_button" + android:onClick="btnBackClick" + android:text="@string/txt_back" + android:textColor="@color/ThemeLight" /> + + + </RelativeLayout> + + +</LinearLayout>
\ No newline at end of file diff --git a/app/res/layout/activity_input_ssid.xml b/app/res/layout/activity_input_ssid.xml index aa7e8f3..c525692 100644 --- a/app/res/layout/activity_input_ssid.xml +++ b/app/res/layout/activity_input_ssid.xml @@ -6,7 +6,6 @@ android:padding="20dp" tools:context=".InputSsidActivity" > - <!-- --> <Button android:id="@+id/btn_select_hidden_ssid" diff --git a/app/res/layout/activity_ipv4_settings.xml b/app/res/layout/activity_ipv4_settings.xml index 5c0887c..dc30bc0 100644 --- a/app/res/layout/activity_ipv4_settings.xml +++ b/app/res/layout/activity_ipv4_settings.xml @@ -6,21 +6,10 @@ android:padding="15dp" tools:context=".Ipv4SettingsActivity" > - <TextView - android:id="@+id/lbl_ip_settings" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_margin="10dp" - android:background="@color/black" - android:text="@string/text_ipv4_settings" - android:textColor="@color/ThemeLight" - android:textSize="12pt" /> - <CheckBox android:id="@+id/chk_use_dhcp" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_below="@id/lbl_ip_settings" android:layout_margin="10dp" android:text="@string/text_use_dhcp" android:textColor="@color/ThemeLight" diff --git a/app/res/layout/activity_main.xml b/app/res/layout/activity_main.xml index b9359ba..41f6367 100644 --- a/app/res/layout/activity_main.xml +++ b/app/res/layout/activity_main.xml @@ -4,25 +4,75 @@ android:background="@color/black" android:padding="15dp" > - <TextView - android:id="@+id/txt_main" - android:layout_width="fill_parent" + <RelativeLayout + android:id="@+id/top_frame" + android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerHorizontal="true" - android:layout_marginBottom="10dp" - android:layout_marginTop="10dp" - android:background="@color/black" - android:maxLines="10" - android:text="" - android:textColor="@color/ThemeLight" /> + android:background="@drawable/top_frame" + android:orientation="vertical" + android:padding="10dp" > + + <TextView + android:id="@+id/txt_status" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:background="#80000000" + android:textColor="@color/ThemeLight" + android:textIsSelectable="true" + android:layout_marginTop="10dp" + android:layout_marginBottom="10dp" /> + + <ImageView + android:id="@+id/img_logo" + android:layout_below="@id/txt_status" + android:layout_width="90dp" + android:layout_height="90dp" + android:layout_margin="5dp" + android:paddingRight="5dp" + android:contentDescription="repwifi_logo" > + </ImageView> + + <Button + android:id="@+id/btn_info" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_marginTop="10dp" + android:layout_toRightOf="@id/img_logo" + android:layout_below="@id/txt_status" + android:background="@drawable/repwifi_button" + android:onClick="onBtnInfoClick" + android:text="@string/text_connection_info" + android:textColor="@color/ThemeLight" + android:paddingTop="0dp" + android:paddingBottom="0dp" /> + + <Button + android:id="@+id/btn_disconnect" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_marginTop="10dp" + android:layout_below="@id/btn_info" + android:layout_toRightOf="@id/img_logo" + android:background="@drawable/repwifi_button" + android:onClick="onBtnDisconnectClick" + android:text="@string/disconnect" + android:textColor="@color/ThemeLight" /> + + </RelativeLayout> + <Button + android:layout_below="@id/top_frame" android:id="@+id/btn_scan" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_centerHorizontal="true" - android:layout_marginBottom="20dp" - android:layout_marginTop="15dp" + android:layout_marginBottom="15dp" + android:layout_marginTop="20dp" android:background="@drawable/repwifi_button" android:onClick="btnScanClick" android:paddingLeft="5dp" @@ -36,21 +86,21 @@ android:layout_height="wrap_content" android:layout_below="@id/btn_scan" android:layout_centerHorizontal="true" - android:layout_marginBottom="20dp" + android:layout_marginBottom="15dp" android:background="@drawable/repwifi_button" android:onClick="btnHiddenClick" android:paddingLeft="5dp" android:paddingRight="5dp" android:text="@string/connect_hidden" android:textColor="@color/ThemeLight" /> - + <Button android:id="@+id/btn_manage_nets" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_below="@id/btn_hidden_ssid" android:layout_centerHorizontal="true" - android:layout_marginBottom="20dp" + android:layout_marginBottom="15dp" android:background="@drawable/repwifi_button" android:onClick="btnManageClick" android:paddingLeft="5dp" @@ -58,13 +108,4 @@ android:text="@string/manage_networks" android:textColor="@color/ThemeLight" /> - <ImageView - android:id="@+id/img_logo" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_below="@id/btn_manage_nets" - android:layout_centerHorizontal="true" - android:layout_margin="20dp" > - </ImageView> - </RelativeLayout>
\ No newline at end of file diff --git a/app/res/layout/activity_show_status.xml b/app/res/layout/activity_show_status.xml index 5afc596..1491015 100644 --- a/app/res/layout/activity_show_status.xml +++ b/app/res/layout/activity_show_status.xml @@ -12,32 +12,7 @@ android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:background="@color/black" + android:textIsSelectable="true" android:textColor="@color/ThemeLight" /> - - <Button - android:id="@+id/btn_disconnect" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@id/txt_status" - android:layout_centerHorizontal="true" - android:layout_centerVertical="true" - android:layout_marginTop="10dp" - android:background="@drawable/repwifi_button" - android:onClick="onBtnDisconnectClick" - android:text="@string/disconnect" - android:textColor="@color/ThemeLight" /> - - <Button - android:id="@+id/btn_back" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@id/txt_status" - android:layout_centerHorizontal="true" - android:layout_centerVertical="true" - android:layout_marginTop="10dp" - android:background="@drawable/repwifi_button" - android:onClick="onBtnMainClick" - android:text="@string/back_main" - android:textColor="@color/ThemeLight" /> - + </RelativeLayout>
\ No newline at end of file diff --git a/app/res/layout/activity_vpn_settings.xml b/app/res/layout/activity_vpn_settings.xml index 4378baa..e90f611 100644 --- a/app/res/layout/activity_vpn_settings.xml +++ b/app/res/layout/activity_vpn_settings.xml @@ -14,25 +14,34 @@ android:background="@color/black" android:textColor="@color/ThemeLight" android:textIsSelectable="false" - android:textSize="9pt" /> + android:textSize="7pt" /> - <Spinner - android:id="@+id/spin_vpn_profile" + <RelativeLayout + android:id="@+id/layout_vpn_profile" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/lbl_vpn_settings" android:layout_margin="10dp" - android:background="@drawable/button_bg" - android:inputType="text" - android:padding="1pt" - android:textColor="@color/ThemeLight" - android:textSize="10pt" /> + android:background="@drawable/repwifi_button"> + android:textAlignment="center" + + <Spinner + android:id="@+id/spin_vpn_profile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:inputType="text" + android:textAlignment="center" + android:gravity="center" + android:textSize="10pt" + android:popupBackground="@color/ThemeLight" /> + + </RelativeLayout> <Button android:id="@+id/btn_save_vpn_settings" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_below="@id/spin_vpn_profile" + android:layout_below="@id/layout_vpn_profile" android:layout_centerHorizontal="true" android:layout_marginTop="25dp" android:background="@drawable/repwifi_button" diff --git a/app/res/raw/make_system_app.sh b/app/res/raw/make_system_app.sh new file mode 100644 index 0000000..ce79a2c --- /dev/null +++ b/app/res/raw/make_system_app.sh @@ -0,0 +1,93 @@ +#!/bin/bash + +# +# 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 script copies the input APK into the /system/priv-app folder +# this makes the target App a system app, having system internal permissions. +# +# Input args: $1 = [destination-dir], $2=[source-file], $3=[system-partition] +# +# ---------------------------------------------------------------------------- + +LOG_TAG="RepWifi" +DESTDIR=$1 +SRCAPK=$2 +SYSPART=$3 + + +function logE(){ + log -p e -t $LOG_TAG $1 +} + +function logD(){ + log -p d -t $LOG_TAG $1 +} + +# remounts /system readonly and restarts android's environment +# exits the script with the provided exit code $1 +function finalize(){ + + mount -o ro,remount "$SYSPART" + start + exit $1 + +} + +logD "Starting attempt to make myself a system app:" +logD "Source apk: $SRCAPK" +logD "Destination dir: $DESTDIR" + +# stop android's user shell to prevent userspace actions +# while the system partition is writeable: +stop + +# mount /system partition read-write: +mount -o rw,remount $SYSPART + +if [[ -e $DESTDIR ]]; then + logD "Destination dir already exists." +else + logD "Destination dir doesn't exist; creating it.." + mkdir "$DESTDIR" + + if [[ $? != 0 ]]; then + logE "FAILED to create destination dir" + finalize 1 + fi +fi + +# copy the source APK into the system apps' directory +logD "Copying apk.." +cp "$SRCAPK" "$DESTDIR" + +if [[ $? == 0 ]]; then + logD "Succeeded in making myself a system app!" + finalize 0 +else + logE "Failed to make myself a system app!" + finalize 2 +fi + +# just in case: +mount -o ro,remount "$SYSPART" +start + diff --git a/app/res/values-fr/strings.xml b/app/res/values-fr/strings.xml index 0c97583..fd0eb10 100644 --- a/app/res/values-fr/strings.xml +++ b/app/res/values-fr/strings.xml @@ -3,7 +3,7 @@ <!-- This file is part of RepWifiApp. - RepWifiApp is Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> + RepWifiApp is Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> French translation is Copyright (C) 2017 Nicola Spanti This program is free software: you can redistribute it and/or modify @@ -24,10 +24,10 @@ <string name="menu_credits">Info et crédits</string> <string name="menu_closeapp">Tuer RepWifi</string> <string name="scan_networks">Scan des réseaux</string> - <string name="title_activity_select_network">Sélectionner un réseau</string> + <string name="title_activity_select_network">Selectionner un réseau</string> <string name="rescan">Répéter le scan</string> <string name="button_text_next">Prochain ></string> - <string name="insert_nets_password">Entrer un mot de passe pour se connecter à :</string> + <string name="insert_nets_password">Fournisser un mot de passe pour se connecter à :</string> <string name="show_password">Montrer le mot de passe</string> <string name="title_activity_connect">ActivitéDeConnexion</string> <string name="title_activity_show_status">Statut de connexion</string> @@ -39,30 +39,30 @@ <string name="delete">Supprimer les informations sur le réseau</string> <string name="manage_networks">Gérer les réseaux</string> <string name="title_activity_credits">Crédits</string> - <string name="credit_text">Copyright 2017 Filippo \"Fil\" Bergamo<br/><br/>Cette app est un logiciel libre.<br/>Elle est sous licence <a href="https://www.gnu.org/licenses/gpl.txt">GPL v3</a><br/><br/>Cette app est développée en tant que contribution au <br/><a href="https://www.replicant.us">projet Replicant</a><br/>Pour signaler des bogues, demander des fonctionnalités, ou n\'importe quelle autre demande d\'aide, veuillez vous référer à :<br/><a href="https://redmine.replicant.us/projects/replicant/boards">Forum de Replicant</a><br/><br/>Le code source est hébergé sur : <br/><a href="https://git.replicant.us/contrib/Fil/RepWifiApp/">https://git.replicant.us/contrib/Fil/RepWifiApp/</a><br/><br/><b><u><big>Merci GNUs:</big></u></b><br/><br/><b>Tiberiu - Technoethical</b><br/>pour avoir fait le travail initial de porter le Wi-Fi sur Replicant 4.2.<br/><br/><b>Wolfgang Wiedmeyer</b><br/>pour le portage du Wi-Fi libre sur Replicant 6.0 et pour l\'aide sur les scripts.<br/><br/><b>Nicola Spanti</b><br/>pour la traduction en français.<br/><br/><b>Nichlas Severinsen</b><br/>pour la publication de cette app sur F-Droid.<br/><br/></string> + <string name="credit_text">Copyright 2017, 2018 Filippo \"Fil\" Bergamo<br/><br/>Cette app est du logiciel libre.<br/>Elle est sous licence <a href="https://www.gnu.org/licenses/gpl.txt">GPL v3</a><br/><br/>Cette app est développé en tant que contribution au <br/><a href="https://www.replicant.us">projet Replicant</a><br/>Pour signaler des bogues, demander des fonctionnalités, ou n\'importe quelle autre demande d\'aide, veuillez vous référer à :<br/><a href="https://redmine.replicant.us/projects/replicant/boards">Forum de Replicant</a><br/><br/>Le code source est hébergé sur : <br/><a href="https://git.replicant.us/contrib/Fil/RepWifiApp/">https://git.replicant.us/contrib/Fil/RepWifiApp/</a><br/><br/><b><u><big>Merci GNUs:</big></u></b><br/><br/><b>Tiberiu - Technoethical</b><br/>pour avoir fait le travail initial de porter le Wi-Fi sur Replicant 4.2.<br/><br/><b>Wolfgang Wiedmeyer</b><br/>pour le portage du Wi-Fi libre sur Replicant 6.0 et pour l\'aide sur les scripts.<br/><br/><b>Nicola Spanti</b><br/>pour la traduction en français.<br/><br/><b>Nichlas Severinsen</b><br/>pour la publication de cette app sur F-Droid.<br/><br/></string> <string name="menu_config">Paramètres</string> - <string name="msg_interface_not_found">Pas d\'adaptateur Wi-Fi trouvé !\nVeuillez connecter un adaptateur Wi-Fi géré et réessayer.</string> - <string name="msg_confirm_delete_network">Êtes-vous sûr de vouloir supprimer ce réseau ?</string> + <string name="msg_interface_not_found">Pas d\'adaptateur Wi-Fi trouvé !\nVeuillez connecter un adaptateur Wi-Fi géréet réessayer.</string> + <string name="msg_confirm_delete_network">Êtes vous sûr de vouloir supprimer ce réseau ?</string> <string name="yes">Oui</string> <string name="no">Non</string> <string name="retry">Réessayer</string> <string name="msg_root_disabled">Les droits root sont désactivés.\n\nIl semble que les droits root soient désactivés sur cette appareil.\nRepWifi a besoin des droits root pour fonctionner.\nVeuillez activer les droits root pour les applications dans :\nParamètres > Options pour développeur > Droits roots > "Applications seulement"\nQuand ce sera fait, redémarrer RepWifi et donner la permission d\'utiliser les droits, si c\'est demandé.</string> - <string name="msg_root_denied">Droits root refusés.\nIl semble que vous avez refusé les droits root à RepWifi.\nRepWifi a besoin des droits root pour fonctionner.\nVeuillez redémarrer l\'application, puis accorder les droits root à RepWifi, quand cela est demandé.</string> + <string name="msg_root_denied">Droits root refusés.\nIl semble que vous avez refusé les droits root à RepWifi.\nRepWifi a besoin des drots root pour fonctionner.\nVeuillez redémarrer l\'application, puis accorder les droits root à RepWifi, quand cela est demandé.</string> <string name="connect_hidden">Se connecter aux réseaux cachés</string> - <string name="input_ssid">Ou.. Entrer le nom du réseau (SSID)</string> - <string name="title_activity_input_ssid">Entrer le nom du réseau</string> + <string name="input_ssid">Ou.. Fournisser le nom du réseau (SSID)</string> + <string name="title_activity_input_ssid">Fournisser le nom du réseau</string> <string name="menu_settings">Paramètres</string> <string name="select_saved_net">Utiliser le réseau enregistré</string> <string name="receiver_description">RepWifi est attentif à l\'événement de démarrage. Cela lui permet d\'avoir une fonctionnalité de démarrage automatique et une autre lié aux notifications.</string> - <string name="confirm_reset_settings">Êtes-vous sûr de vouloir remettre tous les paramètres aux valeurs par défaut ?</string> - <string name="confirm_exit_app">Êtes-vous sûr de vouloir tuer RepWifi ? Cela vous déconnectera du Wi-Fi.</string> - <string name="confirm_kill_backend">Êtes-vous sûr de vouloir tuer les processus d\'arrière-plan ?</string> - <string name="summary_kill_backend">Des fois les programmes d\'arrière-plan restent coincés dans des états incohérents.\nCliquer sur le bouton restaure le comportement normal en tuant les instances en cours de wpa_supplicant et dhcpcd.</string> + <string name="confirm_reset_settings">Êtes vous sûr de vouloir de remettre tous les paramètres aux valeurs par défaut ?</string> + <string name="confirm_exit_app">Êtes vous sûr de vouloir tuer RepWifi ? Cela vous déconnectera du Wi-Fi.</string> + <string name="confirm_kill_backend">Êtes vous sûr de vouloir tuer les processus d\'arrière-plan ?</string> + <string name="summary_kill_backend">Des fois les programmes d\'arrière-plan restent coincés dans des états incohérents.\nCliquer sur le boutton restaure le comportement normal en tuant les instances en cours de wpa_supplicant et dhcpcd.</string> <string name="text_presented_by">Fait par :</string> <string name="title_input_password">Saisir le mot de passe</string> - <string name="msg_password_empty">Le mot de passe ne peut pas être vide !</string> + <string name="msg_password_empty">Le mot de passe ne peut pas être nul !</string> <string name="title_input_ssid">Saisir les paramètres du réseau</string> - <string name="msg_network_name_empty">Le nom du réseau ne peut pas être vide !</string> + <string name="msg_network_name_empty">Le nom du réseau ne peut pas être nul !</string> <string name="msg_connecting_to">Connexion à </string> <string name="title_scanning">Scan...</string> <string name="msg_scanning_for_nets">Scan pour trouver des réseaux...</string> @@ -99,7 +99,7 @@ <string name="summary_dns1">Définir l\'adresse IPv4 du serveur DNS primaire.\nSi c\'est laissé vide, RepWifi va essayer d\'utiliser la passerelle par défaut comme résolveur DNS.</string> <string name="summary_dns2">Définir l\'adresse IPv4 du serveur DNS secondaire.\nSi la première adresse est vide, la valeur de ce champ va être ignorée.</string> <string name="title_autoconnect">Activer l\'auto-connexion</string> - <string name="summary_autoconnect">Quand l\'adaptateur Wi-Fi est connecté, RepWifi se connecte automatiquement aux réseaux connus atteignables.\n(Expérimental).</string> + <string name="summary_autoconnect">Quand l\'adaptateur Wi-Fi est connecté, RepWifi se connecte automatiquement aux réseaux connus atteignables.\n(Fonctionnalité expérimental)</string> <string name="title_progbar">Barre de progression animée</string> <string name="summary_progbar">Affiche une barre de progression animée pendant que des tâches de fond sont réalisées.\nGarder la désactivée pour (à priori) économiser de l\'énergie pour l\'adaptateur Wi-Fi.</string> <string name="title_start_at_boot">Activer le démarrage automatique au démarrage du système</string> @@ -108,6 +108,8 @@ <string name="summary_restore_default">cliquer pour rétablir les paramètres par défaut\n(ensuite revenez à l\'écran principal pour appliquer la réinitialisation)</string> <string name="title_advanced_settings">Avancé & Débogage</string> <string name="title_general_settings">Options</string> + <string name="title_activity_input_password">Entrer le mot de passe pour le réseau</string> + <string name="title_activity_settings">Paramètres</string> <string name="summary_advanced_settings">définir les options avancées et variables de débogage</string> <string name="summary_general_settings">définir les options générales</string> <string name="text_use_dhcp">Utiliser DHCP</string> diff --git a/app/res/values-fr/strings_activity_input_password.xml b/app/res/values-fr/strings_activity_input_password.xml deleted file mode 100644 index c27ca7a..0000000 --- a/app/res/values-fr/strings_activity_input_password.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - - <!-- - This file is part of RepWifiApp. - RepWifiApp is Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> - French translation is Copyright (C) 2017 Nicola Spanti - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see <https://www.gnu.org/licenses/>. - --> - - <string name="title_activity_input_password">Entrer le mot de passe pour le réseau</string> - -</resources> diff --git a/app/res/values-fr/strings_activity_settings.xml b/app/res/values-fr/strings_activity_settings.xml deleted file mode 100644 index 260bcb0..0000000 --- a/app/res/values-fr/strings_activity_settings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - - <!-- - This file is part of RepWifiApp. - RepWifiApp is Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> - French translation is Copyright (C) 2017 Nicola Spanti - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see <https://www.gnu.org/licenses/>. - --> - - <string name="title_activity_settings">Paramètres</string> - -</resources> diff --git a/app/res/values/strings.xml b/app/res/values/strings.xml index 8e8cd36..1073697 100644 --- a/app/res/values/strings.xml +++ b/app/res/values/strings.xml @@ -1,4 +1,25 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- +// +// 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/>. +// +// ******************************************************************** +--> <resources> <string name="app_name">RepWifi</string> @@ -20,7 +41,7 @@ <string name="delete">Delete network info</string> <string name="manage_networks">Manage Networks</string> <string name="title_activity_credits">Credits</string> - <string name="credit_text">Copyright 2017 Filippo \"Fil\" Bergamo<br/><br/>This app is Free Software.<br/>It\'s licensed under the terms of <a href="https://www.gnu.org/licenses/gpl.txt">GPL v3</a><br/><br/>This app is developed as a contribution to <br/><a href="http://www.replicant.us">The Replicant Project</a><br/>To report on bugs, request features, or any help request, please refer to:<br/><a href="http://redmine.replicant.us/projects/replicant/boards">Replicant\'s Forum</a><br/><br/>Source code is hosted at: <br/><a href="https://git.replicant.us/contrib/Fil/RepWifiApp/">https://git.replicant.us/contrib/Fil/RepWifiApp/</a><br/><br/><b><u><big>Thank GNUs:</big></u></b><br/><br/><b>Tiberiu - Technoethical</b><br/>for having done the initial job of porting libre WiFi to Replicant 4.2.<br/><br/><b>Wolfgang Wiedmeyer</b><br/>for porting libre WiFi to Replicant 6.0 and for helping with the scripts.<br/><br/><b>Nicola Spanti</b><br/>for the French translation.<br/><br/><b>Nichlas Severinsen</b><br/>for publishing this app on F-Droid.<br/><br/></string> + <string name="credit_text">Copyright 2017, 2018 Filippo \"Fil\" Bergamo<br/><br/>This app is Free Software.<br/>It\'s licensed under the terms of <a href="https://www.gnu.org/licenses/gpl.txt">GPL v3</a><br/><br/>This app is developed as a contribution to <br/><a href="http://www.replicant.us">The Replicant Project</a><br/>To report on bugs, request features, or any help request, please refer to:<br/><a href="http://redmine.replicant.us/projects/replicant/boards">Replicant\'s Forum</a><br/><br/>Source code is hosted at: <br/><a href="https://git.replicant.us/contrib/Fil/RepWifiApp/">https://git.replicant.us/contrib/Fil/RepWifiApp/</a><br/><br/><b><u><big>Thank GNUs:</big></u></b><br/><br/><b>Tiberiu - Technoethical</b><br/>for having done the initial job of porting libre WiFi to Replicant 4.2.<br/><br/><b>Wolfgang Wiedmeyer</b><br/>for porting libre WiFi to Replicant 6.0 and for helping with the scripts.<br/><br/><b>Nicola Spanti</b><br/>for the French translation.<br/><br/><b>Nichlas Severinsen</b><br/>for publishing this app on F-Droid.<br/><br/></string> <string name="menu_config">Settings</string> <string name="msg_interface_not_found">WiFi adapter not found!\nPlease plug in a supported WiFi adapter and retry.</string> <string name="msg_confirm_delete_network">Are you sure you want to delete this network?</string> @@ -34,13 +55,11 @@ <string name="title_activity_input_ssid">Insert Network\'s Name</string> <string name="menu_settings">Settings</string> <string name="select_saved_net">Use Saved Network</string> - <string name="dns1_default">185.121.177.177</string> - <string name="dns2_default">169.239.202.202</string> <string name="receiver_description">RepWifi\'s listener for startup event. Provides auto-start and notification features to RepWifi.</string> <string name="confirm_reset_settings">Are you sure you want to reset all settings to default?</string> <string name="confirm_exit_app">Are you sure you want to kill RepWifi? This will disconnect you from WiFi.</string> <string name="confirm_kill_backend">Are you sure you want to kill the back-end processes?</string> - <string name="summary_kill_backend">Sometimes back-end programs get stuck in inconsistent states.\nClicking this button restores normal behaviour by killing running instances of wpa_supplicant and dhcpcd."</string> + <string name="summary_kill_backend">Sometimes back-end programs get stuck in inconsistent states.\nClicking this button restores normal behaviour by killing running instances of wpa_supplicant and dhcpcd.</string> <string name="text_presented_by">Brought to you by:</string> <string name="title_input_password">Input Password</string> <string name="msg_password_empty">Password can\'t be empty!</string> @@ -82,7 +101,7 @@ <string name="summary_dns1">Set the IPv4 address of the primary DNS server.\nIf left blank, RepWifi will try to use the default gateway as a DNS resolver.</string> <string name="summary_dns2">Set the IPv4 address of the secondary DNS server.\nIf the primary address is blank, this will be ignored.</string> <string name="title_autoconnect">Enable Autoconnect</string> - <string name="summary_autoconnect">When the WiFi dongle is attached, RepWifi connects automatically to reachable known networks.\n(Experimental).</string> + <string name="summary_autoconnect">When the WiFi dongle is attached, RepWifi connects automatically to reachable known networks.\n(Experimental Feature)</string> <string name="title_progbar">Animated progress bar</string> <string name="summary_progbar">Show an animated progress bar while doing long background tasks.\nKeep it disabled to (hopefully) save some extra power for the WiFi dongle.</string> <string name="title_start_at_boot">Enable Start at Boot</string> @@ -117,5 +136,22 @@ <string name="txt_save">Save</string> <string name="summary_vpn_settings">Select the name of a VPN profile that you want to be launched automatically when you connect to this network.\nLeave blank to disable automatic VPN connection.\nTo use VPN capability, "[VPN_EXT_APP]" must be installed in the system.</string> <string name="msg_vpn_no_profile">No profile found.\nPlease add a VPN profile inside [VPN_EXT_APP] before using this feature.</string> + <string name="msg_dns_fail">FAILED setting DNS!</string> + <string name="msg_gw_failed">FAILED to get gateway!</string> + <string name="msg_connection_timeout">Timeout exceeded while trying to connect.\nThis could be due to a wrong password.\nPlease re-enter the correct password and retry.</string> + <string name="msg_confirm_stop_job">Do you really want to stop current activity?</string> + <string name="msg_vpn_service_error">ERROR while binding to VPN service!</string> + <string name="msg_vpn_error_manual_open">ERROR while binding to VPN service.\nYou need to manually launch "[VPN_EXT_APP]" the first time, before using it with RepWifi.\nPlease launch the vpn app, then retry, and allow RepWifi permission to use the vpn app, when asked.</string> + <string name="text_broadcast_address">Broadcast address</string> + <string name="text_hardware_address">Hardware address</string> + <string name="text_connection_info">Connection Info</string> + <string name="msg_connection_timeout_nopass">Timeout exceeded while trying to connect.\n</string> + <string name="msg_make_system_app">RepWifi is installed as a regular app.\nRepWifi needs to be a \"System app\" to work properly.\nTo make repwifi a system app just confirm this dialog.\n\n*** The device will be rebooted ***\nPlease save any opened job before proceeding.\n\nDo you want to proceed making RepWifi a system app?</string> + <string name="msg_confirm_make_system_app">Are you sure you want to proceed?\nThe device will reboot!</string> + <string name="msg_chose_no_system_app">You chose not to make RepWifi a system app, so RepWifi won\'t work properly.\nTo make RepWifi a system app, close and re-open it, then just confirm when asked about making it a system app.</string> + <string name="msg_error_generic">ERROR!</string> + <string name="msg_fail_connservice">FATAL ERROR! Failed to bind to inner connection service. Try restarting the application.</string> + <string name="summary_monitor_connection_state">If checked, the background service keeps on monitoring the state of the connection, once every [CHK_STS_INTERVAL] seconds, reporting any state change in the status bar notification.(Experimental feature)</string> + <string name="title_monitor_connection_state">Monitor connection state</string> </resources>
\ No newline at end of file diff --git a/app/res/values/strings_activity_input_password.xml b/app/res/values/strings_activity_input_password.xml deleted file mode 100644 index 31b8865..0000000 --- a/app/res/values/strings_activity_input_password.xml +++ /dev/null @@ -1,8 +0,0 @@ -<resources> - - <string name="title_activity_input_password">Insert passwrod for network</string> - - <!-- Strings related to login --> - - -</resources>
\ No newline at end of file diff --git a/app/res/values/strings_activity_settings.xml b/app/res/values/strings_activity_settings.xml deleted file mode 100644 index dfc25ca..0000000 --- a/app/res/values/strings_activity_settings.xml +++ /dev/null @@ -1,14 +0,0 @@ -<resources> - - <string name="title_activity_settings">Settings</string> - - <!-- Strings related to Settings --> - - - <!-- Example General settings --> - - <!-- Example settings for Data & Sync --> - - <!-- Example settings for Notifications --> - -</resources>
\ No newline at end of file diff --git a/app/res/xml/general_settings.xml b/app/res/xml/general_settings.xml index bd9fdf9..5d7d6c0 100644 --- a/app/res/xml/general_settings.xml +++ b/app/res/xml/general_settings.xml @@ -3,7 +3,7 @@ <EditTextPreference android:id="@+id/pref_dns1" - android:defaultValue="@string/dns1_default" + android:defaultValue="" android:key="dns1" android:summary="@string/summary_dns1" android:title="@string/title_dns_1" @@ -14,7 +14,7 @@ <EditTextPreference android:id="@+id/pref_dns2" - android:defaultValue="@string/dns2_default" + android:defaultValue="" android:key="dns2" android:summary="@string/summary_dns2" android:title="@string/title_dns_2" @@ -30,6 +30,13 @@ android:title="@string/title_autoconnect" /> <CheckBoxPreference + android:id="@+id/pref_monitor_connection" + android:defaultValue="false" + android:key="monitor_connection" + android:summary="@string/summary_monitor_connection_state" + android:title="@string/title_monitor_connection_state" /> + + <CheckBoxPreference android:id="@+id/pref_progbar" android:defaultValue="true" android:key="enable_progbar" 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; + } + +} |