]> git.ipfire.org Git - thirdparty/strongswan.git/blobdiff - src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileDetailActivity.java
android: Show profile ID at bottom of advanced settings
[thirdparty/strongswan.git] / src / frontends / android / app / src / main / java / org / strongswan / android / ui / VpnProfileDetailActivity.java
index bf64370cf5a135690ff84823a7f0d1c331b17bba..a811244e276614768090197d2d622cafe2b99bd0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2016 Tobias Brunner
+ * Copyright (C) 2012-2017 Tobias Brunner
  * Copyright (C) 2012 Giuliano Grassi
  * Copyright (C) 2012 Ralf Sager
  * HSR Hochschule fuer Technik Rapperswil
@@ -22,6 +22,7 @@ import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.os.AsyncTask;
+import android.os.Build;
 import android.os.Bundle;
 import android.security.KeyChain;
 import android.security.KeyChainAliasCallback;
@@ -36,6 +37,7 @@ import android.text.SpannableString;
 import android.text.Spanned;
 import android.text.TextUtils;
 import android.text.TextWatcher;
+import android.text.method.LinkMovementMethod;
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -53,10 +55,12 @@ import android.widget.EditText;
 import android.widget.MultiAutoCompleteTextView;
 import android.widget.RelativeLayout;
 import android.widget.Spinner;
+import android.widget.Switch;
 import android.widget.TextView;
 
 import org.strongswan.android.R;
 import org.strongswan.android.data.VpnProfile;
+import org.strongswan.android.data.VpnProfile.SelectedAppsHandling;
 import org.strongswan.android.data.VpnProfileDataSource;
 import org.strongswan.android.data.VpnType;
 import org.strongswan.android.data.VpnType.VpnTypeFeature;
@@ -65,13 +69,19 @@ import org.strongswan.android.security.TrustedCertificateEntry;
 import org.strongswan.android.ui.adapter.CertificateIdentitiesAdapter;
 import org.strongswan.android.ui.widget.TextInputLayoutHelper;
 import org.strongswan.android.utils.Constants;
+import org.strongswan.android.utils.IPRangeSet;
+import org.strongswan.android.utils.Utils;
 
 import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.SortedSet;
