2 * Copyright (C) 2012-2016 Tobias Brunner
3 * Copyright (C) 2012 Giuliano Grassi
4 * Copyright (C) 2012 Ralf Sager
5 * HSR Hochschule fuer Technik Rapperswil
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>.
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
18 package org
.strongswan
.android
.ui
;
20 import android
.app
.Dialog
;
21 import android
.content
.Context
;
22 import android
.content
.DialogInterface
;
23 import android
.content
.Intent
;
24 import android
.os
.AsyncTask
;
25 import android
.os
.Bundle
;
26 import android
.security
.KeyChain
;
27 import android
.security
.KeyChainAliasCallback
;
28 import android
.security
.KeyChainException
;
29 import android
.support
.v4
.content
.LocalBroadcastManager
;
30 import android
.support
.v7
.app
.AlertDialog
;
31 import android
.support
.v7
.app
.AppCompatActivity
;
32 import android
.support
.v7
.app
.AppCompatDialogFragment
;
33 import android
.text
.Editable
;
34 import android
.text
.Html
;
35 import android
.text
.SpannableString
;
36 import android
.text
.Spanned
;
37 import android
.text
.TextUtils
;
38 import android
.text
.TextWatcher
;
39 import android
.util
.Log
;
40 import android
.view
.Menu
;
41 import android
.view
.MenuInflater
;
42 import android
.view
.MenuItem
;
43 import android
.view
.View
;
44 import android
.view
.View
.OnClickListener
;
45 import android
.view
.ViewGroup
;
46 import android
.widget
.AdapterView
;
47 import android
.widget
.AdapterView
.OnItemSelectedListener
;
48 import android
.widget
.ArrayAdapter
;
49 import android
.widget
.CheckBox
;
50 import android
.widget
.CompoundButton
;
51 import android
.widget
.CompoundButton
.OnCheckedChangeListener
;
52 import android
.widget
.EditText
;
53 import android
.widget
.MultiAutoCompleteTextView
;
54 import android
.widget
.RelativeLayout
;
55 import android
.widget
.Spinner
;
56 import android
.widget
.TextView
;
58 import org
.strongswan
.android
.R
;
59 import org
.strongswan
.android
.data
.VpnProfile
;
60 import org
.strongswan
.android
.data
.VpnProfileDataSource
;
61 import org
.strongswan
.android
.data
.VpnType
;
62 import org
.strongswan
.android
.data
.VpnType
.VpnTypeFeature
;
63 import org
.strongswan
.android
.logic
.TrustedCertificateManager
;
64 import org
.strongswan
.android
.security
.TrustedCertificateEntry
;
65 import org
.strongswan
.android
.ui
.adapter
.CertificateIdentitiesAdapter
;
66 import org
.strongswan
.android
.ui
.widget
.TextInputLayoutHelper
;
67 import org
.strongswan
.android
.utils
.Constants
;
69 import java
.security
.cert
.X509Certificate
;
70 import java
.util
.UUID
;
72 public class VpnProfileDetailActivity
extends AppCompatActivity
74 private static final int SELECT_TRUSTED_CERTIFICATE
= 0;
76 private VpnProfileDataSource mDataSource
;
78 private TrustedCertificateEntry mCertEntry
;
79 private String mUserCertLoading
;
80 private CertificateIdentitiesAdapter mSelectUserIdAdapter
;
81 private String mSelectedUserId
;
82 private TrustedCertificateEntry mUserCertEntry
;
83 private VpnType mVpnType
= VpnType
.IKEV2_EAP
;
84 private VpnProfile mProfile
;
85 private MultiAutoCompleteTextView mName
;
86 private TextInputLayoutHelper mNameWrap
;
87 private EditText mGateway
;
88 private TextInputLayoutHelper mGatewayWrap
;
89 private Spinner mSelectVpnType
;
90 private ViewGroup mUsernamePassword
;
91 private EditText mUsername
;
92 private TextInputLayoutHelper mUsernameWrap
;
93 private EditText mPassword
;
94 private ViewGroup mUserCertificate
;
95 private RelativeLayout mSelectUserCert
;
96 private Spinner mSelectUserId
;
97 private CheckBox mCheckAuto
;
98 private RelativeLayout mSelectCert
;
99 private RelativeLayout mTncNotice
;
100 private CheckBox mShowAdvanced
;
101 private ViewGroup mAdvancedSettings
;
102 private MultiAutoCompleteTextView mRemoteId
;
103 private TextInputLayoutHelper mRemoteIdWrap
;
104 private EditText mMTU
;
105 private TextInputLayoutHelper mMTUWrap
;
106 private EditText mPort
;
107 private TextInputLayoutHelper mPortWrap
;
108 private CheckBox mBlockIPv4
;
109 private CheckBox mBlockIPv6
;
112 public void onCreate(Bundle savedInstanceState
)
114 super.onCreate(savedInstanceState
);
116 /* the title is set when we load the profile, if any */
117 getSupportActionBar().setDisplayHomeAsUpEnabled(true);
119 mDataSource
= new VpnProfileDataSource(this);
122 setContentView(R
.layout
.profile_detail_view
);
124 mName
= (MultiAutoCompleteTextView
)findViewById(R
.id
.name
);
125 mNameWrap
= (TextInputLayoutHelper
)findViewById(R
.id
.name_wrap
);
126 mGateway
= (EditText
)findViewById(R
.id
.gateway
);
127 mGatewayWrap
= (TextInputLayoutHelper
) findViewById(R
.id
.gateway_wrap
);
128 mSelectVpnType
= (Spinner
)findViewById(R
.id
.vpn_type
);
129 mTncNotice
= (RelativeLayout
)findViewById(R
.id
.tnc_notice
);
131 mUsernamePassword
= (ViewGroup
)findViewById(R
.id
.username_password_group
);
132 mUsername
= (EditText
)findViewById(R
.id
.username
);
133 mUsernameWrap
= (TextInputLayoutHelper
) findViewById(R
.id
.username_wrap
);
134 mPassword
= (EditText
)findViewById(R
.id
.password
);
136 mUserCertificate
= (ViewGroup
)findViewById(R
.id
.user_certificate_group
);
137 mSelectUserCert
= (RelativeLayout
)findViewById(R
.id
.select_user_certificate
);
138 mSelectUserId
= (Spinner
)findViewById(R
.id
.select_user_id
);
140 mCheckAuto
= (CheckBox
)findViewById(R
.id
.ca_auto
);
141 mSelectCert
= (RelativeLayout
)findViewById(R
.id
.select_certificate
);
143 mShowAdvanced
= (CheckBox
)findViewById(R
.id
.show_advanced
);
144 mAdvancedSettings
= (ViewGroup
)findViewById(R
.id
.advanced_settings
);
146 mRemoteId
= (MultiAutoCompleteTextView
)findViewById(R
.id
.remote_id
);
147 mRemoteIdWrap
= (TextInputLayoutHelper
) findViewById(R
.id
.remote_id_wrap
);
148 mMTU
= (EditText
)findViewById(R
.id
.mtu
);
149 mMTUWrap
= (TextInputLayoutHelper
) findViewById(R
.id
.mtu_wrap
);
150 mPort
= (EditText
)findViewById(R
.id
.port
);
151 mPortWrap
= (TextInputLayoutHelper
) findViewById(R
.id
.port_wrap
);
152 mBlockIPv4
= (CheckBox
)findViewById(R
.id
.split_tunneling_v4
);
153 mBlockIPv6
= (CheckBox
)findViewById(R
.id
.split_tunneling_v6
);
155 final SpaceTokenizer spaceTokenizer
= new SpaceTokenizer();
156 mName
.setTokenizer(spaceTokenizer
);
157 mRemoteId
.setTokenizer(spaceTokenizer
);
158 final ArrayAdapter
<String
> completeAdapter
= new ArrayAdapter
<>(this, android
.R
.layout
.simple_dropdown_item_1line
);
159 mName
.setAdapter(completeAdapter
);
160 mRemoteId
.setAdapter(completeAdapter
);
162 mGateway
.addTextChangedListener(new TextWatcher() {
164 public void beforeTextChanged(CharSequence s
, int start
, int count
, int after
) {}
167 public void onTextChanged(CharSequence s
, int start
, int before
, int count
) {}
170 public void afterTextChanged(Editable s
)
172 completeAdapter
.clear();
173 completeAdapter
.add(mGateway
.getText().toString());
174 if (TextUtils
.isEmpty(mGateway
.getText()))
176 mNameWrap
.setHelperText(getString(R
.string
.profile_name_hint
));
177 mRemoteIdWrap
.setHelperText(getString(R
.string
.profile_remote_id_hint
));
181 mNameWrap
.setHelperText(String
.format(getString(R
.string
.profile_name_hint_gateway
), mGateway
.getText()));
182 mRemoteIdWrap
.setHelperText(String
.format(getString(R
.string
.profile_remote_id_hint_gateway
), mGateway
.getText()));
187 mSelectVpnType
.setOnItemSelectedListener(new OnItemSelectedListener() {
189 public void onItemSelected(AdapterView
<?
> parent
, View view
, int position
, long id
)
191 mVpnType
= VpnType
.values()[position
];
192 updateCredentialView();
196 public void onNothingSelected(AdapterView
<?
> parent
)
197 { /* should not happen */
198 mVpnType
= VpnType
.IKEV2_EAP
;
199 updateCredentialView();
203 ((TextView
)mTncNotice
.findViewById(android
.R
.id
.text1
)).setText(R
.string
.tnc_notice_title
);
204 ((TextView
)mTncNotice
.findViewById(android
.R
.id
.text2
)).setText(R
.string
.tnc_notice_subtitle
);
205 mTncNotice
.setOnClickListener(new OnClickListener() {
207 public void onClick(View v
)
209 new TncNoticeDialog().show(VpnProfileDetailActivity
.this.getSupportFragmentManager(), "TncNotice");
213 mSelectUserCert
.setOnClickListener(new SelectUserCertOnClickListener());
214 mSelectUserIdAdapter
= new CertificateIdentitiesAdapter(this);
215 mSelectUserId
.setAdapter(mSelectUserIdAdapter
);
216 mSelectUserId
.setOnItemSelectedListener(new OnItemSelectedListener() {
218 public void onItemSelected(AdapterView
<?
> parent
, View view
, int position
, long id
)
220 if (mUserCertEntry
!= null)
221 { /* we don't store the subject DN as it is in the reverse order and the default anyway */
222 mSelectedUserId
= position
== 0 ?
null : mSelectUserIdAdapter
.getItem(position
);
227 public void onNothingSelected(AdapterView
<?
> parent
)
229 mSelectedUserId
= null;
233 mCheckAuto
.setOnCheckedChangeListener(new OnCheckedChangeListener() {
235 public void onCheckedChanged(CompoundButton buttonView
, boolean isChecked
)
237 updateCertificateSelector();
241 mSelectCert
.setOnClickListener(new OnClickListener() {
243 public void onClick(View v
)
245 Intent intent
= new Intent(VpnProfileDetailActivity
.this, TrustedCertificatesActivity
.class);
246 intent
.setAction(TrustedCertificatesActivity
.SELECT_CERTIFICATE
);
247 startActivityForResult(intent
, SELECT_TRUSTED_CERTIFICATE
);
251 mShowAdvanced
.setOnCheckedChangeListener(new OnCheckedChangeListener() {
253 public void onCheckedChanged(CompoundButton buttonView
, boolean isChecked
)
255 updateAdvancedSettings();
259 mId
= savedInstanceState
== null ?
null : savedInstanceState
.getLong(VpnProfileDataSource
.KEY_ID
);
262 Bundle extras
= getIntent().getExtras();
263 mId
= extras
== null ?
null : extras
.getLong(VpnProfileDataSource
.KEY_ID
);
266 loadProfileData(savedInstanceState
);
268 updateCredentialView();
269 updateCertificateSelector();
270 updateAdvancedSettings();
274 protected void onDestroy()
281 protected void onSaveInstanceState(Bundle outState
)
283 super.onSaveInstanceState(outState
);
286 outState
.putLong(VpnProfileDataSource
.KEY_ID
, mId
);
288 if (mUserCertEntry
!= null)
290 outState
.putString(VpnProfileDataSource
.KEY_USER_CERTIFICATE
, mUserCertEntry
.getAlias());
292 if (mSelectedUserId
!= null)
294 outState
.putString(VpnProfileDataSource
.KEY_LOCAL_ID
, mSelectedUserId
);
296 if (mCertEntry
!= null)
298 outState
.putString(VpnProfileDataSource
.KEY_CERTIFICATE
, mCertEntry
.getAlias());
303 public boolean onCreateOptionsMenu(Menu menu
)
305 MenuInflater inflater
= getMenuInflater();
306 inflater
.inflate(R
.menu
.profile_edit
, menu
);
311 public boolean onOptionsItemSelected(MenuItem item
)
313 switch (item
.getItemId())
315 case android
.R
.id
.home
:
316 case R
.id
.menu_cancel
:
319 case R
.id
.menu_accept
:
323 return super.onOptionsItemSelected(item
);
328 protected void onActivityResult(int requestCode
, int resultCode
, Intent data
)
332 case SELECT_TRUSTED_CERTIFICATE
:
333 if (resultCode
== RESULT_OK
)
335 String alias
= data
.getStringExtra(VpnProfileDataSource
.KEY_CERTIFICATE
);
336 X509Certificate certificate
= TrustedCertificateManager
.getInstance().getCACertificateFromAlias(alias
);
337 mCertEntry
= certificate
== null ?
null : new TrustedCertificateEntry(alias
, certificate
);
338 updateCertificateSelector();
342 super.onActivityResult(requestCode
, resultCode
, data
);
347 * Update the UI to enter credentials depending on the type of VPN currently selected
349 private void updateCredentialView()
351 mUsernamePassword
.setVisibility(mVpnType
.has(VpnTypeFeature
.USER_PASS
) ? View
.VISIBLE
: View
.GONE
);
352 mUserCertificate
.setVisibility(mVpnType
.has(VpnTypeFeature
.CERTIFICATE
) ? View
.VISIBLE
: View
.GONE
);
353 mTncNotice
.setVisibility(mVpnType
.has(VpnTypeFeature
.BYOD
) ? View
.VISIBLE
: View
.GONE
);
355 if (mVpnType
.has(VpnTypeFeature
.CERTIFICATE
))
357 mSelectUserId
.setEnabled(false);
358 if (mUserCertLoading
!= null)
360 ((TextView
)mSelectUserCert
.findViewById(android
.R
.id
.text1
)).setText(mUserCertLoading
);
361 ((TextView
)mSelectUserCert
.findViewById(android
.R
.id
.text2
)).setText(R
.string
.loading
);
363 else if (mUserCertEntry
!= null)
364 { /* clear any errors and set the new data */
365 ((TextView
)mSelectUserCert
.findViewById(android
.R
.id
.text1
)).setError(null);
366 ((TextView
)mSelectUserCert
.findViewById(android
.R
.id
.text1
)).setText(mUserCertEntry
.getAlias());
367 ((TextView
)mSelectUserCert
.findViewById(android
.R
.id
.text2
)).setText(mUserCertEntry
.getCertificate().getSubjectDN().toString());
368 mSelectUserIdAdapter
.setCertificate(mUserCertEntry
);
369 mSelectUserId
.setSelection(mSelectUserIdAdapter
.getPosition(mSelectedUserId
));
370 mSelectUserId
.setEnabled(true);
374 ((TextView
)mSelectUserCert
.findViewById(android
.R
.id
.text1
)).setText(R
.string
.profile_user_select_certificate_label
);
375 ((TextView
)mSelectUserCert
.findViewById(android
.R
.id
.text2
)).setText(R
.string
.profile_user_select_certificate
);
376 mSelectUserIdAdapter
.setCertificate(null);
382 * Show an alert in case the previously selected certificate is not found anymore
383 * or the user did not select a certificate in the spinner.
385 private void showCertificateAlert()
387 AlertDialog
.Builder adb
= new AlertDialog
.Builder(VpnProfileDetailActivity
.this);
388 adb
.setTitle(R
.string
.alert_text_nocertfound_title
);
389 adb
.setMessage(R
.string
.alert_text_nocertfound
);
390 adb
.setPositiveButton(android
.R
.string
.ok
, new DialogInterface
.OnClickListener() {
392 public void onClick(DialogInterface dialog
, int id
)
401 * Update the CA certificate selection UI depending on whether the
402 * certificate should be automatically selected or not.
404 private void updateCertificateSelector()
406 if (!mCheckAuto
.isChecked())
408 mSelectCert
.setEnabled(true);
409 mSelectCert
.setVisibility(View
.VISIBLE
);
411 if (mCertEntry
!= null)
413 ((TextView
)mSelectCert
.findViewById(android
.R
.id
.text1
)).setText(mCertEntry
.getSubjectPrimary());
414 ((TextView
)mSelectCert
.findViewById(android
.R
.id
.text2
)).setText(mCertEntry
.getSubjectSecondary());
418 ((TextView
)mSelectCert
.findViewById(android
.R
.id
.text1
)).setText(R
.string
.profile_ca_select_certificate_label
);
419 ((TextView
)mSelectCert
.findViewById(android
.R
.id
.text2
)).setText(R
.string
.profile_ca_select_certificate
);
424 mSelectCert
.setEnabled(false);
425 mSelectCert
.setVisibility(View
.GONE
);
430 * Update the advanced settings UI depending on whether any advanced
431 * settings have already been made.
433 private void updateAdvancedSettings()
435 boolean show
= mShowAdvanced
.isChecked();
436 if (!show
&& mProfile
!= null)
438 Integer st
= mProfile
.getSplitTunneling();
439 show
= mProfile
.getRemoteId() != null || mProfile
.getMTU() != null ||
440 mProfile
.getPort() != null || (st
!= null && st
!= 0);
442 mShowAdvanced
.setVisibility(!show ? View
.VISIBLE
: View
.GONE
);
443 mAdvancedSettings
.setVisibility(show ? View
.VISIBLE
: View
.GONE
);
447 * Save or update the profile depending on whether we actually have a
448 * profile object or not (this was created in updateProfileData)
450 private void saveProfile()
454 if (mProfile
!= null)
457 if (mProfile
.getUUID() == null)
459 mProfile
.setUUID(UUID
.randomUUID());
461 mDataSource
.updateVpnProfile(mProfile
);
465 mProfile
= new VpnProfile();
467 mDataSource
.insertProfile(mProfile
);
469 Intent intent
= new Intent(Constants
.VPN_PROFILES_CHANGED
);
470 intent
.putExtra(Constants
.VPN_PROFILES_SINGLE
, mProfile
.getId());
471 LocalBroadcastManager
.getInstance(this).sendBroadcast(intent
);
473 setResult(RESULT_OK
, new Intent().putExtra(VpnProfileDataSource
.KEY_ID
, mProfile
.getId()));
479 * Verify the user input and display error messages.
480 * @return true if the input is valid
482 private boolean verifyInput()
484 boolean valid
= true;
485 if (mGateway
.getText().toString().trim().isEmpty())
487 mGatewayWrap
.setError(getString(R
.string
.alert_text_no_input_gateway
));
490 if (mVpnType
.has(VpnTypeFeature
.USER_PASS
))
492 if (mUsername
.getText().toString().trim().isEmpty())
494 mUsernameWrap
.setError(getString(R
.string
.alert_text_no_input_username
));
498 if (mVpnType
.has(VpnTypeFeature
.CERTIFICATE
) && mUserCertEntry
== null)
499 { /* let's show an error icon */
500 ((TextView
)mSelectUserCert
.findViewById(android
.R
.id
.text1
)).setError("");
503 if (!mCheckAuto
.isChecked() && mCertEntry
== null)
505 showCertificateAlert();
508 if (!validateInteger(mMTU
, Constants
.MTU_MIN
, Constants
.MTU_MAX
))
510 mMTUWrap
.setError(String
.format(getString(R
.string
.alert_text_out_of_range
), Constants
.MTU_MIN
, Constants
.MTU_MAX
));
513 if (!validateInteger(mPort
, 1, 65535))
515 mPortWrap
.setError(String
.format(getString(R
.string
.alert_text_out_of_range
), 1, 65535));
522 * Update the profile object with the data entered by the user
524 private void updateProfileData()
526 /* the name is optional, we default to the gateway if none is given */
527 String name
= mName
.getText().toString().trim();
528 String gateway
= mGateway
.getText().toString().trim();
529 mProfile
.setName(name
.isEmpty() ? gateway
: name
);
530 mProfile
.setGateway(gateway
);
531 mProfile
.setVpnType(mVpnType
);
532 if (mVpnType
.has(VpnTypeFeature
.USER_PASS
))
534 mProfile
.setUsername(mUsername
.getText().toString().trim());
535 String password
= mPassword
.getText().toString().trim();
536 password
= password
.isEmpty() ?
null : password
;
537 mProfile
.setPassword(password
);
539 if (mVpnType
.has(VpnTypeFeature
.CERTIFICATE
))
541 mProfile
.setUserCertificateAlias(mUserCertEntry
.getAlias());
542 mProfile
.setLocalId(mSelectedUserId
);
544 String certAlias
= mCheckAuto
.isChecked() ?
null : mCertEntry
.getAlias();
545 mProfile
.setCertificateAlias(certAlias
);
546 String remote_id
= mRemoteId
.getText().toString().trim();
547 mProfile
.setRemoteId(remote_id
.isEmpty() ?
null : remote_id
);
548 mProfile
.setMTU(getInteger(mMTU
));
549 mProfile
.setPort(getInteger(mPort
));
551 st
|= mBlockIPv4
.isChecked() ? VpnProfile
.SPLIT_TUNNELING_BLOCK_IPV4
: 0;
552 st
|= mBlockIPv6
.isChecked() ? VpnProfile
.SPLIT_TUNNELING_BLOCK_IPV6
: 0;
553 mProfile
.setSplitTunneling(st
== 0 ?
null : st
);
557 * Load an existing profile if we got an ID
559 * @param savedInstanceState previously saved state
561 private void loadProfileData(Bundle savedInstanceState
)
563 String useralias
= null, local_id
= null, alias
= null;
565 getSupportActionBar().setTitle(R
.string
.add_profile
);
566 if (mId
!= null && mId
!= 0)
568 mProfile
= mDataSource
.getVpnProfile(mId
);
569 if (mProfile
!= null)
571 mName
.setText(mProfile
.getName());
572 mGateway
.setText(mProfile
.getGateway());
573 mVpnType
= mProfile
.getVpnType();
574 mUsername
.setText(mProfile
.getUsername());
575 mPassword
.setText(mProfile
.getPassword());
576 mRemoteId
.setText(mProfile
.getRemoteId());
577 mMTU
.setText(mProfile
.getMTU() != null ? mProfile
.getMTU().toString() : null);
578 mPort
.setText(mProfile
.getPort() != null ? mProfile
.getPort().toString() : null);
579 mBlockIPv4
.setChecked(mProfile
.getSplitTunneling() != null && (mProfile
.getSplitTunneling() & VpnProfile
.SPLIT_TUNNELING_BLOCK_IPV4
) != 0);
580 mBlockIPv6
.setChecked(mProfile
.getSplitTunneling() != null && (mProfile
.getSplitTunneling() & VpnProfile
.SPLIT_TUNNELING_BLOCK_IPV6
) != 0);
581 useralias
= mProfile
.getUserCertificateAlias();
582 local_id
= mProfile
.getLocalId();
583 alias
= mProfile
.getCertificateAlias();
584 getSupportActionBar().setTitle(mProfile
.getName());
588 Log
.e(VpnProfileDetailActivity
.class.getSimpleName(),
589 "VPN profile with id " + mId
+ " not found");
594 mSelectVpnType
.setSelection(mVpnType
.ordinal());
596 /* check if the user selected a user certificate previously */
597 useralias
= savedInstanceState
== null ? useralias
: savedInstanceState
.getString(VpnProfileDataSource
.KEY_USER_CERTIFICATE
);
598 local_id
= savedInstanceState
== null ? local_id
: savedInstanceState
.getString(VpnProfileDataSource
.KEY_LOCAL_ID
);
599 if (useralias
!= null)
601 UserCertificateLoader loader
= new UserCertificateLoader(this, useralias
);
602 mUserCertLoading
= useralias
;
603 mSelectedUserId
= local_id
;
607 /* check if the user selected a CA certificate previously */
608 alias
= savedInstanceState
== null ? alias
: savedInstanceState
.getString(VpnProfileDataSource
.KEY_CERTIFICATE
);
609 mCheckAuto
.setChecked(alias
== null);
612 X509Certificate certificate
= TrustedCertificateManager
.getInstance().getCACertificateFromAlias(alias
);
613 if (certificate
!= null)
615 mCertEntry
= new TrustedCertificateEntry(alias
, certificate
);
618 { /* previously selected certificate is not here anymore */
619 showCertificateAlert();
626 * Get the integer value in the given text box or null if empty
628 * @param view text box (numeric entry assumed)
630 private Integer
getInteger(EditText view
)
632 String value
= view
.getText().toString().trim();
635 return value
.isEmpty() ?
null : Integer
.valueOf(value
);
637 catch (NumberFormatException e
)
644 * Check that the value in the given text box is a valid integer in the given range
646 * @param view text box (numeric entry assumed)
647 * @param min minimum value (inclusive)
648 * @param max maximum value (inclusive)
650 private boolean validateInteger(EditText view
, Integer min
, Integer max
)
652 String value
= view
.getText().toString().trim();
659 Integer val
= Integer
.valueOf(value
);
660 return min
<= val
&& val
<= max
;
662 catch (NumberFormatException e
)
668 private class SelectUserCertOnClickListener
implements OnClickListener
, KeyChainAliasCallback
671 public void onClick(View v
)
673 String useralias
= mUserCertEntry
!= null ? mUserCertEntry
.getAlias() : null;
674 KeyChain
.choosePrivateKeyAlias(VpnProfileDetailActivity
.this, this, new String
[] { "RSA" }, null, null, -1, useralias
);
678 public void alias(final String alias
)
681 { /* otherwise the dialog was canceled, the request denied */
684 final X509Certificate
[] chain
= KeyChain
.getCertificateChain(VpnProfileDetailActivity
.this, alias
);
685 /* alias() is not called from our main thread */
686 runOnUiThread(new Runnable() {
690 if (chain
!= null && chain
.length
> 0)
692 mUserCertEntry
= new TrustedCertificateEntry(alias
, chain
[0]);
694 updateCredentialView();
698 catch (KeyChainException
| InterruptedException e
)
707 * Load the selected user certificate asynchronously. This cannot be done
708 * from the main thread as getCertificateChain() calls back to our main
709 * thread to bind to the KeyChain service resulting in a deadlock.
711 private class UserCertificateLoader
extends AsyncTask
<Void
, Void
, X509Certificate
>
713 private final Context mContext
;
714 private final String mAlias
;
716 public UserCertificateLoader(Context context
, String alias
)
723 protected X509Certificate
doInBackground(Void
... params
)
725 X509Certificate
[] chain
= null;
728 chain
= KeyChain
.getCertificateChain(mContext
, mAlias
);
730 catch (KeyChainException
| InterruptedException e
)
734 if (chain
!= null && chain
.length
> 0)
742 protected void onPostExecute(X509Certificate result
)
746 mUserCertEntry
= new TrustedCertificateEntry(mAlias
, result
);
749 { /* previously selected certificate is not here anymore */
750 ((TextView
)mSelectUserCert
.findViewById(android
.R
.id
.text1
)).setError("");
751 mUserCertEntry
= null;
753 mUserCertLoading
= null;
754 updateCredentialView();
759 * Dialog with notification message if EAP-TNC is used.
761 public static class TncNoticeDialog
extends AppCompatDialogFragment
764 public Dialog
onCreateDialog(Bundle savedInstanceState
)
766 return new AlertDialog
.Builder(getActivity())
767 .setTitle(R
.string
.tnc_notice_title
)
768 .setMessage(Html
.fromHtml(getString(R
.string
.tnc_notice_details
)))
769 .setPositiveButton(android
.R
.string
.ok
, new DialogInterface
.OnClickListener() {
771 public void onClick(DialogInterface dialog
, int id
)
780 * Tokenizer implementation that separates by white-space
782 public static class SpaceTokenizer
implements MultiAutoCompleteTextView
.Tokenizer
785 public int findTokenStart(CharSequence text
, int cursor
)
789 while (i
> 0 && !Character
.isWhitespace(text
.charAt(i
- 1)))
797 public int findTokenEnd(CharSequence text
, int cursor
)
800 int len
= text
.length();
804 if (Character
.isWhitespace(text
.charAt(i
)))
817 public CharSequence
terminateToken(CharSequence text
)
819 int i
= text
.length();
821 if (i
> 0 && Character
.isWhitespace(text
.charAt(i
- 1)))
827 if (text
instanceof Spanned
)
829 SpannableString sp
= new SpannableString(text
+ " ");
830 TextUtils
.copySpansFrom((Spanned
) text
, 0, text
.length(), Object
.class, sp
, 0);