android: Expose managed configuration globally and notify listeners on changes

Triggers a broadcast if the configuration changed and updates the
profile list accordingly (previously only handled removal of multiple
profiles).

If the app resumes, the configuration is also loaded and listeners are
notified in case the config was updated while the app was in the
background.
This commit is contained in:
Markus Pfeiffer 2023-11-21 15:37:22 +01:00 committed by Tobias Brunner
parent 8796e9bb31
commit 36f62585bb
3 changed files with 85 additions and 6 deletions

View File

@ -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'

View File

@ -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<String> 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.

View File

@ -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<VpnProfile> profiles = mVpnProfiles.iterator();
final Iterator<VpnProfile> 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();
}