+import java.util.TreeSet;
 import java.util.UUID;
 
 public class VpnProfileDetailActivity extends AppCompatActivity
 {
        private static final int SELECT_TRUSTED_CERTIFICATE = 0;
+       private static final int SELECT_APPLICATIONS = 1;
 
        private VpnProfileDataSource mDataSource;
        private Long mId;
@@ -81,6 +91,8 @@ public class VpnProfileDetailActivity extends AppCompatActivity
        private String mSelectedUserId;
        private TrustedCertificateEntry mUserCertEntry;
        private VpnType mVpnType = VpnType.IKEV2_EAP;
+       private SelectedAppsHandling mSelectedAppsHandling = SelectedAppsHandling.SELECTED_APPS_DISABLE;
+       private SortedSet<String> mSelectedApps = new TreeSet<>();
        private VpnProfile mProfile;
        private MultiAutoCompleteTextView mName;
        private TextInputLayoutHelper mNameWrap;
@@ -105,8 +117,23 @@ public class VpnProfileDetailActivity extends AppCompatActivity
        private TextInputLayoutHelper mMTUWrap;
        private EditText mPort;
        private TextInputLayoutHelper mPortWrap;
+       private Switch mCertReq;
+       private EditText mNATKeepalive;
+       private TextInputLayoutHelper mNATKeepaliveWrap;
+       private EditText mIncludedSubnets;
+       private TextInputLayoutHelper mIncludedSubnetsWrap;
+       private EditText mExcludedSubnets;
+       private TextInputLayoutHelper mExcludedSubnetsWrap;
        private CheckBox mBlockIPv4;
        private CheckBox mBlockIPv6;
+       private Spinner mSelectSelectedAppsHandling;
+       private RelativeLayout mSelectApps;
+       private TextInputLayoutHelper mIkeProposalWrap;
+       private EditText mIkeProposal;
+       private TextInputLayoutHelper mEspProposalWrap;
+       private EditText mEspProposal;
+       private TextView mProfileIdLabel;
+       private TextView mProfileId;
 
        @Override
        public void onCreate(Bundle savedInstanceState)
@@ -149,9 +176,29 @@ public class VpnProfileDetailActivity extends AppCompatActivity
                mMTUWrap = (TextInputLayoutHelper) findViewById(R.id.mtu_wrap);
                mPort = (EditText)findViewById(R.id.port);
                mPortWrap = (TextInputLayoutHelper) findViewById(R.id.port_wrap);
+               mNATKeepalive = (EditText)findViewById(R.id.nat_keepalive);
+               mNATKeepaliveWrap = (TextInputLayoutHelper) findViewById(R.id.nat_keepalive_wrap);
+               mCertReq = (Switch)findViewById(R.id.cert_req);
+               mIncludedSubnets = (EditText)findViewById(R.id.included_subnets);
+               mIncludedSubnetsWrap = (TextInputLayoutHelper)findViewById(R.id.included_subnets_wrap);
+               mExcludedSubnets = (EditText)findViewById(R.id.excluded_subnets);
+               mExcludedSubnetsWrap = (TextInputLayoutHelper)findViewById(R.id.excluded_subnets_wrap);
                mBlockIPv4 = (CheckBox)findViewById(R.id.split_tunneling_v4);
                mBlockIPv6 = (CheckBox)findViewById(R.id.split_tunneling_v6);
 
+               mSelectSelectedAppsHandling = (Spinner)findViewById(R.id.apps_handling);
+               mSelectApps = (RelativeLayout)findViewById(R.id.select_applications);
+
+               mIkeProposal = (EditText)findViewById(R.id.ike_proposal);
+               mIkeProposalWrap = (TextInputLayoutHelper)findViewById(R.id.ike_proposal_wrap);
+               mEspProposal = (EditText)findViewById(R.id.esp_proposal);
+               mEspProposalWrap = (TextInputLayoutHelper)findViewById(R.id.esp_proposal_wrap);
+               /* make the link clickable */
+               ((TextView)findViewById(R.id.proposal_intro)).setMovementMethod(LinkMovementMethod.getInstance());
+
+               mProfileIdLabel = (TextView)findViewById(R.id.profile_id_label);
+               mProfileId = (TextView)findViewById(R.id.profile_id);
+
                final SpaceTokenizer spaceTokenizer = new SpaceTokenizer();
                mName.setTokenizer(spaceTokenizer);
                mRemoteId.setTokenizer(spaceTokenizer);
@@ -159,6 +206,13 @@ public class VpnProfileDetailActivity extends AppCompatActivity
                mName.setAdapter(completeAdapter);
                mRemoteId.setAdapter(completeAdapter);
 
+               if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
+               {
+                       findViewById(R.id.apps).setVisibility(View.GONE);
+                       mSelectSelectedAppsHandling.setVisibility(View.GONE);
+                       mSelectApps.setVisibility(View.GONE);
+               }
+
                mGateway.addTextChangedListener(new TextWatcher() {
                        @Override
                        public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@@ -256,6 +310,32 @@ public class VpnProfileDetailActivity extends AppCompatActivity
                        }
                });
 
