]>
Commit | Line | Data |
---|---|---|
56a922b2 | 1 | /* |
291ef58c | 2 | * Copyright (C) 2012-2017 Tobias Brunner |
56a922b2 TB |
3 | * Copyright (C) 2012 Giuliano Grassi |
4 | * Copyright (C) 2012 Ralf Sager | |
ea15f20a | 5 | * HSR Hochschule fuer Technik Rapperswil |
56a922b2 TB |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License as published by the | |
9 | * Free Software Foundation; either version 2 of the License, or (at your | |
10 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
14 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 | * for more details. | |
16 | */ | |
17 | ||
18 | package org.strongswan.android.ui; | |
19 | ||
e1a98e79 | 20 | import android.app.Dialog; |
f46da851 | 21 | import android.content.Context; |
fcb54480 | 22 | import android.content.DialogInterface; |
a3e2f127 | 23 | import android.content.Intent; |
f46da851 | 24 | import android.os.AsyncTask; |
98ab7572 | 25 | import android.os.Build; |
56a922b2 | 26 | import android.os.Bundle; |
f46da851 TB |
27 | import android.security.KeyChain; |
28 | import android.security.KeyChainAliasCallback; | |
29 | import android.security.KeyChainException; | |
cf6110f1 | 30 | import android.support.v4.content.LocalBroadcastManager; |
77c1c28d | 31 | import android.support.v7.app.AlertDialog; |
6b318282 | 32 | import android.support.v7.app.AppCompatActivity; |
77c1c28d | 33 | import android.support.v7.app.AppCompatDialogFragment; |
fd23ed8c | 34 | import android.text.Editable; |
e1a98e79 | 35 | import android.text.Html; |
e7a12cc8 TB |
36 | import android.text.SpannableString; |
37 | import android.text.Spanned; | |
fd23ed8c TB |
38 | import android.text.TextUtils; |
39 | import android.text.TextWatcher; | |
2d1f65fe | 40 | import android.text.method.LinkMovementMethod; |
56a922b2 | 41 | import android.util.Log; |
a3e2f127 TB |
42 | import android.view.Menu; |
43 | import android.view.MenuInflater; | |
44 | import android.view.MenuItem; | |
fcb54480 | 45 | import android.view.View; |
4db2d633 | 46 | import android.view.View.OnClickListener; |
825c192d TB |
47 | import android.view.ViewGroup; |
48 | import android.widget.AdapterView; | |
49 | import android.widget.AdapterView.OnItemSelectedListener; | |
e7a12cc8 | 50 | import android.widget.ArrayAdapter; |
fcb54480 TB |
51 | import android.widget.CheckBox; |
52 | import android.widget.CompoundButton; | |
53 | import android.widget.CompoundButton.OnCheckedChangeListener; | |
56a922b2 | 54 | import android.widget.EditText; |
e7a12cc8 | 55 | import android.widget.MultiAutoCompleteTextView; |
d4bf6bfb | 56 | import android.widget.RelativeLayout; |
825c192d | 57 | import android.widget.Spinner; |
8ae7f8b7 | 58 | import android.widget.Switch; |
d4bf6bfb | 59 | import android.widget.TextView; |
56a922b2 | 60 | |
6b318282 TB |
61 | import org.strongswan.android.R; |
62 | import org.strongswan.android.data.VpnProfile; | |
291ef58c | 63 | import org.strongswan.android.data.VpnProfile.SelectedAppsHandling; |
6b318282 TB |
64 | import org.strongswan.android.data.VpnProfileDataSource; |
65 | import org.strongswan.android.data.VpnType; | |
66 | import org.strongswan.android.data.VpnType.VpnTypeFeature; | |
67 | import org.strongswan.android.logic.TrustedCertificateManager; | |
68 | import org.strongswan.android.security.TrustedCertificateEntry; | |
67fa05aa | 69 | import org.strongswan.android.ui.adapter.CertificateIdentitiesAdapter; |
ea15f20a | 70 | import org.strongswan.android.ui.widget.TextInputLayoutHelper; |
cf6110f1 | 71 | import org.strongswan.android.utils.Constants; |
1a63e8e4 | 72 | import org.strongswan.android.utils.IPRangeSet; |
9f962f6c | 73 | import org.strongswan.android.utils.Utils; |
6b318282 TB |
74 | |
75 | import java.security.cert.X509Certificate; | |
291ef58c TB |
76 | import java.util.ArrayList; |
77 | import java.util.SortedSet; | |
78 | import java.util.TreeSet; | |
c4ab9af7 | 79 | import java.util.UUID; |
6b318282 TB |
80 | |
81 | public class VpnProfileDetailActivity extends AppCompatActivity | |
56a922b2 | 82 | { |
4db2d633 | 83 | private static final int SELECT_TRUSTED_CERTIFICATE = 0; |
291ef58c | 84 | private static final int SELECT_APPLICATIONS = 1; |
4db2d633 | 85 | |
56a922b2 TB |
86 | private VpnProfileDataSource mDataSource; |
87 | private Long mId; | |
4db2d633 | 88 | private TrustedCertificateEntry mCertEntry; |
f46da851 | 89 | private String mUserCertLoading; |
67fa05aa TB |
90 | private CertificateIdentitiesAdapter mSelectUserIdAdapter; |
91 | private String mSelectedUserId; | |
f46da851 | 92 | private TrustedCertificateEntry mUserCertEntry; |
825c192d | 93 | private VpnType mVpnType = VpnType.IKEV2_EAP; |
291ef58c TB |
94 | private SelectedAppsHandling mSelectedAppsHandling = SelectedAppsHandling.SELECTED_APPS_DISABLE; |
95 | private SortedSet<String> mSelectedApps = new TreeSet<>(); | |
56a922b2 | 96 | private VpnProfile mProfile; |
e7a12cc8 | 97 | private MultiAutoCompleteTextView mName; |
ea15f20a | 98 | private TextInputLayoutHelper mNameWrap; |
56a922b2 | 99 | private EditText mGateway; |
ea15f20a | 100 | private TextInputLayoutHelper mGatewayWrap; |
825c192d TB |
101 | private Spinner mSelectVpnType; |
102 | private ViewGroup mUsernamePassword; | |
56a922b2 | 103 | private EditText mUsername; |
ea15f20a | 104 | private TextInputLayoutHelper mUsernameWrap; |
56a922b2 | 105 | private EditText mPassword; |
f46da851 | 106 | private ViewGroup mUserCertificate; |
d4bf6bfb | 107 | private RelativeLayout mSelectUserCert; |
67fa05aa | 108 | private Spinner mSelectUserId; |
fcb54480 | 109 | private CheckBox mCheckAuto; |
d4bf6bfb TB |
110 | private RelativeLayout mSelectCert; |
111 | private RelativeLayout mTncNotice; | |
c6822051 TB |
112 | private CheckBox mShowAdvanced; |
113 | private ViewGroup mAdvancedSettings; | |
e7a12cc8 | 114 | private MultiAutoCompleteTextView mRemoteId; |
c5fee223 | 115 | private TextInputLayoutHelper mRemoteIdWrap; |
c6822051 | 116 | private EditText mMTU; |
ea15f20a | 117 | private TextInputLayoutHelper mMTUWrap; |
6c0ec35c | 118 | private EditText mPort; |
ea15f20a | 119 | private TextInputLayoutHelper mPortWrap; |
8ae7f8b7 | 120 | private Switch mCertReq; |
a2aa0ca0 TB |
121 | private EditText mNATKeepalive; |
122 | private TextInputLayoutHelper mNATKeepaliveWrap; | |
05c5e894 TB |
123 | private EditText mIncludedSubnets; |
124 | private TextInputLayoutHelper mIncludedSubnetsWrap; | |
1a63e8e4 TB |
125 | private EditText mExcludedSubnets; |
126 | private TextInputLayoutHelper mExcludedSubnetsWrap; | |
3ee84fa9 TB |
127 | private CheckBox mBlockIPv4; |
128 | private CheckBox mBlockIPv6; | |
291ef58c TB |
129 | private Spinner mSelectSelectedAppsHandling; |
130 | private RelativeLayout mSelectApps; | |
2d1f65fe TB |
131 | private TextInputLayoutHelper mIkeProposalWrap; |
132 | private EditText mIkeProposal; | |
133 | private TextInputLayoutHelper mEspProposalWrap; | |
134 | private EditText mEspProposal; | |
71f4a20a TB |
135 | private TextView mProfileIdLabel; |
136 | private TextView mProfileId; | |
4db2d633 | 137 | |
56a922b2 TB |
138 | @Override |
139 | public void onCreate(Bundle savedInstanceState) | |
140 | { | |
141 | super.onCreate(savedInstanceState); | |
142 | ||
143 | /* the title is set when we load the profile, if any */ | |
6b318282 | 144 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); |
56a922b2 TB |
145 | |
146 | mDataSource = new VpnProfileDataSource(this); | |
147 | mDataSource.open(); | |
148 | ||
149 | setContentView(R.layout.profile_detail_view); | |
150 | ||
e7a12cc8 | 151 | mName = (MultiAutoCompleteTextView)findViewById(R.id.name); |
ea15f20a | 152 | mNameWrap = (TextInputLayoutHelper)findViewById(R.id.name_wrap); |
56a922b2 | 153 | mGateway = (EditText)findViewById(R.id.gateway); |
ea15f20a | 154 | mGatewayWrap = (TextInputLayoutHelper) findViewById(R.id.gateway_wrap); |
825c192d | 155 | mSelectVpnType = (Spinner)findViewById(R.id.vpn_type); |
d4bf6bfb | 156 | mTncNotice = (RelativeLayout)findViewById(R.id.tnc_notice); |
825c192d TB |
157 | |
158 | mUsernamePassword = (ViewGroup)findViewById(R.id.username_password_group); | |
56a922b2 | 159 | mUsername = (EditText)findViewById(R.id.username); |
ea15f20a | 160 | mUsernameWrap = (TextInputLayoutHelper) findViewById(R.id.username_wrap); |
825c192d | 161 | mPassword = (EditText)findViewById(R.id.password); |
56a922b2 | 162 | |
f46da851 | 163 | mUserCertificate = (ViewGroup)findViewById(R.id.user_certificate_group); |
d4bf6bfb | 164 | mSelectUserCert = (RelativeLayout)findViewById(R.id.select_user_certificate); |
67fa05aa | 165 | mSelectUserId = (Spinner)findViewById(R.id.select_user_id); |
f46da851 | 166 | |
fcb54480 | 167 | mCheckAuto = (CheckBox)findViewById(R.id.ca_auto); |
d4bf6bfb | 168 | mSelectCert = (RelativeLayout)findViewById(R.id.select_certificate); |
825c192d | 169 | |
c6822051 TB |
170 | mShowAdvanced = (CheckBox)findViewById(R.id.show_advanced); |
171 | mAdvancedSettings = (ViewGroup)findViewById(R.id.advanced_settings); | |
172 | ||
e7a12cc8 | 173 | mRemoteId = (MultiAutoCompleteTextView)findViewById(R.id.remote_id); |
c5fee223 | 174 | mRemoteIdWrap = (TextInputLayoutHelper) findViewById(R.id.remote_id_wrap); |
c6822051 | 175 | mMTU = (EditText)findViewById(R.id.mtu); |
ea15f20a | 176 | mMTUWrap = (TextInputLayoutHelper) findViewById(R.id.mtu_wrap); |
6c0ec35c | 177 | mPort = (EditText)findViewById(R.id.port); |
ea15f20a | 178 | mPortWrap = (TextInputLayoutHelper) findViewById(R.id.port_wrap); |
a2aa0ca0 TB |
179 | mNATKeepalive = (EditText)findViewById(R.id.nat_keepalive); |
180 | mNATKeepaliveWrap = (TextInputLayoutHelper) findViewById(R.id.nat_keepalive_wrap); | |
8ae7f8b7 | 181 | mCertReq = (Switch)findViewById(R.id.cert_req); |
05c5e894 TB |
182 | mIncludedSubnets = (EditText)findViewById(R.id.included_subnets); |
183 | mIncludedSubnetsWrap = (TextInputLayoutHelper)findViewById(R.id.included_subnets_wrap); | |
1a63e8e4 TB |
184 | mExcludedSubnets = (EditText)findViewById(R.id.excluded_subnets); |
185 | mExcludedSubnetsWrap = (TextInputLayoutHelper)findViewById(R.id.excluded_subnets_wrap); | |
3ee84fa9 TB |
186 | mBlockIPv4 = (CheckBox)findViewById(R.id.split_tunneling_v4); |
187 | mBlockIPv6 = (CheckBox)findViewById(R.id.split_tunneling_v6); | |
c6822051 | 188 | |
291ef58c TB |
189 | mSelectSelectedAppsHandling = (Spinner)findViewById(R.id.apps_handling); |
190 | mSelectApps = (RelativeLayout)findViewById(R.id.select_applications); | |
191 | ||
2d1f65fe TB |
192 | mIkeProposal = (EditText)findViewById(R.id.ike_proposal); |
193 | mIkeProposalWrap = (TextInputLayoutHelper)findViewById(R.id.ike_proposal_wrap); | |
194 | mEspProposal = (EditText)findViewById(R.id.esp_proposal); | |
195 | mEspProposalWrap = (TextInputLayoutHelper)findViewById(R.id.esp_proposal_wrap); | |
196 | /* make the link clickable */ | |
197 | ((TextView)findViewById(R.id.proposal_intro)).setMovementMethod(LinkMovementMethod.getInstance()); | |
198 | ||
71f4a20a TB |
199 | mProfileIdLabel = (TextView)findViewById(R.id.profile_id_label); |
200 | mProfileId = (TextView)findViewById(R.id.profile_id); | |
201 | ||
e7a12cc8 TB |
202 | final SpaceTokenizer spaceTokenizer = new SpaceTokenizer(); |
203 | mName.setTokenizer(spaceTokenizer); | |
204 | mRemoteId.setTokenizer(spaceTokenizer); | |
205 | final ArrayAdapter<String> completeAdapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line); | |
206 | mName.setAdapter(completeAdapter); | |
207 | mRemoteId.setAdapter(completeAdapter); | |
208 | ||
98ab7572 TB |
209 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) |
210 | { | |
211 | findViewById(R.id.apps).setVisibility(View.GONE); | |
212 | mSelectSelectedAppsHandling.setVisibility(View.GONE); | |
213 | mSelectApps.setVisibility(View.GONE); | |
214 | } | |
215 | ||
fd23ed8c TB |
216 | mGateway.addTextChangedListener(new TextWatcher() { |
217 | @Override | |
218 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {} | |
219 | ||
220 | @Override | |
221 | public void onTextChanged(CharSequence s, int start, int before, int count) {} | |
222 | ||
223 | @Override | |
ea15f20a TB |
224 | public void afterTextChanged(Editable s) |
225 | { | |
e7a12cc8 TB |
226 | completeAdapter.clear(); |
227 | completeAdapter.add(mGateway.getText().toString()); | |
fd23ed8c TB |
228 | if (TextUtils.isEmpty(mGateway.getText())) |
229 | { | |
ea15f20a | 230 | mNameWrap.setHelperText(getString(R.string.profile_name_hint)); |
c5fee223 | 231 | mRemoteIdWrap.setHelperText(getString(R.string.profile_remote_id_hint)); |
fd23ed8c TB |
232 | } |
233 | else | |
234 | { | |
ea15f20a | 235 | mNameWrap.setHelperText(String.format(getString(R.string.profile_name_hint_gateway), mGateway.getText())); |
c5fee223 | 236 | mRemoteIdWrap.setHelperText(String.format(getString(R.string.profile_remote_id_hint_gateway), mGateway.getText())); |
fd23ed8c TB |
237 | } |
238 | } | |
239 | }); | |
240 | ||
825c192d TB |
241 | mSelectVpnType.setOnItemSelectedListener(new OnItemSelectedListener() { |
242 | @Override | |
243 | public void onItemSelected(AdapterView<?> parent, View view, int position, long id) | |
244 | { | |
245 | mVpnType = VpnType.values()[position]; | |
f46da851 | 246 | updateCredentialView(); |
825c192d TB |
247 | } |
248 | ||
249 | @Override | |
250 | public void onNothingSelected(AdapterView<?> parent) | |
251 | { /* should not happen */ | |
252 | mVpnType = VpnType.IKEV2_EAP; | |
f46da851 | 253 | updateCredentialView(); |
825c192d TB |
254 | } |
255 | }); | |
256 | ||
d4bf6bfb TB |
257 | ((TextView)mTncNotice.findViewById(android.R.id.text1)).setText(R.string.tnc_notice_title); |
258 | ((TextView)mTncNotice.findViewById(android.R.id.text2)).setText(R.string.tnc_notice_subtitle); | |
e1a98e79 TB |
259 | mTncNotice.setOnClickListener(new OnClickListener() { |
260 | @Override | |
261 | public void onClick(View v) | |
262 | { | |
77c1c28d | 263 | new TncNoticeDialog().show(VpnProfileDetailActivity.this.getSupportFragmentManager(), "TncNotice"); |
e1a98e79 TB |
264 | } |
265 | }); | |
266 | ||
f46da851 | 267 | mSelectUserCert.setOnClickListener(new SelectUserCertOnClickListener()); |
67fa05aa TB |
268 | mSelectUserIdAdapter = new CertificateIdentitiesAdapter(this); |
269 | mSelectUserId.setAdapter(mSelectUserIdAdapter); | |
270 | mSelectUserId.setOnItemSelectedListener(new OnItemSelectedListener() { | |
271 | @Override | |
272 | public void onItemSelected(AdapterView<?> parent, View view, int position, long id) | |
273 | { | |
274 | if (mUserCertEntry != null) | |
275 | { /* we don't store the subject DN as it is in the reverse order and the default anyway */ | |
276 | mSelectedUserId = position == 0 ? null : mSelectUserIdAdapter.getItem(position); | |
277 | } | |
278 | } | |
279 | ||
280 | @Override | |
281 | public void onNothingSelected(AdapterView<?> parent) | |
282 | { | |
283 | mSelectedUserId = null; | |
284 | } | |
285 | }); | |
f46da851 | 286 | |
fcb54480 TB |
287 | mCheckAuto.setOnCheckedChangeListener(new OnCheckedChangeListener() { |
288 | @Override | |
289 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) | |
290 | { | |
4db2d633 TB |
291 | updateCertificateSelector(); |
292 | } | |
293 | }); | |
fcb54480 | 294 | |
4db2d633 TB |
295 | mSelectCert.setOnClickListener(new OnClickListener() { |
296 | @Override | |
297 | public void onClick(View v) | |
298 | { | |
299 | Intent intent = new Intent(VpnProfileDetailActivity.this, TrustedCertificatesActivity.class); | |
9c841b1f | 300 | intent.setAction(TrustedCertificatesActivity.SELECT_CERTIFICATE); |
4db2d633 | 301 | startActivityForResult(intent, SELECT_TRUSTED_CERTIFICATE); |
fcb54480 TB |
302 | } |
303 | }); | |
304 | ||
c6822051 TB |
305 | mShowAdvanced.setOnCheckedChangeListener(new OnCheckedChangeListener() { |
306 | @Override | |
307 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) | |
308 | { | |
309 | updateAdvancedSettings(); | |
310 | } | |
311 | }); | |
312 | ||
291ef58c TB |
313 | mSelectSelectedAppsHandling.setOnItemSelectedListener(new OnItemSelectedListener() { |
314 | @Override | |
315 | public void onItemSelected(AdapterView<?> parent, View view, int position, long id) | |
316 | { | |
317 | mSelectedAppsHandling = SelectedAppsHandling.values()[position]; | |
318 | updateAppsSelector(); | |
319 | } | |
320 | ||
321 | @Override | |
322 | public void onNothingSelected(AdapterView<?> parent) | |
323 | { /* should not happen */ | |
324 | mSelectedAppsHandling = SelectedAppsHandling.SELECTED_APPS_DISABLE; | |
325 | updateAppsSelector(); | |
326 | } | |
327 | }); | |
328 | ||
329 | mSelectApps.setOnClickListener(new OnClickListener() { | |
330 | @Override | |
331 | public void onClick(View v) | |
332 | { | |
333 | Intent intent = new Intent(VpnProfileDetailActivity.this, SelectedApplicationsActivity.class); | |
334 | intent.putExtra(VpnProfileDataSource.KEY_SELECTED_APPS_LIST, new ArrayList<>(mSelectedApps)); | |
335 | startActivityForResult(intent, SELECT_APPLICATIONS); | |
336 | } | |
337 | }); | |
338 | ||
56a922b2 TB |
339 | mId = savedInstanceState == null ? null : savedInstanceState.getLong(VpnProfileDataSource.KEY_ID); |
340 | if (mId == null) | |
341 | { | |
342 | Bundle extras = getIntent().getExtras(); | |
343 | mId = extras == null ? null : extras.getLong(VpnProfileDataSource.KEY_ID); | |
344 | } | |
345 | ||
4db2d633 | 346 | loadProfileData(savedInstanceState); |
fcb54480 | 347 | |
f46da851 | 348 | updateCredentialView(); |
4db2d633 | 349 | updateCertificateSelector(); |
c6822051 | 350 | updateAdvancedSettings(); |
291ef58c | 351 | updateAppsSelector(); |
56a922b2 TB |
352 | } |
353 | ||
354 | @Override | |
355 | protected void onDestroy() | |
356 | { | |
357 | super.onDestroy(); | |
358 | mDataSource.close(); | |
359 | } | |
360 | ||
361 | @Override | |
362 | protected void onSaveInstanceState(Bundle outState) | |
363 | { | |
364 | super.onSaveInstanceState(outState); | |
cb431e12 TB |
365 | if (mId != null) |
366 | { | |
367 | outState.putLong(VpnProfileDataSource.KEY_ID, mId); | |
368 | } | |
f46da851 TB |
369 | if (mUserCertEntry != null) |
370 | { | |
371 | outState.putString(VpnProfileDataSource.KEY_USER_CERTIFICATE, mUserCertEntry.getAlias()); | |
372 | } | |
67fa05aa TB |
373 | if (mSelectedUserId != null) |
374 | { | |
375 | outState.putString(VpnProfileDataSource.KEY_LOCAL_ID, mSelectedUserId); | |
376 | } | |
4db2d633 TB |
377 | if (mCertEntry != null) |
378 | { | |
379 | outState.putString(VpnProfileDataSource.KEY_CERTIFICATE, mCertEntry.getAlias()); | |
380 | } | |
291ef58c | 381 | outState.putStringArrayList(VpnProfileDataSource.KEY_SELECTED_APPS_LIST, new ArrayList<>(mSelectedApps)); |
56a922b2 TB |
382 | } |
383 | ||
a3e2f127 TB |
384 | @Override |
385 | public boolean onCreateOptionsMenu(Menu menu) | |
386 | { | |
387 | MenuInflater inflater = getMenuInflater(); | |
388 | inflater.inflate(R.menu.profile_edit, menu); | |
389 | return true; | |
390 | } | |
391 | ||
392 | @Override | |
393 | public boolean onOptionsItemSelected(MenuItem item) | |
394 | { | |
395 | switch (item.getItemId()) | |
396 | { | |
397 | case android.R.id.home: | |
398 | case R.id.menu_cancel: | |
399 | finish(); | |
400 | return true; | |
401 | case R.id.menu_accept: | |
402 | saveProfile(); | |
403 | return true; | |
404 | default: | |
405 | return super.onOptionsItemSelected(item); | |
406 | } | |
407 | } | |
408 | ||
4db2d633 TB |
409 | @Override |
410 | protected void onActivityResult(int requestCode, int resultCode, Intent data) | |
411 | { | |
412 | switch (requestCode) | |
413 | { | |
414 | case SELECT_TRUSTED_CERTIFICATE: | |
415 | if (resultCode == RESULT_OK) | |
416 | { | |
417 | String alias = data.getStringExtra(VpnProfileDataSource.KEY_CERTIFICATE); | |
418 | X509Certificate certificate = TrustedCertificateManager.getInstance().getCACertificateFromAlias(alias); | |
419 | mCertEntry = certificate == null ? null : new TrustedCertificateEntry(alias, certificate); | |
420 | updateCertificateSelector(); | |
421 | } | |
422 | break; | |
291ef58c TB |
423 | case SELECT_APPLICATIONS: |
424 | if (resultCode == RESULT_OK) | |
425 | { | |
426 | ArrayList<String> selection = data.getStringArrayListExtra(VpnProfileDataSource.KEY_SELECTED_APPS_LIST); | |
427 | mSelectedApps = new TreeSet<>(selection); | |
428 | updateAppsSelector(); | |
429 | } | |
430 | break; | |
4db2d633 TB |
431 | default: |
432 | super.onActivityResult(requestCode, resultCode, data); | |
433 | } | |
434 | } | |
435 | ||
825c192d | 436 | /** |
f46da851 | 437 | * Update the UI to enter credentials depending on the type of VPN currently selected |
825c192d | 438 | */ |
f46da851 | 439 | private void updateCredentialView() |
825c192d | 440 | { |
a6408973 TB |
441 | mUsernamePassword.setVisibility(mVpnType.has(VpnTypeFeature.USER_PASS) ? View.VISIBLE : View.GONE); |
442 | mUserCertificate.setVisibility(mVpnType.has(VpnTypeFeature.CERTIFICATE) ? View.VISIBLE : View.GONE); | |
443 | mTncNotice.setVisibility(mVpnType.has(VpnTypeFeature.BYOD) ? View.VISIBLE : View.GONE); | |
f46da851 | 444 | |
a6408973 | 445 | if (mVpnType.has(VpnTypeFeature.CERTIFICATE)) |
f46da851 | 446 | { |
67fa05aa | 447 | mSelectUserId.setEnabled(false); |
f46da851 TB |
448 | if (mUserCertLoading != null) |
449 | { | |
d4bf6bfb TB |
450 | ((TextView)mSelectUserCert.findViewById(android.R.id.text1)).setText(mUserCertLoading); |
451 | ((TextView)mSelectUserCert.findViewById(android.R.id.text2)).setText(R.string.loading); | |
f46da851 TB |
452 | } |
453 | else if (mUserCertEntry != null) | |
454 | { /* clear any errors and set the new data */ | |
d4bf6bfb TB |
455 | ((TextView)mSelectUserCert.findViewById(android.R.id.text1)).setError(null); |
456 | ((TextView)mSelectUserCert.findViewById(android.R.id.text1)).setText(mUserCertEntry.getAlias()); | |
457 | ((TextView)mSelectUserCert.findViewById(android.R.id.text2)).setText(mUserCertEntry.getCertificate().getSubjectDN().toString()); | |
67fa05aa TB |
458 | mSelectUserIdAdapter.setCertificate(mUserCertEntry); |
459 | mSelectUserId.setSelection(mSelectUserIdAdapter.getPosition(mSelectedUserId)); | |
460 | mSelectUserId.setEnabled(true); | |
f46da851 TB |
461 | } |
462 | else | |
463 | { | |
d4bf6bfb TB |
464 | ((TextView)mSelectUserCert.findViewById(android.R.id.text1)).setText(R.string.profile_user_select_certificate_label); |
465 | ((TextView)mSelectUserCert.findViewById(android.R.id.text2)).setText(R.string.profile_user_select_certificate); | |
67fa05aa | 466 | mSelectUserIdAdapter.setCertificate(null); |
f46da851 TB |
467 | } |
468 | } | |
825c192d TB |
469 | } |
470 | ||
fcb54480 TB |
471 | /** |
472 | * Show an alert in case the previously selected certificate is not found anymore | |
473 | * or the user did not select a certificate in the spinner. | |
474 | */ | |
475 | private void showCertificateAlert() | |
476 | { | |
477 | AlertDialog.Builder adb = new AlertDialog.Builder(VpnProfileDetailActivity.this); | |
478 | adb.setTitle(R.string.alert_text_nocertfound_title); | |
479 | adb.setMessage(R.string.alert_text_nocertfound); | |
480 | adb.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { | |
481 | @Override | |
482 | public void onClick(DialogInterface dialog, int id) | |
483 | { | |
484 | dialog.cancel(); | |
485 | } | |
486 | }); | |
487 | adb.show(); | |
488 | } | |
489 | ||
490 | /** | |
4db2d633 TB |
491 | * Update the CA certificate selection UI depending on whether the |
492 | * certificate should be automatically selected or not. | |
fcb54480 | 493 | */ |
4db2d633 | 494 | private void updateCertificateSelector() |
fcb54480 | 495 | { |
4db2d633 | 496 | if (!mCheckAuto.isChecked()) |
fcb54480 | 497 | { |
4db2d633 TB |
498 | mSelectCert.setEnabled(true); |
499 | mSelectCert.setVisibility(View.VISIBLE); | |
fcb54480 | 500 | |
4db2d633 TB |
501 | if (mCertEntry != null) |
502 | { | |
d4bf6bfb TB |
503 | ((TextView)mSelectCert.findViewById(android.R.id.text1)).setText(mCertEntry.getSubjectPrimary()); |
504 | ((TextView)mSelectCert.findViewById(android.R.id.text2)).setText(mCertEntry.getSubjectSecondary()); | |
4db2d633 TB |
505 | } |
506 | else | |
507 | { | |
d4bf6bfb TB |
508 | ((TextView)mSelectCert.findViewById(android.R.id.text1)).setText(R.string.profile_ca_select_certificate_label); |
509 | ((TextView)mSelectCert.findViewById(android.R.id.text2)).setText(R.string.profile_ca_select_certificate); | |
4db2d633 | 510 | } |
fcb54480 | 511 | } |
4db2d633 | 512 | else |
fcb54480 | 513 | { |
4db2d633 TB |
514 | mSelectCert.setEnabled(false); |
515 | mSelectCert.setVisibility(View.GONE); | |
fcb54480 TB |
516 | } |
517 | } | |
518 | ||
291ef58c TB |
519 | /** |
520 | * Update the application selection UI | |
521 | */ | |
522 | private void updateAppsSelector() | |
523 | { | |
524 | if (mSelectedAppsHandling == SelectedAppsHandling.SELECTED_APPS_DISABLE) | |
525 | { | |
526 | mSelectApps.setEnabled(false); | |
527 | mSelectApps.setVisibility(View.GONE); | |
528 | ||
529 | } | |
530 | else | |
531 | { | |
532 | mSelectApps.setEnabled(true); | |
533 | mSelectApps.setVisibility(View.VISIBLE); | |
534 | ||
535 | ((TextView)mSelectApps.findViewById(android.R.id.text1)).setText(R.string.profile_select_apps); | |
536 | String selected; | |
537 | switch (mSelectedApps.size()) | |
538 | { | |
539 | case 0: | |
540 | selected = getString(R.string.profile_select_no_apps); | |
541 | break; | |
542 | case 1: | |
543 | selected = getString(R.string.profile_select_one_app); | |
544 | break; | |
545 | default: | |
546 | selected = getString(R.string.profile_select_x_apps, mSelectedApps.size()); | |
547 | break; | |
548 | } | |
549 | ((TextView)mSelectApps.findViewById(android.R.id.text2)).setText(selected); | |
550 | } | |
551 | } | |
552 | ||
c6822051 TB |
553 | /** |
554 | * Update the advanced settings UI depending on whether any advanced | |
555 | * settings have already been made. | |
556 | */ | |
557 | private void updateAdvancedSettings() | |
558 | { | |
559 | boolean show = mShowAdvanced.isChecked(); | |
560 | if (!show && mProfile != null) | |
561 | { | |
8ae7f8b7 | 562 | Integer st = mProfile.getSplitTunneling(), flags = mProfile.getFlags(); |
c5fee223 | 563 | show = mProfile.getRemoteId() != null || mProfile.getMTU() != null || |
8ae7f8b7 TB |
564 | mProfile.getPort() != null || mProfile.getNATKeepAlive() != null || |
565 | (flags != null && flags != 0) || (st != null && st != 0) || | |
291ef58c | 566 | mProfile.getIncludedSubnets() != null || mProfile.getExcludedSubnets() != null || |
2d1f65fe TB |
567 | mProfile.getSelectedAppsHandling() != SelectedAppsHandling.SELECTED_APPS_DISABLE || |
568 | mProfile.getIkeProposal() != null || mProfile.getEspProposal() != null; | |
c6822051 TB |
569 | } |
570 | mShowAdvanced.setVisibility(!show ? View.VISIBLE : View.GONE); | |
571 | mAdvancedSettings.setVisibility(show ? View.VISIBLE : View.GONE); | |
71f4a20a TB |
572 | |
573 | if (show && mProfile == null) | |
574 | { | |
575 | mProfileIdLabel.setVisibility(View.GONE); | |
576 | mProfileId.setVisibility(View.GONE); | |
577 | } | |
c6822051 TB |
578 | } |
579 | ||
a3e2f127 TB |
580 | /** |
581 | * Save or update the profile depending on whether we actually have a | |
582 | * profile object or not (this was created in updateProfileData) | |
583 | */ | |
584 | private void saveProfile() | |
585 | { | |
586 | if (verifyInput()) | |
587 | { | |
588 | if (mProfile != null) | |
589 | { | |
590 | updateProfileData(); | |
c4ab9af7 TB |
591 | if (mProfile.getUUID() == null) |
592 | { | |
593 | mProfile.setUUID(UUID.randomUUID()); | |
594 | } | |
a3e2f127 TB |
595 | mDataSource.updateVpnProfile(mProfile); |
596 | } | |
597 | else | |
598 | { | |
599 | mProfile = new VpnProfile(); | |
600 | updateProfileData(); | |
601 | mDataSource.insertProfile(mProfile); | |
602 | } | |
cf6110f1 TB |
603 | Intent intent = new Intent(Constants.VPN_PROFILES_CHANGED); |
604 | intent.putExtra(Constants.VPN_PROFILES_SINGLE, mProfile.getId()); | |
605 | LocalBroadcastManager.getInstance(this).sendBroadcast(intent); | |
606 | ||
a3e2f127 TB |
607 | setResult(RESULT_OK, new Intent().putExtra(VpnProfileDataSource.KEY_ID, mProfile.getId())); |
608 | finish(); | |
609 | } | |
610 | } | |
611 | ||
612 | /** | |
613 | * Verify the user input and display error messages. | |
614 | * @return true if the input is valid | |
615 | */ | |
616 | private boolean verifyInput() | |
617 | { | |
618 | boolean valid = true; | |
619 | if (mGateway.getText().toString().trim().isEmpty()) | |
620 | { | |
ea15f20a | 621 | mGatewayWrap.setError(getString(R.string.alert_text_no_input_gateway)); |
a3e2f127 TB |
622 | valid = false; |
623 | } | |
a6408973 | 624 | if (mVpnType.has(VpnTypeFeature.USER_PASS)) |
a3e2f127 | 625 | { |
825c192d TB |
626 | if (mUsername.getText().toString().trim().isEmpty()) |
627 | { | |
ea15f20a | 628 | mUsernameWrap.setError(getString(R.string.alert_text_no_input_username)); |
825c192d TB |
629 | valid = false; |
630 | } | |
a3e2f127 | 631 | } |
a6408973 | 632 | if (mVpnType.has(VpnTypeFeature.CERTIFICATE) && mUserCertEntry == null) |
f46da851 | 633 | { /* let's show an error icon */ |
d4bf6bfb | 634 | ((TextView)mSelectUserCert.findViewById(android.R.id.text1)).setError(""); |
f46da851 TB |
635 | valid = false; |
636 | } | |
4db2d633 | 637 | if (!mCheckAuto.isChecked() && mCertEntry == null) |
fcb54480 TB |
638 | { |
639 | showCertificateAlert(); | |
640 | valid = false; | |
641 | } | |
cf6110f1 | 642 | if (!validateInteger(mMTU, Constants.MTU_MIN, Constants.MTU_MAX)) |
c6822051 | 643 | { |
cf6110f1 | 644 | mMTUWrap.setError(String.format(getString(R.string.alert_text_out_of_range), Constants.MTU_MIN, Constants.MTU_MAX)); |
c6822051 TB |
645 | valid = false; |
646 | } | |
05c5e894 TB |
647 | if (!validateSubnets(mIncludedSubnets)) |
648 | { | |
649 | mIncludedSubnetsWrap.setError(getString(R.string.alert_text_no_subnets)); | |
650 | valid = false; | |
651 | } | |
1a63e8e4 TB |
652 | if (!validateSubnets(mExcludedSubnets)) |
653 | { | |
654 | mExcludedSubnetsWrap.setError(getString(R.string.alert_text_no_subnets)); | |
655 | valid = false; | |
656 | } | |
ef2ad9db | 657 | if (!validateInteger(mPort, 1, 65535)) |
6c0ec35c | 658 | { |
ea15f20a | 659 | mPortWrap.setError(String.format(getString(R.string.alert_text_out_of_range), 1, 65535)); |
6c0ec35c TB |
660 | valid = false; |
661 | } | |
a2aa0ca0 TB |
662 | if (!validateInteger(mNATKeepalive, Constants.NAT_KEEPALIVE_MIN, Constants.NAT_KEEPALIVE_MAX)) |
663 | { | |
664 | mNATKeepaliveWrap.setError(String.format(getString(R.string.alert_text_out_of_range), | |
665 | Constants.NAT_KEEPALIVE_MIN, Constants.NAT_KEEPALIVE_MAX)); | |
666 | valid = false; | |
667 | } | |
9f962f6c TB |
668 | if (!validateProposal(mIkeProposal, true)) |
669 | { | |
670 | mIkeProposalWrap.setError(getString(R.string.alert_text_no_proposal)); | |
671 | valid = false; | |
672 | } | |
673 | if (!validateProposal(mEspProposal, false)) | |
674 | { | |
675 | mEspProposalWrap.setError(getString(R.string.alert_text_no_proposal)); | |
676 | valid = false; | |
677 | } | |
a3e2f127 TB |
678 | return valid; |
679 | } | |
680 | ||
681 | /** | |
682 | * Update the profile object with the data entered by the user | |
683 | */ | |
684 | private void updateProfileData() | |
685 | { | |
686 | /* the name is optional, we default to the gateway if none is given */ | |
687 | String name = mName.getText().toString().trim(); | |
688 | String gateway = mGateway.getText().toString().trim(); | |
689 | mProfile.setName(name.isEmpty() ? gateway : name); | |
690 | mProfile.setGateway(gateway); | |
825c192d | 691 | mProfile.setVpnType(mVpnType); |
a6408973 | 692 | if (mVpnType.has(VpnTypeFeature.USER_PASS)) |
825c192d TB |
693 | { |
694 | mProfile.setUsername(mUsername.getText().toString().trim()); | |
695 | String password = mPassword.getText().toString().trim(); | |
696 | password = password.isEmpty() ? null : password; | |
697 | mProfile.setPassword(password); | |
698 | } | |
a6408973 | 699 | if (mVpnType.has(VpnTypeFeature.CERTIFICATE)) |
f46da851 TB |
700 | { |
701 | mProfile.setUserCertificateAlias(mUserCertEntry.getAlias()); | |
67fa05aa | 702 | mProfile.setLocalId(mSelectedUserId); |
f46da851 | 703 | } |
4db2d633 TB |
704 | String certAlias = mCheckAuto.isChecked() ? null : mCertEntry.getAlias(); |
705 | mProfile.setCertificateAlias(certAlias); | |
c5fee223 TB |
706 | String remote_id = mRemoteId.getText().toString().trim(); |
707 | mProfile.setRemoteId(remote_id.isEmpty() ? null : remote_id); | |
c6822051 | 708 | mProfile.setMTU(getInteger(mMTU)); |
6c0ec35c | 709 | mProfile.setPort(getInteger(mPort)); |
a2aa0ca0 | 710 | mProfile.setNATKeepAlive(getInteger(mNATKeepalive)); |
8ae7f8b7 TB |
711 | int flags = 0; |
712 | flags |= !mCertReq.isChecked() ? VpnProfile.FLAGS_SUPPRESS_CERT_REQS : 0; | |
713 | mProfile.setFlags(flags); | |
05c5e894 TB |
714 | String included = mIncludedSubnets.getText().toString().trim(); |
715 | mProfile.setIncludedSubnets(included.isEmpty() ? null : included); | |
1a63e8e4 TB |
716 | String excluded = mExcludedSubnets.getText().toString().trim(); |
717 | mProfile.setExcludedSubnets(excluded.isEmpty() ? null : excluded); | |
3ee84fa9 TB |
718 | int st = 0; |
719 | st |= mBlockIPv4.isChecked() ? VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4 : 0; | |
720 | st |= mBlockIPv6.isChecked() ? VpnProfile.SPLIT_TUNNELING_BLOCK_IPV6 : 0; | |
721 | mProfile.setSplitTunneling(st == 0 ? null : st); | |
291ef58c TB |
722 | mProfile.setSelectedAppsHandling(mSelectedAppsHandling); |
723 | mProfile.setSelectedApps(mSelectedApps); | |
2d1f65fe TB |
724 | String ike = mIkeProposal.getText().toString().trim(); |
725 | mProfile.setIkeProposal(ike.isEmpty() ? null : ike); | |
726 | String esp = mEspProposal.getText().toString().trim(); | |
727 | mProfile.setEspProposal(esp.isEmpty() ? null : esp); | |
a3e2f127 TB |
728 | } |
729 | ||
56a922b2 TB |
730 | /** |
731 | * Load an existing profile if we got an ID | |
4db2d633 TB |
732 | * |
733 | * @param savedInstanceState previously saved state | |
56a922b2 | 734 | */ |
4db2d633 | 735 | private void loadProfileData(Bundle savedInstanceState) |
56a922b2 | 736 | { |
67fa05aa | 737 | String useralias = null, local_id = null, alias = null; |
8ae7f8b7 | 738 | Integer flags = null; |
4db2d633 | 739 | |
6b318282 | 740 | getSupportActionBar().setTitle(R.string.add_profile); |
7fedacb2 | 741 | if (mId != null && mId != 0) |
56a922b2 TB |
742 | { |
743 | mProfile = mDataSource.getVpnProfile(mId); | |
744 | if (mProfile != null) | |
745 | { | |
746 | mName.setText(mProfile.getName()); | |
747 | mGateway.setText(mProfile.getGateway()); | |
825c192d | 748 | mVpnType = mProfile.getVpnType(); |
56a922b2 TB |
749 | mUsername.setText(mProfile.getUsername()); |
750 | mPassword.setText(mProfile.getPassword()); | |
c5fee223 | 751 | mRemoteId.setText(mProfile.getRemoteId()); |
c6822051 | 752 | mMTU.setText(mProfile.getMTU() != null ? mProfile.getMTU().toString() : null); |
6c0ec35c | 753 | mPort.setText(mProfile.getPort() != null ? mProfile.getPort().toString() : null); |
a2aa0ca0 | 754 | mNATKeepalive.setText(mProfile.getNATKeepAlive() != null ? mProfile.getNATKeepAlive().toString() : null); |
05c5e894 | 755 | mIncludedSubnets.setText(mProfile.getIncludedSubnets()); |
1a63e8e4 | 756 | mExcludedSubnets.setText(mProfile.getExcludedSubnets()); |
cf6110f1 TB |
757 | mBlockIPv4.setChecked(mProfile.getSplitTunneling() != null && (mProfile.getSplitTunneling() & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4) != 0); |
758 | mBlockIPv6.setChecked(mProfile.getSplitTunneling() != null && (mProfile.getSplitTunneling() & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV6) != 0); | |
291ef58c TB |
759 | mSelectedAppsHandling = mProfile.getSelectedAppsHandling(); |
760 | mSelectedApps = mProfile.getSelectedAppsSet(); | |
2d1f65fe TB |
761 | mIkeProposal.setText(mProfile.getIkeProposal()); |
762 | mEspProposal.setText(mProfile.getEspProposal()); | |
71f4a20a | 763 | mProfileId.setText(mProfile.getUUID().toString()); |
8ae7f8b7 | 764 | flags = mProfile.getFlags(); |
f46da851 | 765 | useralias = mProfile.getUserCertificateAlias(); |
67fa05aa | 766 | local_id = mProfile.getLocalId(); |
4db2d633 | 767 | alias = mProfile.getCertificateAlias(); |
6b318282 | 768 | getSupportActionBar().setTitle(mProfile.getName()); |
56a922b2 TB |
769 | } |
770 | else | |
771 | { | |
772 | Log.e(VpnProfileDetailActivity.class.getSimpleName(), | |
773 | "VPN profile with id " + mId + " not found"); | |
774 | finish(); | |
775 | } | |
776 | } | |
4db2d633 | 777 | |
825c192d | 778 | mSelectVpnType.setSelection(mVpnType.ordinal()); |
8ae7f8b7 | 779 | mCertReq.setChecked(flags == null || (flags & VpnProfile.FLAGS_SUPPRESS_CERT_REQS) == 0); |
825c192d | 780 | |
f46da851 | 781 | /* check if the user selected a user certificate previously */ |
67fa05aa TB |
782 | useralias = savedInstanceState == null ? useralias : savedInstanceState.getString(VpnProfileDataSource.KEY_USER_CERTIFICATE); |
783 | local_id = savedInstanceState == null ? local_id : savedInstanceState.getString(VpnProfileDataSource.KEY_LOCAL_ID); | |
f46da851 TB |
784 | if (useralias != null) |
785 | { | |
786 | UserCertificateLoader loader = new UserCertificateLoader(this, useralias); | |
787 | mUserCertLoading = useralias; | |
67fa05aa | 788 | mSelectedUserId = local_id; |
f46da851 TB |
789 | loader.execute(); |
790 | } | |
791 | ||
792 | /* check if the user selected a CA certificate previously */ | |
4db2d633 TB |
793 | alias = savedInstanceState == null ? alias : savedInstanceState.getString(VpnProfileDataSource.KEY_CERTIFICATE); |
794 | mCheckAuto.setChecked(alias == null); | |
795 | if (alias != null) | |
796 | { | |
797 | X509Certificate certificate = TrustedCertificateManager.getInstance().getCACertificateFromAlias(alias); | |
798 | if (certificate != null) | |
799 | { | |
800 | mCertEntry = new TrustedCertificateEntry(alias, certificate); | |
801 | } | |
802 | else | |
803 | { /* previously selected certificate is not here anymore */ | |
804 | showCertificateAlert(); | |
805 | mCertEntry = null; | |
806 | } | |
807 | } | |
291ef58c TB |
808 | |
809 | mSelectSelectedAppsHandling.setSelection(mSelectedAppsHandling.ordinal()); | |
810 | if (savedInstanceState != null) | |
811 | { | |
812 | ArrayList<String> selectedApps = savedInstanceState.getStringArrayList(VpnProfileDataSource.KEY_SELECTED_APPS_LIST); | |
813 | mSelectedApps = new TreeSet<>(selectedApps); | |
814 | } | |
56a922b2 | 815 | } |
f46da851 | 816 | |
c6822051 TB |
817 | /** |
818 | * Get the integer value in the given text box or null if empty | |
819 | * | |
820 | * @param view text box (numeric entry assumed) | |
821 | */ | |
822 | private Integer getInteger(EditText view) | |
823 | { | |
824 | String value = view.getText().toString().trim(); | |
6294f28b TB |
825 | try |
826 | { | |
827 | return value.isEmpty() ? null : Integer.valueOf(value); | |
828 | } | |
829 | catch (NumberFormatException e) | |
830 | { | |
831 | return null; | |
832 | } | |
c6822051 TB |
833 | } |
834 | ||
ef2ad9db TB |
835 | /** |
836 | * Check that the value in the given text box is a valid integer in the given range | |
837 | * | |
838 | * @param view text box (numeric entry assumed) | |
839 | * @param min minimum value (inclusive) | |
840 | * @param max maximum value (inclusive) | |
841 | */ | |
842 | private boolean validateInteger(EditText view, Integer min, Integer max) | |
843 | { | |
844 | String value = view.getText().toString().trim(); | |
845 | try | |
846 | { | |
847 | if (value.isEmpty()) | |
848 | { | |
849 | return true; | |
850 | } | |
851 | Integer val = Integer.valueOf(value); | |
852 | return min <= val && val <= max; | |
853 | } | |
854 | catch (NumberFormatException e) | |
855 | { | |
856 | return false; | |
857 | } | |
858 | } | |
859 | ||
1a63e8e4 TB |
860 | /** |
861 | * Check that the value in the given text box is a valid list of subnets/ranges | |
862 | * | |
863 | * @param view text box | |
864 | */ | |
865 | private boolean validateSubnets(EditText view) | |
866 | { | |
867 | String value = view.getText().toString().trim(); | |
868 | return value.isEmpty() || IPRangeSet.fromString(value) != null; | |
869 | } | |
870 | ||
9f962f6c TB |
871 | /** |
872 | * Check that the value in the given text box is a valid proposal | |
873 | * | |
874 | * @param view text box | |
875 | */ | |
876 | private boolean validateProposal(EditText view, boolean ike) | |
877 | { | |
878 | String value = view.getText().toString().trim(); | |
879 | return value.isEmpty() || Utils.isProposalValid(ike, value); | |
880 | } | |
881 | ||
f46da851 TB |
882 | private class SelectUserCertOnClickListener implements OnClickListener, KeyChainAliasCallback |
883 | { | |
884 | @Override | |
885 | public void onClick(View v) | |
886 | { | |
887 | String useralias = mUserCertEntry != null ? mUserCertEntry.getAlias() : null; | |
888 | KeyChain.choosePrivateKeyAlias(VpnProfileDetailActivity.this, this, new String[] { "RSA" }, null, null, -1, useralias); | |
889 | } | |
890 | ||
891 | @Override | |
892 | public void alias(final String alias) | |
893 | { | |
894 | if (alias != null) | |
895 | { /* otherwise the dialog was canceled, the request denied */ | |
896 | try | |
897 | { | |
898 | final X509Certificate[] chain = KeyChain.getCertificateChain(VpnProfileDetailActivity.this, alias); | |
899 | /* alias() is not called from our main thread */ | |
900 | runOnUiThread(new Runnable() { | |
901 | @Override | |
902 | public void run() | |
903 | { | |
904 | if (chain != null && chain.length > 0) | |
905 | { | |
906 | mUserCertEntry = new TrustedCertificateEntry(alias, chain[0]); | |
907 | } | |
908 | updateCredentialView(); | |
909 | } | |
910 | }); | |
911 | } | |
cf6110f1 | 912 | catch (KeyChainException | InterruptedException e) |
f46da851 TB |
913 | { |
914 | e.printStackTrace(); | |
915 | } | |
916 | } | |
917 | } | |
918 | } | |
919 | ||
920 | /** | |
921 | * Load the selected user certificate asynchronously. This cannot be done | |
922 | * from the main thread as getCertificateChain() calls back to our main | |
923 | * thread to bind to the KeyChain service resulting in a deadlock. | |
924 | */ | |
925 | private class UserCertificateLoader extends AsyncTask<Void, Void, X509Certificate> | |
926 | { | |
927 | private final Context mContext; | |
928 | private final String mAlias; | |
929 | ||
930 | public UserCertificateLoader(Context context, String alias) | |
931 | { | |
932 | mContext = context; | |
933 | mAlias = alias; | |
934 | } | |
935 | ||
936 | @Override | |
937 | protected X509Certificate doInBackground(Void... params) | |
938 | { | |
939 | X509Certificate[] chain = null; | |
940 | try | |
941 | { | |
942 | chain = KeyChain.getCertificateChain(mContext, mAlias); | |
943 | } | |
cf6110f1 | 944 | catch (KeyChainException | InterruptedException e) |
f46da851 TB |
945 | { |
946 | e.printStackTrace(); | |
947 | } | |
948 | if (chain != null && chain.length > 0) | |
949 | { | |
950 | return chain[0]; | |
951 | } | |
952 | return null; | |
953 | } | |
954 | ||
955 | @Override | |
956 | protected void onPostExecute(X509Certificate result) | |
957 | { | |
958 | if (result != null) | |
959 | { | |
960 | mUserCertEntry = new TrustedCertificateEntry(mAlias, result); | |
961 | } | |
962 | else | |
963 | { /* previously selected certificate is not here anymore */ | |
d4bf6bfb | 964 | ((TextView)mSelectUserCert.findViewById(android.R.id.text1)).setError(""); |
f46da851 TB |
965 | mUserCertEntry = null; |
966 | } | |
967 | mUserCertLoading = null; | |
968 | updateCredentialView(); | |
969 | } | |
970 | } | |
e1a98e79 TB |
971 | |
972 | /** | |
973 | * Dialog with notification message if EAP-TNC is used. | |
974 | */ | |
77c1c28d | 975 | public static class TncNoticeDialog extends AppCompatDialogFragment |
e1a98e79 TB |
976 | { |
977 | @Override | |
978 | public Dialog onCreateDialog(Bundle savedInstanceState) | |
979 | { | |
980 | return new AlertDialog.Builder(getActivity()) | |
981 | .setTitle(R.string.tnc_notice_title) | |
982 | .setMessage(Html.fromHtml(getString(R.string.tnc_notice_details))) | |
983 | .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { | |
984 | @Override | |
985 | public void onClick(DialogInterface dialog, int id) | |
986 | { | |
987 | dialog.dismiss(); | |
988 | } | |
989 | }).create(); | |
990 | } | |
991 | } | |
e7a12cc8 TB |
992 | |
993 | /** | |
994 | * Tokenizer implementation that separates by white-space | |
995 | */ | |
996 | public static class SpaceTokenizer implements MultiAutoCompleteTextView.Tokenizer | |
997 | { | |
998 | @Override | |
999 | public int findTokenStart(CharSequence text, int cursor) | |
1000 | { | |
1001 | int i = cursor; | |
1002 | ||
1003 | while (i > 0 && !Character.isWhitespace(text.charAt(i - 1))) | |
1004 | { | |
1005 | i--; | |
1006 | } | |
1007 | return i; | |
1008 | } | |
1009 | ||
1010 | @Override | |
1011 | public int findTokenEnd(CharSequence text, int cursor) | |
1012 | { | |
1013 | int i = cursor; | |
1014 | int len = text.length(); | |
1015 | ||
1016 | while (i < len) | |
1017 | { | |
1018 | if (Character.isWhitespace(text.charAt(i))) | |
1019 | { | |
1020 | return i; | |
1021 | } | |
1022 | else | |
1023 | { | |
1024 | i++; | |
1025 | } | |
1026 | } | |
1027 | return len; | |
1028 | } | |
1029 | ||
1030 | @Override | |
1031 | public CharSequence terminateToken(CharSequence text) | |
1032 | { | |
1033 | int i = text.length(); | |
1034 | ||
1035 | if (i > 0 && Character.isWhitespace(text.charAt(i - 1))) | |
1036 | { | |
1037 | return text; | |
1038 | } | |
1039 | else | |
1040 | { | |
1041 | if (text instanceof Spanned) | |
1042 | { | |
1043 | SpannableString sp = new SpannableString(text + " "); | |
1044 | TextUtils.copySpansFrom((Spanned) text, 0, text.length(), Object.class, sp, 0); | |
1045 | return sp; | |
1046 | } | |
1047 | else | |
1048 | { | |
1049 | return text + " "; | |
1050 | } | |
1051 | } | |
1052 | } | |
1053 | } | |
56a922b2 | 1054 | } |