Compare commits
22 Commits
laforge/sw
...
master
Author | SHA1 | Date |
---|---|---|
![]() |
e6a904de69 | |
![]() |
ce607d774b | |
![]() |
1c4e134f26 | |
![]() |
5d6e69a6e4 | |
![]() |
4ae9b482f2 | |
![]() |
1aa68a78f8 | |
![]() |
358d3d0ba1 | |
![]() |
a14337bcde | |
![]() |
3eadd0fc3a | |
![]() |
2f1bf11dca | |
![]() |
b3cdbe6693 | |
![]() |
26354d0aba | |
![]() |
a8cc146240 | |
![]() |
c976165533 | |
![]() |
dc351a30e1 | |
![]() |
93c494e295 | |
![]() |
7cd50aeb64 | |
![]() |
a885e38265 | |
![]() |
6f3725ea8b | |
![]() |
5831009941 | |
![]() |
44e16a63ef | |
![]() |
2f9114bce1 |
|
@ -46,7 +46,7 @@ jobs:
|
|||
# for C builds, so we follow the "any CI" instructions
|
||||
- name: Install sonar-scanner
|
||||
env:
|
||||
SONAR_SCANNER_VERSION: 4.4.0.2170
|
||||
SONAR_SCANNER_VERSION: 4.6.2.2472
|
||||
run: |
|
||||
export SONAR_SCANNER_HOME=$HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux
|
||||
curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-linux.zip
|
||||
|
|
|
@ -52,3 +52,4 @@ coverage/
|
|||
/*.files
|
||||
/*.includes
|
||||
test-driver
|
||||
nbproject/
|
||||
|
|
|
@ -6,7 +6,7 @@ TARBALL=$SRCDIR/.tarball-git-version
|
|||
if test -f $TARBALL; then
|
||||
V=$(cat $TARBALL)
|
||||
elif test -d $SRCDIR/.git; then
|
||||
V=$(git -C $SRCDIR describe --tags HEAD 2>/dev/null)
|
||||
V=$(git -C $SRCDIR describe --exclude 'android-*' --tags HEAD 2>/dev/null)
|
||||
fi
|
||||
|
||||
if test -z "$V"; then
|
||||
|
|
|
@ -37,7 +37,7 @@ build_botan()
|
|||
|
||||
build_wolfssl()
|
||||
{
|
||||
WOLFSSL_REV=0caf3ba456f1 # v4.7.1r + SHA-3 fix
|
||||
WOLFSSL_REV=v4.8.0-stable
|
||||
WOLFSSL_DIR=$DEPS_BUILD_DIR/wolfssl
|
||||
|
||||
if test -d "$WOLFSSL_DIR"; then
|
||||
|
@ -468,7 +468,7 @@ sonarcloud)
|
|||
-Dsonar.projectKey=${SONAR_PROJECT} \
|
||||
-Dsonar.organization=${SONAR_ORGANIZATION} \
|
||||
-Dsonar.login=${SONAR_TOKEN} \
|
||||
-Dsonar.projectVersion=$(git describe)+${BUILD_NUMBER} \
|
||||
-Dsonar.projectVersion=$(git describe --exclude 'android-*')+${BUILD_NUMBER} \
|
||||
-Dsonar.sources=. \
|
||||
-Dsonar.cfamily.threads=2 \
|
||||
-Dsonar.cfamily.cache.enabled=true \
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
compileSdkVersion 30
|
||||
|
||||
defaultConfig {
|
||||
applicationId "org.strongswan.android"
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 29
|
||||
versionCode 74
|
||||
versionName "2.3.2"
|
||||
targetSdkVersion 30
|
||||
versionCode 75
|
||||
versionName "2.3.3"
|
||||
}
|
||||
|
||||
sourceSets.main {
|
||||
|
@ -46,13 +46,13 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.3.0'
|
||||
implementation 'androidx.preference:preference:1.1.1'
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation 'com.google.android.material:material:1.2.1'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation 'org.mockito:mockito-core:2.28.2'
|
||||
testImplementation 'org.powermock:powermock-core:2.0.2'
|
||||
testImplementation 'org.powermock:powermock-module-junit4:2.0.2'
|
||||
testImplementation 'org.powermock:powermock-api-mockito2:2.0.2'
|
||||
implementation 'com.google.android.material:material:1.4.0'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'org.mockito:mockito-core:3.11.2'
|
||||
testImplementation 'org.powermock:powermock-core:2.0.9'
|
||||
testImplementation 'org.powermock:powermock-module-junit4:2.0.9'
|
||||
testImplementation 'org.powermock:powermock-api-mockito2:2.0.9'
|
||||
}
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||
<!-- necessary to allow users to select ex-/included apps and EAP-TNC -->
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||
tools:ignore="QueryAllPackagesPermission" />
|
||||
|
||||
<application
|
||||
android:name=".logic.StrongSwanApplication"
|
||||
|
|
|
@ -192,7 +192,7 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe
|
|||
mAppDir = getFilesDir().getAbsolutePath();
|
||||
|
||||
/* handler used to do changes in the main UI thread */
|
||||
mHandler = new Handler();
|
||||
mHandler = new Handler(getMainLooper());
|
||||
|
||||
mDataSource = new VpnProfileDataSource(this);
|
||||
mDataSource.open();
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
package org.strongswan.android.logic;
|
||||
|
||||
import java.security.Security;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.strongswan.android.security.LocalCertificateKeyStoreProvider;
|
||||
import org.strongswan.android.ui.MainActivity;
|
||||
|
@ -23,10 +26,16 @@ import org.strongswan.android.ui.MainActivity;
|
|||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.core.os.HandlerCompat;
|
||||
|
||||
public class StrongSwanApplication extends Application
|
||||
{
|
||||
private static Context mContext;
|
||||
private final ExecutorService mExecutorService = Executors.newFixedThreadPool(4);
|
||||
private final Handler mMainHandler = HandlerCompat.createAsync(Looper.getMainLooper());
|
||||
|
||||
static {
|
||||
Security.addProvider(new LocalCertificateKeyStoreProvider());
|
||||
|
@ -48,6 +57,24 @@ public class StrongSwanApplication extends Application
|
|||
return StrongSwanApplication.mContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a thread pool to run tasks in separate threads
|
||||
* @return thread pool
|
||||
*/
|
||||
public Executor getExecutor()
|
||||
{
|
||||
return mExecutorService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a handler to execute stuff by the main thread.
|
||||
* @return handler
|
||||
*/
|
||||
public Handler getHandler()
|
||||
{
|
||||
return mMainHandler;
|
||||
}
|
||||
|
||||
/*
|
||||
* The libraries are extracted to /data/data/org.strongswan.android/...
|
||||
* during installation. On newer releases most are loaded in JNI_OnLoad.
|
||||
|
|
|
@ -22,6 +22,7 @@ import android.os.Binder;
|
|||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
|
||||
|
@ -107,7 +108,7 @@ public class VpnStateService extends Service
|
|||
{
|
||||
/* this handler allows us to notify listeners from the UI thread and
|
||||
* not from the threads that actually report any state changes */
|
||||
mHandler = new RetryHandler(this);
|
||||
mHandler = new RetryHandler(getMainLooper(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -536,8 +537,9 @@ public class VpnStateService extends Service
|
|||
private static class RetryHandler extends Handler {
|
||||
WeakReference<VpnStateService> mService;
|
||||
|
||||
public RetryHandler(VpnStateService service)
|
||||
public RetryHandler(Looper looper, VpnStateService service)
|
||||
{
|
||||
super(looper);
|
||||
mService = new WeakReference<>(service);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,9 +16,11 @@
|
|||
package org.strongswan.android.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.FileObserver;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -36,6 +38,7 @@ import java.io.StringReader;
|
|||
import java.util.ArrayList;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
public class LogFragment extends Fragment
|
||||
|
@ -55,9 +58,17 @@ public class LogFragment extends Fragment
|
|||
|
||||
mLogFilePath = getActivity().getFilesDir() + File.separator + CharonVpnService.LOG_FILE;
|
||||
|
||||
mLogHandler = new Handler();
|
||||
mLogHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
mDirectoryObserver = new LogDirectoryObserver(getActivity().getFilesDir().getAbsolutePath());
|
||||
File logdir = getActivity().getFilesDir();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||
{
|
||||
mDirectoryObserver = new LogDirectoryObserver(logdir);
|
||||
}
|
||||
else
|
||||
{
|
||||
mDirectoryObserver = new LogDirectoryObserver(logdir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -223,14 +234,20 @@ public class LogFragment extends Fragment
|
|||
*/
|
||||
private class LogDirectoryObserver extends FileObserver
|
||||
{
|
||||
private final File mFile;
|
||||
private long mSize;
|
||||
private static final int mMask = FileObserver.CREATE | FileObserver.MODIFY | FileObserver.DELETE;
|
||||
private final File mFile = new File(mLogFilePath);
|
||||
private long mSize = mFile.length();
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public LogDirectoryObserver(String path)
|
||||
{
|
||||
super(path, FileObserver.CREATE | FileObserver.MODIFY | FileObserver.DELETE);
|
||||
mFile = new File(mLogFilePath);
|
||||
mSize = mFile.length();
|
||||
super(path, mMask);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||
public LogDirectoryObserver(File path)
|
||||
{
|
||||
super(path, mMask);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,7 +20,6 @@ package org.strongswan.android.ui;
|
|||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.format.Formatter;
|
||||
|
@ -30,6 +29,7 @@ import android.widget.Toast;
|
|||
|
||||
import org.strongswan.android.R;
|
||||
import org.strongswan.android.data.VpnProfile;
|
||||
import org.strongswan.android.logic.StrongSwanApplication;
|
||||
import org.strongswan.android.logic.TrustedCertificateManager;
|
||||
import org.strongswan.android.ui.VpnProfileListFragment.OnVpnProfileSelectedListener;
|
||||
|
||||
|
@ -68,8 +68,10 @@ public class MainActivity extends AppCompatActivity implements OnVpnProfileSelec
|
|||
bar.setDisplayShowTitleEnabled(false);
|
||||
bar.setIcon(R.mipmap.ic_app);
|
||||
|
||||
/* load CA certificates in a background task */
|
||||
new LoadCertificatesTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
/* load CA certificates in a background thread */
|
||||
((StrongSwanApplication)getApplication()).getExecutor().execute(() -> {
|
||||
TrustedCertificateManager.getInstance().load();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -157,18 +159,6 @@ public class MainActivity extends AppCompatActivity implements OnVpnProfileSelec
|
|||
dialog.show(this.getSupportFragmentManager(), DIALOG_TAG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that loads the cached CA certificates.
|
||||
*/
|
||||
private class LoadCertificatesTask extends AsyncTask<Void, Void, TrustedCertificateManager>
|
||||
{
|
||||
@Override
|
||||
protected TrustedCertificateManager doInBackground(Void... params)
|
||||
{
|
||||
return TrustedCertificateManager.getInstance().load();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismiss dialog if shown
|
||||
*/
|
||||
|
|
|
@ -26,6 +26,8 @@ import android.widget.TextView;
|
|||
import org.strongswan.android.R;
|
||||
import org.strongswan.android.logic.imc.RemediationInstruction;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.ListFragment;
|
||||
|
||||
public class RemediationInstructionFragment extends ListFragment
|
||||
|
@ -49,9 +51,9 @@ public class RemediationInstructionFragment extends ListFragment
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState)
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState)
|
||||
{
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
if (savedInstanceState != null)
|
||||
{
|
||||
|
|
|
@ -26,6 +26,8 @@ import org.strongswan.android.ui.adapter.RemediationInstructionAdapter;
|
|||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.ListFragment;
|
||||
|
||||
public class RemediationInstructionsFragment extends ListFragment
|
||||
|
@ -46,9 +48,9 @@ public class RemediationInstructionsFragment extends ListFragment
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState)
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState)
|
||||
{
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
if (savedInstanceState != null)
|
||||
{
|
||||
|
|
|
@ -40,6 +40,7 @@ import java.util.List;
|
|||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.fragment.app.ListFragment;
|
||||
|
@ -53,9 +54,9 @@ public class SelectedApplicationsListFragment extends ListFragment implements Lo
|
|||
private SortedSet<String> mSelection;
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(@Nullable Bundle savedInstanceState)
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState)
|
||||
{
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package org.strongswan.android.ui;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.DialogInterface;
|
||||
|
@ -37,6 +36,8 @@ import java.security.cert.CertificateException;
|
|||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.app.AppCompatDialogFragment;
|
||||
|
@ -44,10 +45,21 @@ import androidx.fragment.app.FragmentTransaction;
|
|||
|
||||
public class TrustedCertificateImportActivity extends AppCompatActivity
|
||||
{
|
||||
private static final int OPEN_DOCUMENT = 0;
|
||||
private static final String DIALOG_TAG = "Dialog";
|
||||
private Uri mCertificateUri;
|
||||
|
||||
private final ActivityResultLauncher<Intent> mOpenDocument = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
if (result.getResultCode() == RESULT_OK && result.getData() != null)
|
||||
{
|
||||
mCertificateUri = result.getData().getData();
|
||||
return;
|
||||
}
|
||||
finish();
|
||||
}
|
||||
);
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
|
@ -71,7 +83,7 @@ public class TrustedCertificateImportActivity extends AppCompatActivity
|
|||
openIntent.setType("*/*");
|
||||
try
|
||||
{
|
||||
startActivityForResult(openIntent, OPEN_DOCUMENT);
|
||||
mOpenDocument.launch(openIntent);
|
||||
}
|
||||
catch (ActivityNotFoundException e)
|
||||
{ /* some devices are unable to browse for files */
|
||||
|
@ -81,23 +93,6 @@ public class TrustedCertificateImportActivity extends AppCompatActivity
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
switch (requestCode)
|
||||
{
|
||||
case OPEN_DOCUMENT:
|
||||
if (resultCode == Activity.RESULT_OK && data != null)
|
||||
{
|
||||
mCertificateUri = data.getData();
|
||||
return;
|
||||
}
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostResume()
|
||||
{
|
||||
|
@ -214,7 +209,7 @@ public class TrustedCertificateImportActivity extends AppCompatActivity
|
|||
if (activity.storeCertificate(certificate))
|
||||
{
|
||||
Toast.makeText(getActivity(), R.string.cert_imported_successfully, Toast.LENGTH_LONG).show();
|
||||
getActivity().setResult(Activity.RESULT_OK);
|
||||
getActivity().setResult(RESULT_OK);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -41,6 +41,8 @@ import java.util.Map.Entry;
|
|||
import java.util.Observable;
|
||||
import java.util.Observer;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.ListFragment;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.app.LoaderManager.LoaderCallbacks;
|
||||
|
@ -63,9 +65,9 @@ public class TrustedCertificateListFragment extends ListFragment implements Load
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState)
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState)
|
||||
{
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
setEmptyText(getString(R.string.no_certificates));
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
|
||||
package org.strongswan.android.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
@ -24,6 +22,7 @@ import android.view.Menu;
|
|||
import android.view.MenuItem;
|
||||
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import com.google.android.material.tabs.TabLayoutMediator;
|
||||
|
||||
import org.strongswan.android.R;
|
||||
import org.strongswan.android.data.VpnProfileDataSource;
|
||||
|
@ -34,22 +33,34 @@ import org.strongswan.android.ui.CertificateDeleteConfirmationDialog.OnCertifica
|
|||
|
||||
import java.security.KeyStore;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentPagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
|
||||
public class TrustedCertificatesActivity extends AppCompatActivity implements TrustedCertificateListFragment.OnTrustedCertificateSelectedListener, OnCertificateDeleteListener
|
||||
{
|
||||
public static final String SELECT_CERTIFICATE = "org.strongswan.android.action.SELECT_CERTIFICATE";
|
||||
private static final String DIALOG_TAG = "Dialog";
|
||||
private static final int IMPORT_CERTIFICATE = 0;
|
||||
private TrustedCertificatesPagerAdapter mAdapter;
|
||||
private ViewPager mPager;
|
||||
private ViewPager2 mPager;
|
||||
private boolean mSelect;
|
||||
|
||||
private final ActivityResultLauncher<Intent> mImportCertificate = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
if (result.getResultCode() == RESULT_OK)
|
||||
{
|
||||
reloadCertificates();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
|
@ -59,13 +70,15 @@ public class TrustedCertificatesActivity extends AppCompatActivity implements Tr
|
|||
ActionBar actionBar = getSupportActionBar();
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
mAdapter = new TrustedCertificatesPagerAdapter(getSupportFragmentManager(), this);
|
||||
mAdapter = new TrustedCertificatesPagerAdapter(this);
|
||||
|
||||
mPager = (ViewPager)findViewById(R.id.viewpager);
|
||||
mPager = (ViewPager2)findViewById(R.id.viewpager);
|
||||
mPager.setAdapter(mAdapter);
|
||||
|
||||
TabLayout tabs = (TabLayout)findViewById(R.id.tabs);
|
||||
tabs.setupWithViewPager(mPager);
|
||||
new TabLayoutMediator(tabs, mPager, (tab, position) -> {
|
||||
tab.setText(mAdapter.getTitle(position));
|
||||
}).attach();
|
||||
|
||||
mSelect = SELECT_CERTIFICATE.equals(getIntent().getAction());
|
||||
}
|
||||
|
@ -100,27 +113,12 @@ public class TrustedCertificatesActivity extends AppCompatActivity implements Tr
|
|||
return true;
|
||||
case R.id.menu_import_certificate:
|
||||
Intent intent = new Intent(this, TrustedCertificateImportActivity.class);
|
||||
startActivityForResult(intent, IMPORT_CERTIFICATE);
|
||||
mImportCertificate.launch(intent);
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
switch (requestCode)
|
||||
{
|
||||
case IMPORT_CERTIFICATE:
|
||||
if (resultCode == Activity.RESULT_OK)
|
||||
{
|
||||
reloadCertificates();
|
||||
}
|
||||
return;
|
||||
}
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrustedCertificateSelected(TrustedCertificateEntry selected)
|
||||
{
|
||||
|
@ -129,7 +127,7 @@ public class TrustedCertificatesActivity extends AppCompatActivity implements Tr
|
|||
/* the user selected a certificate, return to calling activity */
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(VpnProfileDataSource.KEY_CERTIFICATE, selected.getAlias());
|
||||
setResult(Activity.RESULT_OK, intent);
|
||||
setResult(RESULT_OK, intent);
|
||||
finish();
|
||||
}
|
||||
else if (mAdapter.getSource(mPager.getCurrentItem()) == TrustedCertificateSource.LOCAL)
|
||||
|
@ -163,28 +161,21 @@ public class TrustedCertificatesActivity extends AppCompatActivity implements Tr
|
|||
TrustedCertificateManager.getInstance().reset();
|
||||
}
|
||||
|
||||
public static class TrustedCertificatesPagerAdapter extends FragmentPagerAdapter
|
||||
public static class TrustedCertificatesPagerAdapter extends FragmentStateAdapter
|
||||
{
|
||||
private TrustedCertificatesTab mTabs[];
|
||||
|
||||
public TrustedCertificatesPagerAdapter(FragmentManager fm, Context context)
|
||||
public TrustedCertificatesPagerAdapter(@NonNull FragmentActivity fragmentActivity)
|
||||
{
|
||||
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
|
||||
super(fragmentActivity);
|
||||
mTabs = new TrustedCertificatesTab[]{
|
||||
new TrustedCertificatesTab(context.getString(R.string.system_tab), TrustedCertificateSource.SYSTEM),
|
||||
new TrustedCertificatesTab(context.getString(R.string.user_tab), TrustedCertificateSource.USER),
|
||||
new TrustedCertificatesTab(context.getString(R.string.local_tab), TrustedCertificateSource.LOCAL),
|
||||
new TrustedCertificatesTab(fragmentActivity.getString(R.string.system_tab), TrustedCertificateSource.SYSTEM),
|
||||
new TrustedCertificatesTab(fragmentActivity.getString(R.string.user_tab), TrustedCertificateSource.USER),
|
||||
new TrustedCertificatesTab(fragmentActivity.getString(R.string.local_tab), TrustedCertificateSource.LOCAL),
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount()
|
||||
{
|
||||
return mTabs.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position)
|
||||
public CharSequence getTitle(int position)
|
||||
{
|
||||
return mTabs[position].getTitle();
|
||||
}
|
||||
|
@ -195,7 +186,14 @@ public class TrustedCertificatesActivity extends AppCompatActivity implements Tr
|
|||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position)
|
||||
public int getItemCount()
|
||||
{
|
||||
return mTabs.length;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment createFragment(int position)
|
||||
{
|
||||
TrustedCertificateListFragment fragment = new TrustedCertificateListFragment();
|
||||
Bundle args = new Bundle();
|
||||
|
|
|
@ -44,6 +44,8 @@ import org.strongswan.android.logic.VpnStateService;
|
|||
import org.strongswan.android.logic.VpnStateService.State;
|
||||
import org.strongswan.android.utils.Constants;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
@ -59,8 +61,6 @@ public class VpnProfileControlActivity extends AppCompatActivity
|
|||
public static final String DISCONNECT = "org.strongswan.android.action.DISCONNECT";
|
||||
public static final String EXTRA_VPN_PROFILE_ID = "org.strongswan.android.VPN_PROFILE_ID";
|
||||
|
||||
private static final int PREPARE_VPN_SERVICE = 0;
|
||||
private static final int ADD_TO_POWER_WHITELIST = 1;
|
||||
private static final String WAITING_FOR_RESULT = "WAITING_FOR_RESULT";
|
||||
private static final String PROFILE_NAME = "PROFILE_NAME";
|
||||
private static final String PROFILE_REQUIRES_PASSWORD = "REQUIRES_PASSWORD";
|
||||
|
@ -87,6 +87,33 @@ public class VpnProfileControlActivity extends AppCompatActivity
|
|||
}
|
||||
};
|
||||
|
||||
private final ActivityResultLauncher<Intent> mPrepareVpnService = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
mWaitingForResult = false;
|
||||
if (result.getResultCode() == RESULT_OK && mProfileInfo != null)
|
||||
{
|
||||
onVpnServicePrepared();
|
||||
}
|
||||
else
|
||||
{ /* this happens if the always-on VPN feature is activated by a different app or the user declined */
|
||||
VpnNotSupportedError.showWithMessage(this, R.string.vpn_not_supported_no_permission);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
private final ActivityResultLauncher<Intent> mAddToPowerWhitelist = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
mWaitingForResult = false;
|
||||
if (mProfileInfo != null && mService != null)
|
||||
{
|
||||
mService.connect(mProfileInfo, true);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
);
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
|
@ -173,7 +200,7 @@ public class VpnProfileControlActivity extends AppCompatActivity
|
|||
try
|
||||
{
|
||||
mWaitingForResult = true;
|
||||
startActivityForResult(intent, PREPARE_VPN_SERVICE);
|
||||
mPrepareVpnService.launch(intent);
|
||||
}
|
||||
catch (ActivityNotFoundException ex)
|
||||
{
|
||||
|
@ -187,7 +214,23 @@ public class VpnProfileControlActivity extends AppCompatActivity
|
|||
}
|
||||
else
|
||||
{ /* user already granted permission to use VpnService */
|
||||
onActivityResult(PREPARE_VPN_SERVICE, RESULT_OK, null);
|
||||
onVpnServicePrepared();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the VpnService has been prepared and permission has been granted
|
||||
* by the user.
|
||||
*/
|
||||
protected void onVpnServicePrepared()
|
||||
{
|
||||
if (checkPowerWhitelist())
|
||||
{
|
||||
if (mService != null)
|
||||
{
|
||||
mService.connect(mProfileInfo, true);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,48 +262,6 @@ public class VpnProfileControlActivity extends AppCompatActivity
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
switch (requestCode)
|
||||
{
|
||||
case PREPARE_VPN_SERVICE:
|
||||
mWaitingForResult = false;
|
||||
if (resultCode == RESULT_OK && mProfileInfo != null)
|
||||
{
|
||||
if (checkPowerWhitelist())
|
||||
{
|
||||
if (mService != null)
|
||||
{
|
||||
mService.connect(mProfileInfo, true);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
}
|
||||
else
|
||||
{ /* this happens if the always-on VPN feature is activated by a different app or the user declined */
|
||||
if (getSupportFragmentManager().isStateSaved())
|
||||
{ /* onActivityResult() might be called when we aren't active anymore e.g. if the
|
||||
* user pressed the home button, if the activity is started again we land here
|
||||
* before onNewIntent() is called */
|
||||
return;
|
||||
}
|
||||
VpnNotSupportedError.showWithMessage(this, R.string.vpn_not_supported_no_permission);
|
||||
}
|
||||
break;
|
||||
case ADD_TO_POWER_WHITELIST:
|
||||
mWaitingForResult = false;
|
||||
if (mProfileInfo != null && mService != null)
|
||||
{
|
||||
mService.connect(mProfileInfo, true);
|
||||
}
|
||||
finish();
|
||||
break;
|
||||
default:
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we are currently connected to a VPN connection
|
||||
*
|
||||
|
@ -597,7 +598,7 @@ public class VpnProfileControlActivity extends AppCompatActivity
|
|||
activity.mWaitingForResult = true;
|
||||
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
|
||||
Uri.parse("package:" + activity.getPackageName()));
|
||||
activity.startActivityForResult(intent, ADD_TO_POWER_WHITELIST);
|
||||
activity.mAddToPowerWhitelist.launch(intent);
|
||||
}).create();
|
||||
}
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@ import android.app.Dialog;
|
|||
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.os.Handler;
|
||||
import android.security.KeyChain;
|
||||
import android.security.KeyChainAliasCallback;
|
||||
import android.security.KeyChainException;
|
||||
|
@ -43,6 +43,7 @@ import android.view.ViewGroup;
|
|||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemSelectedListener;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||
|
@ -50,7 +51,6 @@ 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;
|
||||
|
@ -59,6 +59,7 @@ 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;
|
||||
import org.strongswan.android.logic.StrongSwanApplication;
|
||||
import org.strongswan.android.logic.TrustedCertificateManager;
|
||||
import org.strongswan.android.security.TrustedCertificateEntry;
|
||||
import org.strongswan.android.ui.adapter.CertificateIdentitiesAdapter;
|
||||
|
@ -73,18 +74,19 @@ import java.util.ArrayList;
|
|||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.app.AppCompatDialogFragment;
|
||||
import androidx.appcompat.widget.SwitchCompat;
|
||||
import androidx.core.text.HtmlCompat;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
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;
|
||||
private TrustedCertificateEntry mCertEntry;
|
||||
|
@ -119,12 +121,12 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
|||
private TextInputLayoutHelper mMTUWrap;
|
||||
private EditText mPort;
|
||||
private TextInputLayoutHelper mPortWrap;
|
||||
private Switch mCertReq;
|
||||
private Switch mUseCrl;
|
||||
private Switch mUseOcsp;
|
||||
private Switch mStrictRevocation;
|
||||
private Switch mRsaPss;
|
||||
private Switch mIPv6Transport;
|
||||
private SwitchCompat mCertReq;
|
||||
private SwitchCompat mUseCrl;
|
||||
private SwitchCompat mUseOcsp;
|
||||
private SwitchCompat mStrictRevocation;
|
||||
private SwitchCompat mRsaPss;
|
||||
private SwitchCompat mIPv6Transport;
|
||||
private EditText mNATKeepalive;
|
||||
private TextInputLayoutHelper mNATKeepaliveWrap;
|
||||
private EditText mIncludedSubnets;
|
||||
|
@ -144,6 +146,41 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
|||
private EditText mDnsServers;
|
||||
private TextInputLayoutHelper mDnsServersWrap;
|
||||
|
||||
private final ActivityResultLauncher<Intent> mInstallPKCS12 = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
if (result.getResultCode() == RESULT_OK)
|
||||
{
|
||||
mSelectUserCert.performClick();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
private final ActivityResultLauncher<Intent> mSelectTrustedCertificate = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
if (result.getResultCode() == RESULT_OK)
|
||||
{
|
||||
String alias = result.getData().getStringExtra(VpnProfileDataSource.KEY_CERTIFICATE);
|
||||
X509Certificate certificate = TrustedCertificateManager.getInstance().getCACertificateFromAlias(alias);
|
||||
mCertEntry = certificate == null ? null : new TrustedCertificateEntry(alias, certificate);
|
||||
updateCertificateSelector();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
private final ActivityResultLauncher<Intent> mSelectApplications = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
if (result.getResultCode() == RESULT_OK)
|
||||
{
|
||||
ArrayList<String> selection = result.getData().getStringArrayListExtra(VpnProfileDataSource.KEY_SELECTED_APPS_LIST);
|
||||
mSelectedApps = new TreeSet<>(selection);
|
||||
updateAppsSelector();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
|
@ -190,7 +227,7 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
|||
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);
|
||||
mCertReq = findViewById(R.id.cert_req);
|
||||
mUseCrl = findViewById(R.id.use_crl);
|
||||
mUseOcsp = findViewById(R.id.use_ocsp);
|
||||
mStrictRevocation= findViewById(R.id.strict_revocation);
|
||||
|
@ -283,6 +320,10 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
|||
});
|
||||
|
||||
mSelectUserCert.setOnClickListener(new SelectUserCertOnClickListener());
|
||||
((Button)findViewById(R.id.install_user_certificate)).setOnClickListener(v -> {
|
||||
Intent intent = KeyChain.createInstallIntent();
|
||||
mInstallPKCS12.launch(intent);
|
||||
});
|
||||
mSelectUserIdAdapter = new CertificateIdentitiesAdapter(this);
|
||||
mLocalId.setAdapter(mSelectUserIdAdapter);
|
||||
|
||||
|
@ -300,7 +341,7 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
|||
{
|
||||
Intent intent = new Intent(VpnProfileDetailActivity.this, TrustedCertificatesActivity.class);
|
||||
intent.setAction(TrustedCertificatesActivity.SELECT_CERTIFICATE);
|
||||
startActivityForResult(intent, SELECT_TRUSTED_CERTIFICATE);
|
||||
mSelectTrustedCertificate.launch(intent);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -334,7 +375,7 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
|||
{
|
||||
Intent intent = new Intent(VpnProfileDetailActivity.this, SelectedApplicationsActivity.class);
|
||||
intent.putExtra(VpnProfileDataSource.KEY_SELECTED_APPS_LIST, new ArrayList<>(mSelectedApps));
|
||||
startActivityForResult(intent, SELECT_APPLICATIONS);
|
||||
mSelectApplications.launch(intent);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -404,33 +445,6 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
switch (requestCode)
|
||||
{
|
||||
case SELECT_TRUSTED_CERTIFICATE:
|
||||
if (resultCode == RESULT_OK)
|
||||
{
|
||||
String alias = data.getStringExtra(VpnProfileDataSource.KEY_CERTIFICATE);
|
||||
X509Certificate certificate = TrustedCertificateManager.getInstance().getCACertificateFromAlias(alias);
|
||||
mCertEntry = certificate == null ? null : new TrustedCertificateEntry(alias, certificate);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the UI to enter credentials depending on the type of VPN currently selected
|
||||
*/
|
||||
|
@ -791,9 +805,22 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
|||
useralias = savedInstanceState == null ? useralias : savedInstanceState.getString(VpnProfileDataSource.KEY_USER_CERTIFICATE);
|
||||
if (useralias != null)
|
||||
{
|
||||
UserCertificateLoader loader = new UserCertificateLoader(this, useralias);
|
||||
mUserCertLoading = useralias;
|
||||
loader.execute();
|
||||
UserCertificateLoader loader = new UserCertificateLoader(((StrongSwanApplication)getApplication()).getExecutor(),
|
||||
((StrongSwanApplication)getApplication()).getHandler());
|
||||
loader.loadCertifiate(this, useralias, result -> {
|
||||
if (result != null)
|
||||
{
|
||||
mUserCertEntry = new TrustedCertificateEntry(mUserCertLoading, result);
|
||||
}
|
||||
else
|
||||
{ /* previously selected certificate is not here anymore */
|
||||
((TextView)mSelectUserCert.findViewById(android.R.id.text1)).setError("");
|
||||
mUserCertEntry = null;
|
||||
}
|
||||
mUserCertLoading = null;
|
||||
updateCredentialView();
|
||||
});
|
||||
}
|
||||
|
||||
/* check if the user selected a CA certificate previously */
|
||||
|
@ -961,55 +988,53 @@ public class VpnProfileDetailActivity extends AppCompatActivity
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback interface for the user certificate loader.
|
||||
*/
|
||||
private interface UserCertificateLoaderCallback {
|
||||
void onComplete(X509Certificate result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the selected user certificate asynchronously. This cannot be done
|
||||
* from the main thread as getCertificateChain() calls back to our main
|
||||
* thread to bind to the KeyChain service resulting in a deadlock.
|
||||
*/
|
||||
private class UserCertificateLoader extends AsyncTask<Void, Void, X509Certificate>
|
||||
private class UserCertificateLoader
|
||||
{
|
||||
private final Context mContext;
|
||||
private final String mAlias;
|
||||
private final Executor mExecutor;
|
||||
private final Handler mHandler;
|
||||
|
||||
public UserCertificateLoader(Context context, String alias)
|
||||
public UserCertificateLoader(Executor executor, Handler handler)
|
||||
{
|
||||
mContext = context;
|
||||
mAlias = alias;
|
||||
mExecutor = executor;
|
||||
mHandler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected X509Certificate doInBackground(Void... params)
|
||||
public void loadCertifiate(Context context, String alias, UserCertificateLoaderCallback callback)
|
||||
{
|
||||
X509Certificate[] chain = null;
|
||||
try
|
||||
{
|
||||
chain = KeyChain.getCertificateChain(mContext, mAlias);
|
||||
}
|
||||
catch (KeyChainException | InterruptedException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (chain != null && chain.length > 0)
|
||||
{
|
||||
return chain[0];
|
||||
}
|
||||
return null;
|
||||
mExecutor.execute(() -> {
|
||||
X509Certificate[] chain = null;
|
||||
try
|
||||
{
|
||||
chain = KeyChain.getCertificateChain(context, alias);
|
||||
}
|
||||
catch (KeyChainException | InterruptedException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (chain != null && chain.length > 0)
|
||||
{
|
||||
complete(chain[0], callback);
|
||||
return;
|
||||
}
|
||||
complete(null, callback);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(X509Certificate result)
|
||||
protected void complete(X509Certificate result, UserCertificateLoaderCallback callback)
|
||||
{
|
||||
if (result != null)
|
||||
{
|
||||
mUserCertEntry = new TrustedCertificateEntry(mAlias, result);
|
||||
}
|
||||
else
|
||||
{ /* previously selected certificate is not here anymore */
|
||||
((TextView)mSelectUserCert.findViewById(android.R.id.text1)).setError("");
|
||||
mUserCertEntry = null;
|
||||
}
|
||||
mUserCertLoading = null;
|
||||
updateCredentialView();
|
||||
mHandler.post(() -> callback.onComplete(result));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
package org.strongswan.android.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
|
@ -74,6 +73,8 @@ import java.util.UUID;
|
|||
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.AsyncTaskLoader;
|
||||
|
@ -84,8 +85,6 @@ public class VpnProfileImportActivity extends AppCompatActivity
|
|||
{
|
||||
private static final String PKCS12_INSTALLED = "PKCS12_INSTALLED";
|
||||
private static final String PROFILE_URI = "PROFILE_URI";
|
||||
private static final int INSTALL_PKCS12 = 0;
|
||||