+               mSelectSelectedAppsHandling.setOnItemSelectedListener(new OnItemSelectedListener() {
+                       @Override
+                       public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
+                       {
+                               mSelectedAppsHandling = SelectedAppsHandling.values()[position];
+                               updateAppsSelector();
+                       }
+
+                       @Override
+                       public void onNothingSelected(AdapterView<?> parent)
+                       {       /* should not happen */
+                               mSelectedAppsHandling = SelectedAppsHandling.SELECTED_APPS_DISABLE;
+                               updateAppsSelector();
+                       }
+               });
+
+               mSelectApps.setOnClickListener(new OnClickListener() {
+                       @Override
+                       public void onClick(View v)
+                       {
+                               Intent intent = new Intent(VpnProfileDetailActivity.this, SelectedApplicationsActivity.class);
+                               intent.putExtra(VpnProfileDataSource.KEY_SELECTED_APPS_LIST, new ArrayList<>(mSelectedApps));
+                               startActivityForResult(intent, SELECT_APPLICATIONS);
+                       }
+               });
+
                mId = savedInstanceState == null ? null : savedInstanceState.getLong(VpnProfileDataSource.KEY_ID);
                if (mId == null)
                {
@@ -268,6 +348,7 @@ public class VpnProfileDetailActivity extends AppCompatActivity
                updateCredentialView();
                updateCertificateSelector();
                updateAdvancedSettings();
+               updateAppsSelector();
        }
 
        @Override
@@ -297,6 +378,7 @@ public class VpnProfileDetailActivity extends AppCompatActivity
                {
                        outState.putString(VpnProfileDataSource.KEY_CERTIFICATE, mCertEntry.getAlias());
                }
+               outState.putStringArrayList(VpnProfileDataSource.KEY_SELECTED_APPS_LIST, new ArrayList<>(mSelectedApps));
        }
 
        @Override
@@ -338,6 +420,14 @@ public class VpnProfileDetailActivity extends AppCompatActivity
                                        updateCertificateSelector();
                                }
                                break;
+                       case SELECT_APPLICATIONS:
+                               if (resultCode == RESULT_OK)
+                               {
+                                       ArrayList<String> selection = data.getStringArrayListExtra(VpnProfileDataSource.KEY_SELECTED_APPS_LIST);
+                                       mSelectedApps = new TreeSet<>(selection);
+                                       updateAppsSelector();
+                               }
+                               break;
                        default:
                                super.onActivityResult(requestCode, resultCode, data);
                }
@@ -426,6 +516,40 @@ public class VpnProfileDetailActivity extends AppCompatActivity
                }
        }
 
