From 26354d0aba4868a8f7571eb14ef75c13de5df324 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 12 Jul 2021 17:58:16 +0200 Subject: [PATCH] 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. --- .../android/logic/StrongSwanApplication.java | 27 ++++++ .../strongswan/android/ui/MainActivity.java | 20 ++--- .../android/ui/VpnProfileDetailActivity.java | 89 +++++++++++-------- 3 files changed, 83 insertions(+), 53 deletions(-) diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/logic/StrongSwanApplication.java b/src/frontends/android/app/src/main/java/org/strongswan/android/logic/StrongSwanApplication.java index a868b72cd4..505c00cc99 100644 --- a/src/frontends/android/app/src/main/java/org/strongswan/android/logic/StrongSwanApplication.java +++ b/src/frontends/android/app/src/main/java/org/strongswan/android/logic/StrongSwanApplication.java @@ -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. diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/MainActivity.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/MainActivity.java index 18953d1858..9b47b7f219 100644 --- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/MainActivity.java +++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/MainActivity.java @@ -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 - { - @Override - protected TrustedCertificateManager doInBackground(Void... params) - { - return TrustedCertificateManager.getInstance().load(); - } - } - /** * Dismiss dialog if shown */ diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileDetailActivity.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileDetailActivity.java index 391c3e4dbc..65d97ba216 100644 --- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileDetailActivity.java +++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileDetailActivity.java @@ -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; @@ -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,6 +74,7 @@ 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; @@ -788,9 +790,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 */ @@ -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 * 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 + 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)); } } -- 2.47.2