From 0157b7b54f864258ae437c3566fe2cb74f0da61e Mon Sep 17 00:00:00 2001 From: cyBerta Date: Wed, 1 Jan 2020 21:24:50 +0100 Subject: add new icons for tethering --- .../bitmaskclient/fragments/TetheringDialog.java | 76 +++++++++++++++ .../bitmaskclient/views/IconCheckboxEntry.java | 106 +++++++++++++++++++++ .../main/res/drawable-hdpi/ic_access_point_36.png | Bin 0 -> 2246 bytes app/src/main/res/drawable-hdpi/ic_bluetooth.png | Bin 0 -> 1043 bytes app/src/main/res/drawable-hdpi/ic_check_bold.png | Bin 0 -> 808 bytes app/src/main/res/drawable-hdpi/ic_usb.png | Bin 0 -> 1000 bytes app/src/main/res/drawable-hdpi/ic_wifi.png | Bin 0 -> 1496 bytes app/src/main/res/drawable-ldpi/ic_bluetooth.png | Bin 0 -> 797 bytes app/src/main/res/drawable-ldpi/ic_check_bold.png | Bin 0 -> 652 bytes app/src/main/res/drawable-ldpi/ic_usb.png | Bin 0 -> 644 bytes app/src/main/res/drawable-ldpi/ic_wifi.png | Bin 0 -> 881 bytes .../main/res/drawable-mdpi/ic_access_point_36.png | Bin 0 -> 1301 bytes app/src/main/res/drawable-mdpi/ic_bluetooth.png | Bin 0 -> 980 bytes app/src/main/res/drawable-mdpi/ic_check_bold.png | Bin 0 -> 725 bytes app/src/main/res/drawable-mdpi/ic_usb.png | Bin 0 -> 859 bytes app/src/main/res/drawable-mdpi/ic_wifi.png | Bin 0 -> 1123 bytes .../main/res/drawable-xhdpi/ic_access_point_36.png | Bin 0 -> 2878 bytes app/src/main/res/drawable-xhdpi/ic_bluetooth.png | Bin 0 -> 1300 bytes app/src/main/res/drawable-xhdpi/ic_check_bold.png | Bin 0 -> 900 bytes app/src/main/res/drawable-xhdpi/ic_usb.png | Bin 0 -> 1397 bytes app/src/main/res/drawable-xhdpi/ic_wifi.png | Bin 0 -> 1894 bytes app/src/main/res/drawable-xxhdpi/ic_bluetooth.png | Bin 0 -> 1805 bytes app/src/main/res/drawable-xxhdpi/ic_check_bold.png | Bin 0 -> 1212 bytes app/src/main/res/drawable-xxhdpi/ic_usb.png | Bin 0 -> 1707 bytes app/src/main/res/drawable-xxhdpi/ic_wifi.png | Bin 0 -> 2639 bytes .../res/drawable-xxxhdpi/ic_access_point_36.png | Bin 0 -> 5906 bytes app/src/main/res/drawable-xxxhdpi/ic_bluetooth.png | Bin 0 -> 2138 bytes .../main/res/drawable-xxxhdpi/ic_check_bold.png | Bin 0 -> 1494 bytes app/src/main/res/drawable-xxxhdpi/ic_usb.png | Bin 0 -> 2359 bytes app/src/main/res/drawable-xxxhdpi/ic_wifi.png | Bin 0 -> 3399 bytes app/src/main/res/layout/d_list_selection.xml | 63 ++++++++++++ .../res/layout/v_icon_select_text_list_item.xml | 58 +++++++++++ 32 files changed, 303 insertions(+) create mode 100644 app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/views/IconCheckboxEntry.java create mode 100644 app/src/main/res/drawable-hdpi/ic_access_point_36.png create mode 100644 app/src/main/res/drawable-hdpi/ic_bluetooth.png create mode 100644 app/src/main/res/drawable-hdpi/ic_check_bold.png create mode 100644 app/src/main/res/drawable-hdpi/ic_usb.png create mode 100644 app/src/main/res/drawable-hdpi/ic_wifi.png create mode 100644 app/src/main/res/drawable-ldpi/ic_bluetooth.png create mode 100644 app/src/main/res/drawable-ldpi/ic_check_bold.png create mode 100644 app/src/main/res/drawable-ldpi/ic_usb.png create mode 100644 app/src/main/res/drawable-ldpi/ic_wifi.png create mode 100644 app/src/main/res/drawable-mdpi/ic_access_point_36.png create mode 100644 app/src/main/res/drawable-mdpi/ic_bluetooth.png create mode 100644 app/src/main/res/drawable-mdpi/ic_check_bold.png create mode 100644 app/src/main/res/drawable-mdpi/ic_usb.png create mode 100644 app/src/main/res/drawable-mdpi/ic_wifi.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_access_point_36.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_bluetooth.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_check_bold.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_usb.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_wifi.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_bluetooth.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_check_bold.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_usb.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_wifi.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_access_point_36.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_bluetooth.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_check_bold.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_usb.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_wifi.png create mode 100644 app/src/main/res/layout/d_list_selection.xml create mode 100644 app/src/main/res/layout/v_icon_select_text_list_item.xml (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java new file mode 100644 index 00000000..90e3c5a1 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java @@ -0,0 +1,76 @@ +package se.leap.bitmaskclient.fragments; + +import android.app.Dialog; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatDialogFragment; +import android.support.v7.widget.AppCompatTextView; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.CheckBox; + +import butterknife.ButterKnife; +import butterknife.InjectView; +import se.leap.bitmaskclient.R; +import se.leap.bitmaskclient.views.IconTextView; + +import static se.leap.bitmaskclient.utils.PreferenceHelper.saveShowAlwaysOnDialog; + + +/** + * Created by cyberta on 25.02.18. + */ + + + +public class TetheringDialog extends AppCompatDialogFragment { + + public final static String TAG = TetheringDialog.class.getName(); + + @InjectView(R.id.do_not_show_again) + CheckBox doNotShowAgainCheckBox; + + @InjectView(R.id.user_message) + IconTextView userMessage; + + @InjectView(R.id.block_vpn_user_message) + AppCompatTextView blockVpnUserMessage; + + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + LayoutInflater inflater = getActivity().getLayoutInflater(); + View view = inflater.inflate(R.layout.d_checkbox_confirm, null); + ButterKnife.inject(this, view); + + userMessage.setIcon(R.drawable.ic_settings); + userMessage.setText(getString(R.string.always_on_vpn_user_message)); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + blockVpnUserMessage.setVisibility(View.VISIBLE); + } + + builder.setView(view) + .setPositiveButton(android.R.string.ok, (dialog, id) -> { + if (doNotShowAgainCheckBox.isChecked()) { + saveShowAlwaysOnDialog(getContext(), false); + } + Intent intent = new Intent("android.net.vpn.SETTINGS"); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + }) + .setNegativeButton(R.string.cancel, (dialog, id) -> dialog.cancel()); + return builder.create(); + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/views/IconCheckboxEntry.java b/app/src/main/java/se/leap/bitmaskclient/views/IconCheckboxEntry.java new file mode 100644 index 00000000..cd151885 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/views/IconCheckboxEntry.java @@ -0,0 +1,106 @@ +package se.leap.bitmaskclient.views; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.support.annotation.ColorRes; +import android.support.annotation.DrawableRes; +import android.support.annotation.Nullable; +import android.support.annotation.StringRes; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import se.leap.bitmaskclient.R; + + +public class IconTextEntry extends LinearLayout { + + private TextView textView; + private ImageView iconView; + private TextView subtitleView; + + public IconTextEntry(Context context) { + super(context); + initLayout(context, null); + } + + public IconTextEntry(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + initLayout(context, attrs); + } + + public IconTextEntry(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initLayout(context, attrs); + } + + @TargetApi(21) + public IconTextEntry(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + initLayout(context, attrs); + } + + void initLayout(Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater) context + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View rootview = inflater.inflate(R.layout.v_icon_text_list_item, this, true); + textView = rootview.findViewById(android.R.id.text1); + subtitleView = rootview.findViewById(R.id.subtitle); + iconView = rootview.findViewById(R.id.material_icon); + + if (attrs != null) { + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.IconTextEntry); + + String entryText = typedArray.getString(R.styleable.IconTextEntry_text); + if (entryText != null) { + textView.setText(entryText); + } + + String subtitle = typedArray.getString(R.styleable.IconTextEntry_subtitle); + if (subtitle != null) { + subtitleView.setText(subtitle); + subtitleView.setVisibility(VISIBLE); + } + + Drawable drawable = typedArray.getDrawable(R.styleable.IconTextEntry_icon); + if (drawable != null) { + iconView.setImageDrawable(drawable); + } + + typedArray.recycle(); + } + + + } + + public void setText(@StringRes int id) { + textView.setText(id); + } + + public void setSubtitle(String text) { + subtitleView.setText(text); + subtitleView.setVisibility(VISIBLE); + } + + public void hideSubtitle() { + subtitleView.setVisibility(GONE); + } + + public void setSubtitleColor(@ColorRes int color) { + subtitleView.setTextColor(getContext().getResources().getColor(color)); + } + + public void setText(CharSequence text) { + textView.setText(text); + } + + public void setIcon(@DrawableRes int id) { + iconView.setImageResource(id); + } + +} diff --git a/app/src/main/res/drawable-hdpi/ic_access_point_36.png b/app/src/main/res/drawable-hdpi/ic_access_point_36.png new file mode 100644 index 00000000..03444d0f Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_access_point_36.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_bluetooth.png b/app/src/main/res/drawable-hdpi/ic_bluetooth.png new file mode 100644 index 00000000..e7c1589b Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_bluetooth.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_check_bold.png b/app/src/main/res/drawable-hdpi/ic_check_bold.png new file mode 100644 index 00000000..28418346 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_check_bold.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_usb.png b/app/src/main/res/drawable-hdpi/ic_usb.png new file mode 100644 index 00000000..b9de586f Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_usb.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_wifi.png b/app/src/main/res/drawable-hdpi/ic_wifi.png new file mode 100644 index 00000000..ca6b94a3 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_wifi.png differ diff --git a/app/src/main/res/drawable-ldpi/ic_bluetooth.png b/app/src/main/res/drawable-ldpi/ic_bluetooth.png new file mode 100644 index 00000000..3a73c82f Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_bluetooth.png differ diff --git a/app/src/main/res/drawable-ldpi/ic_check_bold.png b/app/src/main/res/drawable-ldpi/ic_check_bold.png new file mode 100644 index 00000000..4f765ed4 Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_check_bold.png differ diff --git a/app/src/main/res/drawable-ldpi/ic_usb.png b/app/src/main/res/drawable-ldpi/ic_usb.png new file mode 100644 index 00000000..d48d2f50 Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_usb.png differ diff --git a/app/src/main/res/drawable-ldpi/ic_wifi.png b/app/src/main/res/drawable-ldpi/ic_wifi.png new file mode 100644 index 00000000..56ad6403 Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_wifi.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_access_point_36.png b/app/src/main/res/drawable-mdpi/ic_access_point_36.png new file mode 100644 index 00000000..c461a0a5 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_access_point_36.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_bluetooth.png b/app/src/main/res/drawable-mdpi/ic_bluetooth.png new file mode 100644 index 00000000..a1cecd2b Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_bluetooth.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_check_bold.png b/app/src/main/res/drawable-mdpi/ic_check_bold.png new file mode 100644 index 00000000..872ef957 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_check_bold.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_usb.png b/app/src/main/res/drawable-mdpi/ic_usb.png new file mode 100644 index 00000000..3ad5ebc1 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_usb.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_wifi.png b/app/src/main/res/drawable-mdpi/ic_wifi.png new file mode 100644 index 00000000..21a69023 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_wifi.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_access_point_36.png b/app/src/main/res/drawable-xhdpi/ic_access_point_36.png new file mode 100644 index 00000000..4ae3d1d9 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_access_point_36.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_bluetooth.png b/app/src/main/res/drawable-xhdpi/ic_bluetooth.png new file mode 100644 index 00000000..32a854e5 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_bluetooth.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_check_bold.png b/app/src/main/res/drawable-xhdpi/ic_check_bold.png new file mode 100644 index 00000000..da6a1ecb Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_check_bold.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_usb.png b/app/src/main/res/drawable-xhdpi/ic_usb.png new file mode 100644 index 00000000..c11940b1 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_usb.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_wifi.png b/app/src/main/res/drawable-xhdpi/ic_wifi.png new file mode 100644 index 00000000..cf1bb909 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_wifi.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_bluetooth.png b/app/src/main/res/drawable-xxhdpi/ic_bluetooth.png new file mode 100644 index 00000000..9c30e5b4 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_bluetooth.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_check_bold.png b/app/src/main/res/drawable-xxhdpi/ic_check_bold.png new file mode 100644 index 00000000..f6b50706 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_check_bold.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_usb.png b/app/src/main/res/drawable-xxhdpi/ic_usb.png new file mode 100644 index 00000000..c78e33af Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_usb.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_wifi.png b/app/src/main/res/drawable-xxhdpi/ic_wifi.png new file mode 100644 index 00000000..ea9e08a9 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_wifi.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_access_point_36.png b/app/src/main/res/drawable-xxxhdpi/ic_access_point_36.png new file mode 100644 index 00000000..4a2f25c1 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_access_point_36.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_bluetooth.png b/app/src/main/res/drawable-xxxhdpi/ic_bluetooth.png new file mode 100644 index 00000000..6eccbbd6 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_bluetooth.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_check_bold.png b/app/src/main/res/drawable-xxxhdpi/ic_check_bold.png new file mode 100644 index 00000000..19029a0d Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_check_bold.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_usb.png b/app/src/main/res/drawable-xxxhdpi/ic_usb.png new file mode 100644 index 00000000..4bebd840 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_usb.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_wifi.png b/app/src/main/res/drawable-xxxhdpi/ic_wifi.png new file mode 100644 index 00000000..b5a05f7a Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_wifi.png differ diff --git a/app/src/main/res/layout/d_list_selection.xml b/app/src/main/res/layout/d_list_selection.xml new file mode 100644 index 00000000..a9a84c0e --- /dev/null +++ b/app/src/main/res/layout/d_list_selection.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/v_icon_select_text_list_item.xml b/app/src/main/res/layout/v_icon_select_text_list_item.xml new file mode 100644 index 00000000..64cc474a --- /dev/null +++ b/app/src/main/res/layout/v_icon_select_text_list_item.xml @@ -0,0 +1,58 @@ + + + + + + + + + -- cgit v1.2.3 From 87bdccc31f5fcd10e697d30225105ca40114582c Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 2 Jan 2020 22:57:50 +0100 Subject: add new strings for tethering --- app/src/main/java/se/leap/bitmaskclient/Constants.java | 4 ++++ app/src/main/res/values/strings.xml | 8 ++++++++ 2 files changed, 12 insertions(+) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/Constants.java b/app/src/main/java/se/leap/bitmaskclient/Constants.java index 5ed28806..58a92360 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Constants.java +++ b/app/src/main/java/se/leap/bitmaskclient/Constants.java @@ -16,6 +16,9 @@ public interface Constants { String EXCLUDED_APPS = "excluded_apps"; String USE_PLUGGABLE_TRANSPORTS = "usePluggableTransports"; String SU_PERMISSION = "su_permission"; + String TETHERING_BLUETOOTH = "tethering_bluetooth"; + String TETHERING_WIFI = "tethering_wifi"; + String TETHERING_USB = "tethering_usb"; ////////////////////////////////////////////// @@ -55,6 +58,7 @@ public interface Constants { String EIP_ACTION_START_BLOCKING_VPN = "se.leap.bitmaskclient.EIP_ACTION_START_BLOCKING_VPN"; String EIP_ACTION_STOP_BLOCKING_VPN = "se.leap.bitmaskclient.EIP_ACTION_STOP_BLOCKING_VPN"; String EIP_ACTION_PREPARE_VPN = "se.leap.bitmaskclient.EIP_ACTION_PREPARE_VPN"; + String EIP_ACTION_CONFIGURE_TETHERING = "se.leap.bitmaskclient.EIP_ACTION_CONFIGURE_TETHERING"; String EIP_RECEIVER = "EIP.RECEIVER"; String EIP_REQUEST = "EIP.REQUEST"; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b52e34dd..f8e6b887 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -105,6 +105,14 @@ Background data connections will hibernate when your phone is inactive. Always-on VPN Open Android System Settings + VPN Hotspot + Share your VPN + Please make sure that you have enabled tethering in the %s first! + system settings + Share your VPN with other devices via: + Wifi hotspot + USB tethering + Bluetooth tethering Do not show again To enable always-on VPN in Android VPN Settings click on the configure icon [img src] and turn the switch on. To protect your privacy optimally, you should also activate the option \"Block connections without VPN\". -- cgit v1.2.3 From 921ee995ef0f0e7f2076ac3538fed289bcb82ba9 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 2 Jan 2020 23:06:36 +0100 Subject: implement basic UI for VPN tethering --- .../drawer/NavigationDrawerFragment.java | 30 +++- .../main/java/se/leap/bitmaskclient/eip/EIP.java | 4 + .../java/se/leap/bitmaskclient/eip/EipCommand.java | 11 ++ .../bitmaskclient/fragments/TetheringDialog.java | 164 +++++++++++++++++---- .../leap/bitmaskclient/utils/PreferenceHelper.java | 27 ++++ .../bitmaskclient/views/IconCheckboxEntry.java | 91 ++++-------- app/src/main/res/layout/d_list_selection.xml | 26 +--- app/src/main/res/layout/f_drawer_main.xml | 10 ++ .../res/layout/v_icon_select_text_list_item.xml | 45 +++--- 9 files changed, 274 insertions(+), 134 deletions(-) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java index 3144d62c..85c982df 100644 --- a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java @@ -60,6 +60,7 @@ import se.leap.bitmaskclient.fragments.AboutFragment; import se.leap.bitmaskclient.fragments.AlwaysOnDialog; import se.leap.bitmaskclient.fragments.ExcludeAppsFragment; import se.leap.bitmaskclient.fragments.LogFragment; +import se.leap.bitmaskclient.fragments.TetheringDialog; import se.leap.bitmaskclient.utils.PreferenceHelper; import se.leap.bitmaskclient.views.IconSwitchEntry; import se.leap.bitmaskclient.views.IconTextEntry; @@ -236,6 +237,7 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen initUseBridgesEntry(); initSaveBatteryEntry(); initAlwaysOnVpnEntry(); + initTetheringEntry(); initExcludeAppsEntry(); initDonateEntry(); initLogEntry(); @@ -321,6 +323,19 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen } } + private void initTetheringEntry() { + IconTextEntry tethering = drawerView.findViewById(R.id.tethering); + if (PreferenceHelper.hasSuPermission(getContext())) { + tethering.setVisibility(VISIBLE); + tethering.setOnClickListener((buttonView) -> { + showTetheringAlert(); + }); + } else { + tethering.setVisibility(GONE); + } + + } + private void initExcludeAppsEntry() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { IconTextEntry excludeApps = drawerView.findViewById(R.id.exclude_apps); @@ -442,13 +457,26 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen saveBattery(getContext(), true); }) .setNegativeButton(activity.getString(android.R.string.no), (dialog, which) -> saveBattery.setCheckedQuietly(false)) - .setOnDismissListener(dialog -> showEnableExperimentalFeature = false) + .setOnDismissListener(dialog -> showSaveBattery = false) .setOnCancelListener(dialog -> saveBattery.setCheckedQuietly(false)).show(); } catch (IllegalStateException e) { e.printStackTrace(); } } + public void showTetheringAlert() { + try { + + FragmentTransaction fragmentTransaction = new FragmentManagerEnhanced( + getActivity().getSupportFragmentManager()).removePreviousFragment( + TetheringDialog.TAG); + DialogFragment newFragment = new TetheringDialog(); + newFragment.show(fragmentTransaction, TetheringDialog.TAG); + } catch (IllegalStateException | NullPointerException e) { + e.printStackTrace(); + } + } + public void showAlwaysOnDialog() { try { diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java index 1d67cd12..1186b54f 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -61,6 +61,7 @@ import static de.blinkt.openvpn.core.connection.Connection.TransportType.OPENVPN import static se.leap.bitmaskclient.Constants.BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT; import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY; import static se.leap.bitmaskclient.Constants.EIP_ACTION_CHECK_CERT_VALIDITY; +import static se.leap.bitmaskclient.Constants.EIP_ACTION_CONFIGURE_TETHERING; import static se.leap.bitmaskclient.Constants.EIP_ACTION_IS_RUNNING; import static se.leap.bitmaskclient.Constants.EIP_ACTION_START; import static se.leap.bitmaskclient.Constants.EIP_ACTION_START_ALWAYS_ON_VPN; @@ -190,6 +191,9 @@ public final class EIP extends JobIntentService implements Observer { disconnect(); earlyRoutes(); break; + case EIP_ACTION_CONFIGURE_TETHERING: + Log.d(TAG, "TODO: implement tethering configuration"); + break; } } diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java index eb266e3c..d2667e42 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java @@ -6,6 +6,7 @@ import android.content.Intent; import android.os.ResultReceiver; import android.support.annotation.NonNull; import android.support.annotation.VisibleForTesting; +import android.support.v4.content.ContextCompat; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -13,6 +14,7 @@ import org.jetbrains.annotations.Nullable; import se.leap.bitmaskclient.Provider; import static se.leap.bitmaskclient.Constants.EIP_ACTION_CHECK_CERT_VALIDITY; +import static se.leap.bitmaskclient.Constants.EIP_ACTION_CONFIGURE_TETHERING; import static se.leap.bitmaskclient.Constants.EIP_ACTION_START; import static se.leap.bitmaskclient.Constants.EIP_ACTION_START_BLOCKING_VPN; import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP; @@ -90,4 +92,13 @@ public class EipCommand { execute(context, EIP_ACTION_CHECK_CERT_VALIDITY, resultReceiver, null); } + public static void configureTethering(@NonNull Context context) { + execute(context, EIP_ACTION_CONFIGURE_TETHERING); + } + + @VisibleForTesting + public static void configureTethering(@NonNull Context context, ResultReceiver resultReceiver) { + execute(context, EIP_ACTION_CONFIGURE_TETHERING); + } + } diff --git a/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java index 90e3c5a1..96f7f0d4 100644 --- a/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java @@ -2,48 +2,113 @@ package se.leap.bitmaskclient.fragments; import android.app.Dialog; import android.content.Intent; -import android.os.Build; +import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.provider.Settings; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatDialogFragment; import android.support.v7.widget.AppCompatTextView; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.method.LinkMovementMethod; +import android.text.style.ClickableSpan; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; -import android.widget.CheckBox; +import android.view.ViewGroup; import butterknife.ButterKnife; import butterknife.InjectView; import se.leap.bitmaskclient.R; -import se.leap.bitmaskclient.views.IconTextView; - -import static se.leap.bitmaskclient.utils.PreferenceHelper.saveShowAlwaysOnDialog; - +import se.leap.bitmaskclient.eip.EipCommand; +import se.leap.bitmaskclient.utils.PreferenceHelper; +import se.leap.bitmaskclient.views.IconCheckboxEntry; /** * Created by cyberta on 25.02.18. */ - - public class TetheringDialog extends AppCompatDialogFragment { public final static String TAG = TetheringDialog.class.getName(); - @InjectView(R.id.do_not_show_again) - CheckBox doNotShowAgainCheckBox; + @InjectView(R.id.tvTitle) + AppCompatTextView title; @InjectView(R.id.user_message) - IconTextView userMessage; + AppCompatTextView userMessage; - @InjectView(R.id.block_vpn_user_message) - AppCompatTextView blockVpnUserMessage; + @InjectView(R.id.selection_list_view) + RecyclerView selectionListView; + DialogListAdapter adapter; + private DialogListAdapter.ViewModel[] dataset; + public static class DialogListAdapter extends RecyclerView.Adapter { - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + interface OnItemClickListener { + void onItemClick(ViewModel item); + } + + private ViewModel[] dataSet; + private OnItemClickListener clickListener; + + public DialogListAdapter(ViewModel[] dataSet, OnItemClickListener clickListener) { + this.dataSet = dataSet; + this.clickListener = clickListener; + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { + IconCheckboxEntry v = new IconCheckboxEntry(viewGroup.getContext()); + return new ViewHolder(v); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) { + viewHolder.bind(dataSet[i], clickListener); + } + + @Override + public int getItemCount() { + return dataSet.length; + } + + public static class ViewModel { + + public Drawable image; + public String text; + public boolean checked; + + ViewModel(Drawable image, String text, boolean checked) { + Log.d(TAG, "init ViewModel with string: " + text + " checked: " + checked); + + this.image = image; + this.text = text; + this.checked = checked; + } + } + + public static class ViewHolder extends RecyclerView.ViewHolder { + + ViewHolder(IconCheckboxEntry v) { + super(v); + } + + public void bind(ViewModel model, OnItemClickListener onClickListener) { + Log.d(TAG, "bind ViewModel"); + + ((IconCheckboxEntry) this.itemView).bind(model); + this.itemView.setOnClickListener(v -> { + model.checked = !model.checked; + ((IconCheckboxEntry) itemView).setChecked(model.checked); + onClickListener.onItemClick(model); + }); + } + } } @NonNull @@ -51,26 +116,67 @@ public class TetheringDialog extends AppCompatDialogFragment { public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); LayoutInflater inflater = getActivity().getLayoutInflater(); - View view = inflater.inflate(R.layout.d_checkbox_confirm, null); + View view = inflater.inflate(R.layout.d_list_selection, null); ButterKnife.inject(this, view); - userMessage.setIcon(R.drawable.ic_settings); - userMessage.setText(getString(R.string.always_on_vpn_user_message)); + title.setText(R.string.tethering); + userMessage.setMovementMethod(LinkMovementMethod.getInstance()); + userMessage.setLinkTextColor(getContext().getResources().getColor(R.color.colorPrimary)); + userMessage.setText(createUserMessage()); + + initDataset(); + adapter = new DialogListAdapter(dataset, this::onItemClick); + selectionListView.setAdapter(adapter); + selectionListView.setLayoutManager(new LinearLayoutManager(getActivity())); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - blockVpnUserMessage.setVisibility(View.VISIBLE); - } builder.setView(view) .setPositiveButton(android.R.string.ok, (dialog, id) -> { - if (doNotShowAgainCheckBox.isChecked()) { - saveShowAlwaysOnDialog(getContext(), false); - } - Intent intent = new Intent("android.net.vpn.SETTINGS"); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); + PreferenceHelper.wifiTethering(getContext(), dataset[0].checked); + PreferenceHelper.usbTethering(getContext(), dataset[1].checked); + PreferenceHelper.bluetoothTethering(getContext(), dataset[2].checked); + EipCommand.configureTethering(getContext()); }) .setNegativeButton(R.string.cancel, (dialog, id) -> dialog.cancel()); return builder.create(); } + + public void onItemClick(DialogListAdapter.ViewModel item) { + + } + + private CharSequence createUserMessage() { + String tetheringMessage = getString(R.string.tethering_message); + String systemSettings = getString(R.string.tethering_system_settings); + String systemSettingsMessage = getString(R.string.tethering_enabled_message, systemSettings); + String wholeMessage = systemSettingsMessage + "\n\n" + tetheringMessage; + int startIndex = wholeMessage.indexOf(systemSettings, 0); + int endIndex = startIndex + systemSettings.length(); + + Spannable spannable = new SpannableString(wholeMessage); + spannable.setSpan(new ClickableSpan() { + @Override + public void onClick(@NonNull View widget) { + Intent intent = new Intent(Settings.ACTION_WIRELESS_SETTINGS); + startActivity(intent); + } + }, startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + + return spannable; + } + + private void initDataset() { + dataset = new DialogListAdapter.ViewModel[] { + new DialogListAdapter.ViewModel(getContext().getResources().getDrawable(R.drawable.ic_wifi), + getContext().getString(R.string.tethering_wifi), + PreferenceHelper.getWifiTethering(getContext())), + new DialogListAdapter.ViewModel(getContext().getResources().getDrawable(R.drawable.ic_usb), + getContext().getString(R.string.tethering_usb), + PreferenceHelper.getUsbTethering(getContext())), + new DialogListAdapter.ViewModel(getContext().getResources().getDrawable(R.drawable.ic_bluetooth), + getContext().getString(R.string.tethering_bluetooth), + PreferenceHelper.getBluetoothTethering(getContext())) + }; + } + } diff --git a/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java b/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java index de2058c7..25e4b797 100644 --- a/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java @@ -32,6 +32,9 @@ import static se.leap.bitmaskclient.Constants.PROVIDER_PRIVATE_KEY; import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE; import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; import static se.leap.bitmaskclient.Constants.SU_PERMISSION; +import static se.leap.bitmaskclient.Constants.TETHERING_BLUETOOTH; +import static se.leap.bitmaskclient.Constants.TETHERING_USB; +import static se.leap.bitmaskclient.Constants.TETHERING_WIFI; import static se.leap.bitmaskclient.Constants.USE_PLUGGABLE_TRANSPORTS; /** @@ -146,6 +149,30 @@ public class PreferenceHelper { return getBoolean(context, DEFAULT_SHARED_PREFS_BATTERY_SAVER, false); } + public static void usbTethering(Context context, boolean isEnabled) { + putBoolean(context, TETHERING_USB, isEnabled); + } + + public static boolean getUsbTethering(Context context) { + return getBoolean(context, TETHERING_USB, false); + } + + public static void wifiTethering(Context context, boolean isEnabled) { + putBoolean(context, TETHERING_WIFI, isEnabled); + } + + public static boolean getWifiTethering(Context context) { + return getBoolean(context, TETHERING_WIFI, false); + } + + public static void bluetoothTethering(Context context, boolean isEnabled) { + putBoolean(context, TETHERING_BLUETOOTH, isEnabled); + } + + public static boolean getBluetoothTethering(Context context) { + return getBoolean(context, TETHERING_BLUETOOTH, false); + } + public static void saveShowAlwaysOnDialog(Context context, boolean showAlwaysOnDialog) { putBoolean(context, ALWAYS_ON_SHOW_DIALOG, showAlwaysOnDialog); } diff --git a/app/src/main/java/se/leap/bitmaskclient/views/IconCheckboxEntry.java b/app/src/main/java/se/leap/bitmaskclient/views/IconCheckboxEntry.java index cd151885..933d391b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/views/IconCheckboxEntry.java +++ b/app/src/main/java/se/leap/bitmaskclient/views/IconCheckboxEntry.java @@ -2,45 +2,51 @@ package se.leap.bitmaskclient.views; import android.annotation.TargetApi; import android.content.Context; -import android.content.res.TypedArray; import android.graphics.drawable.Drawable; -import android.support.annotation.ColorRes; -import android.support.annotation.DrawableRes; import android.support.annotation.Nullable; -import android.support.annotation.StringRes; +import android.support.v4.content.ContextCompat; +import android.support.v4.graphics.drawable.DrawableCompat; +import android.support.v7.widget.AppCompatImageView; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; -import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import butterknife.ButterKnife; +import butterknife.InjectView; import se.leap.bitmaskclient.R; +import se.leap.bitmaskclient.fragments.TetheringDialog; -public class IconTextEntry extends LinearLayout { +public class IconCheckboxEntry extends LinearLayout { - private TextView textView; - private ImageView iconView; - private TextView subtitleView; + @InjectView(android.R.id.text1) + TextView textView; - public IconTextEntry(Context context) { + @InjectView(R.id.material_icon) + AppCompatImageView iconView; + + @InjectView(R.id.checked_icon) + AppCompatImageView checkedIcon; + + public IconCheckboxEntry(Context context) { super(context); initLayout(context, null); } - public IconTextEntry(Context context, @Nullable AttributeSet attrs) { + public IconCheckboxEntry(Context context, @Nullable AttributeSet attrs) { super(context, attrs); initLayout(context, attrs); } - public IconTextEntry(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + public IconCheckboxEntry(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initLayout(context, attrs); } @TargetApi(21) - public IconTextEntry(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + public IconCheckboxEntry(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initLayout(context, attrs); } @@ -48,59 +54,24 @@ public class IconTextEntry extends LinearLayout { void initLayout(Context context, AttributeSet attrs) { LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - View rootview = inflater.inflate(R.layout.v_icon_text_list_item, this, true); - textView = rootview.findViewById(android.R.id.text1); - subtitleView = rootview.findViewById(R.id.subtitle); - iconView = rootview.findViewById(R.id.material_icon); - - if (attrs != null) { - TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.IconTextEntry); - - String entryText = typedArray.getString(R.styleable.IconTextEntry_text); - if (entryText != null) { - textView.setText(entryText); - } - - String subtitle = typedArray.getString(R.styleable.IconTextEntry_subtitle); - if (subtitle != null) { - subtitleView.setText(subtitle); - subtitleView.setVisibility(VISIBLE); - } - - Drawable drawable = typedArray.getDrawable(R.styleable.IconTextEntry_icon); - if (drawable != null) { - iconView.setImageDrawable(drawable); - } - - typedArray.recycle(); - } - + View rootview = inflater.inflate(R.layout.v_icon_select_text_list_item, this, true); + ButterKnife.inject(this, rootview); - } - - public void setText(@StringRes int id) { - textView.setText(id); - } - - public void setSubtitle(String text) { - subtitleView.setText(text); - subtitleView.setVisibility(VISIBLE); - } - - public void hideSubtitle() { - subtitleView.setVisibility(GONE); - } + Drawable checkIcon = DrawableCompat.wrap(getResources().getDrawable(R.drawable.ic_check_bold)); + DrawableCompat.setTint(checkIcon, ContextCompat.getColor(getContext(), R.color.colorSuccess)); + checkedIcon.setImageDrawable(checkIcon); - public void setSubtitleColor(@ColorRes int color) { - subtitleView.setTextColor(getContext().getResources().getColor(color)); } - public void setText(CharSequence text) { - textView.setText(text); + public void bind(TetheringDialog.DialogListAdapter.ViewModel model) { + textView.setText(model.text); + iconView.setImageDrawable(model.image); + setChecked(model.checked); } - public void setIcon(@DrawableRes int id) { - iconView.setImageResource(id); + public void setChecked(boolean checked) { + checkedIcon.setVisibility(checked ? VISIBLE : GONE); + checkedIcon.setContentDescription(checked ? "selected" : "unselected"); } } diff --git a/app/src/main/res/layout/d_list_selection.xml b/app/src/main/res/layout/d_list_selection.xml index a9a84c0e..ef963303 100644 --- a/app/src/main/res/layout/d_list_selection.xml +++ b/app/src/main/res/layout/d_list_selection.xml @@ -1,5 +1,5 @@ - @@ -18,46 +18,34 @@ android:layout_marginTop="@dimen/add_button_margin" android:layout_marginLeft="@dimen/activity_horizontal_margin" android:layout_marginRight="@dimen/activity_horizontal_margin" - android:text="@string/always_on_vpn" android:textAllCaps="true" android:textAppearance="@style/TextAppearance.AppCompat.Title" android:textStyle="bold" /> - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/f_drawer_main.xml b/app/src/main/res/layout/f_drawer_main.xml index f6c9b2bb..505bd714 100644 --- a/app/src/main/res/layout/f_drawer_main.xml +++ b/app/src/main/res/layout/f_drawer_main.xml @@ -88,6 +88,16 @@ android:visibility="gone" /> + + + - + - Date: Thu, 2 Jan 2020 23:07:31 +0100 Subject: rename battery flag in navigation drawer --- .../se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java index 85c982df..de24802a 100644 --- a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java @@ -118,7 +118,7 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen private SharedPreferences preferences; private final static String KEY_SHOW_SAVE_BATTERY_ALERT = "KEY_SHOW_SAVE_BATTERY_ALERT"; - private boolean showEnableExperimentalFeature = false; + private volatile boolean showSaveBattery = false; AlertDialog alertDialog; @Override @@ -430,8 +430,9 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - if (showEnableExperimentalFeature) { + if (showSaveBattery) { outState.putBoolean(KEY_SHOW_SAVE_BATTERY_ALERT, true); + alertDialog.dismiss(); } } @@ -449,7 +450,7 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen try { AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getActivity()); - showEnableExperimentalFeature = true; + showSaveBattery = true; alertDialog = alertBuilder .setTitle(activity.getString(R.string.save_battery)) .setMessage(activity.getString(R.string.save_battery_message)) -- cgit v1.2.3 From 2c4d5096453dfe5dfe5f1b2067930de3e6962ea3 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 2 Jan 2020 23:13:49 +0100 Subject: remove logs --- .../main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java | 5 ----- 1 file changed, 5 deletions(-) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java index 96f7f0d4..145264d5 100644 --- a/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java @@ -15,7 +15,6 @@ import android.text.Spannable; import android.text.SpannableString; import android.text.method.LinkMovementMethod; import android.text.style.ClickableSpan; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -84,8 +83,6 @@ public class TetheringDialog extends AppCompatDialogFragment { public boolean checked; ViewModel(Drawable image, String text, boolean checked) { - Log.d(TAG, "init ViewModel with string: " + text + " checked: " + checked); - this.image = image; this.text = text; this.checked = checked; @@ -99,8 +96,6 @@ public class TetheringDialog extends AppCompatDialogFragment { } public void bind(ViewModel model, OnItemClickListener onClickListener) { - Log.d(TAG, "bind ViewModel"); - ((IconCheckboxEntry) this.itemView).bind(model); this.itemView.setOnClickListener(v -> { model.checked = !model.checked; -- cgit v1.2.3 From f0ec974d143556e4729ac21c7fcf6a1f1a3687df Mon Sep 17 00:00:00 2001 From: cyBerta Date: Fri, 3 Jan 2020 01:01:56 +0100 Subject: detect hotspot state and disable/enable controls in TetheringDialog accordingly --- app/src/main/AndroidManifest.xml | 1 + .../java/se/leap/bitmaskclient/BitmaskApp.java | 3 ++ .../se/leap/bitmaskclient/EipSetupObserver.java | 1 - .../bitmaskclient/fragments/TetheringDialog.java | 21 +++++++-- .../tethering/WifiHotspotBroadcastReceiver.java | 20 ++++++++ .../tethering/WifiHotspotObserver.java | 55 ++++++++++++++++++++++ .../bitmaskclient/tethering/WifiHotspotState.java | 9 ++++ .../bitmaskclient/views/IconCheckboxEntry.java | 15 ++++-- 8 files changed, 117 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotBroadcastReceiver.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotObserver.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotState.java (limited to 'app') diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9e8c0d98..83394209 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,6 +20,7 @@ + diff --git a/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java b/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java index 45664653..6a657d33 100644 --- a/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java +++ b/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java @@ -8,6 +8,8 @@ import android.support.v7.app.AppCompatDelegate; import com.squareup.leakcanary.LeakCanary; import com.squareup.leakcanary.RefWatcher; +import se.leap.bitmaskclient.tethering.WifiHotspotObserver; + import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; import static se.leap.bitmaskclient.utils.PreferenceHelper.getSavedProviderFromSharedPreferences; @@ -38,6 +40,7 @@ public class BitmaskApp extends MultiDexApplication { providerObservable.updateProvider(getSavedProviderFromSharedPreferences(preferences)); EipSetupObserver.init(this, preferences); AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); + WifiHotspotObserver.init(this); } /** diff --git a/app/src/main/java/se/leap/bitmaskclient/EipSetupObserver.java b/app/src/main/java/se/leap/bitmaskclient/EipSetupObserver.java index 74f132b9..84516ee3 100644 --- a/app/src/main/java/se/leap/bitmaskclient/EipSetupObserver.java +++ b/app/src/main/java/se/leap/bitmaskclient/EipSetupObserver.java @@ -44,7 +44,6 @@ import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.Constants.PROVIDER_PROFILE; import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE; import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE; -import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_NOK; /** * Created by cyberta on 05.12.18. diff --git a/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java index 145264d5..04d3071f 100644 --- a/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java @@ -23,6 +23,7 @@ import butterknife.ButterKnife; import butterknife.InjectView; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.eip.EipCommand; +import se.leap.bitmaskclient.tethering.WifiHotspotObserver; import se.leap.bitmaskclient.utils.PreferenceHelper; import se.leap.bitmaskclient.views.IconCheckboxEntry; @@ -81,11 +82,13 @@ public class TetheringDialog extends AppCompatDialogFragment { public Drawable image; public String text; public boolean checked; + public boolean enabled; - ViewModel(Drawable image, String text, boolean checked) { + ViewModel(Drawable image, String text, boolean checked, boolean enabled) { this.image = image; this.text = text; this.checked = checked; + this.enabled = enabled; } } @@ -136,6 +139,13 @@ public class TetheringDialog extends AppCompatDialogFragment { return builder.create(); } + @Override + public void onResume() { + super.onResume(); + dataset[0].enabled = WifiHotspotObserver.getInstance().isEnabled(); + adapter.notifyDataSetChanged(); + } + public void onItemClick(DialogListAdapter.ViewModel item) { } @@ -164,13 +174,16 @@ public class TetheringDialog extends AppCompatDialogFragment { dataset = new DialogListAdapter.ViewModel[] { new DialogListAdapter.ViewModel(getContext().getResources().getDrawable(R.drawable.ic_wifi), getContext().getString(R.string.tethering_wifi), - PreferenceHelper.getWifiTethering(getContext())), + PreferenceHelper.getWifiTethering(getContext()), + WifiHotspotObserver.getInstance().isEnabled()), new DialogListAdapter.ViewModel(getContext().getResources().getDrawable(R.drawable.ic_usb), getContext().getString(R.string.tethering_usb), - PreferenceHelper.getUsbTethering(getContext())), + PreferenceHelper.getUsbTethering(getContext()), + true), new DialogListAdapter.ViewModel(getContext().getResources().getDrawable(R.drawable.ic_bluetooth), getContext().getString(R.string.tethering_bluetooth), - PreferenceHelper.getBluetoothTethering(getContext())) + PreferenceHelper.getBluetoothTethering(getContext()), + true) }; } diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotBroadcastReceiver.java b/app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotBroadcastReceiver.java new file mode 100644 index 00000000..1d81eff5 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotBroadcastReceiver.java @@ -0,0 +1,20 @@ +package se.leap.bitmaskclient.tethering; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.net.wifi.WifiManager; + +public class WifiHotspotBroadcastReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if ("android.net.wifi.WIFI_AP_STATE_CHANGED".equals(intent.getAction())) { + int apState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 0); + if (WifiHotspotState.WIFI_AP_STATE_ENABLED.ordinal() == apState % 10) { + WifiHotspotObserver.getInstance().setEnabled(true); + } else { + WifiHotspotObserver.getInstance().setEnabled(false); + } + } + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotObserver.java b/app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotObserver.java new file mode 100644 index 00000000..578c5552 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotObserver.java @@ -0,0 +1,55 @@ +package se.leap.bitmaskclient.tethering; + +import android.content.Context; +import android.content.IntentFilter; +import android.net.wifi.WifiManager; + +import java.lang.reflect.Method; + +public class WifiHotspotObserver { + private static WifiHotspotObserver instance; + + private boolean isEnabled; + private WifiManager wifiManager; + private WifiHotspotBroadcastReceiver broadcastReceiver; + + private WifiHotspotObserver() { + } + + public static void init(Context context) { + if (instance == null) { + instance = new WifiHotspotObserver(); + instance.broadcastReceiver = new WifiHotspotBroadcastReceiver(); + IntentFilter intentFilter = new IntentFilter("android.net.wifi.WIFI_AP_STATE_CHANGED"); + context.getApplicationContext().registerReceiver(instance.broadcastReceiver, intentFilter); + instance.wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE); + instance.setEnabled(instance.isWifiApEnabled()); + } + } + + private boolean isWifiApEnabled() { + try { + Method method = instance.wifiManager.getClass().getMethod("getWifiApState"); + int tmp = ((Integer) method.invoke(wifiManager)); + return WifiHotspotState.WIFI_AP_STATE_ENABLED.ordinal() == tmp % 10; + } catch (Exception e) { + return false; + } + } + + public static WifiHotspotObserver getInstance() { + if (instance == null) { + throw new RuntimeException("Call init() first!"); + } + + return instance; + } + + void setEnabled(boolean enabled) { + isEnabled = enabled; + } + + public boolean isEnabled() { + return isEnabled; + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotState.java b/app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotState.java new file mode 100644 index 00000000..9cd7f793 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotState.java @@ -0,0 +1,9 @@ +package se.leap.bitmaskclient.tethering; + +public enum WifiHotspotState { + WIFI_AP_STATE_DISABLING, + WIFI_AP_STATE_DISABLED, + WIFI_AP_STATE_ENABLING, + WIFI_AP_STATE_ENABLED, + WIFI_AP_STATE_FAILED +} diff --git a/app/src/main/java/se/leap/bitmaskclient/views/IconCheckboxEntry.java b/app/src/main/java/se/leap/bitmaskclient/views/IconCheckboxEntry.java index 933d391b..ca44592e 100644 --- a/app/src/main/java/se/leap/bitmaskclient/views/IconCheckboxEntry.java +++ b/app/src/main/java/se/leap/bitmaskclient/views/IconCheckboxEntry.java @@ -57,15 +57,24 @@ public class IconCheckboxEntry extends LinearLayout { View rootview = inflater.inflate(R.layout.v_icon_select_text_list_item, this, true); ButterKnife.inject(this, rootview); - Drawable checkIcon = DrawableCompat.wrap(getResources().getDrawable(R.drawable.ic_check_bold)); - DrawableCompat.setTint(checkIcon, ContextCompat.getColor(getContext(), R.color.colorSuccess)); - checkedIcon.setImageDrawable(checkIcon); + } public void bind(TetheringDialog.DialogListAdapter.ViewModel model) { + this.setEnabled(model.enabled); textView.setText(model.text); + textView.setEnabled(model.enabled); + + Drawable checkIcon = DrawableCompat.wrap(getResources().getDrawable(R.drawable.ic_check_bold)).mutate(); + if (model.enabled) { + DrawableCompat.setTint(checkIcon, ContextCompat.getColor(getContext(), R.color.colorSuccess)); + } else { + DrawableCompat.setTint(checkIcon, ContextCompat.getColor(getContext(), R.color.colorDisabled)); + } + iconView.setImageDrawable(model.image); + checkedIcon.setImageDrawable(checkIcon); setChecked(model.checked); } -- cgit v1.2.3 From 01e2f7e8f76248c791bd61429687da7c2cb584b9 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Fri, 3 Jan 2020 02:24:57 +0100 Subject: implement simple detection for usb tethering --- .../java/se/leap/bitmaskclient/BitmaskApp.java | 4 +- .../bitmaskclient/fragments/TetheringDialog.java | 9 ++- .../tethering/TetheringBroadcastReceiver.java | 22 ++++++ .../bitmaskclient/tethering/TetheringObserver.java | 92 ++++++++++++++++++++++ .../tethering/WifiHotspotBroadcastReceiver.java | 20 ----- .../tethering/WifiHotspotObserver.java | 55 ------------- 6 files changed, 121 insertions(+), 81 deletions(-) create mode 100644 app/src/main/java/se/leap/bitmaskclient/tethering/TetheringBroadcastReceiver.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObserver.java delete mode 100644 app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotBroadcastReceiver.java delete mode 100644 app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotObserver.java (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java b/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java index 6a657d33..83691802 100644 --- a/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java +++ b/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java @@ -8,7 +8,7 @@ import android.support.v7.app.AppCompatDelegate; import com.squareup.leakcanary.LeakCanary; import com.squareup.leakcanary.RefWatcher; -import se.leap.bitmaskclient.tethering.WifiHotspotObserver; +import se.leap.bitmaskclient.tethering.TetheringObserver; import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; import static se.leap.bitmaskclient.utils.PreferenceHelper.getSavedProviderFromSharedPreferences; @@ -40,7 +40,7 @@ public class BitmaskApp extends MultiDexApplication { providerObservable.updateProvider(getSavedProviderFromSharedPreferences(preferences)); EipSetupObserver.init(this, preferences); AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); - WifiHotspotObserver.init(this); + TetheringObserver.init(this); } /** diff --git a/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java index 04d3071f..65287afe 100644 --- a/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java @@ -23,7 +23,7 @@ import butterknife.ButterKnife; import butterknife.InjectView; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.eip.EipCommand; -import se.leap.bitmaskclient.tethering.WifiHotspotObserver; +import se.leap.bitmaskclient.tethering.TetheringObserver; import se.leap.bitmaskclient.utils.PreferenceHelper; import se.leap.bitmaskclient.views.IconCheckboxEntry; @@ -142,7 +142,8 @@ public class TetheringDialog extends AppCompatDialogFragment { @Override public void onResume() { super.onResume(); - dataset[0].enabled = WifiHotspotObserver.getInstance().isEnabled(); + dataset[0].enabled = TetheringObserver.getInstance().isWifiTetheringEnabled(); + dataset[1].enabled = TetheringObserver.getInstance().isUsbTetheringEnabled(); adapter.notifyDataSetChanged(); } @@ -175,11 +176,11 @@ public class TetheringDialog extends AppCompatDialogFragment { new DialogListAdapter.ViewModel(getContext().getResources().getDrawable(R.drawable.ic_wifi), getContext().getString(R.string.tethering_wifi), PreferenceHelper.getWifiTethering(getContext()), - WifiHotspotObserver.getInstance().isEnabled()), + TetheringObserver.getInstance().isWifiTetheringEnabled()), new DialogListAdapter.ViewModel(getContext().getResources().getDrawable(R.drawable.ic_usb), getContext().getString(R.string.tethering_usb), PreferenceHelper.getUsbTethering(getContext()), - true), + TetheringObserver.getInstance().isUsbTetheringEnabled()), new DialogListAdapter.ViewModel(getContext().getResources().getDrawable(R.drawable.ic_bluetooth), getContext().getString(R.string.tethering_bluetooth), PreferenceHelper.getBluetoothTethering(getContext()), diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringBroadcastReceiver.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringBroadcastReceiver.java new file mode 100644 index 00000000..c5840171 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringBroadcastReceiver.java @@ -0,0 +1,22 @@ +package se.leap.bitmaskclient.tethering; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.net.wifi.WifiManager; + +public class TetheringBroadcastReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if ("android.net.wifi.WIFI_AP_STATE_CHANGED".equals(intent.getAction())) { + int apState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 0); + if (WifiHotspotState.WIFI_AP_STATE_ENABLED.ordinal() == apState % 10) { + TetheringObserver.getInstance().setWifiTetheringEnabled(true); + } else { + TetheringObserver.getInstance().setWifiTetheringEnabled(false); + } + } else if ("android.net.conn.TETHER_STATE_CHANGED".equals(intent.getAction())) { + TetheringObserver.getInstance().updateUsbTetheringState(); + } + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObserver.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObserver.java new file mode 100644 index 00000000..1d1fa951 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObserver.java @@ -0,0 +1,92 @@ +package se.leap.bitmaskclient.tethering; + +import android.content.Context; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.wifi.WifiManager; + +import java.lang.reflect.Method; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.util.Enumeration; + +public class TetheringObserver { + private static TetheringObserver instance; + + private boolean isWifiTetheringEnabled; + private boolean isUsbTetheringEnabled; + private WifiManager wifiManager; + private ConnectivityManager connectivityManager; + private TetheringBroadcastReceiver broadcastReceiver; + + private TetheringObserver() { + } + + public static void init(Context context) { + if (instance == null) { + instance = new TetheringObserver(); + instance.broadcastReceiver = new TetheringBroadcastReceiver(); + IntentFilter intentFilter = new IntentFilter("android.net.wifi.WIFI_AP_STATE_CHANGED"); + intentFilter.addAction("android.net.conn.TETHER_STATE_CHANGED"); + context.getApplicationContext().registerReceiver(instance.broadcastReceiver, intentFilter); + instance.wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE); + instance.setWifiTetheringEnabled(instance.isWifiApEnabled()); + instance.connectivityManager = (ConnectivityManager) context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE); + } + } + + private boolean isWifiApEnabled() { + try { + Method method = instance.wifiManager.getClass().getMethod("getWifiApState"); + int tmp = ((Integer) method.invoke(wifiManager)); + return WifiHotspotState.WIFI_AP_STATE_ENABLED.ordinal() == tmp % 10; + } catch (Exception e) { + return false; + } + } + + private boolean getUsbTetheringState() { + try { + + for(Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { + NetworkInterface networkInterface = en.nextElement(); + for(Enumeration enumIpAddr = networkInterface.getInetAddresses(); enumIpAddr.hasMoreElements();){ + InetAddress inetAddress = enumIpAddr.nextElement(); + if(!networkInterface.isLoopback()){ + if(networkInterface.getName().contains("rndis") || networkInterface.getName().contains("usb")){ + return true; + } + } + } + } + } catch(Exception e){ + e.printStackTrace(); + } + + return false; + } + + public static TetheringObserver getInstance() { + if (instance == null) { + throw new RuntimeException("Call init() first!"); + } + + return instance; + } + + void setWifiTetheringEnabled(boolean enabled) { + isWifiTetheringEnabled = enabled; + } + + public boolean isWifiTetheringEnabled() { + return isWifiTetheringEnabled; + } + + void updateUsbTetheringState() { + isUsbTetheringEnabled = getUsbTetheringState(); + } + + public boolean isUsbTetheringEnabled() { + return isUsbTetheringEnabled; + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotBroadcastReceiver.java b/app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotBroadcastReceiver.java deleted file mode 100644 index 1d81eff5..00000000 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotBroadcastReceiver.java +++ /dev/null @@ -1,20 +0,0 @@ -package se.leap.bitmaskclient.tethering; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.net.wifi.WifiManager; - -public class WifiHotspotBroadcastReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - if ("android.net.wifi.WIFI_AP_STATE_CHANGED".equals(intent.getAction())) { - int apState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 0); - if (WifiHotspotState.WIFI_AP_STATE_ENABLED.ordinal() == apState % 10) { - WifiHotspotObserver.getInstance().setEnabled(true); - } else { - WifiHotspotObserver.getInstance().setEnabled(false); - } - } - } -} diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotObserver.java b/app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotObserver.java deleted file mode 100644 index 578c5552..00000000 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotObserver.java +++ /dev/null @@ -1,55 +0,0 @@ -package se.leap.bitmaskclient.tethering; - -import android.content.Context; -import android.content.IntentFilter; -import android.net.wifi.WifiManager; - -import java.lang.reflect.Method; - -public class WifiHotspotObserver { - private static WifiHotspotObserver instance; - - private boolean isEnabled; - private WifiManager wifiManager; - private WifiHotspotBroadcastReceiver broadcastReceiver; - - private WifiHotspotObserver() { - } - - public static void init(Context context) { - if (instance == null) { - instance = new WifiHotspotObserver(); - instance.broadcastReceiver = new WifiHotspotBroadcastReceiver(); - IntentFilter intentFilter = new IntentFilter("android.net.wifi.WIFI_AP_STATE_CHANGED"); - context.getApplicationContext().registerReceiver(instance.broadcastReceiver, intentFilter); - instance.wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE); - instance.setEnabled(instance.isWifiApEnabled()); - } - } - - private boolean isWifiApEnabled() { - try { - Method method = instance.wifiManager.getClass().getMethod("getWifiApState"); - int tmp = ((Integer) method.invoke(wifiManager)); - return WifiHotspotState.WIFI_AP_STATE_ENABLED.ordinal() == tmp % 10; - } catch (Exception e) { - return false; - } - } - - public static WifiHotspotObserver getInstance() { - if (instance == null) { - throw new RuntimeException("Call init() first!"); - } - - return instance; - } - - void setEnabled(boolean enabled) { - isEnabled = enabled; - } - - public boolean isEnabled() { - return isEnabled; - } -} -- cgit v1.2.3 From e67c55d2d39fea60ee06ca14ca7b61ba638b7f2a Mon Sep 17 00:00:00 2001 From: cyBerta Date: Wed, 8 Jan 2020 05:06:23 +0100 Subject: implement bluetooth tethering detection, refactor TetheringObservable --- .../java/se/leap/bitmaskclient/BitmaskApp.java | 4 +- .../bitmaskclient/fragments/TetheringDialog.java | 38 +++++++-- .../tethering/TetheringBroadcastReceiver.java | 13 ++- .../tethering/TetheringObservable.java | 50 ++++++++++++ .../bitmaskclient/tethering/TetheringObserver.java | 92 --------------------- .../tethering/TetheringStateManager.java | 93 ++++++++++++++++++++++ 6 files changed, 186 insertions(+), 104 deletions(-) create mode 100644 app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java delete mode 100644 app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObserver.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java b/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java index 83691802..48910fb5 100644 --- a/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java +++ b/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java @@ -8,7 +8,7 @@ import android.support.v7.app.AppCompatDelegate; import com.squareup.leakcanary.LeakCanary; import com.squareup.leakcanary.RefWatcher; -import se.leap.bitmaskclient.tethering.TetheringObserver; +import se.leap.bitmaskclient.tethering.TetheringStateManager; import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; import static se.leap.bitmaskclient.utils.PreferenceHelper.getSavedProviderFromSharedPreferences; @@ -40,7 +40,7 @@ public class BitmaskApp extends MultiDexApplication { providerObservable.updateProvider(getSavedProviderFromSharedPreferences(preferences)); EipSetupObserver.init(this, preferences); AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); - TetheringObserver.init(this); + TetheringStateManager.getInstance().init(this); } /** diff --git a/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java index 65287afe..1d125245 100644 --- a/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java @@ -15,15 +15,19 @@ import android.text.Spannable; import android.text.SpannableString; import android.text.method.LinkMovementMethod; import android.text.style.ClickableSpan; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import java.util.Observable; +import java.util.Observer; + import butterknife.ButterKnife; import butterknife.InjectView; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.eip.EipCommand; -import se.leap.bitmaskclient.tethering.TetheringObserver; +import se.leap.bitmaskclient.tethering.TetheringObservable; import se.leap.bitmaskclient.utils.PreferenceHelper; import se.leap.bitmaskclient.views.IconCheckboxEntry; @@ -31,7 +35,7 @@ import se.leap.bitmaskclient.views.IconCheckboxEntry; * Created by cyberta on 25.02.18. */ -public class TetheringDialog extends AppCompatDialogFragment { +public class TetheringDialog extends AppCompatDialogFragment implements Observer { public final static String TAG = TetheringDialog.class.getName(); @@ -142,9 +146,17 @@ public class TetheringDialog extends AppCompatDialogFragment { @Override public void onResume() { super.onResume(); - dataset[0].enabled = TetheringObserver.getInstance().isWifiTetheringEnabled(); - dataset[1].enabled = TetheringObserver.getInstance().isUsbTetheringEnabled(); + dataset[0].enabled = TetheringObservable.getInstance().isWifiTetheringEnabled(); + dataset[1].enabled = TetheringObservable.getInstance().isUsbTetheringEnabled(); + dataset[2].enabled = TetheringObservable.getInstance().isBluetoothTetheringEnabled(); adapter.notifyDataSetChanged(); + TetheringObservable.getInstance().addObserver(this); + } + + @Override + public void onPause() { + super.onPause(); + TetheringObservable.getInstance().deleteObserver(this); } public void onItemClick(DialogListAdapter.ViewModel item) { @@ -176,16 +188,28 @@ public class TetheringDialog extends AppCompatDialogFragment { new DialogListAdapter.ViewModel(getContext().getResources().getDrawable(R.drawable.ic_wifi), getContext().getString(R.string.tethering_wifi), PreferenceHelper.getWifiTethering(getContext()), - TetheringObserver.getInstance().isWifiTetheringEnabled()), + TetheringObservable.getInstance().isWifiTetheringEnabled()), new DialogListAdapter.ViewModel(getContext().getResources().getDrawable(R.drawable.ic_usb), getContext().getString(R.string.tethering_usb), PreferenceHelper.getUsbTethering(getContext()), - TetheringObserver.getInstance().isUsbTetheringEnabled()), + TetheringObservable.getInstance().isUsbTetheringEnabled()), new DialogListAdapter.ViewModel(getContext().getResources().getDrawable(R.drawable.ic_bluetooth), getContext().getString(R.string.tethering_bluetooth), PreferenceHelper.getBluetoothTethering(getContext()), - true) + TetheringObservable.getInstance().isUsbTetheringEnabled()) }; } + @Override + public void update(Observable o, Object arg) { + if (o instanceof TetheringObservable) { + TetheringObservable observable = (TetheringObservable) o; + Log.d(TAG, "TetheringObservable is updated"); + dataset[0].enabled = observable.isWifiTetheringEnabled(); + dataset[1].enabled = observable.isUsbTetheringEnabled(); + dataset[2].enabled = observable.isBluetoothTetheringEnabled(); + adapter.notifyDataSetChanged(); + } + } + } diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringBroadcastReceiver.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringBroadcastReceiver.java index c5840171..54c312d7 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringBroadcastReceiver.java +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringBroadcastReceiver.java @@ -4,19 +4,26 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.net.wifi.WifiManager; +import android.util.Log; public class TetheringBroadcastReceiver extends BroadcastReceiver { + private static final String TAG = TetheringBroadcastReceiver.class.getSimpleName(); + @Override public void onReceive(Context context, Intent intent) { if ("android.net.wifi.WIFI_AP_STATE_CHANGED".equals(intent.getAction())) { + Log.d(TAG, "TETHERING WIFI_AP_STATE_CHANGED"); int apState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 0); if (WifiHotspotState.WIFI_AP_STATE_ENABLED.ordinal() == apState % 10) { - TetheringObserver.getInstance().setWifiTetheringEnabled(true); + TetheringObservable.setWifiTethering(true); } else { - TetheringObserver.getInstance().setWifiTetheringEnabled(false); + TetheringObservable.setWifiTethering(false); } } else if ("android.net.conn.TETHER_STATE_CHANGED".equals(intent.getAction())) { - TetheringObserver.getInstance().updateUsbTetheringState(); + Log.d(TAG, "TETHERING TETHER_STATE_CHANGED"); + TetheringStateManager.updateUsbTetheringState(); + TetheringStateManager.updateBluetoothTetheringState(); + TetheringStateManager.updateWifiTetheringState(); } } } diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java new file mode 100644 index 00000000..594258bf --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java @@ -0,0 +1,50 @@ +package se.leap.bitmaskclient.tethering; + +import java.util.Observable; + +public class TetheringObservable extends Observable { + private static TetheringObservable instance; + + private boolean isWifiTetheringEnabled; + private boolean isUsbTetheringEnabled; + private boolean isBluetoothTetheringEnabled; + + private TetheringObservable() { } + + public static TetheringObservable getInstance() { + if (instance == null) { + instance = new TetheringObservable(); + } + return instance; + } + + static void setWifiTethering(boolean enabled) { + getInstance().isWifiTetheringEnabled = enabled; + getInstance().setChanged(); + getInstance().notifyObservers(); + } + + static void setUsbTethering(boolean enabled) { + getInstance().isUsbTetheringEnabled = enabled; + getInstance().setChanged(); + getInstance().notifyObservers(); + } + + static void setBluetoothTethering(boolean enabled) { + getInstance().isBluetoothTetheringEnabled = enabled; + getInstance().setChanged(); + getInstance().notifyObservers(); + } + + public boolean isBluetoothTetheringEnabled() { + return isBluetoothTetheringEnabled; + } + + public boolean isUsbTetheringEnabled() { + return isUsbTetheringEnabled; + } + + public boolean isWifiTetheringEnabled() { + return isWifiTetheringEnabled; + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObserver.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObserver.java deleted file mode 100644 index 1d1fa951..00000000 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObserver.java +++ /dev/null @@ -1,92 +0,0 @@ -package se.leap.bitmaskclient.tethering; - -import android.content.Context; -import android.content.IntentFilter; -import android.net.ConnectivityManager; -import android.net.wifi.WifiManager; - -import java.lang.reflect.Method; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.util.Enumeration; - -public class TetheringObserver { - private static TetheringObserver instance; - - private boolean isWifiTetheringEnabled; - private boolean isUsbTetheringEnabled; - private WifiManager wifiManager; - private ConnectivityManager connectivityManager; - private TetheringBroadcastReceiver broadcastReceiver; - - private TetheringObserver() { - } - - public static void init(Context context) { - if (instance == null) { - instance = new TetheringObserver(); - instance.broadcastReceiver = new TetheringBroadcastReceiver(); - IntentFilter intentFilter = new IntentFilter("android.net.wifi.WIFI_AP_STATE_CHANGED"); - intentFilter.addAction("android.net.conn.TETHER_STATE_CHANGED"); - context.getApplicationContext().registerReceiver(instance.broadcastReceiver, intentFilter); - instance.wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE); - instance.setWifiTetheringEnabled(instance.isWifiApEnabled()); - instance.connectivityManager = (ConnectivityManager) context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE); - } - } - - private boolean isWifiApEnabled() { - try { - Method method = instance.wifiManager.getClass().getMethod("getWifiApState"); - int tmp = ((Integer) method.invoke(wifiManager)); - return WifiHotspotState.WIFI_AP_STATE_ENABLED.ordinal() == tmp % 10; - } catch (Exception e) { - return false; - } - } - - private boolean getUsbTetheringState() { - try { - - for(Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { - NetworkInterface networkInterface = en.nextElement(); - for(Enumeration enumIpAddr = networkInterface.getInetAddresses(); enumIpAddr.hasMoreElements();){ - InetAddress inetAddress = enumIpAddr.nextElement(); - if(!networkInterface.isLoopback()){ - if(networkInterface.getName().contains("rndis") || networkInterface.getName().contains("usb")){ - return true; - } - } - } - } - } catch(Exception e){ - e.printStackTrace(); - } - - return false; - } - - public static TetheringObserver getInstance() { - if (instance == null) { - throw new RuntimeException("Call init() first!"); - } - - return instance; - } - - void setWifiTetheringEnabled(boolean enabled) { - isWifiTetheringEnabled = enabled; - } - - public boolean isWifiTetheringEnabled() { - return isWifiTetheringEnabled; - } - - void updateUsbTetheringState() { - isUsbTetheringEnabled = getUsbTetheringState(); - } - - public boolean isUsbTetheringEnabled() { - return isUsbTetheringEnabled; - } -} diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java new file mode 100644 index 00000000..1e2521b8 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java @@ -0,0 +1,93 @@ +package se.leap.bitmaskclient.tethering; + +import android.content.Context; +import android.content.IntentFilter; +import android.net.wifi.WifiManager; + +import java.lang.reflect.Method; +import java.net.NetworkInterface; +import java.util.Enumeration; + +import se.leap.bitmaskclient.utils.Cmd; + +public class TetheringStateManager { + private static final String TAG = TetheringStateManager.class.getSimpleName(); + private static TetheringStateManager instance; + + private WifiManager wifiManager; + + private TetheringStateManager() { } + + public static TetheringStateManager getInstance() { + if (instance == null) { + instance = new TetheringStateManager(); + } + return instance; + } + + public void init(Context context) { + TetheringBroadcastReceiver broadcastReceiver = new TetheringBroadcastReceiver(); + IntentFilter intentFilter = new IntentFilter("android.net.conn.TETHER_STATE_CHANGED"); + intentFilter.addAction("android.net.wifi.WIFI_AP_STATE_CHANGED"); + context.getApplicationContext().registerReceiver(broadcastReceiver, intentFilter); + instance.wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE); + updateWifiTetheringState(); + updateUsbTetheringState(); + updateBluetoothTetheringState(); + } + + private static boolean isWifiApEnabled() { + try { + Method method = instance.wifiManager.getClass().getMethod("getWifiApState"); + int tmp = ((Integer) method.invoke(instance.wifiManager)); + return WifiHotspotState.WIFI_AP_STATE_ENABLED.ordinal() == tmp % 10; + } catch (Exception e) { + return false; + } + } + + + private static boolean getUsbTetheringState() { + try { + for(Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { + NetworkInterface networkInterface = en.nextElement(); + if(!networkInterface.isLoopback()){ + if(networkInterface.getName().contains("rndis") || networkInterface.getName().contains("usb")){ + return true; + } + } + } + } catch(Exception e){ + e.printStackTrace(); + } + + return false; + } + + // Check whether Bluetooth tethering is enabled. + private static boolean isBluetoothTetheringEnabled() { + StringBuilder log = new StringBuilder(); + boolean hasBtPan = false; + try { + hasBtPan = Cmd.runBlockingCmd(new String[] {"ifconfig bt-pan"}, log) == 0; + //Log.d(TAG, "ifconfig result: " + log.toString()); + } catch (Exception e) { + e.printStackTrace(); + } + return hasBtPan; + + } + + static void updateUsbTetheringState() { + TetheringObservable.setUsbTethering(getUsbTetheringState()); + } + + static void updateBluetoothTetheringState() { + TetheringObservable.setBluetoothTethering(isBluetoothTetheringEnabled()); + } + + static void updateWifiTetheringState() { + TetheringObservable.setWifiTethering(isWifiApEnabled()); + } + +} -- cgit v1.2.3 From 5d5830d2c6d169f5c3bb4784ed72f840cd12fb7b Mon Sep 17 00:00:00 2001 From: cyBerta Date: Fri, 10 Jan 2020 03:11:02 +0100 Subject: Don't change the wifi tethering state if a check fails with an exception, unit tests for TetheringStateManager, some minor refacorings related to that --- .../tethering/TetheringBroadcastReceiver.java | 25 +- .../tethering/TetheringStateManager.java | 53 ++-- .../tethering/WifiManagerWrapper.java | 41 +++ .../main/java/se/leap/bitmaskclient/utils/Cmd.java | 2 - .../tethering/TetheringStateManagerTest.java | 279 +++++++++++++++++++++ 5 files changed, 382 insertions(+), 18 deletions(-) create mode 100644 app/src/main/java/se/leap/bitmaskclient/tethering/WifiManagerWrapper.java create mode 100644 app/src/test/java/se/leap/bitmaskclient/tethering/TetheringStateManagerTest.java (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringBroadcastReceiver.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringBroadcastReceiver.java index 54c312d7..8dab49ce 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringBroadcastReceiver.java +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringBroadcastReceiver.java @@ -1,3 +1,20 @@ +/** + * Copyright (c) 2020LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package se.leap.bitmaskclient.tethering; import android.content.BroadcastReceiver; @@ -15,9 +32,13 @@ public class TetheringBroadcastReceiver extends BroadcastReceiver { Log.d(TAG, "TETHERING WIFI_AP_STATE_CHANGED"); int apState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 0); if (WifiHotspotState.WIFI_AP_STATE_ENABLED.ordinal() == apState % 10) { - TetheringObservable.setWifiTethering(true); + if (!TetheringObservable.getInstance().isWifiTetheringEnabled()) { + TetheringObservable.setWifiTethering(true); + } } else { - TetheringObservable.setWifiTethering(false); + if (TetheringObservable.getInstance().isWifiTetheringEnabled()) { + TetheringObservable.setWifiTethering(false); + } } } else if ("android.net.conn.TETHER_STATE_CHANGED".equals(intent.getAction())) { Log.d(TAG, "TETHERING TETHER_STATE_CHANGED"); diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java index 1e2521b8..0d4f56d8 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java @@ -1,20 +1,44 @@ +/** + * Copyright (c) 2020 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package se.leap.bitmaskclient.tethering; import android.content.Context; import android.content.IntentFilter; -import android.net.wifi.WifiManager; -import java.lang.reflect.Method; import java.net.NetworkInterface; import java.util.Enumeration; import se.leap.bitmaskclient.utils.Cmd; +/** + * This manager tries to figure out the current tethering states for Wifi, USB and Bluetooth + * The default behavior differs for failing attempts to get these states: + * Wifi: keeps old state + * USB: defaults to false + * Bluetooth defaults to false + * For Wifi there's a second method to check the current state (see TetheringBroadcastReceiver). + * Either of both methods can change the state if they succeed, but are ignored if they fail. + * This should avoid any interference between both methods. + */ public class TetheringStateManager { private static final String TAG = TetheringStateManager.class.getSimpleName(); private static TetheringStateManager instance; - private WifiManager wifiManager; + private WifiManagerWrapper wifiManager; private TetheringStateManager() { } @@ -30,20 +54,14 @@ public class TetheringStateManager { IntentFilter intentFilter = new IntentFilter("android.net.conn.TETHER_STATE_CHANGED"); intentFilter.addAction("android.net.wifi.WIFI_AP_STATE_CHANGED"); context.getApplicationContext().registerReceiver(broadcastReceiver, intentFilter); - instance.wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE); + instance.wifiManager = new WifiManagerWrapper(context); updateWifiTetheringState(); updateUsbTetheringState(); updateBluetoothTetheringState(); } - private static boolean isWifiApEnabled() { - try { - Method method = instance.wifiManager.getClass().getMethod("getWifiApState"); - int tmp = ((Integer) method.invoke(instance.wifiManager)); - return WifiHotspotState.WIFI_AP_STATE_ENABLED.ordinal() == tmp % 10; - } catch (Exception e) { - return false; - } + private static boolean isWifiApEnabled() throws Exception { + return instance.wifiManager.isWifiAPEnabled(); } @@ -64,7 +82,6 @@ public class TetheringStateManager { return false; } - // Check whether Bluetooth tethering is enabled. private static boolean isBluetoothTetheringEnabled() { StringBuilder log = new StringBuilder(); boolean hasBtPan = false; @@ -87,7 +104,15 @@ public class TetheringStateManager { } static void updateWifiTetheringState() { - TetheringObservable.setWifiTethering(isWifiApEnabled()); + boolean lastState = TetheringObservable.getInstance().isWifiTetheringEnabled(); + try { + boolean currentState = isWifiApEnabled(); + if (currentState != lastState) { + TetheringObservable.setWifiTethering(currentState); + } + } catch (Exception e) { + e.printStackTrace(); + } } } diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/WifiManagerWrapper.java b/app/src/main/java/se/leap/bitmaskclient/tethering/WifiManagerWrapper.java new file mode 100644 index 00000000..ed395d7f --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/WifiManagerWrapper.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2020 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package se.leap.bitmaskclient.tethering; + +import android.content.Context; +import android.net.wifi.WifiManager; + +import java.lang.reflect.Method; + +/** + * This Wrapper allows better Unit testing. + */ +class WifiManagerWrapper { + + private WifiManager wifiManager; + + WifiManagerWrapper(Context context) { + this.wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE); + } + + boolean isWifiAPEnabled() throws Exception { + Method method = wifiManager.getClass().getMethod("getWifiApState"); + int tmp = ((Integer) method.invoke(wifiManager)); + return WifiHotspotState.WIFI_AP_STATE_ENABLED.ordinal() == tmp % 10; + } + +} diff --git a/app/src/main/java/se/leap/bitmaskclient/utils/Cmd.java b/app/src/main/java/se/leap/bitmaskclient/utils/Cmd.java index a72658a4..d033ed24 100644 --- a/app/src/main/java/se/leap/bitmaskclient/utils/Cmd.java +++ b/app/src/main/java/se/leap/bitmaskclient/utils/Cmd.java @@ -18,7 +18,6 @@ package se.leap.bitmaskclient.utils; import android.support.annotation.WorkerThread; -import android.util.Log; import java.io.IOException; import java.io.InputStreamReader; @@ -43,7 +42,6 @@ public class Cmd { try { for (String cmd : cmds) { - Log.d(TAG, "executing CMD: " + cmd); out.write(cmd); out.write("\n"); } diff --git a/app/src/test/java/se/leap/bitmaskclient/tethering/TetheringStateManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/tethering/TetheringStateManagerTest.java new file mode 100644 index 00000000..295714c3 --- /dev/null +++ b/app/src/test/java/se/leap/bitmaskclient/tethering/TetheringStateManagerTest.java @@ -0,0 +1,279 @@ +/** + * Copyright (c) 2020 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package se.leap.bitmaskclient.tethering; + +import android.content.Context; +import android.content.IntentFilter; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; + +import se.leap.bitmaskclient.utils.Cmd; + +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.when; + + +@RunWith(PowerMockRunner.class) +@PrepareForTest({WifiManagerWrapper.class, TetheringStateManager.class, Cmd.class, NetworkInterface.class}) +public class TetheringStateManagerTest { + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + Context mockContext; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + IntentFilter intentFilter; + + TetheringObservable observable; + + @Before + public void setup() throws Exception { + PowerMockito.whenNew(IntentFilter.class).withArguments(anyString()).thenReturn(intentFilter); + PowerMockito.whenNew(IntentFilter.class).withNoArguments().thenReturn(intentFilter); + observable = TetheringObservable.getInstance(); + + } + + @Test + public void updateUsbTetheringState_findsRndisX_returnsTrue() throws Exception { + WifiManagerWrapper mockWrapper = mock(WifiManagerWrapper.class); + when(mockWrapper.isWifiAPEnabled()).thenReturn(false); + PowerMockito.whenNew(WifiManagerWrapper.class).withAnyArguments().thenReturn(mockWrapper); + + PowerMockito.mockStatic(NetworkInterface.class); + NetworkInterface mock1 = PowerMockito.mock(NetworkInterface.class); + when(mock1.isLoopback()).thenReturn(false); + when(mock1.getName()).thenReturn("eth0"); + NetworkInterface mock2 = PowerMockito.mock(NetworkInterface.class); + when(mock2.isLoopback()).thenReturn(false); + when(mock2.getName()).thenReturn("rndis0"); + + NetworkInterface[] networkInterfaces = new NetworkInterface[2]; + networkInterfaces[0] = mock1; + networkInterfaces[1] = mock2; + + PowerMockito.when(NetworkInterface.getNetworkInterfaces()).then(new Answer>() { + @Override + public Enumeration answer(InvocationOnMock invocation) throws Throwable { + return Collections.enumeration(Arrays.asList(networkInterfaces)); + } + }); + + TetheringObservable.setUsbTethering(false); + TetheringStateManager.getInstance().init(mockContext); + TetheringStateManager manager = TetheringStateManager.getInstance(); + assertTrue(observable.isUsbTetheringEnabled()); + } + + @Test + public void updateUsbTetheringState_doesntFindRndisX_returnsFalse() throws Exception { + WifiManagerWrapper mockWrapper = mock(WifiManagerWrapper.class); + when(mockWrapper.isWifiAPEnabled()).thenReturn(false); + PowerMockito.whenNew(WifiManagerWrapper.class).withAnyArguments().thenReturn(mockWrapper); + + PowerMockito.mockStatic(NetworkInterface.class); + NetworkInterface mock1 = PowerMockito.mock(NetworkInterface.class); + when(mock1.isLoopback()).thenReturn(false); + when(mock1.getName()).thenReturn("eth0"); + NetworkInterface mock2 = PowerMockito.mock(NetworkInterface.class); + when(mock2.isLoopback()).thenReturn(false); + when(mock2.getName()).thenReturn("wifi0"); + + NetworkInterface[] networkInterfaces = new NetworkInterface[2]; + networkInterfaces[0] = mock1; + networkInterfaces[1] = mock2; + + PowerMockito.when(NetworkInterface.getNetworkInterfaces()).then(new Answer>() { + @Override + public Enumeration answer(InvocationOnMock invocation) throws Throwable { + return Collections.enumeration(Arrays.asList(networkInterfaces)); + } + }); + + TetheringObservable.setUsbTethering(true); + TetheringStateManager.getInstance().init(mockContext); + TetheringStateManager manager = TetheringStateManager.getInstance(); + assertFalse(observable.isUsbTetheringEnabled()); + } + + @Test + public void updateUsbTetheringState_ThrowsException_returnsFalse() throws Exception { + WifiManagerWrapper mockWrapper = mock(WifiManagerWrapper.class); + when(mockWrapper.isWifiAPEnabled()).thenReturn(false); + PowerMockito.whenNew(WifiManagerWrapper.class).withAnyArguments().thenReturn(mockWrapper); + + PowerMockito.mockStatic(NetworkInterface.class); + PowerMockito.when(NetworkInterface.getNetworkInterfaces()).thenThrow(new SocketException()); + + TetheringObservable.setUsbTethering(true); + TetheringStateManager.getInstance().init(mockContext); + TetheringStateManager manager = TetheringStateManager.getInstance(); + assertFalse(observable.isUsbTetheringEnabled()); + } + + @Test + public void updateBluetoothTetheringState_btDeviceFound_returnTrue() throws Exception { + WifiManagerWrapper mockWrapper = mock(WifiManagerWrapper.class); + when(mockWrapper.isWifiAPEnabled()).thenReturn(true); + PowerMockito.whenNew(WifiManagerWrapper.class).withAnyArguments().thenReturn(mockWrapper); + + mockStatic(Cmd.class); + PowerMockito.when(Cmd.runBlockingCmd(any(), any(StringBuilder.class))).then(new Answer() { + @Override + public Integer answer(InvocationOnMock invocation) throws Throwable { + StringBuilder logStringBuilder = invocation.getArgument(1); + logStringBuilder.append("bt-pan device found"); + return 0; + } + }); + + TetheringObservable.setBluetoothTethering(false); + TetheringStateManager.getInstance().init(mockContext); + TetheringStateManager manager = TetheringStateManager.getInstance(); + assertTrue(observable.isBluetoothTetheringEnabled()); + } + + + @Test + public void updateBluetoothTetheringState_btPanDeviceNotFound_returnFalse() throws Exception { + WifiManagerWrapper mockWrapper = mock(WifiManagerWrapper.class); + when(mockWrapper.isWifiAPEnabled()).thenReturn(true); + PowerMockito.whenNew(WifiManagerWrapper.class).withAnyArguments().thenReturn(mockWrapper); + + mockStatic(Cmd.class); + PowerMockito.when(Cmd.runBlockingCmd(any(), any(StringBuilder.class))).then(new Answer() { + @Override + public Integer answer(InvocationOnMock invocation) throws Throwable { + StringBuilder logStringBuilder = invocation.getArgument(1); + logStringBuilder.append("bt-pan device not found"); + return 1; + } + }); + + TetheringObservable.setBluetoothTethering(true); + TetheringStateManager.getInstance().init(mockContext); + TetheringStateManager manager = TetheringStateManager.getInstance(); + assertFalse(observable.isBluetoothTetheringEnabled()); + } + + @Test + public void updateBluetoothTetheringState_ThrowsException_returnsFalse() throws Exception { + WifiManagerWrapper mockWrapper = mock(WifiManagerWrapper.class); + when(mockWrapper.isWifiAPEnabled()).thenReturn(true); + PowerMockito.whenNew(WifiManagerWrapper.class).withAnyArguments().thenReturn(mockWrapper); + + mockStatic(Cmd.class); + PowerMockito.when(Cmd.runBlockingCmd(any(), any(StringBuilder.class))). + thenThrow(new SecurityException("Creation of subprocess is not allowed")); + + TetheringObservable.setBluetoothTethering(true); + TetheringStateManager.getInstance().init(mockContext); + TetheringStateManager manager = TetheringStateManager.getInstance(); + assertFalse(observable.isBluetoothTetheringEnabled()); + } + + @Test + public void updateBluetoothTetheringState_WifiManagerWrapperThrowsException_hasNoInfluenceOnResult() throws Exception { + WifiManagerWrapper mockWrapper = mock(WifiManagerWrapper.class); + when(mockWrapper.isWifiAPEnabled()).thenThrow(new NoSuchMethodException()); + PowerMockito.whenNew(WifiManagerWrapper.class).withAnyArguments().thenReturn(mockWrapper); + + mockStatic(Cmd.class); + PowerMockito.when(Cmd.runBlockingCmd(any(), any(StringBuilder.class))).then(new Answer() { + @Override + public Integer answer(InvocationOnMock invocation) throws Throwable { + StringBuilder logStringBuilder = invocation.getArgument(1); + logStringBuilder.append("bt-pan device found"); + return 0; + } + }); + + TetheringObservable.setBluetoothTethering(false); + TetheringStateManager.getInstance().init(mockContext); + TetheringStateManager manager = TetheringStateManager.getInstance(); + assertTrue(observable.isBluetoothTetheringEnabled()); + } + + @Test + public void updateWifiTetheringState_ignoreFailingWifiAPReflection_keepsOldValueTrue() throws Exception { + WifiManagerWrapper mockWrapper = mock(WifiManagerWrapper.class); + when(mockWrapper.isWifiAPEnabled()).thenThrow(new NoSuchMethodException()); + PowerMockito.whenNew(WifiManagerWrapper.class).withAnyArguments().thenReturn(mockWrapper); + + TetheringObservable.setWifiTethering(true); + TetheringStateManager.getInstance().init(mockContext); + TetheringStateManager manager = TetheringStateManager.getInstance(); + assertTrue(observable.isWifiTetheringEnabled()); + } + + @Test + public void updateWifiTetheringState_ignoreFailingWifiAPReflection_keepsOldValueFalse() throws Exception { + WifiManagerWrapper mockWrapper = mock(WifiManagerWrapper.class); + when(mockWrapper.isWifiAPEnabled()).thenThrow(new NoSuchMethodException()); + PowerMockito.whenNew(WifiManagerWrapper.class).withAnyArguments().thenReturn(mockWrapper); + + TetheringObservable.setWifiTethering(false); + TetheringStateManager.getInstance().init(mockContext); + TetheringStateManager manager = TetheringStateManager.getInstance(); + assertFalse(observable.isWifiTetheringEnabled()); + } + + @Test + public void updateWifiTetheringState_WifiApReflectionWithoutException_changeValueToTrue() throws Exception { + WifiManagerWrapper mockWrapper = mock(WifiManagerWrapper.class); + when(mockWrapper.isWifiAPEnabled()).thenReturn(true); + PowerMockito.whenNew(WifiManagerWrapper.class).withAnyArguments().thenReturn(mockWrapper); + + TetheringObservable.setWifiTethering(false); + TetheringStateManager.getInstance().init(mockContext); + TetheringStateManager manager = TetheringStateManager.getInstance(); + assertTrue(observable.isWifiTetheringEnabled()); + } + + @Test + public void updateWifiTetheringState_WifiApReflectionWithoutException_changeValueToFalse() throws Exception { + WifiManagerWrapper mockWrapper = mock(WifiManagerWrapper.class); + when(mockWrapper.isWifiAPEnabled()).thenReturn(false); + PowerMockito.whenNew(WifiManagerWrapper.class).withAnyArguments().thenReturn(mockWrapper); + + TetheringObservable.setWifiTethering(true); + TetheringStateManager.getInstance().init(mockContext); + TetheringStateManager manager = TetheringStateManager.getInstance(); + assertFalse(observable.isWifiTetheringEnabled()); + } + + +} \ No newline at end of file -- cgit v1.2.3 From 2ff2358861bcd0c05586f972724ef76e5a67060c Mon Sep 17 00:00:00 2001 From: cyBerta Date: Fri, 10 Jan 2020 03:11:39 +0100 Subject: add some copyright headers --- .../bitmaskclient/tethering/TetheringObservable.java | 16 ++++++++++++++++ .../leap/bitmaskclient/tethering/WifiHotspotState.java | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java index 594258bf..9ea9160f 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java @@ -1,3 +1,19 @@ +/** + * Copyright (c) 2020 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package se.leap.bitmaskclient.tethering; import java.util.Observable; diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotState.java b/app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotState.java index 9cd7f793..f29a87f8 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotState.java +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotState.java @@ -1,3 +1,19 @@ +/** + * Copyright (c) 2020 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package se.leap.bitmaskclient.tethering; public enum WifiHotspotState { -- cgit v1.2.3 From 0fe9d37a223b388103e2924e6d1a28bcb0ae38fd Mon Sep 17 00:00:00 2001 From: cyberta Date: Fri, 24 Jan 2020 14:21:34 -0600 Subject: rearrange firewalling code, move to separate package --- .../de/blinkt/openvpn/core/OpenVPNService.java | 10 +- .../bitmaskclient/firewall/FirewallCallback.java | 7 + .../bitmaskclient/firewall/FirewallManager.java | 73 ++++++++ .../firewall/ShutdownFirewallTask.java | 55 ++++++ .../bitmaskclient/firewall/StartFirewallTask.java | 74 ++++++++ .../leap/bitmaskclient/utils/FirewallHelper.java | 196 --------------------- 6 files changed, 214 insertions(+), 201 deletions(-) create mode 100644 app/src/main/java/se/leap/bitmaskclient/firewall/FirewallCallback.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownFirewallTask.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/firewall/StartFirewallTask.java delete mode 100644 app/src/main/java/se/leap/bitmaskclient/utils/FirewallHelper.java (limited to 'app') diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java index 766dc925..7c17bed9 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java +++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java @@ -47,7 +47,7 @@ import de.blinkt.openvpn.core.connection.Obfs4Connection; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.VpnNotificationManager; import se.leap.bitmaskclient.pluggableTransports.Shapeshifter; -import se.leap.bitmaskclient.utils.FirewallHelper; +import se.leap.bitmaskclient.firewall.FirewallManager; import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_CONNECTED; import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_WAITING_FOR_USER_INPUT; @@ -90,7 +90,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac private Runnable mOpenVPNThread; private VpnNotificationManager notificationManager; private Shapeshifter shapeshifter; - private FirewallHelper firewallHelper; + private FirewallManager firewallManager; private static final int PRIORITY_MIN = -2; private static final int PRIORITY_DEFAULT = 0; @@ -194,7 +194,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac VpnStatus.removeStateListener(this); } } - firewallHelper.shutdownFirewall(); + firewallManager.shutdownFirewall(); } private boolean runningOnAndroidTV() { @@ -449,7 +449,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac mProcessThread.start(); } - firewallHelper.startFirewall(); + firewallManager.startFirewall(); new Handler(getMainLooper()).post(() -> { if (mDeviceStateReceiver != null) { @@ -518,7 +518,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac super.onCreate(); notificationManager = new VpnNotificationManager(this, this); notificationManager.createOpenVpnNotificationChannel(); - firewallHelper = new FirewallHelper(this); + firewallManager = new FirewallManager(this); } @Override diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallCallback.java b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallCallback.java new file mode 100644 index 00000000..f6ccd227 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallCallback.java @@ -0,0 +1,7 @@ +package se.leap.bitmaskclient.firewall; + +interface FirewallCallback { + void onFirewallStarted(boolean success); + void onFirewallStopped(boolean success); + void onSuRequested(boolean success); +} diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java new file mode 100644 index 00000000..a3be46e6 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java @@ -0,0 +1,73 @@ +package se.leap.bitmaskclient.firewall; +/** + * Copyright (c) 2019 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import android.content.Context; + +import de.blinkt.openvpn.core.VpnStatus; +import se.leap.bitmaskclient.utils.PreferenceHelper; + + +public class FirewallManager implements FirewallCallback { + public static String BITMASK_CHAIN = "bitmask_fw"; + static final String TAG = FirewallManager.class.getSimpleName(); + + private Context context; + + public FirewallManager(Context context) { + this.context = context; + } + + + @Override + public void onFirewallStarted(boolean success) { + if (success) { + VpnStatus.logInfo("[FIREWALL] Custom rules established"); + } else { + VpnStatus.logError("[FIREWALL] Could not establish custom rules."); + } + } + + @Override + public void onFirewallStopped(boolean success) { + if (success) { + VpnStatus.logInfo("[FIREWALL] Custom rules deleted"); + } else { + VpnStatus.logError("[FIREWALL] Could not delete custom rules"); + } + } + + @Override + public void onSuRequested(boolean success) { + PreferenceHelper.setSuPermission(context, success); + if (!success) { + VpnStatus.logError("[FIREWALL] Root permission needed to execute custom firewall rules."); + } + } + + + public void startFirewall() { + StartFirewallTask task = new StartFirewallTask(this); + task.execute(); + } + + public void shutdownFirewall() { + ShutdownFirewallTask task = new ShutdownFirewallTask(this); + task.execute(); + } + +} diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownFirewallTask.java b/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownFirewallTask.java new file mode 100644 index 00000000..50fa77cd --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownFirewallTask.java @@ -0,0 +1,55 @@ +package se.leap.bitmaskclient.firewall; + +import android.os.AsyncTask; +import android.util.Log; + +import java.lang.ref.WeakReference; + +import static se.leap.bitmaskclient.firewall.FirewallManager.BITMASK_CHAIN; +import static se.leap.bitmaskclient.utils.Cmd.runBlockingCmd; + +class ShutdownFirewallTask extends AsyncTask { + + private WeakReference callbackWeakReference; + + ShutdownFirewallTask(FirewallCallback callback) { + callbackWeakReference = new WeakReference<>(callback); + } + + @Override + protected Boolean doInBackground(Void... voids) { + boolean success; + StringBuilder log = new StringBuilder(); + String[] deleteChain = new String[]{ + "su", + "id", + "ip6tables --delete OUTPUT --jump " + BITMASK_CHAIN, + "ip6tables --flush " + BITMASK_CHAIN, + "ip6tables --delete-chain " + BITMASK_CHAIN + }; + try { + success = runBlockingCmd(deleteChain, log) == 0; + } catch (Exception e) { + e.printStackTrace(); + Log.e(FirewallManager.TAG, log.toString()); + return false; + } + + try { + boolean allowSu = log.toString().contains("uid=0"); + callbackWeakReference.get().onSuRequested(allowSu); + } catch (Exception e) { + //ignore + } + return success; + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + FirewallCallback callback = callbackWeakReference.get(); + if (callback != null) { + callback.onFirewallStopped(result); + } + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/StartFirewallTask.java b/app/src/main/java/se/leap/bitmaskclient/firewall/StartFirewallTask.java new file mode 100644 index 00000000..9b3a125f --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/StartFirewallTask.java @@ -0,0 +1,74 @@ +package se.leap.bitmaskclient.firewall; + +import android.os.AsyncTask; +import android.util.Log; + +import java.lang.ref.WeakReference; + +import static se.leap.bitmaskclient.firewall.FirewallManager.BITMASK_CHAIN; +import static se.leap.bitmaskclient.utils.Cmd.runBlockingCmd; + +class StartFirewallTask extends AsyncTask { + + private WeakReference callbackWeakReference; + + StartFirewallTask(FirewallCallback callback) { + callbackWeakReference = new WeakReference<>(callback); + } + + @Override + protected Boolean doInBackground(Void... voids) { + StringBuilder log = new StringBuilder(); + String[] bitmaskChain = new String[]{ + "su", + "id", + "ip6tables --list " + BITMASK_CHAIN }; + + + try { + boolean hasBitmaskChain = runBlockingCmd(bitmaskChain, log) == 0; + boolean allowSu = log.toString().contains("uid=0"); + try { + callbackWeakReference.get().onSuRequested(allowSu); + Thread.sleep(1000); + } catch (Exception e) { + //ignore + } + + boolean success; + log = new StringBuilder(); + if (!hasBitmaskChain) { + String[] createChainAndRules = new String[]{ + "su", + "ip6tables --new-chain " + BITMASK_CHAIN, + "ip6tables --insert OUTPUT --jump " + BITMASK_CHAIN, + "ip6tables --append " + BITMASK_CHAIN + " -p tcp --jump REJECT", + "ip6tables --append " + BITMASK_CHAIN + " -p udp --jump REJECT" + }; + success = runBlockingCmd(createChainAndRules, log) == 0; + Log.d(FirewallManager.TAG, "added " + BITMASK_CHAIN + " to ip6tables: " + success); + Log.d(FirewallManager.TAG, log.toString()); + return success; + } else { + String[] addRules = new String[] { + "su", + "ip6tables --append " + BITMASK_CHAIN + " -p tcp --jump REJECT", + "ip6tables --append " + BITMASK_CHAIN + " -p udp --jump REJECT" }; + return runBlockingCmd(addRules, log) == 0; + } + } catch (Exception e) { + e.printStackTrace(); + Log.e(FirewallManager.TAG, log.toString()); + } + return false; + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + FirewallCallback callback = callbackWeakReference.get(); + if (callback != null) { + callback.onFirewallStarted(result); + } + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/utils/FirewallHelper.java b/app/src/main/java/se/leap/bitmaskclient/utils/FirewallHelper.java deleted file mode 100644 index 26e6603a..00000000 --- a/app/src/main/java/se/leap/bitmaskclient/utils/FirewallHelper.java +++ /dev/null @@ -1,196 +0,0 @@ -package se.leap.bitmaskclient.utils; -/** - * Copyright (c) 2019 LEAP Encryption Access Project and contributers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -import android.content.Context; -import android.os.AsyncTask; -import android.util.Log; - -import java.lang.ref.WeakReference; - -import de.blinkt.openvpn.core.VpnStatus; - -import static se.leap.bitmaskclient.utils.Cmd.runBlockingCmd; - -interface FirewallCallback { - void onFirewallStarted(boolean success); - void onFirewallStopped(boolean success); - void onSuRequested(boolean success); -} - - -public class FirewallHelper implements FirewallCallback { - private static String BITMASK_CHAIN = "bitmask_fw"; - private static final String TAG = FirewallHelper.class.getSimpleName(); - - private Context context; - - public FirewallHelper(Context context) { - this.context = context; - } - - - @Override - public void onFirewallStarted(boolean success) { - if (success) { - VpnStatus.logInfo("[FIREWALL] Custom rules established"); - } else { - VpnStatus.logError("[FIREWALL] Could not establish custom rules."); - } - } - - @Override - public void onFirewallStopped(boolean success) { - if (success) { - VpnStatus.logInfo("[FIREWALL] Custom rules deleted"); - } else { - VpnStatus.logError("[FIREWALL] Could not delete custom rules"); - } - } - - @Override - public void onSuRequested(boolean success) { - PreferenceHelper.setSuPermission(context, success); - if (!success) { - VpnStatus.logError("[FIREWALL] Root permission needed to execute custom firewall rules."); - } - } - - - private static class StartFirewallTask extends AsyncTask { - - WeakReference callbackWeakReference; - - StartFirewallTask(FirewallCallback callback) { - callbackWeakReference = new WeakReference<>(callback); - } - - @Override - protected Boolean doInBackground(Void... voids) { - StringBuilder log = new StringBuilder(); - String[] bitmaskChain = new String[]{ - "su", - "id", - "ip6tables --list " + BITMASK_CHAIN }; - - - try { - boolean hasBitmaskChain = runBlockingCmd(bitmaskChain, log) == 0; - boolean allowSu = log.toString().contains("uid=0"); - try { - callbackWeakReference.get().onSuRequested(allowSu); - Thread.sleep(1000); - } catch (Exception e) { - //ignore - } - - boolean success; - log = new StringBuilder(); - if (!hasBitmaskChain) { - String[] createChainAndRules = new String[]{ - "su", - "ip6tables --new-chain " + BITMASK_CHAIN, - "ip6tables --insert OUTPUT --jump " + BITMASK_CHAIN, - "ip6tables --append " + BITMASK_CHAIN + " -p tcp --jump REJECT", - "ip6tables --append " + BITMASK_CHAIN + " -p udp --jump REJECT" - }; - success = runBlockingCmd(createChainAndRules, log) == 0; - Log.d(TAG, "added " + BITMASK_CHAIN + " to ip6tables: " + success); - Log.d(TAG, log.toString()); - return success; - } else { - String[] addRules = new String[] { - "su", - "ip6tables --append " + BITMASK_CHAIN + " -p tcp --jump REJECT", - "ip6tables --append " + BITMASK_CHAIN + " -p udp --jump REJECT" }; - return runBlockingCmd(addRules, log) == 0; - } - } catch (Exception e) { - e.printStackTrace(); - Log.e(TAG, log.toString()); - } - return false; - } - - @Override - protected void onPostExecute(Boolean result) { - super.onPostExecute(result); - FirewallCallback callback = callbackWeakReference.get(); - if (callback != null) { - callback.onFirewallStarted(result); - } - } - } - - private static class ShutdownFirewallTask extends AsyncTask { - - WeakReference callbackWeakReference; - - ShutdownFirewallTask(FirewallCallback callback) { - callbackWeakReference = new WeakReference<>(callback); - } - - @Override - protected Boolean doInBackground(Void... voids) { - boolean success; - StringBuilder log = new StringBuilder(); - String[] deleteChain = new String[]{ - "su", - "id", - "ip6tables --delete OUTPUT --jump " + BITMASK_CHAIN, - "ip6tables --flush " + BITMASK_CHAIN, - "ip6tables --delete-chain " + BITMASK_CHAIN - }; - try { - success = runBlockingCmd(deleteChain, log) == 0; - } catch (Exception e) { - e.printStackTrace(); - Log.e(TAG, log.toString()); - return false; - } - - try { - boolean allowSu = log.toString().contains("uid=0"); - callbackWeakReference.get().onSuRequested(allowSu); - } catch (Exception e) { - //ignore - } - return success; - } - - @Override - protected void onPostExecute(Boolean result) { - super.onPostExecute(result); - FirewallCallback callback = callbackWeakReference.get(); - if (callback != null) { - callback.onFirewallStopped(result); - } - } - } - - - public void startFirewall() { - StartFirewallTask task = new StartFirewallTask(this); - task.execute(); - } - - public void shutdownFirewall() { - ShutdownFirewallTask task = new ShutdownFirewallTask(this); - task.execute(); - } - -} -- cgit v1.2.3 From cade04a80ac91d7a4d9ea839fec75aff3f932651 Mon Sep 17 00:00:00 2001 From: cyberta Date: Sat, 25 Jan 2020 11:20:48 -0600 Subject: implement tethering firewall rules pt.1 --- .../firewall/ConfigureTetheringTask.java | 95 ++++++++++++++++++++++ .../bitmaskclient/firewall/FirewallCallback.java | 1 + .../bitmaskclient/firewall/FirewallManager.java | 11 +++ .../tethering/TetheringObservable.java | 24 +++--- .../bitmaskclient/tethering/TetheringState.java | 7 ++ 5 files changed, 128 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/se/leap/bitmaskclient/firewall/ConfigureTetheringTask.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/tethering/TetheringState.java (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/ConfigureTetheringTask.java b/app/src/main/java/se/leap/bitmaskclient/firewall/ConfigureTetheringTask.java new file mode 100644 index 00000000..29e9542d --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/ConfigureTetheringTask.java @@ -0,0 +1,95 @@ +package se.leap.bitmaskclient.firewall; + +import android.os.AsyncTask; +import android.util.Log; + +import java.lang.ref.WeakReference; + +import se.leap.bitmaskclient.tethering.TetheringState; + +import static se.leap.bitmaskclient.firewall.FirewallManager.BITMASK_FORWARD; +import static se.leap.bitmaskclient.firewall.FirewallManager.BITMASK_POSTROUTING; +import static se.leap.bitmaskclient.utils.Cmd.runBlockingCmd; + +public class ConfigureTetheringTask extends AsyncTask { + + private WeakReference callbackWeakReference; + + ConfigureTetheringTask(FirewallCallback callback) { + callbackWeakReference = new WeakReference<>(callback); + } + + @Override + protected Boolean doInBackground(TetheringState... tetheringStates) { + TetheringState tetheringState = tetheringStates[0]; + StringBuilder log = new StringBuilder(); + + String[] bitmaskChain = new String[]{ + "su", + "id", + "iptables --list " + BITMASK_FORWARD + " && iptables --list " + BITMASK_POSTROUTING }; + + try { + boolean hasBitmaskChain = runBlockingCmd(bitmaskChain, log) == 0; + boolean allowSu = log.toString().contains("uid=0"); + callbackWeakReference.get().onSuRequested(allowSu); + if (!allowSu) { + return false; + } + + boolean success; + log = new StringBuilder(); + if (hasAnyTetheringEnabled(tetheringState)) { + if (!hasBitmaskChain) { + String[] createChains = new String[]{ + "su", + "iptables -t filter --new-chain " + BITMASK_FORWARD, + "iptables -t nat --new-chain " + BITMASK_POSTROUTING, + "iptables -t filter --insert FORWARD --jump " + BITMASK_FORWARD, + "iptables -t nat --insert POSTROUTING --jump " + BITMASK_POSTROUTING, + }; + success = runBlockingCmd(createChains, log) == 0; + Log.d(FirewallManager.TAG, "added " + BITMASK_FORWARD + " and " + BITMASK_POSTROUTING+" to iptables: " + success); + Log.d(FirewallManager.TAG, log.toString()); + } + + String[] addRules = new String[] { + "su", + "iptables -t filter --flush " + BITMASK_FORWARD, + "iptables -t nat --flush " + BITMASK_POSTROUTING, + "iptables -t filter --append " + BITMASK_FORWARD + " --jump ACCEPT", + "iptables -t nat --append " + BITMASK_POSTROUTING + " --jump MASQUERADE" }; + return runBlockingCmd(addRules, log) == 0; + } else { + if (!hasBitmaskChain) return true; + String[] removeChains = new String[] { + "su", + "iptables -t filter --delete FORWARD --jump " + BITMASK_FORWARD, + "iptables -t nat --delete POSTROUTING --jump " + BITMASK_POSTROUTING, + "iptables -t filter --flush " + BITMASK_FORWARD, + "iptables -t nat --flush " + BITMASK_POSTROUTING, + "iptables -t filter --delete-chain " + BITMASK_FORWARD, + "iptables -t nat --delete-chain " + BITMASK_POSTROUTING + }; + return runBlockingCmd(removeChains, log) == 0; + } + } catch (Exception e) { + e.printStackTrace(); + Log.e(FirewallManager.TAG, log.toString()); + } + return false; + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + FirewallCallback callback = callbackWeakReference.get(); + if (callback != null) { + callback.onTetheringConfigured(result); + } + } + + private boolean hasAnyTetheringEnabled(TetheringState state) { + return state.isBluetoothTetheringEnabled || state.isUsbTetheringEnabled || state.isWifiTetheringEnabled; + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallCallback.java b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallCallback.java index f6ccd227..76618b8c 100644 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallCallback.java +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallCallback.java @@ -3,5 +3,6 @@ package se.leap.bitmaskclient.firewall; interface FirewallCallback { void onFirewallStarted(boolean success); void onFirewallStopped(boolean success); + void onTetheringConfigured(boolean success); void onSuRequested(boolean success); } diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java index a3be46e6..a7cb2253 100644 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java @@ -24,6 +24,8 @@ import se.leap.bitmaskclient.utils.PreferenceHelper; public class FirewallManager implements FirewallCallback { public static String BITMASK_CHAIN = "bitmask_fw"; + public static String BITMASK_FORWARD = "bitmask_forward"; + public static String BITMASK_POSTROUTING = "bitmask_postrouting"; static final String TAG = FirewallManager.class.getSimpleName(); private Context context; @@ -51,6 +53,15 @@ public class FirewallManager implements FirewallCallback { } } + @Override + public void onTetheringConfigured(boolean success) { + if (success) { + VpnStatus.logInfo("[FIREWALL] Rules for tethering configured"); + } else { + VpnStatus.logError("[FIREWALL] Could not establish rules for tethering "); + } + } + @Override public void onSuRequested(boolean success) { PreferenceHelper.setSuPermission(context, success); diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java index 9ea9160f..fc06ee12 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java @@ -21,11 +21,11 @@ import java.util.Observable; public class TetheringObservable extends Observable { private static TetheringObservable instance; - private boolean isWifiTetheringEnabled; - private boolean isUsbTetheringEnabled; - private boolean isBluetoothTetheringEnabled; + private TetheringState tetheringState; - private TetheringObservable() { } + private TetheringObservable() { + tetheringState = new TetheringState(); + } public static TetheringObservable getInstance() { if (instance == null) { @@ -35,32 +35,36 @@ public class TetheringObservable extends Observable { } static void setWifiTethering(boolean enabled) { - getInstance().isWifiTetheringEnabled = enabled; + getInstance().tetheringState.isWifiTetheringEnabled = enabled; getInstance().setChanged(); getInstance().notifyObservers(); } static void setUsbTethering(boolean enabled) { - getInstance().isUsbTetheringEnabled = enabled; + getInstance().tetheringState.isUsbTetheringEnabled = enabled; getInstance().setChanged(); getInstance().notifyObservers(); } static void setBluetoothTethering(boolean enabled) { - getInstance().isBluetoothTetheringEnabled = enabled; + getInstance().tetheringState.isBluetoothTetheringEnabled = enabled; getInstance().setChanged(); getInstance().notifyObservers(); } public boolean isBluetoothTetheringEnabled() { - return isBluetoothTetheringEnabled; + return tetheringState.isBluetoothTetheringEnabled; } public boolean isUsbTetheringEnabled() { - return isUsbTetheringEnabled; + return tetheringState.isUsbTetheringEnabled; } public boolean isWifiTetheringEnabled() { - return isWifiTetheringEnabled; + return tetheringState.isWifiTetheringEnabled; + } + + public TetheringState getTetheringState() { + return tetheringState; } } diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringState.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringState.java new file mode 100644 index 00000000..50fead91 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringState.java @@ -0,0 +1,7 @@ +package se.leap.bitmaskclient.tethering; + +public class TetheringState { + public boolean isWifiTetheringEnabled; + public boolean isUsbTetheringEnabled; + public boolean isBluetoothTetheringEnabled; +} -- cgit v1.2.3 From c5bb5c18a8884410580b589aa6e8f6aaca046764 Mon Sep 17 00:00:00 2001 From: cyberta Date: Sat, 25 Jan 2020 11:21:47 -0600 Subject: stop setting up firewall if su was not permitted --- .../java/se/leap/bitmaskclient/firewall/StartFirewallTask.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/StartFirewallTask.java b/app/src/main/java/se/leap/bitmaskclient/firewall/StartFirewallTask.java index 9b3a125f..3bf6d684 100644 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/StartFirewallTask.java +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/StartFirewallTask.java @@ -28,11 +28,9 @@ class StartFirewallTask extends AsyncTask { try { boolean hasBitmaskChain = runBlockingCmd(bitmaskChain, log) == 0; boolean allowSu = log.toString().contains("uid=0"); - try { - callbackWeakReference.get().onSuRequested(allowSu); - Thread.sleep(1000); - } catch (Exception e) { - //ignore + callbackWeakReference.get().onSuRequested(allowSu); + if (!allowSu) { + return false; } boolean success; -- cgit v1.2.3 From e29188b051aa57939e56d5fc9110eddb1d9a4bdf Mon Sep 17 00:00:00 2001 From: cyberta Date: Sat, 25 Jan 2020 14:01:18 -0600 Subject: initiate configuring task --- .../java/se/leap/bitmaskclient/firewall/FirewallManager.java | 6 ++++++ .../java/se/leap/bitmaskclient/fragments/TetheringDialog.java | 10 +++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java index a7cb2253..ce402ef8 100644 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java @@ -19,6 +19,7 @@ package se.leap.bitmaskclient.firewall; import android.content.Context; import de.blinkt.openvpn.core.VpnStatus; +import se.leap.bitmaskclient.tethering.TetheringState; import se.leap.bitmaskclient.utils.PreferenceHelper; @@ -81,4 +82,9 @@ public class FirewallManager implements FirewallCallback { task.execute(); } + public void configureTethering(TetheringState state) { + ConfigureTetheringTask task = new ConfigureTetheringTask(this); + task.execute(state); + } + } diff --git a/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java index 1d125245..a8f96986 100644 --- a/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java @@ -27,7 +27,9 @@ import butterknife.ButterKnife; import butterknife.InjectView; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.eip.EipCommand; +import se.leap.bitmaskclient.firewall.FirewallManager; import se.leap.bitmaskclient.tethering.TetheringObservable; +import se.leap.bitmaskclient.tethering.TetheringState; import se.leap.bitmaskclient.utils.PreferenceHelper; import se.leap.bitmaskclient.views.IconCheckboxEntry; @@ -137,7 +139,13 @@ public class TetheringDialog extends AppCompatDialogFragment implements Observer PreferenceHelper.wifiTethering(getContext(), dataset[0].checked); PreferenceHelper.usbTethering(getContext(), dataset[1].checked); PreferenceHelper.bluetoothTethering(getContext(), dataset[2].checked); - EipCommand.configureTethering(getContext()); + FirewallManager firewallManager = new FirewallManager(getContext().getApplicationContext()); + TetheringState runningTethering = TetheringObservable.getInstance().getTetheringState(); + TetheringState vpnTethering = new TetheringState(); + vpnTethering.isWifiTetheringEnabled = runningTethering.isWifiTetheringEnabled && dataset[0].checked; + vpnTethering.isUsbTetheringEnabled = runningTethering.isUsbTetheringEnabled && dataset[1].checked; + vpnTethering.isBluetoothTetheringEnabled = runningTethering.isBluetoothTetheringEnabled && dataset[2].checked; + firewallManager.configureTethering(vpnTethering); }) .setNegativeButton(R.string.cancel, (dialog, id) -> dialog.cancel()); return builder.create(); -- cgit v1.2.3 From 20f906ef16d10d7f69a7355bd590ae99cb64723b Mon Sep 17 00:00:00 2001 From: cyberta Date: Sat, 25 Jan 2020 14:40:13 -0600 Subject: add and remove configuring tethering rules on vpn start and shutdown --- .../de/blinkt/openvpn/core/OpenVPNService.java | 4 +- .../bitmaskclient/firewall/FirewallManager.java | 35 ++++++++--- .../firewall/ShutdownFirewallTask.java | 55 ----------------- .../firewall/ShutdownIPv6FirewallTask.java | 55 +++++++++++++++++ .../bitmaskclient/firewall/StartFirewallTask.java | 72 ---------------------- .../firewall/StartIPv6FirewallTask.java | 72 ++++++++++++++++++++++ .../tethering/TetheringObservable.java | 4 ++ 7 files changed, 161 insertions(+), 136 deletions(-) delete mode 100644 app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownFirewallTask.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownIPv6FirewallTask.java delete mode 100644 app/src/main/java/se/leap/bitmaskclient/firewall/StartFirewallTask.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/firewall/StartIPv6FirewallTask.java (limited to 'app') diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java index 7c17bed9..a48d6477 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java +++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java @@ -194,7 +194,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac VpnStatus.removeStateListener(this); } } - firewallManager.shutdownFirewall(); + firewallManager.stop(); } private boolean runningOnAndroidTV() { @@ -449,7 +449,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac mProcessThread.start(); } - firewallManager.startFirewall(); + firewallManager.start(); new Handler(getMainLooper()).post(() -> { if (mDeviceStateReceiver != null) { diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java index ce402ef8..78166671 100644 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java @@ -19,6 +19,7 @@ package se.leap.bitmaskclient.firewall; import android.content.Context; import de.blinkt.openvpn.core.VpnStatus; +import se.leap.bitmaskclient.tethering.TetheringObservable; import se.leap.bitmaskclient.tethering.TetheringState; import se.leap.bitmaskclient.utils.PreferenceHelper; @@ -72,14 +73,24 @@ public class FirewallManager implements FirewallCallback { } - public void startFirewall() { - StartFirewallTask task = new StartFirewallTask(this); - task.execute(); + public void start() { + startIPv6Firewall(); + if (TetheringObservable.getInstance().hasAnyTetheringEnabled()) { + TetheringState deviceTethering = TetheringObservable.getInstance().getTetheringState(); + TetheringState vpnTethering = new TetheringState(); + vpnTethering.isWifiTetheringEnabled = deviceTethering.isWifiTetheringEnabled && PreferenceHelper.getWifiTethering(context); + vpnTethering.isUsbTetheringEnabled = deviceTethering.isUsbTetheringEnabled && PreferenceHelper.getUsbTethering(context); + vpnTethering.isBluetoothTetheringEnabled = deviceTethering.isBluetoothTetheringEnabled && PreferenceHelper.getBluetoothTethering(context); + configureTethering(vpnTethering); + } } - - public void shutdownFirewall() { - ShutdownFirewallTask task = new ShutdownFirewallTask(this); - task.execute(); + public void stop() { + shutdownIPv6Firewall(); + TetheringState allowedTethering = new TetheringState(); + allowedTethering.isWifiTetheringEnabled = PreferenceHelper.getWifiTethering(context); + allowedTethering.isUsbTetheringEnabled = PreferenceHelper.getUsbTethering(context); + allowedTethering.isBluetoothTetheringEnabled = PreferenceHelper.getBluetoothTethering(context); + configureTethering(allowedTethering); } public void configureTethering(TetheringState state) { @@ -87,4 +98,14 @@ public class FirewallManager implements FirewallCallback { task.execute(state); } + private void startIPv6Firewall() { + StartIPv6FirewallTask task = new StartIPv6FirewallTask(this); + task.execute(); + } + + private void shutdownIPv6Firewall() { + ShutdownIPv6FirewallTask task = new ShutdownIPv6FirewallTask(this); + task.execute(); + } + } diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownFirewallTask.java b/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownFirewallTask.java deleted file mode 100644 index 50fa77cd..00000000 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownFirewallTask.java +++ /dev/null @@ -1,55 +0,0 @@ -package se.leap.bitmaskclient.firewall; - -import android.os.AsyncTask; -import android.util.Log; - -import java.lang.ref.WeakReference; - -import static se.leap.bitmaskclient.firewall.FirewallManager.BITMASK_CHAIN; -import static se.leap.bitmaskclient.utils.Cmd.runBlockingCmd; - -class ShutdownFirewallTask extends AsyncTask { - - private WeakReference callbackWeakReference; - - ShutdownFirewallTask(FirewallCallback callback) { - callbackWeakReference = new WeakReference<>(callback); - } - - @Override - protected Boolean doInBackground(Void... voids) { - boolean success; - StringBuilder log = new StringBuilder(); - String[] deleteChain = new String[]{ - "su", - "id", - "ip6tables --delete OUTPUT --jump " + BITMASK_CHAIN, - "ip6tables --flush " + BITMASK_CHAIN, - "ip6tables --delete-chain " + BITMASK_CHAIN - }; - try { - success = runBlockingCmd(deleteChain, log) == 0; - } catch (Exception e) { - e.printStackTrace(); - Log.e(FirewallManager.TAG, log.toString()); - return false; - } - - try { - boolean allowSu = log.toString().contains("uid=0"); - callbackWeakReference.get().onSuRequested(allowSu); - } catch (Exception e) { - //ignore - } - return success; - } - - @Override - protected void onPostExecute(Boolean result) { - super.onPostExecute(result); - FirewallCallback callback = callbackWeakReference.get(); - if (callback != null) { - callback.onFirewallStopped(result); - } - } -} diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownIPv6FirewallTask.java b/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownIPv6FirewallTask.java new file mode 100644 index 00000000..dbdbf769 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownIPv6FirewallTask.java @@ -0,0 +1,55 @@ +package se.leap.bitmaskclient.firewall; + +import android.os.AsyncTask; +import android.util.Log; + +import java.lang.ref.WeakReference; + +import static se.leap.bitmaskclient.firewall.FirewallManager.BITMASK_CHAIN; +import static se.leap.bitmaskclient.utils.Cmd.runBlockingCmd; + +class ShutdownIPv6FirewallTask extends AsyncTask { + + private WeakReference callbackWeakReference; + + ShutdownIPv6FirewallTask(FirewallCallback callback) { + callbackWeakReference = new WeakReference<>(callback); + } + + @Override + protected Boolean doInBackground(Void... voids) { + boolean success; + StringBuilder log = new StringBuilder(); + String[] deleteChain = new String[]{ + "su", + "id", + "ip6tables --delete OUTPUT --jump " + BITMASK_CHAIN, + "ip6tables --flush " + BITMASK_CHAIN, + "ip6tables --delete-chain " + BITMASK_CHAIN + }; + try { + success = runBlockingCmd(deleteChain, log) == 0; + } catch (Exception e) { + e.printStackTrace(); + Log.e(FirewallManager.TAG, log.toString()); + return false; + } + + try { + boolean allowSu = log.toString().contains("uid=0"); + callbackWeakReference.get().onSuRequested(allowSu); + } catch (Exception e) { + //ignore + } + return success; + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + FirewallCallback callback = callbackWeakReference.get(); + if (callback != null) { + callback.onFirewallStopped(result); + } + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/StartFirewallTask.java b/app/src/main/java/se/leap/bitmaskclient/firewall/StartFirewallTask.java deleted file mode 100644 index 3bf6d684..00000000 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/StartFirewallTask.java +++ /dev/null @@ -1,72 +0,0 @@ -package se.leap.bitmaskclient.firewall; - -import android.os.AsyncTask; -import android.util.Log; - -import java.lang.ref.WeakReference; - -import static se.leap.bitmaskclient.firewall.FirewallManager.BITMASK_CHAIN; -import static se.leap.bitmaskclient.utils.Cmd.runBlockingCmd; - -class StartFirewallTask extends AsyncTask { - - private WeakReference callbackWeakReference; - - StartFirewallTask(FirewallCallback callback) { - callbackWeakReference = new WeakReference<>(callback); - } - - @Override - protected Boolean doInBackground(Void... voids) { - StringBuilder log = new StringBuilder(); - String[] bitmaskChain = new String[]{ - "su", - "id", - "ip6tables --list " + BITMASK_CHAIN }; - - - try { - boolean hasBitmaskChain = runBlockingCmd(bitmaskChain, log) == 0; - boolean allowSu = log.toString().contains("uid=0"); - callbackWeakReference.get().onSuRequested(allowSu); - if (!allowSu) { - return false; - } - - boolean success; - log = new StringBuilder(); - if (!hasBitmaskChain) { - String[] createChainAndRules = new String[]{ - "su", - "ip6tables --new-chain " + BITMASK_CHAIN, - "ip6tables --insert OUTPUT --jump " + BITMASK_CHAIN, - "ip6tables --append " + BITMASK_CHAIN + " -p tcp --jump REJECT", - "ip6tables --append " + BITMASK_CHAIN + " -p udp --jump REJECT" - }; - success = runBlockingCmd(createChainAndRules, log) == 0; - Log.d(FirewallManager.TAG, "added " + BITMASK_CHAIN + " to ip6tables: " + success); - Log.d(FirewallManager.TAG, log.toString()); - return success; - } else { - String[] addRules = new String[] { - "su", - "ip6tables --append " + BITMASK_CHAIN + " -p tcp --jump REJECT", - "ip6tables --append " + BITMASK_CHAIN + " -p udp --jump REJECT" }; - return runBlockingCmd(addRules, log) == 0; - } - } catch (Exception e) { - e.printStackTrace(); - Log.e(FirewallManager.TAG, log.toString()); - } - return false; - } - - @Override - protected void onPostExecute(Boolean result) { - super.onPostExecute(result); - FirewallCallback callback = callbackWeakReference.get(); - if (callback != null) { - callback.onFirewallStarted(result); - } - } -} diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/StartIPv6FirewallTask.java b/app/src/main/java/se/leap/bitmaskclient/firewall/StartIPv6FirewallTask.java new file mode 100644 index 00000000..1dbfb9f8 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/StartIPv6FirewallTask.java @@ -0,0 +1,72 @@ +package se.leap.bitmaskclient.firewall; + +import android.os.AsyncTask; +import android.util.Log; + +import java.lang.ref.WeakReference; + +import static se.leap.bitmaskclient.firewall.FirewallManager.BITMASK_CHAIN; +import static se.leap.bitmaskclient.utils.Cmd.runBlockingCmd; + +class StartIPv6FirewallTask extends AsyncTask { + + private WeakReference callbackWeakReference; + + StartIPv6FirewallTask(FirewallCallback callback) { + callbackWeakReference = new WeakReference<>(callback); + } + + @Override + protected Boolean doInBackground(Void... voids) { + StringBuilder log = new StringBuilder(); + String[] bitmaskChain = new String[]{ + "su", + "id", + "ip6tables --list " + BITMASK_CHAIN }; + + + try { + boolean hasBitmaskChain = runBlockingCmd(bitmaskChain, log) == 0; + boolean allowSu = log.toString().contains("uid=0"); + callbackWeakReference.get().onSuRequested(allowSu); + if (!allowSu) { + return false; + } + + boolean success; + log = new StringBuilder(); + if (!hasBitmaskChain) { + String[] createChainAndRules = new String[]{ + "su", + "ip6tables --new-chain " + BITMASK_CHAIN, + "ip6tables --insert OUTPUT --jump " + BITMASK_CHAIN, + "ip6tables --append " + BITMASK_CHAIN + " -p tcp --jump REJECT", + "ip6tables --append " + BITMASK_CHAIN + " -p udp --jump REJECT" + }; + success = runBlockingCmd(createChainAndRules, log) == 0; + Log.d(FirewallManager.TAG, "added " + BITMASK_CHAIN + " to ip6tables: " + success); + Log.d(FirewallManager.TAG, log.toString()); + return success; + } else { + String[] addRules = new String[] { + "su", + "ip6tables --append " + BITMASK_CHAIN + " -p tcp --jump REJECT", + "ip6tables --append " + BITMASK_CHAIN + " -p udp --jump REJECT" }; + return runBlockingCmd(addRules, log) == 0; + } + } catch (Exception e) { + e.printStackTrace(); + Log.e(FirewallManager.TAG, log.toString()); + } + return false; + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + FirewallCallback callback = callbackWeakReference.get(); + if (callback != null) { + callback.onFirewallStarted(result); + } + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java index fc06ee12..79c1ec6d 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java @@ -67,4 +67,8 @@ public class TetheringObservable extends Observable { public TetheringState getTetheringState() { return tetheringState; } + + public boolean hasAnyTetheringEnabled() { + return tetheringState.isBluetoothTetheringEnabled || tetheringState.isUsbTetheringEnabled || tetheringState.isWifiTetheringEnabled; + } } -- cgit v1.2.3 From ec4cafb1026db8f461d94908dbdfcd8281d235a4 Mon Sep 17 00:00:00 2001 From: cyberta Date: Sat, 25 Jan 2020 14:46:47 -0600 Subject: copyright bla --- .../bitmaskclient/firewall/ConfigureTetheringTask.java | 16 ++++++++++++++++ .../se/leap/bitmaskclient/firewall/FirewallCallback.java | 16 ++++++++++++++++ .../bitmaskclient/firewall/ShutdownIPv6FirewallTask.java | 16 ++++++++++++++++ .../bitmaskclient/firewall/StartIPv6FirewallTask.java | 16 ++++++++++++++++ 4 files changed, 64 insertions(+) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/ConfigureTetheringTask.java b/app/src/main/java/se/leap/bitmaskclient/firewall/ConfigureTetheringTask.java index 29e9542d..3363152e 100644 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/ConfigureTetheringTask.java +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/ConfigureTetheringTask.java @@ -1,3 +1,19 @@ +/** + * Copyright (c) 2020 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package se.leap.bitmaskclient.firewall; import android.os.AsyncTask; diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallCallback.java b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallCallback.java index 76618b8c..c6b3934c 100644 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallCallback.java +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallCallback.java @@ -1,3 +1,19 @@ +/** + * Copyright (c) 2019 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package se.leap.bitmaskclient.firewall; interface FirewallCallback { diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownIPv6FirewallTask.java b/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownIPv6FirewallTask.java index dbdbf769..63d6074d 100644 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownIPv6FirewallTask.java +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownIPv6FirewallTask.java @@ -1,3 +1,19 @@ +/** + * Copyright (c) 2020 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package se.leap.bitmaskclient.firewall; import android.os.AsyncTask; diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/StartIPv6FirewallTask.java b/app/src/main/java/se/leap/bitmaskclient/firewall/StartIPv6FirewallTask.java index 1dbfb9f8..b01270e0 100644 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/StartIPv6FirewallTask.java +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/StartIPv6FirewallTask.java @@ -1,3 +1,19 @@ +/** + * Copyright (c) 2020 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package se.leap.bitmaskclient.firewall; import android.os.AsyncTask; -- cgit v1.2.3 From 7fbea5459e59de1327f2c76a23f5940d67e4ae8d Mon Sep 17 00:00:00 2001 From: cyberta Date: Wed, 29 Jan 2020 02:55:25 -0600 Subject: implement wifi tethering --- .../de/blinkt/openvpn/core/OpenVPNService.java | 3 +- .../main/java/se/leap/bitmaskclient/Constants.java | 6 +- .../firewall/ConfigureTetheringTask.java | 111 ------------ .../bitmaskclient/firewall/FirewallCallback.java | 3 +- .../bitmaskclient/firewall/FirewallManager.java | 75 ++++++--- .../bitmaskclient/firewall/SetupTetheringTask.java | 187 +++++++++++++++++++++ .../firewall/ShutdownTetheringTask.java | 89 ++++++++++ .../bitmaskclient/fragments/TetheringDialog.java | 26 ++- .../tethering/TetheringBroadcastReceiver.java | 11 +- .../tethering/TetheringObservable.java | 62 +++++-- .../bitmaskclient/tethering/TetheringState.java | 34 +++- .../tethering/TetheringStateManager.java | 70 +++++++- .../leap/bitmaskclient/utils/PreferenceHelper.java | 36 ++-- 13 files changed, 503 insertions(+), 210 deletions(-) delete mode 100644 app/src/main/java/se/leap/bitmaskclient/firewall/ConfigureTetheringTask.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/firewall/SetupTetheringTask.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownTetheringTask.java (limited to 'app') diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java index a48d6477..72962493 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java +++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java @@ -518,7 +518,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac super.onCreate(); notificationManager = new VpnNotificationManager(this, this); notificationManager.createOpenVpnNotificationChannel(); - firewallManager = new FirewallManager(this); + firewallManager = new FirewallManager(this, true); } @Override @@ -537,6 +537,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac VpnStatus.flushLog(); notificationManager.deleteNotificationChannel(NOTIFICATION_CHANNEL_BG_ID); notificationManager.deleteNotificationChannel(NOTIFICATION_CHANNEL_NEWSTATUS_ID); + firewallManager.onDestroy(); } private String getTunConfigString() { diff --git a/app/src/main/java/se/leap/bitmaskclient/Constants.java b/app/src/main/java/se/leap/bitmaskclient/Constants.java index 58a92360..6a065d84 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Constants.java +++ b/app/src/main/java/se/leap/bitmaskclient/Constants.java @@ -16,9 +16,9 @@ public interface Constants { String EXCLUDED_APPS = "excluded_apps"; String USE_PLUGGABLE_TRANSPORTS = "usePluggableTransports"; String SU_PERMISSION = "su_permission"; - String TETHERING_BLUETOOTH = "tethering_bluetooth"; - String TETHERING_WIFI = "tethering_wifi"; - String TETHERING_USB = "tethering_usb"; + String ALLOW_TETHERING_BLUETOOTH = "tethering_bluetooth"; + String ALLOW_TETHERING_WIFI = "tethering_wifi"; + String ALLOW_TETHERING_USB = "tethering_usb"; ////////////////////////////////////////////// diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/ConfigureTetheringTask.java b/app/src/main/java/se/leap/bitmaskclient/firewall/ConfigureTetheringTask.java deleted file mode 100644 index 3363152e..00000000 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/ConfigureTetheringTask.java +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Copyright (c) 2020 LEAP Encryption Access Project and contributers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package se.leap.bitmaskclient.firewall; - -import android.os.AsyncTask; -import android.util.Log; - -import java.lang.ref.WeakReference; - -import se.leap.bitmaskclient.tethering.TetheringState; - -import static se.leap.bitmaskclient.firewall.FirewallManager.BITMASK_FORWARD; -import static se.leap.bitmaskclient.firewall.FirewallManager.BITMASK_POSTROUTING; -import static se.leap.bitmaskclient.utils.Cmd.runBlockingCmd; - -public class ConfigureTetheringTask extends AsyncTask { - - private WeakReference callbackWeakReference; - - ConfigureTetheringTask(FirewallCallback callback) { - callbackWeakReference = new WeakReference<>(callback); - } - - @Override - protected Boolean doInBackground(TetheringState... tetheringStates) { - TetheringState tetheringState = tetheringStates[0]; - StringBuilder log = new StringBuilder(); - - String[] bitmaskChain = new String[]{ - "su", - "id", - "iptables --list " + BITMASK_FORWARD + " && iptables --list " + BITMASK_POSTROUTING }; - - try { - boolean hasBitmaskChain = runBlockingCmd(bitmaskChain, log) == 0; - boolean allowSu = log.toString().contains("uid=0"); - callbackWeakReference.get().onSuRequested(allowSu); - if (!allowSu) { - return false; - } - - boolean success; - log = new StringBuilder(); - if (hasAnyTetheringEnabled(tetheringState)) { - if (!hasBitmaskChain) { - String[] createChains = new String[]{ - "su", - "iptables -t filter --new-chain " + BITMASK_FORWARD, - "iptables -t nat --new-chain " + BITMASK_POSTROUTING, - "iptables -t filter --insert FORWARD --jump " + BITMASK_FORWARD, - "iptables -t nat --insert POSTROUTING --jump " + BITMASK_POSTROUTING, - }; - success = runBlockingCmd(createChains, log) == 0; - Log.d(FirewallManager.TAG, "added " + BITMASK_FORWARD + " and " + BITMASK_POSTROUTING+" to iptables: " + success); - Log.d(FirewallManager.TAG, log.toString()); - } - - String[] addRules = new String[] { - "su", - "iptables -t filter --flush " + BITMASK_FORWARD, - "iptables -t nat --flush " + BITMASK_POSTROUTING, - "iptables -t filter --append " + BITMASK_FORWARD + " --jump ACCEPT", - "iptables -t nat --append " + BITMASK_POSTROUTING + " --jump MASQUERADE" }; - return runBlockingCmd(addRules, log) == 0; - } else { - if (!hasBitmaskChain) return true; - String[] removeChains = new String[] { - "su", - "iptables -t filter --delete FORWARD --jump " + BITMASK_FORWARD, - "iptables -t nat --delete POSTROUTING --jump " + BITMASK_POSTROUTING, - "iptables -t filter --flush " + BITMASK_FORWARD, - "iptables -t nat --flush " + BITMASK_POSTROUTING, - "iptables -t filter --delete-chain " + BITMASK_FORWARD, - "iptables -t nat --delete-chain " + BITMASK_POSTROUTING - }; - return runBlockingCmd(removeChains, log) == 0; - } - } catch (Exception e) { - e.printStackTrace(); - Log.e(FirewallManager.TAG, log.toString()); - } - return false; - } - - @Override - protected void onPostExecute(Boolean result) { - super.onPostExecute(result); - FirewallCallback callback = callbackWeakReference.get(); - if (callback != null) { - callback.onTetheringConfigured(result); - } - } - - private boolean hasAnyTetheringEnabled(TetheringState state) { - return state.isBluetoothTetheringEnabled || state.isUsbTetheringEnabled || state.isWifiTetheringEnabled; - } -} diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallCallback.java b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallCallback.java index c6b3934c..15fa426f 100644 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallCallback.java +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallCallback.java @@ -19,6 +19,7 @@ package se.leap.bitmaskclient.firewall; interface FirewallCallback { void onFirewallStarted(boolean success); void onFirewallStopped(boolean success); - void onTetheringConfigured(boolean success); + void onTetheringStarted(boolean success); + void onTetheringStopped(boolean success); void onSuRequested(boolean success); } diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java index 78166671..82888668 100644 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java @@ -18,13 +18,15 @@ package se.leap.bitmaskclient.firewall; import android.content.Context; +import java.util.Observable; +import java.util.Observer; + import de.blinkt.openvpn.core.VpnStatus; import se.leap.bitmaskclient.tethering.TetheringObservable; import se.leap.bitmaskclient.tethering.TetheringState; import se.leap.bitmaskclient.utils.PreferenceHelper; - -public class FirewallManager implements FirewallCallback { +public class FirewallManager implements FirewallCallback, Observer { public static String BITMASK_CHAIN = "bitmask_fw"; public static String BITMASK_FORWARD = "bitmask_forward"; public static String BITMASK_POSTROUTING = "bitmask_postrouting"; @@ -32,11 +34,13 @@ public class FirewallManager implements FirewallCallback { private Context context; - public FirewallManager(Context context) { + public FirewallManager(Context context, boolean observeTethering) { this.context = context; + if (observeTethering) { + TetheringObservable.getInstance().addObserver(this); + } } - @Override public void onFirewallStarted(boolean success) { if (success) { @@ -56,11 +60,20 @@ public class FirewallManager implements FirewallCallback { } @Override - public void onTetheringConfigured(boolean success) { + public void onTetheringStarted(boolean success) { + if (success) { + VpnStatus.logInfo("[FIREWALL] Rules for tethering enabled"); + } else { + VpnStatus.logError("[FIREWALL] Could not enable rules for tethering."); + } + } + + @Override + public void onTetheringStopped(boolean success) { if (success) { - VpnStatus.logInfo("[FIREWALL] Rules for tethering configured"); + VpnStatus.logInfo("[FIREWALL] Rules for tethering successfully disabled"); } else { - VpnStatus.logError("[FIREWALL] Could not establish rules for tethering "); + VpnStatus.logError("[FIREWALL] Could not disable rules for tethering."); } } @@ -72,30 +85,32 @@ public class FirewallManager implements FirewallCallback { } } + public void onDestroy() { + TetheringObservable.getInstance().deleteObserver(this); + } + public void start() { startIPv6Firewall(); - if (TetheringObservable.getInstance().hasAnyTetheringEnabled()) { - TetheringState deviceTethering = TetheringObservable.getInstance().getTetheringState(); - TetheringState vpnTethering = new TetheringState(); - vpnTethering.isWifiTetheringEnabled = deviceTethering.isWifiTetheringEnabled && PreferenceHelper.getWifiTethering(context); - vpnTethering.isUsbTetheringEnabled = deviceTethering.isUsbTetheringEnabled && PreferenceHelper.getUsbTethering(context); - vpnTethering.isBluetoothTetheringEnabled = deviceTethering.isBluetoothTetheringEnabled && PreferenceHelper.getBluetoothTethering(context); - configureTethering(vpnTethering); + TetheringState tetheringState = TetheringObservable.getInstance().getTetheringState(); + if (tetheringState.hasAnyDeviceTetheringEnabled() && tetheringState.hasAnyVpnTetheringAllowed()) { + startTethering(); } } + public void stop() { - shutdownIPv6Firewall(); - TetheringState allowedTethering = new TetheringState(); - allowedTethering.isWifiTetheringEnabled = PreferenceHelper.getWifiTethering(context); - allowedTethering.isUsbTetheringEnabled = PreferenceHelper.getUsbTethering(context); - allowedTethering.isBluetoothTetheringEnabled = PreferenceHelper.getBluetoothTethering(context); - configureTethering(allowedTethering); + stopIPv6Firewall(); + stopTethering(); + } + + public void startTethering() { + SetupTetheringTask task = new SetupTetheringTask(this); + task.execute(); } - public void configureTethering(TetheringState state) { - ConfigureTetheringTask task = new ConfigureTetheringTask(this); - task.execute(state); + private void stopTethering() { + ShutdownTetheringTask task = new ShutdownTetheringTask(this); + task.execute(); } private void startIPv6Firewall() { @@ -103,9 +118,21 @@ public class FirewallManager implements FirewallCallback { task.execute(); } - private void shutdownIPv6Firewall() { + private void stopIPv6Firewall() { ShutdownIPv6FirewallTask task = new ShutdownIPv6FirewallTask(this); task.execute(); } + @Override + public void update(Observable o, Object arg) { + if (o instanceof TetheringObservable) { + TetheringObservable observable = (TetheringObservable) o; + TetheringState state = observable.getTetheringState(); + if (state.hasAnyVpnTetheringAllowed() && state.hasAnyDeviceTetheringEnabled()) { + startTethering(); + } else { + stopTethering(); + } + } + } } diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/SetupTetheringTask.java b/app/src/main/java/se/leap/bitmaskclient/firewall/SetupTetheringTask.java new file mode 100644 index 00000000..93054099 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/SetupTetheringTask.java @@ -0,0 +1,187 @@ +/** + * Copyright (c) 2020 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package se.leap.bitmaskclient.firewall; + +import android.os.AsyncTask; +import android.util.Log; + +import java.lang.ref.WeakReference; +import java.net.NetworkInterface; +import java.util.Enumeration; + +import se.leap.bitmaskclient.tethering.TetheringObservable; +import se.leap.bitmaskclient.tethering.TetheringState; + +import static se.leap.bitmaskclient.firewall.FirewallManager.BITMASK_FORWARD; +import static se.leap.bitmaskclient.firewall.FirewallManager.BITMASK_POSTROUTING; +import static se.leap.bitmaskclient.utils.Cmd.runBlockingCmd; + +public class SetupTetheringTask extends AsyncTask { + + private static final String TAG = SetupTetheringTask.class.getSimpleName(); + private WeakReference callbackWeakReference; + + SetupTetheringTask(FirewallCallback callback) { + callbackWeakReference = new WeakReference<>(callback); + } + + @Override + protected Boolean doInBackground(Void... args) { + TetheringState tetheringState = TetheringObservable.getInstance().getTetheringState(); + StringBuilder log = new StringBuilder(); + + String[] bitmaskChain = new String[]{ + "su", + "id", + "iptables -t filter --list " + BITMASK_FORWARD + " && iptables -t nat --list " + BITMASK_POSTROUTING }; + + try { + boolean hasBitmaskChain = runBlockingCmd(bitmaskChain, log) == 0; + boolean allowSu = log.toString().contains("uid=0"); + callbackWeakReference.get().onSuRequested(allowSu); + if (!allowSu) { + return false; + } + + boolean success = true; + log = new StringBuilder(); + + if (!hasBitmaskChain && tetheringState.hasAnyVpnTetheringAllowed() && tetheringState.hasAnyDeviceTetheringEnabled()) { + createChains(log); + } + + if (tetheringState.tetherWifiVpn()) { + log = new StringBuilder(); + success = addWifiTetheringRules(tetheringState, log); + } else if (!tetheringState.isVpnWifiTetheringAllowed){ + success = removeWifiTetheringRules(tetheringState, log); + } + + if (tetheringState.tetherUsbVpn()) { + success = success && addUsbTetheringRules(tetheringState, log); + } else if (!tetheringState.isVpnUsbTetheringAllowed) { + success = success && removeUsbTetheringRules(tetheringState, log); + } + + if (tetheringState.tetherBluetoothVpn()) { + success = success && addBluetoothTetheringRules(tetheringState, log); + } else if (!tetheringState.isVpnBluetoothTetheringAllowed) { + success = success && removeBluetoothTetheringRules(tetheringState, log); + } + return success; + } catch (Exception e) { + e.printStackTrace(); + Log.e(FirewallManager.TAG, log.toString()); + } + return false; + } + + + //TODO: implement the follwing methods -v + private boolean removeBluetoothTetheringRules(TetheringState tetheringState, StringBuilder log) { + return true; + } + + private boolean removeUsbTetheringRules(TetheringState tetheringState, StringBuilder log) { + return true; + } + + private boolean addBluetoothTetheringRules(TetheringState tetheringState, StringBuilder log) { + return true; + } + + private boolean addUsbTetheringRules(TetheringState tetheringState, StringBuilder log) { + return true; + } + + + private void createChains(StringBuilder log) throws Exception { + boolean success; + String[] createChains = new String[]{ + "su", + "iptables -t filter --new-chain " + BITMASK_FORWARD, + "iptables -t nat --new-chain " + BITMASK_POSTROUTING, + "iptables -t filter --insert FORWARD --jump " + BITMASK_FORWARD, + "iptables -t nat --insert POSTROUTING --jump " + BITMASK_POSTROUTING, + }; + success = runBlockingCmd(createChains, log) == 0; + Log.d(FirewallManager.TAG, "added " + BITMASK_FORWARD + " and " + BITMASK_POSTROUTING+" to iptables: " + success); + Log.d(FirewallManager.TAG, log.toString()); + } + + private boolean addWifiTetheringRules(TetheringState state, StringBuilder log) throws Exception { + Log.d(TAG, "add Wifi tethering Rules"); + String[] addRules = new String[] { + "su", + "iptables -t filter --flush " + BITMASK_FORWARD, + "iptables -t nat --flush " + BITMASK_POSTROUTING, + "iptables -t filter --append " + BITMASK_FORWARD + " --jump ACCEPT", + "iptables -t nat --append " + BITMASK_POSTROUTING + " --jump MASQUERADE", + "if [[ ! `ip rule show from "+ state.wifiAddress+" lookup 61` ]]; " + + "then ip rule add from " + state.wifiAddress + " lookup 61; " + + "fi", + "if [[ ! `ip route list table 61 | grep 'default dev " + getTunName() + " scope link'` ]]; " + + "then ip route add default dev " + getTunName() + " scope link table 61; " + + "fi", + "if [[ ! `ip route list table 61 | grep '"+ state.wifiAddress+" dev "+ state.wifiInterface+" scope link'` ]]; " + + "then ip route add " + state.wifiAddress + " dev " + state.wifiInterface + " scope link table 61; " + + "fi", + "if [[ ! `ip route list table 61 | grep 'broadcast 255.255.255.255 dev " + state.wifiInterface + " scope link'` ]]; " + + "then ip route add broadcast 255.255.255.255 dev " + state.wifiInterface + " scope link table 61; " + + "fi" + }; + + return runBlockingCmd(addRules, log) == 0; + } + + private boolean removeWifiTetheringRules(TetheringState state, StringBuilder log) throws Exception { + Log.d(TAG, "add Wifi tethering Rules"); + String[] removeRules = new String[] { + "su", + "ip route delete broadcast 255.255.255.255 dev " + state.wifiInterface +" scope link table 61", + "ip route delete " + state.lastWifiAddress + " dev " + state.wifiInterface +" scope link table 61", + "ip route delete default dev " + getTunName() + " scope link table 61", + "if [[ `ip rule show from " + state.wifiAddress+ " lookup 61` ]]; " + + "then ip rule del from " + state.wifiAddress + " lookup 61; " + + "fi", + }; + return runBlockingCmd(removeRules, log) == 0; + } + + private String getTunName() { + try { + for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) { + NetworkInterface networkInterface = en.nextElement(); + if (networkInterface.getName().contains("tun")) { + return networkInterface.getName(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + FirewallCallback callback = callbackWeakReference.get(); + if (callback != null) { + callback.onTetheringStarted(result); + } + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownTetheringTask.java b/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownTetheringTask.java new file mode 100644 index 00000000..6c15c3e3 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownTetheringTask.java @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2020 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package se.leap.bitmaskclient.firewall; + +import android.os.AsyncTask; +import android.util.Log; + +import java.lang.ref.WeakReference; + +import se.leap.bitmaskclient.tethering.TetheringObservable; +import se.leap.bitmaskclient.tethering.TetheringState; + +import static se.leap.bitmaskclient.firewall.FirewallManager.BITMASK_FORWARD; +import static se.leap.bitmaskclient.firewall.FirewallManager.BITMASK_POSTROUTING; +import static se.leap.bitmaskclient.utils.Cmd.runBlockingCmd; + +public class ShutdownTetheringTask extends AsyncTask { + + private WeakReference callbackWeakReference; + + ShutdownTetheringTask(FirewallCallback callback) { + callbackWeakReference = new WeakReference<>(callback); + } + + @Override + protected Boolean doInBackground(Void... args) { + TetheringState tetheringState = TetheringObservable.getInstance().getTetheringState(); + StringBuilder log = new StringBuilder(); + + String[] bitmaskChain = new String[]{ + "su", + "id", + "iptables -t filter --list " + BITMASK_FORWARD + " && iptables -t nat --list " + BITMASK_POSTROUTING }; + + try { + boolean hasBitmaskChain = runBlockingCmd(bitmaskChain, log) == 0; + boolean allowSu = log.toString().contains("uid=0"); + callbackWeakReference.get().onSuRequested(allowSu); + if (!allowSu || !hasBitmaskChain) { + return false; + } + + log = new StringBuilder(); + + String[] removeChains = new String[] { + "su", + "ip route flush table 61", + "if [[ `ip rule show from " + tetheringState.wifiAddress+ " lookup 61` ]]; " + + "then ip rule del from " + tetheringState.wifiAddress + " lookup 61; " + + "fi", + "iptables -t filter --delete FORWARD --jump " + BITMASK_FORWARD, + "iptables -t nat --delete POSTROUTING --jump " + BITMASK_POSTROUTING, + "iptables -t filter --flush " + BITMASK_FORWARD, + "iptables -t nat --flush " + BITMASK_POSTROUTING, + "iptables -t filter --delete-chain " + BITMASK_FORWARD, + "iptables -t nat --delete-chain " + BITMASK_POSTROUTING, + }; + return runBlockingCmd(removeChains, log) == 0; + + } catch (Exception e) { + e.printStackTrace(); + Log.e(FirewallManager.TAG, log.toString()); + } + return false; + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + FirewallCallback callback = callbackWeakReference.get(); + if (callback != null) { + callback.onTetheringStarted(result); + } + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java index a8f96986..2e1e8f95 100644 --- a/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java @@ -26,10 +26,8 @@ import java.util.Observer; import butterknife.ButterKnife; import butterknife.InjectView; import se.leap.bitmaskclient.R; -import se.leap.bitmaskclient.eip.EipCommand; import se.leap.bitmaskclient.firewall.FirewallManager; import se.leap.bitmaskclient.tethering.TetheringObservable; -import se.leap.bitmaskclient.tethering.TetheringState; import se.leap.bitmaskclient.utils.PreferenceHelper; import se.leap.bitmaskclient.views.IconCheckboxEntry; @@ -136,16 +134,14 @@ public class TetheringDialog extends AppCompatDialogFragment implements Observer builder.setView(view) .setPositiveButton(android.R.string.ok, (dialog, id) -> { - PreferenceHelper.wifiTethering(getContext(), dataset[0].checked); - PreferenceHelper.usbTethering(getContext(), dataset[1].checked); - PreferenceHelper.bluetoothTethering(getContext(), dataset[2].checked); - FirewallManager firewallManager = new FirewallManager(getContext().getApplicationContext()); - TetheringState runningTethering = TetheringObservable.getInstance().getTetheringState(); - TetheringState vpnTethering = new TetheringState(); - vpnTethering.isWifiTetheringEnabled = runningTethering.isWifiTetheringEnabled && dataset[0].checked; - vpnTethering.isUsbTetheringEnabled = runningTethering.isUsbTetheringEnabled && dataset[1].checked; - vpnTethering.isBluetoothTetheringEnabled = runningTethering.isBluetoothTetheringEnabled && dataset[2].checked; - firewallManager.configureTethering(vpnTethering); + PreferenceHelper.allowWifiTethering(getContext(), dataset[0].checked); + PreferenceHelper.allowUsbTethering(getContext(), dataset[1].checked); + PreferenceHelper.allowBluetoothTethering(getContext(), dataset[2].checked); + TetheringObservable.allowVpnWifiTethering(dataset[0].checked); + TetheringObservable.allowVpnUsbTethering(dataset[1].checked); + TetheringObservable.allowVpnBluetoothTethering(dataset[2].checked); + FirewallManager firewallManager = new FirewallManager(getContext().getApplicationContext(), false); + firewallManager.startTethering(); }) .setNegativeButton(R.string.cancel, (dialog, id) -> dialog.cancel()); return builder.create(); @@ -195,15 +191,15 @@ public class TetheringDialog extends AppCompatDialogFragment implements Observer dataset = new DialogListAdapter.ViewModel[] { new DialogListAdapter.ViewModel(getContext().getResources().getDrawable(R.drawable.ic_wifi), getContext().getString(R.string.tethering_wifi), - PreferenceHelper.getWifiTethering(getContext()), + PreferenceHelper.isWifiTetheringAllowed(getContext()), TetheringObservable.getInstance().isWifiTetheringEnabled()), new DialogListAdapter.ViewModel(getContext().getResources().getDrawable(R.drawable.ic_usb), getContext().getString(R.string.tethering_usb), - PreferenceHelper.getUsbTethering(getContext()), + PreferenceHelper.isUsbTetheringAllowed(getContext()), TetheringObservable.getInstance().isUsbTetheringEnabled()), new DialogListAdapter.ViewModel(getContext().getResources().getDrawable(R.drawable.ic_bluetooth), getContext().getString(R.string.tethering_bluetooth), - PreferenceHelper.getBluetoothTethering(getContext()), + PreferenceHelper.isBluetoothTetheringAllowed(getContext()), TetheringObservable.getInstance().isUsbTetheringEnabled()) }; } diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringBroadcastReceiver.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringBroadcastReceiver.java index 8dab49ce..369a6cf6 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringBroadcastReceiver.java +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringBroadcastReceiver.java @@ -30,16 +30,7 @@ public class TetheringBroadcastReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { if ("android.net.wifi.WIFI_AP_STATE_CHANGED".equals(intent.getAction())) { Log.d(TAG, "TETHERING WIFI_AP_STATE_CHANGED"); - int apState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 0); - if (WifiHotspotState.WIFI_AP_STATE_ENABLED.ordinal() == apState % 10) { - if (!TetheringObservable.getInstance().isWifiTetheringEnabled()) { - TetheringObservable.setWifiTethering(true); - } - } else { - if (TetheringObservable.getInstance().isWifiTetheringEnabled()) { - TetheringObservable.setWifiTethering(false); - } - } + TetheringStateManager.updateWifiTetheringState(); } else if ("android.net.conn.TETHER_STATE_CHANGED".equals(intent.getAction())) { Log.d(TAG, "TETHERING TETHER_STATE_CHANGED"); TetheringStateManager.updateUsbTetheringState(); diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java index 79c1ec6d..b84f3494 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java @@ -34,22 +34,60 @@ public class TetheringObservable extends Observable { return instance; } - static void setWifiTethering(boolean enabled) { - getInstance().tetheringState.isWifiTetheringEnabled = enabled; - getInstance().setChanged(); - getInstance().notifyObservers(); + public static void allowVpnWifiTethering(boolean enabled) { + if (getInstance().tetheringState.isVpnWifiTetheringAllowed != enabled) { + getInstance().tetheringState.isVpnWifiTetheringAllowed = enabled; + getInstance().setChanged(); + getInstance().notifyObservers(); + } + } + + public static void allowVpnUsbTethering(boolean enabled) { + if (getInstance().tetheringState.isUsbTetheringEnabled != enabled) { + getInstance().tetheringState.isUsbTetheringEnabled = enabled; + getInstance().setChanged(); + getInstance().notifyObservers(); + } + } + + public static void allowVpnBluetoothTethering(boolean enabled) { + if (getInstance().tetheringState.isBluetoothTetheringEnabled != enabled) { + getInstance().tetheringState.isBluetoothTetheringEnabled = enabled; + getInstance().setChanged(); + getInstance().notifyObservers(); + } + } + + static void setWifiTethering(boolean enabled, String address, String interfaceName) { + if (getInstance().tetheringState.isWifiTetheringEnabled != enabled || + !getInstance().tetheringState.wifiInterface.equals(interfaceName) || + !getInstance().tetheringState.wifiAddress.equals(address)) { + getInstance().tetheringState.isWifiTetheringEnabled = enabled; + getInstance().tetheringState.wifiInterface = interfaceName; + getInstance().tetheringState.wifiAddress = address; + if ("".equals(address)) { + getInstance().tetheringState.lastWifiAddress = address; + } + getInstance().setChanged(); + getInstance().notifyObservers(); + } + } static void setUsbTethering(boolean enabled) { - getInstance().tetheringState.isUsbTetheringEnabled = enabled; - getInstance().setChanged(); - getInstance().notifyObservers(); + if (getInstance().tetheringState.isUsbTetheringEnabled != enabled) { + getInstance().tetheringState.isUsbTetheringEnabled = enabled; + getInstance().setChanged(); + getInstance().notifyObservers(); + } } static void setBluetoothTethering(boolean enabled) { - getInstance().tetheringState.isBluetoothTetheringEnabled = enabled; - getInstance().setChanged(); - getInstance().notifyObservers(); + if (getInstance().tetheringState.isBluetoothTetheringEnabled != enabled) { + getInstance().tetheringState.isBluetoothTetheringEnabled = enabled; + getInstance().setChanged(); + getInstance().notifyObservers(); + } } public boolean isBluetoothTetheringEnabled() { @@ -67,8 +105,4 @@ public class TetheringObservable extends Observable { public TetheringState getTetheringState() { return tetheringState; } - - public boolean hasAnyTetheringEnabled() { - return tetheringState.isBluetoothTetheringEnabled || tetheringState.isUsbTetheringEnabled || tetheringState.isWifiTetheringEnabled; - } } diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringState.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringState.java index 50fead91..9708639a 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringState.java +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringState.java @@ -1,7 +1,39 @@ package se.leap.bitmaskclient.tethering; -public class TetheringState { +public class TetheringState implements Cloneable { public boolean isWifiTetheringEnabled; public boolean isUsbTetheringEnabled; public boolean isBluetoothTetheringEnabled; + public boolean isVpnWifiTetheringAllowed; + public boolean isVpnUsbTetheringAllowed; + public boolean isVpnBluetoothTetheringAllowed; + public String wifiInterface = ""; + public String usbInterface = ""; + public String bluetoothInterface = ""; + public String wifiAddress = ""; + public String lastWifiAddress = ""; + public String usbAddress = ""; + public String bluetoothAddress = ""; + + public boolean tetherWifiVpn() { + return isWifiTetheringEnabled && isVpnWifiTetheringAllowed; + } + + public boolean tetherUsbVpn() { + return isUsbTetheringEnabled && isVpnUsbTetheringAllowed; + } + + public boolean tetherBluetoothVpn() { + return isBluetoothTetheringEnabled && isVpnBluetoothTetheringAllowed; + } + + public boolean hasAnyDeviceTetheringEnabled() { + return isBluetoothTetheringEnabled || isUsbTetheringEnabled || isWifiTetheringEnabled; + } + + public boolean hasAnyVpnTetheringAllowed() { + return isVpnWifiTetheringAllowed || isVpnUsbTetheringAllowed || isVpnBluetoothTetheringAllowed; + } + + } diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java index 0d4f56d8..58d67872 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java @@ -18,12 +18,20 @@ package se.leap.bitmaskclient.tethering; import android.content.Context; import android.content.IntentFilter; +import android.util.Log; +import java.net.Inet4Address; +import java.net.InterfaceAddress; import java.net.NetworkInterface; import java.util.Enumeration; +import java.util.List; import se.leap.bitmaskclient.utils.Cmd; +import static se.leap.bitmaskclient.utils.PreferenceHelper.isBluetoothTetheringAllowed; +import static se.leap.bitmaskclient.utils.PreferenceHelper.isUsbTetheringAllowed; +import static se.leap.bitmaskclient.utils.PreferenceHelper.isWifiTetheringAllowed; + /** * This manager tries to figure out the current tethering states for Wifi, USB and Bluetooth * The default behavior differs for failing attempts to get these states: @@ -55,15 +63,14 @@ public class TetheringStateManager { intentFilter.addAction("android.net.wifi.WIFI_AP_STATE_CHANGED"); context.getApplicationContext().registerReceiver(broadcastReceiver, intentFilter); instance.wifiManager = new WifiManagerWrapper(context); + TetheringObservable.allowVpnWifiTethering(isWifiTetheringAllowed(context)); + TetheringObservable.allowVpnUsbTethering(isUsbTetheringAllowed(context)); + TetheringObservable.allowVpnBluetoothTethering(isBluetoothTetheringAllowed(context)); updateWifiTetheringState(); updateUsbTetheringState(); updateBluetoothTetheringState(); } - private static boolean isWifiApEnabled() throws Exception { - return instance.wifiManager.isWifiAPEnabled(); - } - private static boolean getUsbTetheringState() { try { @@ -82,6 +89,54 @@ public class TetheringStateManager { return false; } + public static String getWifiAddressRange() { + String interfaceAddress = getWifiInterfaceAddress(); + if (interfaceAddress.split("\\.").length == 4) { + String result = interfaceAddress.substring(0, interfaceAddress.lastIndexOf(".")); + result = result + ".0/24"; + Log.d(TAG, "wifiAddressRange = " + result); + return result; + } + return ""; + } + + private static String getWifiInterfaceAddress() { + NetworkInterface networkInterface = getWlanInterface(); + if (networkInterface != null) { + List ifaceAddresses = networkInterface.getInterfaceAddresses(); + for (InterfaceAddress ifaceAddres : ifaceAddresses) { + if (ifaceAddres.getAddress() instanceof Inet4Address) { + return ifaceAddres.getAddress().getHostAddress(); + } + } + } + return ""; + } + + public static String getWifiInterfaceName() { + NetworkInterface networkInterface = getWlanInterface(); + if (networkInterface != null) { + return networkInterface.getName(); + } + return ""; + } + + private static NetworkInterface getWlanInterface() { + try { + for(Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { + NetworkInterface networkInterface = en.nextElement(); + if(!networkInterface.isLoopback()){ + if(networkInterface.getName().contains("wlan") || networkInterface.getName().contains("eth")){ + return networkInterface; + } + } + } + } catch(Exception e){ + e.printStackTrace(); + } + return null; + } + private static boolean isBluetoothTetheringEnabled() { StringBuilder log = new StringBuilder(); boolean hasBtPan = false; @@ -104,12 +159,9 @@ public class TetheringStateManager { } static void updateWifiTetheringState() { - boolean lastState = TetheringObservable.getInstance().isWifiTetheringEnabled(); + WifiManagerWrapper manager = getInstance().wifiManager; try { - boolean currentState = isWifiApEnabled(); - if (currentState != lastState) { - TetheringObservable.setWifiTethering(currentState); - } + TetheringObservable.setWifiTethering(manager.isWifiAPEnabled(), getWifiAddressRange(), getWifiInterfaceName()); } catch (Exception e) { e.printStackTrace(); } diff --git a/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java b/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java index 25e4b797..073e338c 100644 --- a/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java @@ -3,18 +3,13 @@ package se.leap.bitmaskclient.utils; import android.content.Context; import android.content.SharedPreferences; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import org.json.JSONException; import org.json.JSONObject; import java.net.MalformedURLException; import java.net.URL; -import java.util.ArrayList; import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; import java.util.Set; import de.blinkt.openvpn.VpnProfile; @@ -25,16 +20,15 @@ import static se.leap.bitmaskclient.Constants.ALWAYS_ON_SHOW_DIALOG; import static se.leap.bitmaskclient.Constants.DEFAULT_SHARED_PREFS_BATTERY_SAVER; import static se.leap.bitmaskclient.Constants.EXCLUDED_APPS; import static se.leap.bitmaskclient.Constants.LAST_USED_PROFILE; -import static se.leap.bitmaskclient.Constants.PREFERENCES_APP_VERSION; import static se.leap.bitmaskclient.Constants.PROVIDER_CONFIGURED; import static se.leap.bitmaskclient.Constants.PROVIDER_EIP_DEFINITION; import static se.leap.bitmaskclient.Constants.PROVIDER_PRIVATE_KEY; import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE; import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; import static se.leap.bitmaskclient.Constants.SU_PERMISSION; -import static se.leap.bitmaskclient.Constants.TETHERING_BLUETOOTH; -import static se.leap.bitmaskclient.Constants.TETHERING_USB; -import static se.leap.bitmaskclient.Constants.TETHERING_WIFI; +import static se.leap.bitmaskclient.Constants.ALLOW_TETHERING_BLUETOOTH; +import static se.leap.bitmaskclient.Constants.ALLOW_TETHERING_USB; +import static se.leap.bitmaskclient.Constants.ALLOW_TETHERING_WIFI; import static se.leap.bitmaskclient.Constants.USE_PLUGGABLE_TRANSPORTS; /** @@ -149,28 +143,28 @@ public class PreferenceHelper { return getBoolean(context, DEFAULT_SHARED_PREFS_BATTERY_SAVER, false); } - public static void usbTethering(Context context, boolean isEnabled) { - putBoolean(context, TETHERING_USB, isEnabled); + public static void allowUsbTethering(Context context, boolean isEnabled) { + putBoolean(context, ALLOW_TETHERING_USB, isEnabled); } - public static boolean getUsbTethering(Context context) { - return getBoolean(context, TETHERING_USB, false); + public static boolean isUsbTetheringAllowed(Context context) { + return getBoolean(context, ALLOW_TETHERING_USB, false); } - public static void wifiTethering(Context context, boolean isEnabled) { - putBoolean(context, TETHERING_WIFI, isEnabled); + public static void allowWifiTethering(Context context, boolean isEnabled) { + putBoolean(context, ALLOW_TETHERING_WIFI, isEnabled); } - public static boolean getWifiTethering(Context context) { - return getBoolean(context, TETHERING_WIFI, false); + public static boolean isWifiTetheringAllowed(Context context) { + return getBoolean(context, ALLOW_TETHERING_WIFI, false); } - public static void bluetoothTethering(Context context, boolean isEnabled) { - putBoolean(context, TETHERING_BLUETOOTH, isEnabled); + public static void allowBluetoothTethering(Context context, boolean isEnabled) { + putBoolean(context, ALLOW_TETHERING_BLUETOOTH, isEnabled); } - public static boolean getBluetoothTethering(Context context) { - return getBoolean(context, TETHERING_BLUETOOTH, false); + public static boolean isBluetoothTetheringAllowed(Context context) { + return getBoolean(context, ALLOW_TETHERING_BLUETOOTH, false); } public static void saveShowAlwaysOnDialog(Context context, boolean showAlwaysOnDialog) { -- cgit v1.2.3 From 7a42047640daef05f5771d8cb812a03ee14bbec6 Mon Sep 17 00:00:00 2001 From: cyberta Date: Wed, 29 Jan 2020 12:23:48 -0600 Subject: adapt tests --- .../bitmaskclient/firewall/SetupTetheringTask.java | 4 ++-- .../tethering/TetheringStateManager.java | 7 ++++--- .../tethering/TetheringStateManagerTest.java | 19 ++++++++++++++----- 3 files changed, 20 insertions(+), 10 deletions(-) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/SetupTetheringTask.java b/app/src/main/java/se/leap/bitmaskclient/firewall/SetupTetheringTask.java index 93054099..8bb197b1 100644 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/SetupTetheringTask.java +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/SetupTetheringTask.java @@ -155,8 +155,8 @@ public class SetupTetheringTask extends AsyncTask { "ip route delete broadcast 255.255.255.255 dev " + state.wifiInterface +" scope link table 61", "ip route delete " + state.lastWifiAddress + " dev " + state.wifiInterface +" scope link table 61", "ip route delete default dev " + getTunName() + " scope link table 61", - "if [[ `ip rule show from " + state.wifiAddress+ " lookup 61` ]]; " + - "then ip rule del from " + state.wifiAddress + " lookup 61; " + + "if [[ `ip rule show from " + state.lastWifiAddress+ " lookup 61` ]]; " + + "then ip rule del from " + state.lastWifiAddress + " lookup 61; " + "fi", }; return runBlockingCmd(removeRules, log) == 0; diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java index 58d67872..11e1a83d 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java @@ -18,6 +18,7 @@ package se.leap.bitmaskclient.tethering; import android.content.Context; import android.content.IntentFilter; +import android.support.annotation.VisibleForTesting; import android.util.Log; import java.net.Inet4Address; @@ -94,13 +95,13 @@ public class TetheringStateManager { if (interfaceAddress.split("\\.").length == 4) { String result = interfaceAddress.substring(0, interfaceAddress.lastIndexOf(".")); result = result + ".0/24"; - Log.d(TAG, "wifiAddressRange = " + result); return result; } return ""; } - private static String getWifiInterfaceAddress() { + @VisibleForTesting + static String getWifiInterfaceAddress() { NetworkInterface networkInterface = getWlanInterface(); if (networkInterface != null) { List ifaceAddresses = networkInterface.getInterfaceAddresses(); @@ -113,7 +114,7 @@ public class TetheringStateManager { return ""; } - public static String getWifiInterfaceName() { + private static String getWifiInterfaceName() { NetworkInterface networkInterface = getWlanInterface(); if (networkInterface != null) { return networkInterface.getName(); diff --git a/app/src/test/java/se/leap/bitmaskclient/tethering/TetheringStateManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/tethering/TetheringStateManagerTest.java index 295714c3..85352c53 100644 --- a/app/src/test/java/se/leap/bitmaskclient/tethering/TetheringStateManagerTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/tethering/TetheringStateManagerTest.java @@ -39,6 +39,7 @@ import java.util.Enumeration; import se.leap.bitmaskclient.utils.Cmd; import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; @@ -57,7 +58,7 @@ public class TetheringStateManagerTest { @Mock(answer = Answers.RETURNS_DEEP_STUBS) IntentFilter intentFilter; - TetheringObservable observable; + private TetheringObservable observable; @Before public void setup() throws Exception { @@ -233,7 +234,7 @@ public class TetheringStateManagerTest { when(mockWrapper.isWifiAPEnabled()).thenThrow(new NoSuchMethodException()); PowerMockito.whenNew(WifiManagerWrapper.class).withAnyArguments().thenReturn(mockWrapper); - TetheringObservable.setWifiTethering(true); + TetheringObservable.setWifiTethering(true, "192.168.43.0/24", "wlan0"); TetheringStateManager.getInstance().init(mockContext); TetheringStateManager manager = TetheringStateManager.getInstance(); assertTrue(observable.isWifiTetheringEnabled()); @@ -245,7 +246,7 @@ public class TetheringStateManagerTest { when(mockWrapper.isWifiAPEnabled()).thenThrow(new NoSuchMethodException()); PowerMockito.whenNew(WifiManagerWrapper.class).withAnyArguments().thenReturn(mockWrapper); - TetheringObservable.setWifiTethering(false); + TetheringObservable.setWifiTethering(false, "", ""); TetheringStateManager.getInstance().init(mockContext); TetheringStateManager manager = TetheringStateManager.getInstance(); assertFalse(observable.isWifiTetheringEnabled()); @@ -257,7 +258,7 @@ public class TetheringStateManagerTest { when(mockWrapper.isWifiAPEnabled()).thenReturn(true); PowerMockito.whenNew(WifiManagerWrapper.class).withAnyArguments().thenReturn(mockWrapper); - TetheringObservable.setWifiTethering(false); + TetheringObservable.setWifiTethering(false, "", ""); TetheringStateManager.getInstance().init(mockContext); TetheringStateManager manager = TetheringStateManager.getInstance(); assertTrue(observable.isWifiTetheringEnabled()); @@ -269,11 +270,19 @@ public class TetheringStateManagerTest { when(mockWrapper.isWifiAPEnabled()).thenReturn(false); PowerMockito.whenNew(WifiManagerWrapper.class).withAnyArguments().thenReturn(mockWrapper); - TetheringObservable.setWifiTethering(true); + TetheringObservable.setWifiTethering(true, "", ""); TetheringStateManager.getInstance().init(mockContext); TetheringStateManager manager = TetheringStateManager.getInstance(); assertFalse(observable.isWifiTetheringEnabled()); } + @Test + public void testGetWifiAddressRange() { + mockStatic(TetheringStateManager.class); + PowerMockito.when(TetheringStateManager.getWifiInterfaceAddress()).thenReturn("192.168.40.217/24"); + PowerMockito.when(TetheringStateManager.getInstance()).thenCallRealMethod(); + PowerMockito.when(TetheringStateManager.getWifiAddressRange()).thenCallRealMethod(); + assertEquals("192.168.40.0/24", TetheringStateManager.getWifiAddressRange()); + } } \ No newline at end of file -- cgit v1.2.3 From b9e4195573da146d48e5921c65dc57273d94ccd2 Mon Sep 17 00:00:00 2001 From: cyberta Date: Thu, 30 Jan 2020 16:06:30 -0600 Subject: fix possible NPE in SetupTetheringTask --- .../main/java/se/leap/bitmaskclient/firewall/SetupTetheringTask.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/SetupTetheringTask.java b/app/src/main/java/se/leap/bitmaskclient/firewall/SetupTetheringTask.java index 8bb197b1..49febc24 100644 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/SetupTetheringTask.java +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/SetupTetheringTask.java @@ -52,7 +52,10 @@ public class SetupTetheringTask extends AsyncTask { try { boolean hasBitmaskChain = runBlockingCmd(bitmaskChain, log) == 0; boolean allowSu = log.toString().contains("uid=0"); - callbackWeakReference.get().onSuRequested(allowSu); + FirewallCallback callback = callbackWeakReference.get(); + if (callback != null) { + callback.onSuRequested(allowSu); + } if (!allowSu) { return false; } -- cgit v1.2.3 From 97a117cb3bbc022ee16008dea9896a8dfea7c681 Mon Sep 17 00:00:00 2001 From: cyberta Date: Thu, 30 Jan 2020 16:14:22 -0600 Subject: implement usb tethering --- .../bitmaskclient/firewall/FirewallManager.java | 2 +- .../bitmaskclient/firewall/SetupTetheringTask.java | 104 +++++++++++------ .../firewall/ShutdownTetheringTask.java | 35 +++--- .../bitmaskclient/fragments/TetheringDialog.java | 13 ++- .../tethering/TetheringObservable.java | 37 +++--- .../bitmaskclient/tethering/TetheringState.java | 12 +- .../tethering/TetheringStateManager.java | 127 +++++++++++---------- 7 files changed, 199 insertions(+), 131 deletions(-) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java index 82888668..67cc4625 100644 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java @@ -108,7 +108,7 @@ public class FirewallManager implements FirewallCallback, Observer { task.execute(); } - private void stopTethering() { + public void stopTethering() { ShutdownTetheringTask task = new ShutdownTetheringTask(this); task.execute(); } diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/SetupTetheringTask.java b/app/src/main/java/se/leap/bitmaskclient/firewall/SetupTetheringTask.java index 49febc24..7abd01a8 100644 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/SetupTetheringTask.java +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/SetupTetheringTask.java @@ -21,6 +21,7 @@ import android.util.Log; import java.lang.ref.WeakReference; import java.net.NetworkInterface; +import java.util.ArrayList; import java.util.Enumeration; import se.leap.bitmaskclient.tethering.TetheringObservable; @@ -70,20 +71,28 @@ public class SetupTetheringTask extends AsyncTask { if (tetheringState.tetherWifiVpn()) { log = new StringBuilder(); success = addWifiTetheringRules(tetheringState, log); + logError(success, log); } else if (!tetheringState.isVpnWifiTetheringAllowed){ success = removeWifiTetheringRules(tetheringState, log); + logError(success, log); } + log = new StringBuilder(); if (tetheringState.tetherUsbVpn()) { success = success && addUsbTetheringRules(tetheringState, log); + logError(success, log); } else if (!tetheringState.isVpnUsbTetheringAllowed) { success = success && removeUsbTetheringRules(tetheringState, log); + logError(success, log); } + log = new StringBuilder(); if (tetheringState.tetherBluetoothVpn()) { success = success && addBluetoothTetheringRules(tetheringState, log); + logError(success, log); } else if (!tetheringState.isVpnBluetoothTetheringAllowed) { success = success && removeBluetoothTetheringRules(tetheringState, log); + logError(success, log); } return success; } catch (Exception e) { @@ -93,22 +102,10 @@ public class SetupTetheringTask extends AsyncTask { return false; } - - //TODO: implement the follwing methods -v - private boolean removeBluetoothTetheringRules(TetheringState tetheringState, StringBuilder log) { - return true; - } - - private boolean removeUsbTetheringRules(TetheringState tetheringState, StringBuilder log) { - return true; - } - - private boolean addBluetoothTetheringRules(TetheringState tetheringState, StringBuilder log) { - return true; - } - - private boolean addUsbTetheringRules(TetheringState tetheringState, StringBuilder log) { - return true; + private void logError(boolean success, StringBuilder log) { + if (!success) { + Log.e(TAG, log.toString()); + } } @@ -128,43 +125,76 @@ public class SetupTetheringTask extends AsyncTask { private boolean addWifiTetheringRules(TetheringState state, StringBuilder log) throws Exception { Log.d(TAG, "add Wifi tethering Rules"); - String[] addRules = new String[] { + String[] addRules = getAdditionRules(state.wifiAddress, state.wifiInterface); + return runBlockingCmd(addRules, log) == 0; + } + + private boolean removeWifiTetheringRules(TetheringState state, StringBuilder log) throws Exception { + Log.d(TAG, "add Wifi tethering Rules"); + String[] removeRules = getDeletionRules(state, state.lastSeenWifiAddress, state.lastSeenWifiInterface); + return runBlockingCmd(removeRules, log) == 0; + } + + private boolean addUsbTetheringRules(TetheringState state, StringBuilder log) throws Exception { + Log.d(TAG, "add usb tethering rules"); + String[] addRules = getAdditionRules(state.usbAddress, state.usbInterface); + return runBlockingCmd(addRules, log) == 0; + } + + private boolean removeUsbTetheringRules(TetheringState state, StringBuilder log) throws Exception { + Log.d(TAG, "add usb tethering rules"); + String[] addRules = getDeletionRules(state, state.lastSeenUsbAddress, state.lastSeenUsbInterface); + return runBlockingCmd(addRules, log) == 0; + } + + //TODO: implement the follwing methods -v + private boolean removeBluetoothTetheringRules(TetheringState state, StringBuilder log) { + return true; + } + + private boolean addBluetoothTetheringRules(TetheringState state, StringBuilder log) { + return true; + } + + private String[] getAdditionRules(String addressRange, String interfaceName) { + return new String[] { "su", "iptables -t filter --flush " + BITMASK_FORWARD, "iptables -t nat --flush " + BITMASK_POSTROUTING, "iptables -t filter --append " + BITMASK_FORWARD + " --jump ACCEPT", "iptables -t nat --append " + BITMASK_POSTROUTING + " --jump MASQUERADE", - "if [[ ! `ip rule show from "+ state.wifiAddress+" lookup 61` ]]; " + - "then ip rule add from " + state.wifiAddress + " lookup 61; " + + "if [[ ! `ip rule show from "+ addressRange+" lookup 61` ]]; " + + "then ip rule add from " + addressRange + " lookup 61; " + "fi", "if [[ ! `ip route list table 61 | grep 'default dev " + getTunName() + " scope link'` ]]; " + "then ip route add default dev " + getTunName() + " scope link table 61; " + "fi", - "if [[ ! `ip route list table 61 | grep '"+ state.wifiAddress+" dev "+ state.wifiInterface+" scope link'` ]]; " + - "then ip route add " + state.wifiAddress + " dev " + state.wifiInterface + " scope link table 61; " + + "if [[ ! `ip route list table 61 | grep '"+ addressRange +" dev "+ interfaceName +" scope link'` ]]; " + + "then ip route add " + addressRange + " dev " + interfaceName + " scope link table 61; " + "fi", - "if [[ ! `ip route list table 61 | grep 'broadcast 255.255.255.255 dev " + state.wifiInterface + " scope link'` ]]; " + - "then ip route add broadcast 255.255.255.255 dev " + state.wifiInterface + " scope link table 61; " + + "if [[ ! `ip route list table 61 | grep 'broadcast 255.255.255.255 dev " + interfaceName + " scope link'` ]]; " + + "then ip route add broadcast 255.255.255.255 dev " + interfaceName + " scope link table 61; " + "fi" }; - - return runBlockingCmd(addRules, log) == 0; } - private boolean removeWifiTetheringRules(TetheringState state, StringBuilder log) throws Exception { - Log.d(TAG, "add Wifi tethering Rules"); - String[] removeRules = new String[] { - "su", - "ip route delete broadcast 255.255.255.255 dev " + state.wifiInterface +" scope link table 61", - "ip route delete " + state.lastWifiAddress + " dev " + state.wifiInterface +" scope link table 61", - "ip route delete default dev " + getTunName() + " scope link table 61", - "if [[ `ip rule show from " + state.lastWifiAddress+ " lookup 61` ]]; " + - "then ip rule del from " + state.lastWifiAddress + " lookup 61; " + - "fi", - }; - return runBlockingCmd(removeRules, log) == 0; + private String[] getDeletionRules(TetheringState state, String addressRange, String interfaceName) { + ArrayList list = new ArrayList<>(); + list.add("su"); + list.add("ip route delete broadcast 255.255.255.255 dev " + addressRange +" scope link table 61"); + list.add("ip route delete " + addressRange + " dev " + interfaceName +" scope link table 61"); + if (!state.hasAnyVpnTetheringAllowed() || !state.hasAnyDeviceTetheringEnabled()) { + list.add("ip route delete default dev " + getTunName() + " scope link table 61"); + } + list.add("if [[ `ip rule show from " + addressRange + " lookup 61` ]]; " + + "then ip rule del from " + addressRange + " lookup 61; " + + "fi"); + + return list.toArray(new String[0]); } + + private String getTunName() { try { for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) { diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownTetheringTask.java b/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownTetheringTask.java index 6c15c3e3..dcb3ccba 100644 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownTetheringTask.java +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownTetheringTask.java @@ -20,6 +20,7 @@ import android.os.AsyncTask; import android.util.Log; import java.lang.ref.WeakReference; +import java.util.ArrayList; import se.leap.bitmaskclient.tethering.TetheringObservable; import se.leap.bitmaskclient.tethering.TetheringState; @@ -50,26 +51,30 @@ public class ShutdownTetheringTask extends AsyncTask { boolean hasBitmaskChain = runBlockingCmd(bitmaskChain, log) == 0; boolean allowSu = log.toString().contains("uid=0"); callbackWeakReference.get().onSuRequested(allowSu); - if (!allowSu || !hasBitmaskChain) { + if (!allowSu) { return false; } log = new StringBuilder(); - String[] removeChains = new String[] { - "su", - "ip route flush table 61", - "if [[ `ip rule show from " + tetheringState.wifiAddress+ " lookup 61` ]]; " + - "then ip rule del from " + tetheringState.wifiAddress + " lookup 61; " + - "fi", - "iptables -t filter --delete FORWARD --jump " + BITMASK_FORWARD, - "iptables -t nat --delete POSTROUTING --jump " + BITMASK_POSTROUTING, - "iptables -t filter --flush " + BITMASK_FORWARD, - "iptables -t nat --flush " + BITMASK_POSTROUTING, - "iptables -t filter --delete-chain " + BITMASK_FORWARD, - "iptables -t nat --delete-chain " + BITMASK_POSTROUTING, - }; - return runBlockingCmd(removeChains, log) == 0; + ArrayList removeChains = new ArrayList<>(); + removeChains.add("su"); + removeChains.add("ip route flush table 61"); + removeChains.add("if [[ `ip rule show from " + tetheringState.lastSeenWifiAddress+ " lookup 61` ]]; " + + "then ip rule del from " + tetheringState.lastSeenWifiAddress + " lookup 61; " + + "fi"); + removeChains.add("if [[ `ip rule show from " + tetheringState.lastSeenUsbAddress+ " lookup 61` ]]; " + + "then ip rule del from " + tetheringState.lastSeenUsbAddress + " lookup 61; " + + "fi"); + if (hasBitmaskChain) { + removeChains.add("iptables -t filter --delete FORWARD --jump " + BITMASK_FORWARD); + removeChains.add("iptables -t nat --delete POSTROUTING --jump " + BITMASK_POSTROUTING); + removeChains.add("iptables -t filter --flush " + BITMASK_FORWARD); + removeChains.add("iptables -t nat --flush " + BITMASK_POSTROUTING); + removeChains.add("iptables -t filter --delete-chain " + BITMASK_FORWARD); + removeChains.add("iptables -t nat --delete-chain " + BITMASK_POSTROUTING); + } + return runBlockingCmd(removeChains.toArray(new String[0]), log) == 0; } catch (Exception e) { e.printStackTrace(); diff --git a/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java index 2e1e8f95..52ed16f7 100644 --- a/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java @@ -25,6 +25,7 @@ import java.util.Observer; import butterknife.ButterKnife; import butterknife.InjectView; +import de.blinkt.openvpn.core.VpnStatus; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.firewall.FirewallManager; import se.leap.bitmaskclient.tethering.TetheringObservable; @@ -141,9 +142,15 @@ public class TetheringDialog extends AppCompatDialogFragment implements Observer TetheringObservable.allowVpnUsbTethering(dataset[1].checked); TetheringObservable.allowVpnBluetoothTethering(dataset[2].checked); FirewallManager firewallManager = new FirewallManager(getContext().getApplicationContext(), false); - firewallManager.startTethering(); - }) - .setNegativeButton(R.string.cancel, (dialog, id) -> dialog.cancel()); + if (VpnStatus.isVPNActive()) { + if (TetheringObservable.getInstance().getTetheringState().hasAnyDeviceTetheringEnabled() && + TetheringObservable.getInstance().getTetheringState().hasAnyVpnTetheringAllowed()) { + firewallManager.startTethering(); + } else { + firewallManager.stopTethering(); + } + } + }).setNegativeButton(R.string.cancel, (dialog, id) -> dialog.cancel()); return builder.create(); } diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java index b84f3494..75d29417 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java @@ -16,6 +16,8 @@ */ package se.leap.bitmaskclient.tethering; +import android.support.annotation.NonNull; + import java.util.Observable; public class TetheringObservable extends Observable { @@ -43,40 +45,47 @@ public class TetheringObservable extends Observable { } public static void allowVpnUsbTethering(boolean enabled) { - if (getInstance().tetheringState.isUsbTetheringEnabled != enabled) { - getInstance().tetheringState.isUsbTetheringEnabled = enabled; + if (getInstance().tetheringState.isVpnUsbTetheringAllowed != enabled) { + getInstance().tetheringState.isVpnUsbTetheringAllowed = enabled; getInstance().setChanged(); getInstance().notifyObservers(); } } public static void allowVpnBluetoothTethering(boolean enabled) { - if (getInstance().tetheringState.isBluetoothTetheringEnabled != enabled) { - getInstance().tetheringState.isBluetoothTetheringEnabled = enabled; + if (getInstance().tetheringState.isVpnBluetoothTetheringAllowed != enabled) { + getInstance().tetheringState.isVpnBluetoothTetheringAllowed = enabled; getInstance().setChanged(); getInstance().notifyObservers(); } } - static void setWifiTethering(boolean enabled, String address, String interfaceName) { + static void setWifiTethering(boolean enabled, @NonNull String address, @NonNull String interfaceName) { if (getInstance().tetheringState.isWifiTetheringEnabled != enabled || !getInstance().tetheringState.wifiInterface.equals(interfaceName) || !getInstance().tetheringState.wifiAddress.equals(address)) { - getInstance().tetheringState.isWifiTetheringEnabled = enabled; - getInstance().tetheringState.wifiInterface = interfaceName; - getInstance().tetheringState.wifiAddress = address; - if ("".equals(address)) { - getInstance().tetheringState.lastWifiAddress = address; - } + TetheringState state = getInstance().tetheringState; + state.isWifiTetheringEnabled = enabled; + state.wifiInterface = interfaceName; + state.wifiAddress = address; + state.lastSeenWifiAddress = address.isEmpty() ? state.lastSeenWifiAddress : address; + state.lastSeenWifiInterface = interfaceName.isEmpty() ? state.lastSeenWifiInterface : interfaceName; getInstance().setChanged(); getInstance().notifyObservers(); } } - static void setUsbTethering(boolean enabled) { - if (getInstance().tetheringState.isUsbTetheringEnabled != enabled) { - getInstance().tetheringState.isUsbTetheringEnabled = enabled; + static void setUsbTethering(boolean enabled, @NonNull String address, @NonNull String interfaceName) { + if (getInstance().tetheringState.isUsbTetheringEnabled != enabled || + !getInstance().tetheringState.usbAddress.equals(address) || + !getInstance().tetheringState.usbInterface.equals(interfaceName)) { + TetheringState state = getInstance().tetheringState; + state.isUsbTetheringEnabled = enabled; + state.usbAddress = address; + state.usbInterface = interfaceName; + state.lastSeenUsbAddress = address.isEmpty() ? state.lastSeenUsbAddress : address; + state.lastSeenUsbInterface = interfaceName.isEmpty() ? state.lastSeenUsbInterface : interfaceName; getInstance().setChanged(); getInstance().notifyObservers(); } diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringState.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringState.java index 9708639a..8ef237c6 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringState.java +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringState.java @@ -8,12 +8,18 @@ public class TetheringState implements Cloneable { public boolean isVpnUsbTetheringAllowed; public boolean isVpnBluetoothTetheringAllowed; public String wifiInterface = ""; - public String usbInterface = ""; - public String bluetoothInterface = ""; + public String lastSeenWifiInterface = ""; public String wifiAddress = ""; - public String lastWifiAddress = ""; + public String lastSeenWifiAddress = ""; + public String usbInterface = ""; + public String lastSeenUsbInterface = ""; public String usbAddress = ""; + public String lastSeenUsbAddress = ""; + public String bluetoothInterface = ""; + public String lastSeenBluetoothInterface = ""; public String bluetoothAddress = ""; + public String lastSeenBluetoothAddress = ""; + public boolean tetherWifiVpn() { return isWifiTetheringEnabled && isVpnWifiTetheringAllowed; diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java index 11e1a83d..4a266d87 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java @@ -19,7 +19,6 @@ package se.leap.bitmaskclient.tethering; import android.content.Context; import android.content.IntentFilter; import android.support.annotation.VisibleForTesting; -import android.util.Log; import java.net.Inet4Address; import java.net.InterfaceAddress; @@ -72,26 +71,68 @@ public class TetheringStateManager { updateBluetoothTetheringState(); } + static void updateWifiTetheringState() { + WifiManagerWrapper manager = getInstance().wifiManager; + try { + TetheringObservable.setWifiTethering(manager.isWifiAPEnabled(), getWifiAddressRange(), getWlanInterfaceName()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + static void updateUsbTetheringState() { + TetheringObservable.setUsbTethering(isUsbTetheringEnabled(), getUsbAddressRange(), getUsbInterfaceName()); + } + + static void updateBluetoothTetheringState() { + TetheringObservable.setBluetoothTethering(isBluetoothTetheringEnabled()); + } + + private static String getWifiAddressRange() { + String interfaceAddress = getInterfaceAddress(getWlanInterface()); + return getAddressRange(interfaceAddress); + } + + private static String getUsbAddressRange() { + String interfaceAddress = getInterfaceAddress(getUsbInterface()); + return getAddressRange(interfaceAddress); + } + + private static String getWlanInterfaceName() { + return getInterfaceName(getWlanInterface()); + } - private static boolean getUsbTetheringState() { + private static String getUsbInterfaceName() { + return getInterfaceName(getUsbInterface()); + } + + private static NetworkInterface getWlanInterface() { + return getNetworkInterface(new String[]{"wlan", "eth"}); + } + + private static NetworkInterface getUsbInterface() { + return getNetworkInterface(new String[]{"rndis", "usb"}); + } + + private static boolean isBluetoothTetheringEnabled() { + StringBuilder log = new StringBuilder(); + boolean hasBtPan = false; try { - for(Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { - NetworkInterface networkInterface = en.nextElement(); - if(!networkInterface.isLoopback()){ - if(networkInterface.getName().contains("rndis") || networkInterface.getName().contains("usb")){ - return true; - } - } - } - } catch(Exception e){ + hasBtPan = Cmd.runBlockingCmd(new String[] {"ifconfig bt-pan"}, log) == 0; + //Log.d(TAG, "ifconfig result: " + log.toString()); + } catch (Exception e) { e.printStackTrace(); } + return hasBtPan; - return false; } - public static String getWifiAddressRange() { - String interfaceAddress = getWifiInterfaceAddress(); + private static boolean isUsbTetheringEnabled() { + return getUsbInterface() != null; + } + + @VisibleForTesting + static String getAddressRange(String interfaceAddress) { if (interfaceAddress.split("\\.").length == 4) { String result = interfaceAddress.substring(0, interfaceAddress.lastIndexOf(".")); result = result + ".0/24"; @@ -100,72 +141,42 @@ public class TetheringStateManager { return ""; } - @VisibleForTesting - static String getWifiInterfaceAddress() { - NetworkInterface networkInterface = getWlanInterface(); + private static String getInterfaceAddress(NetworkInterface networkInterface) { if (networkInterface != null) { - List ifaceAddresses = networkInterface.getInterfaceAddresses(); - for (InterfaceAddress ifaceAddres : ifaceAddresses) { - if (ifaceAddres.getAddress() instanceof Inet4Address) { - return ifaceAddres.getAddress().getHostAddress(); - } - } + List ifaceAddresses = networkInterface.getInterfaceAddresses(); + for (InterfaceAddress ifaceAddres : ifaceAddresses) { + if (ifaceAddres.getAddress() instanceof Inet4Address) { + return ifaceAddres.getAddress().getHostAddress(); + } + } } return ""; } - private static String getWifiInterfaceName() { - NetworkInterface networkInterface = getWlanInterface(); + private static String getInterfaceName(NetworkInterface networkInterface) { if (networkInterface != null) { return networkInterface.getName(); } return ""; } - private static NetworkInterface getWlanInterface() { + private static NetworkInterface getNetworkInterface(String[] interfaceNames) { try { for(Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { NetworkInterface networkInterface = en.nextElement(); if(!networkInterface.isLoopback()){ - if(networkInterface.getName().contains("wlan") || networkInterface.getName().contains("eth")){ - return networkInterface; + for (String interfaceName : interfaceNames) { + if (networkInterface.getName().contains(interfaceName)) { + return networkInterface; + } } } } } catch(Exception e){ e.printStackTrace(); } - return null; - } - - private static boolean isBluetoothTetheringEnabled() { - StringBuilder log = new StringBuilder(); - boolean hasBtPan = false; - try { - hasBtPan = Cmd.runBlockingCmd(new String[] {"ifconfig bt-pan"}, log) == 0; - //Log.d(TAG, "ifconfig result: " + log.toString()); - } catch (Exception e) { - e.printStackTrace(); - } - return hasBtPan; - - } - static void updateUsbTetheringState() { - TetheringObservable.setUsbTethering(getUsbTetheringState()); - } - - static void updateBluetoothTetheringState() { - TetheringObservable.setBluetoothTethering(isBluetoothTetheringEnabled()); - } - - static void updateWifiTetheringState() { - WifiManagerWrapper manager = getInstance().wifiManager; - try { - TetheringObservable.setWifiTethering(manager.isWifiAPEnabled(), getWifiAddressRange(), getWifiInterfaceName()); - } catch (Exception e) { - e.printStackTrace(); - } + return null; } } -- cgit v1.2.3 From 3c130ce5f2741d4bc1c521507ab118a3006ec634 Mon Sep 17 00:00:00 2001 From: cyberta Date: Thu, 30 Jan 2020 22:48:35 -0600 Subject: adapt and extend tests --- .../tethering/TetheringStateManagerTest.java | 51 ++++++++++++++++++---- 1 file changed, 42 insertions(+), 9 deletions(-) (limited to 'app') diff --git a/app/src/test/java/se/leap/bitmaskclient/tethering/TetheringStateManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/tethering/TetheringStateManagerTest.java index 85352c53..2e6292a6 100644 --- a/app/src/test/java/se/leap/bitmaskclient/tethering/TetheringStateManagerTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/tethering/TetheringStateManagerTest.java @@ -93,7 +93,7 @@ public class TetheringStateManagerTest { } }); - TetheringObservable.setUsbTethering(false); + TetheringObservable.setUsbTethering(false, "192.168.42.0/24", "rndis0"); TetheringStateManager.getInstance().init(mockContext); TetheringStateManager manager = TetheringStateManager.getInstance(); assertTrue(observable.isUsbTetheringEnabled()); @@ -124,7 +124,7 @@ public class TetheringStateManagerTest { } }); - TetheringObservable.setUsbTethering(true); + TetheringObservable.setUsbTethering(true, "192.168.42.0/24", "rndis0"); TetheringStateManager.getInstance().init(mockContext); TetheringStateManager manager = TetheringStateManager.getInstance(); assertFalse(observable.isUsbTetheringEnabled()); @@ -139,7 +139,7 @@ public class TetheringStateManagerTest { PowerMockito.mockStatic(NetworkInterface.class); PowerMockito.when(NetworkInterface.getNetworkInterfaces()).thenThrow(new SocketException()); - TetheringObservable.setUsbTethering(true); + TetheringObservable.setUsbTethering(true, "192.168.42.0/24", "rndis0"); TetheringStateManager.getInstance().init(mockContext); TetheringStateManager manager = TetheringStateManager.getInstance(); assertFalse(observable.isUsbTetheringEnabled()); @@ -277,12 +277,45 @@ public class TetheringStateManagerTest { } @Test - public void testGetWifiAddressRange() { - mockStatic(TetheringStateManager.class); - PowerMockito.when(TetheringStateManager.getWifiInterfaceAddress()).thenReturn("192.168.40.217/24"); - PowerMockito.when(TetheringStateManager.getInstance()).thenCallRealMethod(); - PowerMockito.when(TetheringStateManager.getWifiAddressRange()).thenCallRealMethod(); - assertEquals("192.168.40.0/24", TetheringStateManager.getWifiAddressRange()); + public void testGetWifiAddressRangee_keepsLastSeenAddressAndInterface() throws Exception { + WifiManagerWrapper mockWrapper = mock(WifiManagerWrapper.class); + when(mockWrapper.isWifiAPEnabled()).thenReturn(true); + PowerMockito.whenNew(WifiManagerWrapper.class).withAnyArguments().thenReturn(mockWrapper); + + //WifiTethering was switched on + TetheringObservable.setWifiTethering(true, "192.168.40.0/24", "wlan0"); + + assertEquals("192.168.40.0/24", observable.getTetheringState().wifiAddress); + assertEquals("192.168.40.0/24", observable.getTetheringState().lastSeenWifiAddress); + assertEquals("wlan0", observable.getTetheringState().wifiInterface); + assertEquals("wlan0", observable.getTetheringState().lastSeenWifiInterface); + //Wifi tethering was switched off + TetheringObservable.setWifiTethering(true, "", ""); + assertEquals("", observable.getTetheringState().wifiAddress); + assertEquals("192.168.40.0/24", observable.getTetheringState().lastSeenWifiAddress); + assertEquals("", observable.getTetheringState().wifiInterface); + assertEquals("wlan0", observable.getTetheringState().lastSeenWifiInterface); + } + + @Test + public void testGetUsbAddressRange_keepsLastSeenAddressAndInterface() throws Exception { + WifiManagerWrapper mockWrapper = mock(WifiManagerWrapper.class); + when(mockWrapper.isWifiAPEnabled()).thenReturn(true); + PowerMockito.whenNew(WifiManagerWrapper.class).withAnyArguments().thenReturn(mockWrapper); + + //WifiTethering was switched on + TetheringObservable.setWifiTethering(true, "192.168.40.0/24", "rndis0"); + + assertEquals("192.168.40.0/24", observable.getTetheringState().usbAddress); + assertEquals("192.168.40.0/24", observable.getTetheringState().lastSeenUsbAddress); + assertEquals("rndis0", observable.getTetheringState().usbInterface); + assertEquals("rndis0", observable.getTetheringState().lastSeenUsbAddress); + //Wifi tethering was switched off + TetheringObservable.setWifiTethering(true, "", ""); + assertEquals("", observable.getTetheringState().usbAddress); + assertEquals("192.168.40.0/24", observable.getTetheringState().lastSeenUsbAddress); + assertEquals("", observable.getTetheringState().usbInterface); } + } \ No newline at end of file -- cgit v1.2.3 From c0fc9a2daa2ca65fe14cf79445b70cc369aca918 Mon Sep 17 00:00:00 2001 From: cyberta Date: Fri, 31 Jan 2020 11:05:26 -0600 Subject: fix test --- .../bitmaskclient/tethering/TetheringStateManagerTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'app') diff --git a/app/src/test/java/se/leap/bitmaskclient/tethering/TetheringStateManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/tethering/TetheringStateManagerTest.java index 2e6292a6..7aeaf4b9 100644 --- a/app/src/test/java/se/leap/bitmaskclient/tethering/TetheringStateManagerTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/tethering/TetheringStateManagerTest.java @@ -303,18 +303,18 @@ public class TetheringStateManagerTest { when(mockWrapper.isWifiAPEnabled()).thenReturn(true); PowerMockito.whenNew(WifiManagerWrapper.class).withAnyArguments().thenReturn(mockWrapper); - //WifiTethering was switched on - TetheringObservable.setWifiTethering(true, "192.168.40.0/24", "rndis0"); - + //UsbTethering was switched on + TetheringObservable.setUsbTethering(true, "192.168.40.0/24", "rndis0"); assertEquals("192.168.40.0/24", observable.getTetheringState().usbAddress); assertEquals("192.168.40.0/24", observable.getTetheringState().lastSeenUsbAddress); assertEquals("rndis0", observable.getTetheringState().usbInterface); - assertEquals("rndis0", observable.getTetheringState().lastSeenUsbAddress); - //Wifi tethering was switched off - TetheringObservable.setWifiTethering(true, "", ""); + assertEquals("rndis0", observable.getTetheringState().lastSeenUsbInterface); + //UsbTethering tethering was switched off + TetheringObservable.setUsbTethering(true, "", ""); assertEquals("", observable.getTetheringState().usbAddress); assertEquals("192.168.40.0/24", observable.getTetheringState().lastSeenUsbAddress); assertEquals("", observable.getTetheringState().usbInterface); + assertEquals("rndis0", observable.getTetheringState().lastSeenUsbInterface); } -- cgit v1.2.3 From e8b11d68870ed59663541c039418cdca30c6408c Mon Sep 17 00:00:00 2001 From: cyberta Date: Fri, 31 Jan 2020 11:20:42 -0600 Subject: hide bluetooth tethering for now --- .../bitmaskclient/fragments/TetheringDialog.java | 35 +++++++++++++++------- .../tethering/TetheringStateManager.java | 2 +- 2 files changed, 25 insertions(+), 12 deletions(-) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java index 52ed16f7..c53d2a6c 100644 --- a/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java @@ -20,6 +20,23 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +/** + * Copyright (c) 2020 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + import java.util.Observable; import java.util.Observer; @@ -32,10 +49,6 @@ import se.leap.bitmaskclient.tethering.TetheringObservable; import se.leap.bitmaskclient.utils.PreferenceHelper; import se.leap.bitmaskclient.views.IconCheckboxEntry; -/** - * Created by cyberta on 25.02.18. - */ - public class TetheringDialog extends AppCompatDialogFragment implements Observer { public final static String TAG = TetheringDialog.class.getName(); @@ -60,7 +73,7 @@ public class TetheringDialog extends AppCompatDialogFragment implements Observer private ViewModel[] dataSet; private OnItemClickListener clickListener; - public DialogListAdapter(ViewModel[] dataSet, OnItemClickListener clickListener) { + DialogListAdapter(ViewModel[] dataSet, OnItemClickListener clickListener) { this.dataSet = dataSet; this.clickListener = clickListener; } @@ -137,10 +150,10 @@ public class TetheringDialog extends AppCompatDialogFragment implements Observer .setPositiveButton(android.R.string.ok, (dialog, id) -> { PreferenceHelper.allowWifiTethering(getContext(), dataset[0].checked); PreferenceHelper.allowUsbTethering(getContext(), dataset[1].checked); - PreferenceHelper.allowBluetoothTethering(getContext(), dataset[2].checked); +// PreferenceHelper.allowBluetoothTethering(getContext(), dataset[2].checked); TetheringObservable.allowVpnWifiTethering(dataset[0].checked); TetheringObservable.allowVpnUsbTethering(dataset[1].checked); - TetheringObservable.allowVpnBluetoothTethering(dataset[2].checked); +// TetheringObservable.allowVpnBluetoothTethering(dataset[2].checked); FirewallManager firewallManager = new FirewallManager(getContext().getApplicationContext(), false); if (VpnStatus.isVPNActive()) { if (TetheringObservable.getInstance().getTetheringState().hasAnyDeviceTetheringEnabled() && @@ -159,7 +172,7 @@ public class TetheringDialog extends AppCompatDialogFragment implements Observer super.onResume(); dataset[0].enabled = TetheringObservable.getInstance().isWifiTetheringEnabled(); dataset[1].enabled = TetheringObservable.getInstance().isUsbTetheringEnabled(); - dataset[2].enabled = TetheringObservable.getInstance().isBluetoothTetheringEnabled(); +// dataset[2].enabled = TetheringObservable.getInstance().isBluetoothTetheringEnabled(); adapter.notifyDataSetChanged(); TetheringObservable.getInstance().addObserver(this); } @@ -204,10 +217,10 @@ public class TetheringDialog extends AppCompatDialogFragment implements Observer getContext().getString(R.string.tethering_usb), PreferenceHelper.isUsbTetheringAllowed(getContext()), TetheringObservable.getInstance().isUsbTetheringEnabled()), - new DialogListAdapter.ViewModel(getContext().getResources().getDrawable(R.drawable.ic_bluetooth), +/* new DialogListAdapter.ViewModel(getContext().getResources().getDrawable(R.drawable.ic_bluetooth), getContext().getString(R.string.tethering_bluetooth), PreferenceHelper.isBluetoothTetheringAllowed(getContext()), - TetheringObservable.getInstance().isUsbTetheringEnabled()) + TetheringObservable.getInstance().isUsbTetheringEnabled())*/ }; } @@ -218,7 +231,7 @@ public class TetheringDialog extends AppCompatDialogFragment implements Observer Log.d(TAG, "TetheringObservable is updated"); dataset[0].enabled = observable.isWifiTetheringEnabled(); dataset[1].enabled = observable.isUsbTetheringEnabled(); - dataset[2].enabled = observable.isBluetoothTetheringEnabled(); +// dataset[2].enabled = observable.isBluetoothTetheringEnabled(); adapter.notifyDataSetChanged(); } } diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java index 4a266d87..4c6abd5b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java @@ -85,7 +85,7 @@ public class TetheringStateManager { } static void updateBluetoothTetheringState() { - TetheringObservable.setBluetoothTethering(isBluetoothTetheringEnabled()); + //TetheringObservable.setBluetoothTethering(isBluetoothTetheringEnabled()); } private static String getWifiAddressRange() { -- cgit v1.2.3 From d1ced827e19a344cedb84c1aaa530fd613accfec Mon Sep 17 00:00:00 2001 From: cyberta Date: Fri, 31 Jan 2020 11:21:58 -0600 Subject: adapt method visibility in TetheringStateManager --- .../java/se/leap/bitmaskclient/tethering/TetheringStateManager.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java index 4c6abd5b..d3c934f6 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java @@ -131,8 +131,7 @@ public class TetheringStateManager { return getUsbInterface() != null; } - @VisibleForTesting - static String getAddressRange(String interfaceAddress) { + private static String getAddressRange(String interfaceAddress) { if (interfaceAddress.split("\\.").length == 4) { String result = interfaceAddress.substring(0, interfaceAddress.lastIndexOf(".")); result = result + ".0/24"; -- cgit v1.2.3 From 0c80399b8708027770b591764d70c1cc5853b70f Mon Sep 17 00:00:00 2001 From: cyberta Date: Fri, 31 Jan 2020 12:08:09 -0600 Subject: ensure firewall is started after vpn tun interface is up, fix indent --- .../de/blinkt/openvpn/core/OpenVPNService.java | 53 ++++++++++------------ .../bitmaskclient/firewall/FirewallManager.java | 14 ++++-- 2 files changed, 33 insertions(+), 34 deletions(-) (limited to 'app') diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java index 72962493..67d3c4f2 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java +++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java @@ -309,7 +309,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac return START_REDELIVER_INTENT; } - /* TODO: check that for Bitmask */ + /* TODO: check that for Bitmask */ // Always show notification here to avoid problem with startForeground timeout VpnStatus.logInfo(R.string.building_configration); VpnStatus.updateStateString("VPN_GENERATE_CONFIG", "", R.string.building_configration, ConnectionStatus.LEVEL_START); @@ -449,14 +449,12 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac mProcessThread.start(); } - firewallManager.start(); - new Handler(getMainLooper()).post(() -> { - if (mDeviceStateReceiver != null) { - unregisterDeviceStateReceiver(); - } - registerDeviceStateReceiver(mManagement); - } + if (mDeviceStateReceiver != null) { + unregisterDeviceStateReceiver(); + } + registerDeviceStateReceiver(mManagement); + } ); } @@ -968,30 +966,25 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac String channel = NOTIFICATION_CHANNEL_NEWSTATUS_ID; // Display byte count only after being connected - { - if (level == LEVEL_CONNECTED) { - mDisplayBytecount = true; - mConnecttime = System.currentTimeMillis(); - if (!runningOnAndroidTV()) - channel = NOTIFICATION_CHANNEL_BG_ID; - } else { - mDisplayBytecount = false; - } + if (level == LEVEL_CONNECTED) { + mDisplayBytecount = true; + mConnecttime = System.currentTimeMillis(); + if (!runningOnAndroidTV()) + channel = NOTIFICATION_CHANNEL_BG_ID; + firewallManager.start(); + } else { + mDisplayBytecount = false; + } - // Other notifications are shown, - // This also mean we are no longer connected, ignore bytecount messages until next - // CONNECTED - // Does not work :( - notificationManager.buildOpenVpnNotification( - mProfile != null ? mProfile.mName : "", - mProfile != null && mProfile.mUsePluggableTransports, - VpnStatus.getLastCleanLogMessage(this), - VpnStatus.getLastCleanLogMessage(this), - level, - 0, - channel); + notificationManager.buildOpenVpnNotification( + mProfile != null ? mProfile.mName : "", + mProfile != null && mProfile.mUsePluggableTransports, + VpnStatus.getLastCleanLogMessage(this), + VpnStatus.getLastCleanLogMessage(this), + level, + 0, + channel); - } } @Override diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java index 67cc4625..30ecebea 100644 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java @@ -31,6 +31,7 @@ public class FirewallManager implements FirewallCallback, Observer { public static String BITMASK_FORWARD = "bitmask_forward"; public static String BITMASK_POSTROUTING = "bitmask_postrouting"; static final String TAG = FirewallManager.class.getSimpleName(); + private boolean isRunning = false; private Context context; @@ -91,14 +92,19 @@ public class FirewallManager implements FirewallCallback, Observer { public void start() { - startIPv6Firewall(); - TetheringState tetheringState = TetheringObservable.getInstance().getTetheringState(); - if (tetheringState.hasAnyDeviceTetheringEnabled() && tetheringState.hasAnyVpnTetheringAllowed()) { - startTethering(); + if (!isRunning) { + isRunning = true; + startIPv6Firewall(); + TetheringState tetheringState = TetheringObservable.getInstance().getTetheringState(); + if (tetheringState.hasAnyDeviceTetheringEnabled() && tetheringState.hasAnyVpnTetheringAllowed()) { + startTethering(); + } } + } public void stop() { + isRunning = false; stopIPv6Firewall(); stopTethering(); } -- cgit v1.2.3 From 47f463122392963d81c3e12c7ffbb0443bead6ce Mon Sep 17 00:00:00 2001 From: cyberta Date: Fri, 31 Jan 2020 17:59:46 -0600 Subject: add UI in navigation drawer to enable/disable ipv6 firewall and to show/hide experimental features --- .../main/java/se/leap/bitmaskclient/Constants.java | 2 + .../drawer/NavigationDrawerFragment.java | 80 ++++++++++++++++++---- .../bitmaskclient/firewall/FirewallManager.java | 17 +++-- .../leap/bitmaskclient/utils/PreferenceHelper.java | 24 ++++++- app/src/main/res/layout/f_drawer_main.xml | 41 +++++++++-- app/src/main/res/values/strings.xml | 7 +- 6 files changed, 140 insertions(+), 31 deletions(-) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/Constants.java b/app/src/main/java/se/leap/bitmaskclient/Constants.java index 6a065d84..60edc941 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Constants.java +++ b/app/src/main/java/se/leap/bitmaskclient/Constants.java @@ -19,6 +19,8 @@ public interface Constants { String ALLOW_TETHERING_BLUETOOTH = "tethering_bluetooth"; String ALLOW_TETHERING_WIFI = "tethering_wifi"; String ALLOW_TETHERING_USB = "tethering_usb"; + String SHOW_EXPERIMENTAL = "show_experimental"; + String USE_IPv6_FIREWALL = "use_ipv6_firewall"; ////////////////////////////////////////////// diff --git a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java index de24802a..104f1edc 100644 --- a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java @@ -43,6 +43,7 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.widget.TextView; import java.util.Set; @@ -56,6 +57,7 @@ import se.leap.bitmaskclient.ProviderListActivity; import se.leap.bitmaskclient.ProviderObservable; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.eip.EipCommand; +import se.leap.bitmaskclient.firewall.FirewallManager; import se.leap.bitmaskclient.fragments.AboutFragment; import se.leap.bitmaskclient.fragments.AlwaysOnDialog; import se.leap.bitmaskclient.fragments.ExcludeAppsFragment; @@ -82,6 +84,7 @@ import static se.leap.bitmaskclient.utils.PreferenceHelper.getSaveBattery; import static se.leap.bitmaskclient.utils.PreferenceHelper.getShowAlwaysOnDialog; import static se.leap.bitmaskclient.utils.PreferenceHelper.getUsePluggableTransports; import static se.leap.bitmaskclient.utils.PreferenceHelper.saveBattery; +import static se.leap.bitmaskclient.utils.PreferenceHelper.showExperimentalFeatures; import static se.leap.bitmaskclient.utils.PreferenceHelper.usePluggableTransports; /** @@ -110,6 +113,9 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen private Toolbar toolbar; private IconTextEntry account; private IconSwitchEntry saveBattery; + private IconTextEntry tethering; + private IconSwitchEntry firewall; + private View experimentalFeatureFooter; private boolean userLearnedDrawer; private volatile boolean wasPaused; @@ -237,8 +243,11 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen initUseBridgesEntry(); initSaveBatteryEntry(); initAlwaysOnVpnEntry(); - initTetheringEntry(); initExcludeAppsEntry(); + initShowExperimentalHint(); + initTetheringEntry(); + initFirewallEntry(); + initExperimentalFeatureFooter(); initDonateEntry(); initLogEntry(); initAboutEntry(); @@ -323,19 +332,6 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen } } - private void initTetheringEntry() { - IconTextEntry tethering = drawerView.findViewById(R.id.tethering); - if (PreferenceHelper.hasSuPermission(getContext())) { - tethering.setVisibility(VISIBLE); - tethering.setOnClickListener((buttonView) -> { - showTetheringAlert(); - }); - } else { - tethering.setVisibility(GONE); - } - - } - private void initExcludeAppsEntry() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { IconTextEntry excludeApps = drawerView.findViewById(R.id.exclude_apps); @@ -354,6 +350,62 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen } } + private void initShowExperimentalHint() { + TextView textView = drawerLayout.findViewById(R.id.show_experimental_features); + textView.setText(showExperimentalFeatures(getContext()) ? R.string.hide_experimental : R.string.show_experimental); + textView.setOnClickListener(v -> { + boolean shown = showExperimentalFeatures(getContext()); + if (shown) { + tethering.setVisibility(GONE); + firewall.setVisibility(GONE); + experimentalFeatureFooter.setVisibility(GONE); + ((TextView) v).setText(R.string.show_experimental); + } else { + tethering.setVisibility(VISIBLE); + firewall.setVisibility(VISIBLE); + experimentalFeatureFooter.setVisibility(VISIBLE); + ((TextView) v).setText(R.string.hide_experimental); + } + PreferenceHelper.setShowExperimentalFeatures(getContext(), !shown); + }); + } + + private void initFirewallEntry() { + firewall = drawerView.findViewById(R.id.enableIPv6Firewall); + boolean show = showExperimentalFeatures(getContext()); + firewall.setVisibility(show ? VISIBLE : GONE); + firewall.setChecked(PreferenceHelper.useIpv6Firewall(this.getContext())); + firewall.setOnCheckedChangeListener((buttonView, isChecked) -> { + if (!buttonView.isPressed()) { + return; + } + PreferenceHelper.setUseIPv6Firewall(getContext(), isChecked); + FirewallManager firewallManager = new FirewallManager(getContext().getApplicationContext(), false); + if (VpnStatus.isVPNActive()) { + if (isChecked) { + firewallManager.startIPv6Firewall(); + } else { + firewallManager.stopIPv6Firewall(); + } + } + }); + } + + private void initTetheringEntry() { + tethering = drawerView.findViewById(R.id.tethering); + boolean show = showExperimentalFeatures(getContext()); + tethering.setVisibility(show ? VISIBLE : GONE); + tethering.setOnClickListener((buttonView) -> { + showTetheringAlert(); + }); + } + + private void initExperimentalFeatureFooter() { + experimentalFeatureFooter = drawerView.findViewById(R.id.experimental_features_footer); + boolean show = showExperimentalFeatures(getContext()); + experimentalFeatureFooter.setVisibility(show ? VISIBLE : GONE); + } + private void initDonateEntry() { if (ENABLE_DONATION) { IconTextEntry donate = drawerView.findViewById(R.id.donate); diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java index 30ecebea..c148497b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java @@ -94,7 +94,9 @@ public class FirewallManager implements FirewallCallback, Observer { public void start() { if (!isRunning) { isRunning = true; - startIPv6Firewall(); + if (PreferenceHelper.useIpv6Firewall(context)) { + startIPv6Firewall(); + } TetheringState tetheringState = TetheringObservable.getInstance().getTetheringState(); if (tetheringState.hasAnyDeviceTetheringEnabled() && tetheringState.hasAnyVpnTetheringAllowed()) { startTethering(); @@ -105,8 +107,13 @@ public class FirewallManager implements FirewallCallback, Observer { public void stop() { isRunning = false; - stopIPv6Firewall(); - stopTethering(); + if (PreferenceHelper.useIpv6Firewall(context)) { + stopIPv6Firewall(); + } + TetheringState tetheringState = TetheringObservable.getInstance().getTetheringState(); + if (tetheringState.hasAnyDeviceTetheringEnabled() && tetheringState.hasAnyVpnTetheringAllowed()) { + stopTethering(); + } } public void startTethering() { @@ -119,12 +126,12 @@ public class FirewallManager implements FirewallCallback, Observer { task.execute(); } - private void startIPv6Firewall() { + public void startIPv6Firewall() { StartIPv6FirewallTask task = new StartIPv6FirewallTask(this); task.execute(); } - private void stopIPv6Firewall() { + public void stopIPv6Firewall() { ShutdownIPv6FirewallTask task = new ShutdownIPv6FirewallTask(this); task.execute(); } diff --git a/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java b/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java index 073e338c..6f9744bc 100644 --- a/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java @@ -16,6 +16,9 @@ import de.blinkt.openvpn.VpnProfile; import se.leap.bitmaskclient.Provider; import static android.content.Context.MODE_PRIVATE; +import static se.leap.bitmaskclient.Constants.ALLOW_TETHERING_BLUETOOTH; +import static se.leap.bitmaskclient.Constants.ALLOW_TETHERING_USB; +import static se.leap.bitmaskclient.Constants.ALLOW_TETHERING_WIFI; import static se.leap.bitmaskclient.Constants.ALWAYS_ON_SHOW_DIALOG; import static se.leap.bitmaskclient.Constants.DEFAULT_SHARED_PREFS_BATTERY_SAVER; import static se.leap.bitmaskclient.Constants.EXCLUDED_APPS; @@ -25,10 +28,9 @@ import static se.leap.bitmaskclient.Constants.PROVIDER_EIP_DEFINITION; import static se.leap.bitmaskclient.Constants.PROVIDER_PRIVATE_KEY; import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE; import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; +import static se.leap.bitmaskclient.Constants.SHOW_EXPERIMENTAL; import static se.leap.bitmaskclient.Constants.SU_PERMISSION; -import static se.leap.bitmaskclient.Constants.ALLOW_TETHERING_BLUETOOTH; -import static se.leap.bitmaskclient.Constants.ALLOW_TETHERING_USB; -import static se.leap.bitmaskclient.Constants.ALLOW_TETHERING_WIFI; +import static se.leap.bitmaskclient.Constants.USE_IPv6_FIREWALL; import static se.leap.bitmaskclient.Constants.USE_PLUGGABLE_TRANSPORTS; /** @@ -167,6 +169,22 @@ public class PreferenceHelper { return getBoolean(context, ALLOW_TETHERING_BLUETOOTH, false); } + public static void setShowExperimentalFeatures(Context context, boolean show) { + putBoolean(context, SHOW_EXPERIMENTAL, show); + } + + public static boolean showExperimentalFeatures(Context context) { + return getBoolean(context, SHOW_EXPERIMENTAL, false); + } + + public static void setUseIPv6Firewall(Context context, boolean useFirewall) { + putBoolean(context, USE_IPv6_FIREWALL, useFirewall); + } + + public static boolean useIpv6Firewall(Context context) { + return getBoolean(context, USE_IPv6_FIREWALL, false); + } + public static void saveShowAlwaysOnDialog(Context context, boolean showAlwaysOnDialog) { putBoolean(context, ALWAYS_ON_SHOW_DIALOG, showAlwaysOnDialog); } diff --git a/app/src/main/res/layout/f_drawer_main.xml b/app/src/main/res/layout/f_drawer_main.xml index 505bd714..191d547f 100644 --- a/app/src/main/res/layout/f_drawer_main.xml +++ b/app/src/main/res/layout/f_drawer_main.xml @@ -88,29 +88,56 @@ android:visibility="gone" /> - + + + + Always-on VPN Open Android System Settings VPN Hotspot - Share your VPN - Please make sure that you have enabled tethering in the %s first! + Firewall + Requires root permissions + Show experimental options + Hide experimental options + Please make sure to enable tethering in the %s first! system settings Share your VPN with other devices via: Wifi hotspot -- cgit v1.2.3 From 7ebd5e9135709ec6785cb60f4bad02094ea4cb62 Mon Sep 17 00:00:00 2001 From: cyberta Date: Fri, 31 Jan 2020 18:23:12 -0600 Subject: add missing icons --- app/src/main/res/drawable-hdpi/ic_cancel.png | Bin 0 -> 1780 bytes app/src/main/res/drawable-ldpi/ic_cancel.png | Bin 0 -> 967 bytes app/src/main/res/drawable-mdpi/ic_cancel.png | Bin 0 -> 1288 bytes app/src/main/res/drawable-xhdpi/ic_cancel.png | Bin 0 -> 2264 bytes app/src/main/res/drawable-xxhdpi/ic_cancel.png | Bin 0 -> 3290 bytes app/src/main/res/drawable-xxxhdpi/ic_cancel.png | Bin 0 -> 4551 bytes 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/src/main/res/drawable-hdpi/ic_cancel.png create mode 100644 app/src/main/res/drawable-ldpi/ic_cancel.png create mode 100644 app/src/main/res/drawable-mdpi/ic_cancel.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_cancel.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_cancel.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_cancel.png (limited to 'app') diff --git a/app/src/main/res/drawable-hdpi/ic_cancel.png b/app/src/main/res/drawable-hdpi/ic_cancel.png new file mode 100644 index 00000000..579b1dff Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_cancel.png differ diff --git a/app/src/main/res/drawable-ldpi/ic_cancel.png b/app/src/main/res/drawable-ldpi/ic_cancel.png new file mode 100644 index 00000000..a396f18e Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_cancel.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_cancel.png b/app/src/main/res/drawable-mdpi/ic_cancel.png new file mode 100644 index 00000000..125c82f1 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_cancel.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_cancel.png b/app/src/main/res/drawable-xhdpi/ic_cancel.png new file mode 100644 index 00000000..aac09d65 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_cancel.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_cancel.png b/app/src/main/res/drawable-xxhdpi/ic_cancel.png new file mode 100644 index 00000000..1e9bce6c Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_cancel.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_cancel.png b/app/src/main/res/drawable-xxxhdpi/ic_cancel.png new file mode 100644 index 00000000..4ef00efc Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_cancel.png differ -- cgit v1.2.3 From 9a4f9c7ae3cf070ef08bff6abcc0a8d7bae52202 Mon Sep 17 00:00:00 2001 From: cyberta Date: Fri, 31 Jan 2020 19:10:09 -0600 Subject: improve wording for IPv6 Firewall --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9b6f3125..b5fc2fa2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -106,7 +106,7 @@ Always-on VPN Open Android System Settings VPN Hotspot - Firewall + Block IPv6 Requires root permissions Show experimental options Hide experimental options -- cgit v1.2.3 From b8ba423d997f5dbb2541b4f4542a2b6b30400485 Mon Sep 17 00:00:00 2001 From: cyberta Date: Sat, 1 Feb 2020 00:22:31 -0600 Subject: comment out tests for commented out bluetooth tethering code --- .../java/se/leap/bitmaskclient/tethering/TetheringStateManagerTest.java | 2 ++ 1 file changed, 2 insertions(+) (limited to 'app') diff --git a/app/src/test/java/se/leap/bitmaskclient/tethering/TetheringStateManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/tethering/TetheringStateManagerTest.java index 7aeaf4b9..b3ab75ba 100644 --- a/app/src/test/java/se/leap/bitmaskclient/tethering/TetheringStateManagerTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/tethering/TetheringStateManagerTest.java @@ -145,6 +145,7 @@ public class TetheringStateManagerTest { assertFalse(observable.isUsbTetheringEnabled()); } +/* //TODO enable these tests as soon as bluetooth tethering has been enabled again @Test public void updateBluetoothTetheringState_btDeviceFound_returnTrue() throws Exception { WifiManagerWrapper mockWrapper = mock(WifiManagerWrapper.class); @@ -227,6 +228,7 @@ public class TetheringStateManagerTest { TetheringStateManager manager = TetheringStateManager.getInstance(); assertTrue(observable.isBluetoothTetheringEnabled()); } + */ @Test public void updateWifiTetheringState_ignoreFailingWifiAPReflection_keepsOldValueTrue() throws Exception { -- cgit v1.2.3