+       /**
+        * Update the application selection UI
+        */
+       private void updateAppsSelector()
+       {
+               if (mSelectedAppsHandling == SelectedAppsHandling.SELECTED_APPS_DISABLE)
+               {
+                       mSelectApps.setEnabled(false);
+                       mSelectApps.setVisibility(View.GONE);
+
+               }
+               else
+               {
+                       mSelectApps.setEnabled(true);
+                       mSelectApps.setVisibility(View.VISIBLE);
+
+                       ((TextView)mSelectApps.findViewById(android.R.id.text1)).setText(R.string.profile_select_apps);
+                       String selected;
+                       switch (mSelectedApps.size())
+                       {
+                               case 0:
+                                       selected = getString(R.string.profile_select_no_apps);
+                                       break;
+                               case 1:
+                                       selected = getString(R.string.profile_select_one_app);
+                                       break;
+                               default:
+                                       selected = getString(R.string.profile_select_x_apps, mSelectedApps.size());
+                                       break;
+                       }
+                       ((TextView)mSelectApps.findViewById(android.R.id.text2)).setText(selected);
+               }
+       }
+
        /**
         * Update the advanced settings UI depending on whether any advanced
         * settings have already been made.
@@ -435,12 +559,22 @@ public class VpnProfileDetailActivity extends AppCompatActivity
                boolean show = mShowAdvanced.isChecked();
                if (!show && mProfile != null)
                {
-                       Integer st = mProfile.getSplitTunneling();
+                       Integer st = mProfile.getSplitTunneling(), flags = mProfile.getFlags();
                        show = mProfile.getRemoteId() != null || mProfile.getMTU() != null ||
-                                  mProfile.getPort() != null || (st != null && st != 0);
+                                  mProfile.getPort() != null || mProfile.getNATKeepAlive() != null ||
+                                  (flags != null && flags != 0) || (st != null && st != 0) ||
+                                  mProfile.getIncludedSubnets() != null || mProfile.getExcludedSubnets() != null ||
+                                  mProfile.getSelectedAppsHandling() != SelectedAppsHandling.SELECTED_APPS_DISABLE ||
+                                  mProfile.getIkeProposal() != null || mProfile.getEspProposal() != null;
                }
                mShowAdvanced.setVisibility(!show ? View.VISIBLE : View.GONE);
                mAdvancedSettings.setVisibility(show ? View.VISIBLE : View.GONE);
+
+               if (show && mProfile == null)
+               {
+                       mProfileIdLabel.setVisibility(View.GONE);
+                       mProfileId.setVisibility(View.GONE);
+               }
        }
 
        /**
@@ -510,11 +644,37 @@ public class VpnProfileDetailActivity extends AppCompatActivity
                        mMTUWrap.setError(String.format(getString(R.string.alert_text_out_of_range), Constants.MTU_MIN, Constants.MTU_MAX));
                        valid = false;
                }
+               if (!validateSubnets(mIncludedSubnets))
+               {
+                       mIncludedSubnetsWrap.setError(getString(R.string.alert_text_no_subnets));
+                       valid = false;
+               }
+               if (!validateSubnets(mExcludedSubnets))
+               {
+                       mExcludedSubnetsWrap.setError(getString(R.string.alert_text_no_subnets));
+                       valid = false;
+               }
                if (!validateInteger(mPort, 1, 65535))
                {
                        mPortWrap.setError(String.format(getString(R.string.alert_text_out_of_range), 1, 65535));
                        valid = false;
                }
+               if (!validateInteger(mNATKeepalive, Constants.NAT_KEEPALIVE_MIN, Constants.NAT_KEEPALIVE_MAX))
+               {
+                       mNATKeepaliveWrap.setError(String.format(getString(R.string.alert_text_out_of_range),
+                                                                                                        Constants.NAT_KEEPALIVE_MIN, Constants.NAT_KEEPALIVE_MAX));
+                       valid = false;
+               }
+               if (!validateProposal(mIkeProposal, true))
+               {
+                       mIkeProposalWrap.setError(getString(R.string.alert_text_no_proposal));
+                       valid = false;
+               }
+               if (!validateProposal(mEspProposal, false))
+               {
+                       mEspProposalWrap.setError(getString(R.string.alert_text_no_proposal));
+                       valid = false;
+               }
                return valid;
        }
 
@@ -547,10 +707,24 @@ public class VpnProfileDetailActivity extends AppCompatActivity
                mProfile.setRemoteId(remote_id.isEmpty() ? null : remote_id);
                mProfile.setMTU(getInteger(mMTU));
                mProfile.setPort(getInteger(mPort));
+               mProfile.setNATKeepAlive(getInteger(mNATKeepalive));
+               int flags = 0;
+               flags |= !mCertReq.isChecked() ? VpnProfile.FLAGS_SUPPRESS_CERT_REQS : 0;
+               mProfile.setFlags(flags);
+               String included = mIncludedSubnets.getText().toString().trim();
+               mProfile.setIncludedSubnets(included.isEmpty() ? null : included);
+               String excluded = mExcludedSubnets.getText().toString().trim();
+               mProfile.setExcludedSubnets(excluded.isEmpty() ? null : excluded);
                int st = 0;
                st |= mBlockIPv4.isChecked() ? VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4 : 0;
                st |= mBlockIPv6.isChecked() ? VpnProfile.SPLIT_TUNNELING_BLOCK_IPV6 : 0;
                mProfile.setSplitTunneling(st == 0 ? null : st);
+               mProfile.setSelectedAppsHandling(mSelectedAppsHandling);
+               mProfile.setSelectedApps(mSelectedApps);
+               String ike = mIkeProposal.getText().toString().trim();
+               mProfile.setIkeProposal(ike.isEmpty() ? null : ike);
+               String esp = mEspProposal.getText().toString().trim();
+               mProfile.setEspProposal(esp.isEmpty() ? null : esp);
        }
 
        /**
@@ -561,6 +735,7 @@ public class VpnProfileDetailActivity extends AppCompatActivity
        private void loadProfileData(Bundle savedInstanceState)
        {
                String useralias = null, local_id = null, alias = null;
+               Integer flags = null;
 
                getSupportActionBar().setTitle(R.string.add_profile);
                if (mId != null && mId != 0)
@@ -576,8 +751,17 @@ public class VpnProfileDetailActivity extends AppCompatActivity
                                mRemoteId.setText(mProfile.getRemoteId());
                                mMTU.setText(mProfile.getMTU() != null ? mProfile.getMTU().toString() : null);
                                mPort.setText(mProfile.getPort() != null ? mProfile.getPort().toString() : null);
+                               mNATKeepalive.setText(mProfile.getNATKeepAlive() != null ? mProfile.getNATKeepAlive().toString() : null);
+                               mIncludedSubnets.setText(mProfile.getIncludedSubnets());
+                               mExcludedSubnets.setText(mProfile.getExcludedSubnets());
                                mBlockIPv4.setChecked(mProfile.getSplitTunneling() != null && (mProfile.getSplitTunneling() & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4) != 0);
                                mBlockIPv6.setChecked(mProfile.getSplitTunneling() != null && (mProfile.getSplitTunneling() & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV6) != 0);
+                               mSelectedAppsHandling = mProfile.getSelectedAppsHandling();
+                               mSelectedApps = mProfile.getSelectedAppsSet();
+                               mIkeProposal.setText(mProfile.getIkeProposal());
+                               mEspProposal.setText(mProfile.getEspProposal());
+                               mProfileId.setText(mProfile.getUUID().toString());
+                               flags = mProfile.getFlags();
                                useralias = mProfile.getUserCertificateAlias();
                                local_id = mProfile.getLocalId();
                                alias = mProfile.getCertificateAlias();
@@ -592,6 +776,7 @@ public class VpnProfileDetailActivity extends AppCompatActivity
                }
 
                mSelectVpnType.setSelection(mVpnType.ordinal());
+               mCertReq.setChecked(flags == null || (flags & VpnProfile.FLAGS_SUPPRESS_CERT_REQS) == 0);
 
                /* check if the user selected a user certificate previously */
                useralias = savedInstanceState == null ? useralias : savedInstanceState.getString(VpnProfileDataSource.KEY_USER_CERTIFICATE);
