android: Replace deprecated AsyncTask instances

As suggested by the Android docs, we use a global thread pool and handler
to avoid recreating them repeatedly.  Four threads should be more than
enough as we only use this to load CA certificates when the app starts
initially and to load user certs when editing a profile.
This commit is contained in:
Tobias Brunner
2021-07-14 09:59:02 +02:00
parent a8cc146240
commit 26354d0aba
3 changed files with 83 additions and 53 deletions
@@ -16,6 +16,9 @@
package org.strongswan.android.logic; package org.strongswan.android.logic;
import java.security.Security; 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.security.LocalCertificateKeyStoreProvider;
import org.strongswan.android.ui.MainActivity; import org.strongswan.android.ui.MainActivity;
@@ -23,10 +26,16 @@ import org.strongswan.android.ui.MainActivity;
import android.app.Application; import android.app.Application;
import android.content.Context; import android.content.Context;
import android.os.Build; import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import androidx.core.os.HandlerCompat;
public class StrongSwanApplication extends Application public class StrongSwanApplication extends Application
{ {
private static Context mContext; private static Context mContext;
private final ExecutorService mExecutorService = Executors.newFixedThreadPool(4);
private final Handler mMainHandler = HandlerCompat.createAsync(Looper.getMainLooper());
static { static {
Security.addProvider(new LocalCertificateKeyStoreProvider()); Security.addProvider(new LocalCertificateKeyStoreProvider());
@@ -48,6 +57,24 @@ public class StrongSwanApplication extends Application
return StrongSwanApplication.mContext; 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/... * The libraries are extracted to /data/data/org.strongswan.android/...
* during installation. On newer releases most are loaded in JNI_OnLoad. * during installation. On newer releases most are loaded in JNI_OnLoad.
@@ -20,7 +20,6 @@ package org.strongswan.android.ui;
import android.app.Dialog; import android.app.Dialog;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.text.format.Formatter; import android.text.format.Formatter;
@@ -30,6 +29,7 @@ import android.widget.Toast;
import org.strongswan.android.R; import org.strongswan.android.R;
import org.strongswan.android.data.VpnProfile; import org.strongswan.android.data.VpnProfile;
import org.strongswan.android.logic.StrongSwanApplication;
import org.strongswan.android.logic.TrustedCertificateManager; import org.strongswan.android.logic.TrustedCertificateManager;
import org.strongswan.android.ui.VpnProfileListFragment.OnVpnProfileSelectedListener; import org.strongswan.android.ui.VpnProfileListFragment.OnVpnProfileSelectedListener;
@@ -68,8 +68,10 @@ public class MainActivity extends AppCompatActivity implements OnVpnProfileSelec
bar.setDisplayShowTitleEnabled(false); bar.setDisplayShowTitleEnabled(false);
bar.setIcon(R.mipmap.ic_app); bar.setIcon(R.mipmap.ic_app);
/* load CA certificates in a background task */ /* load CA certificates in a background thread */
new LoadCertificatesTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); ((StrongSwanApplication)getApplication()).getExecutor().execute(() -> {
TrustedCertificateManager.getInstance().load();
});
} }
@Override @Override
@@ -157,18 +159,6 @@ public class MainActivity extends AppCompatActivity implements OnVpnProfileSelec
dialog.show(this.getSupportFragmentManager(), DIALOG_TAG); 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 * Dismiss dialog if shown
*/ */
@@ -21,9 +21,9 @@ import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.security.KeyChain; import android.security.KeyChain;
import android.security.KeyChainAliasCallback; import android.security.KeyChainAliasCallback;
import android.security.KeyChainException; import android.security.KeyChainException;
@@ -59,6 +59,7 @@ import org.strongswan.android.data.VpnProfile.SelectedAppsHandling;
import org.strongswan.android.data.VpnProfileDataSource; import org.strongswan.android.data.VpnProfileDataSource;
import org.strongswan.android.data.VpnType; import org.strongswan.android.data.VpnType;
import org.strongswan.android.data.VpnType.VpnTypeFeature; import org.strongswan.android.data.VpnType.VpnTypeFeature;
import org.strongswan.android.logic.StrongSwanApplication;
import org.strongswan.android.logic.TrustedCertificateManager; import org.strongswan.android.logic.TrustedCertificateManager;
import org.strongswan.android.security.TrustedCertificateEntry; import org.strongswan.android.security.TrustedCertificateEntry;
import org.strongswan.android.ui.adapter.CertificateIdentitiesAdapter; import org.strongswan.android.ui.adapter.CertificateIdentitiesAdapter;
@@ -73,6 +74,7 @@ import java.util.ArrayList;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.Executor;
import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts; import androidx.activity.result.contract.ActivityResultContracts;
@@ -788,9 +790,22 @@ public class VpnProfileDetailActivity extends AppCompatActivity
useralias = savedInstanceState == null ? useralias : savedInstanceState.getString(VpnProfileDataSource.KEY_USER_CERTIFICATE); useralias = savedInstanceState == null ? useralias : savedInstanceState.getString(VpnProfileDataSource.KEY_USER_CERTIFICATE);
if (useralias != null) if (useralias != null)
{ {
UserCertificateLoader loader = new UserCertificateLoader(this, useralias);
mUserCertLoading = 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 */ /* check if the user selected a CA certificate previously */
@@ -958,55 +973,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 * Load the selected user certificate asynchronously. This cannot be done
* from the main thread as getCertificateChain() calls back to our main * from the main thread as getCertificateChain() calls back to our main
* thread to bind to the KeyChain service resulting in a deadlock. * 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 Executor mExecutor;
private final String mAlias; private final Handler mHandler;
public UserCertificateLoader(Context context, String alias) public UserCertificateLoader(Executor executor, Handler handler)
{ {
mContext = context; mExecutor = executor;
mAlias = alias; mHandler = handler;
} }
@Override public void loadCertifiate(Context context, String alias, UserCertificateLoaderCallback callback)
protected X509Certificate doInBackground(Void... params)
{ {
X509Certificate[] chain = null; mExecutor.execute(() -> {
try X509Certificate[] chain = null;
{ try
chain = KeyChain.getCertificateChain(mContext, mAlias); {
} chain = KeyChain.getCertificateChain(context, alias);
catch (KeyChainException | InterruptedException e) }
{ catch (KeyChainException | InterruptedException e)
e.printStackTrace(); {
} e.printStackTrace();
if (chain != null && chain.length > 0) }
{ if (chain != null && chain.length > 0)
return chain[0]; {
} complete(chain[0], callback);
return null; return;
}
complete(null, callback);
});
} }
@Override protected void complete(X509Certificate result, UserCertificateLoaderCallback callback)
protected void onPostExecute(X509Certificate result)
{ {
if (result != null) mHandler.post(() -> callback.onComplete(result));
{
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();
} }
} }