diff --git a/src/frontends/android/app/build.gradle b/src/frontends/android/app/build.gradle index 195422791..237134771 100644 --- a/src/frontends/android/app/build.gradle +++ b/src/frontends/android/app/build.gradle @@ -45,6 +45,7 @@ android { dependencies { implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'androidx.lifecycle:lifecycle-process:2.7.0' implementation 'androidx.preference:preference:1.2.1' implementation 'com.google.android.material:material:1.10.0' testImplementation 'junit:junit:4.13.2' 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 ac9866155..d3805a284 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 @@ -1,5 +1,6 @@ /* - * Copyright (C) 2014 Tobias Brunner + * Copyright (C) 2023 Relution GmbH + * Copyright (C) 2014-2024 Tobias Brunner * * Copyright (C) secunet Security Networks AG * @@ -17,25 +18,53 @@ package org.strongswan.android.logic; import android.app.Application; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.os.Handler; import android.os.Looper; +import android.util.Log; +import org.strongswan.android.data.ManagedConfigurationService; import org.strongswan.android.security.LocalCertificateKeyStoreProvider; +import org.strongswan.android.utils.Constants; import java.security.Security; +import java.util.HashSet; +import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import androidx.annotation.NonNull; import androidx.core.os.HandlerCompat; +import androidx.lifecycle.DefaultLifecycleObserver; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.ProcessLifecycleOwner; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; -public class StrongSwanApplication extends Application +public class StrongSwanApplication extends Application implements DefaultLifecycleObserver { + private static final String TAG = StrongSwanApplication.class.getSimpleName(); + private static Context mContext; + private final ExecutorService mExecutorService = Executors.newFixedThreadPool(4); private final Handler mMainHandler = HandlerCompat.createAsync(Looper.getMainLooper()); + private ManagedConfigurationService mManagedConfigurationService; + + private final BroadcastReceiver mRestrictionsReceiver = new BroadcastReceiver() + { + @Override + public void onReceive(Context context, Intent intent) + { + Log.d(TAG, "Managed configuration changed"); + reloadManagedConfigurationAndNotifyListeners(); + } + }; + static { Security.addProvider(new LocalCertificateKeyStoreProvider()); @@ -46,6 +75,39 @@ public class StrongSwanApplication extends Application { super.onCreate(); StrongSwanApplication.mContext = getApplicationContext(); + + mManagedConfigurationService = new ManagedConfigurationService(mContext); + ProcessLifecycleOwner.get().getLifecycle().addObserver(this); + } + + @Override + public void onResume(@NonNull LifecycleOwner owner) + { + reloadManagedConfigurationAndNotifyListeners(); + + final IntentFilter restrictionsFilter = new IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED); + registerReceiver(mRestrictionsReceiver, restrictionsFilter); + } + + @Override + public void onPause(@NonNull LifecycleOwner owner) + { + unregisterReceiver(mRestrictionsReceiver); + } + + private void reloadManagedConfigurationAndNotifyListeners() + { + final Set uuids = new HashSet<>(mManagedConfigurationService.getManagedProfiles().keySet()); + + mManagedConfigurationService.loadConfiguration(); + mManagedConfigurationService.updateSettings(); + + uuids.addAll(mManagedConfigurationService.getManagedProfiles().keySet()); + + Log.d(TAG, "Send profiles changed broadcast"); + Intent profilesChanged = new Intent(Constants.VPN_PROFILES_CHANGED); + profilesChanged.putExtra(Constants.VPN_PROFILES_MULTIPLE, uuids.toArray(new String[0])); + LocalBroadcastManager.getInstance(mContext).sendBroadcast(profilesChanged); } /** @@ -78,6 +140,16 @@ public class StrongSwanApplication extends Application return mMainHandler; } + /** + * Returns a service providing access to the app's managed configuration. + * + * @return managed configuration + */ + public ManagedConfigurationService getManagedConfigurationService() + { + return mManagedConfigurationService; + } + /* * 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/VpnProfileListFragment.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileListFragment.java index c56962d72..051604f3e 100644 --- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileListFragment.java +++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileListFragment.java @@ -89,18 +89,24 @@ public class VpnProfileListFragment extends Fragment } else if ((uuids = intent.getStringArrayExtra(Constants.VPN_PROFILES_MULTIPLE)) != null) { - for (String id : uuids) + for (final String id : uuids) { - Iterator profiles = mVpnProfiles.iterator(); + final Iterator profiles = mVpnProfiles.iterator(); while (profiles.hasNext()) { - VpnProfile profile = profiles.next(); + final VpnProfile profile = profiles.next(); if (Objects.equals(profile.getUUID().toString(), id)) - { + { /* in case this was an edit, we remove it first */ profiles.remove(); break; } } + + VpnProfile profile = mDataSource.getVpnProfile(id); + if (profile != null) + { + mVpnProfiles.add(profile); + } } mListAdapter.notifyDataSetChanged(); }