@@ -620,6 +805,13 @@ public class VpnProfileDetailActivity extends AppCompatActivity
                                mCertEntry = null;
                        }
                }
+
+               mSelectSelectedAppsHandling.setSelection(mSelectedAppsHandling.ordinal());
+               if (savedInstanceState != null)
+               {
+                       ArrayList<String> selectedApps = savedInstanceState.getStringArrayList(VpnProfileDataSource.KEY_SELECTED_APPS_LIST);
+                       mSelectedApps = new TreeSet<>(selectedApps);
+               }
        }
 
        /**
@@ -665,6 +857,28 @@ public class VpnProfileDetailActivity extends AppCompatActivity
                }
        }
 
+       /**
+        * Check that the value in the given text box is a valid list of subnets/ranges
+        *
+        * @param view text box
+        */
+       private boolean validateSubnets(EditText view)
+       {
+               String value = view.getText().toString().trim();
+               return value.isEmpty() || IPRangeSet.fromString(value) != null;
+       }
+
+       /**
+        * Check that the value in the given text box is a valid proposal
+        *
+        * @param view text box
+        */
+       private boolean validateProposal(EditText view, boolean ike)
+       {
+               String value = view.getText().toString().trim();
+               return value.isEmpty() || Utils.isProposalValid(ike, value);
+       }
+
        private class SelectUserCertOnClickListener implements OnClickListener, KeyChainAliasCallback
        {
                @Override