summaryrefslogtreecommitdiff
path: root/app/src/main/java/de
diff options
context:
space:
mode:
authorcyBerta <cyberta@riseup.net>2019-07-06 17:37:48 +0200
committercyBerta <cyberta@riseup.net>2019-07-12 17:03:55 +0200
commit65a09aa799e525d3bc60f5f4f489a3d70c6a8554 (patch)
tree21664e9cc83c368395bacdc8abe3292c9a7db89d /app/src/main/java/de
parentf5b8dae753448ed698486af8b49b977a58d4fcdc (diff)
get rid of ics-openvpn's ProfileManager, reduces boilerplate and dead code
Diffstat (limited to 'app/src/main/java/de')
-rw-r--r--app/src/main/java/de/blinkt/openvpn/LaunchVPN.java19
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java68
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java6
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVPNStatusService.java2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java263
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/StatusListener.java109
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java33
8 files changed, 37 insertions, 465 deletions
diff --git a/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java b/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java
index 5bf178ae..60e4abb6 100644
--- a/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java
+++ b/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java
@@ -5,10 +5,6 @@
package de.blinkt.openvpn;
-import se.leap.bitmaskclient.R;
-
-import se.leap.bitmaskclient.R;
-
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
@@ -25,10 +21,12 @@ import java.io.IOException;
import de.blinkt.openvpn.core.ConnectionStatus;
import de.blinkt.openvpn.core.Preferences;
-import de.blinkt.openvpn.core.ProfileManager;
import de.blinkt.openvpn.core.VPNLaunchHelper;
import de.blinkt.openvpn.core.VpnStatus;
import se.leap.bitmaskclient.MainActivity;
+import se.leap.bitmaskclient.R;
+
+import static se.leap.bitmaskclient.Constants.PROVIDER_PROFILE;
/**
* This Activity actually handles two stages of a launcher shortcut's life cycle.
@@ -61,7 +59,6 @@ public class LaunchVPN extends Activity {
public static final String EXTRA_NAME = "de.blinkt.openvpn.shortcutProfileName";
public static final String EXTRA_HIDELOG = "de.blinkt.openvpn.showNoLogWindow";
public static final String CLEARLOG = "clearlogconnect";
- public static final String EXTRA_TEMP_VPN_PROFILE = "se.leap.bitmask.tempVpnProfile";
private static final int START_VPN_PROFILE = 70;
@@ -94,15 +91,8 @@ public class LaunchVPN extends Activity {
VpnStatus.clearLog();
// we got called to be the starting point, most likely a shortcut
- String shortcutUUID = intent.getStringExtra(EXTRA_KEY);
- String shortcutName = intent.getStringExtra(EXTRA_NAME);
mhideLog = intent.getBooleanExtra(EXTRA_HIDELOG, false);
- VpnProfile profileToConnect = (VpnProfile) intent.getExtras().getSerializable(EXTRA_TEMP_VPN_PROFILE);
- if (profileToConnect == null)
- profileToConnect = ProfileManager.get(this, shortcutUUID);
-
- if (shortcutName != null && profileToConnect == null)
- profileToConnect = ProfileManager.getInstance(this).getProfileByName(shortcutName);
+ VpnProfile profileToConnect = (VpnProfile) intent.getExtras().getSerializable(PROVIDER_PROFILE);
if (profileToConnect == null) {
VpnStatus.logError(R.string.shortcut_profile_notfound);
@@ -126,7 +116,6 @@ public class LaunchVPN extends Activity {
if(!mhideLog && showLogWindow)
showLogWindow();
- ProfileManager.updateLRU(this, mSelectedProfile);
VPNLaunchHelper.startOpenVpn(mSelectedProfile, getBaseContext());
finish();
diff --git a/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java b/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java
index c396f181..eeed29bc 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java
@@ -145,7 +145,7 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL
boolean screenOffPause = prefs.getBoolean("screenoff", false);
if (screenOffPause) {
- if (ProfileManager.getLastConnectedVpn() != null && !ProfileManager.getLastConnectedVpn().mPersistTun)
+ if (VpnStatus.getLastConnectedVpnProfile() != null && !VpnStatus.getLastConnectedVpnProfile().mPersistTun)
VpnStatus.logError(R.string.screen_nopersistenttun);
screen = connectState.PENDINGDISCONNECT;
diff --git a/app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java b/app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java
deleted file mode 100644
index 5b1307b7..00000000
--- a/app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (c) 2012-2016 Arne Schwabe
- * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
- */
-
-package de.blinkt.openvpn.core;
-
-import android.annotation.TargetApi;
-import android.app.Application;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.content.Context;
-import android.graphics.Color;
-import android.os.Build;
-
-/*
-import org.acra.ACRA;
-import org.acra.ReportingInteractionMode;
-import org.acra.annotation.ReportsCrashes;
-*/
-
-import se.leap.bitmaskclient.R;
-
-public class ICSOpenVPNApplication extends Application {
- private StatusListener mStatus;
-
- @Override
- public void onCreate() {
- super.onCreate();
- PRNGFixes.apply();
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
-
- createNotificationChannels();
- mStatus = new StatusListener();
- mStatus.init(getApplicationContext());
-
- }
-
- @TargetApi(Build.VERSION_CODES.O)
- private void createNotificationChannels() {
- NotificationManager mNotificationManager =
- (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-
- // Background message
- CharSequence name = getString(R.string.channel_name_background);
- NotificationChannel mChannel = new NotificationChannel(OpenVPNService.NOTIFICATION_CHANNEL_BG_ID,
- name, NotificationManager.IMPORTANCE_MIN);
-
- mChannel.setDescription(getString(R.string.channel_description_background));
- mChannel.enableLights(false);
-
- mChannel.setLightColor(Color.DKGRAY);
- mNotificationManager.createNotificationChannel(mChannel);
-
- // Connection status change messages
-
- name = getString(R.string.channel_name_status);
- mChannel = new NotificationChannel(OpenVPNService.NOTIFICATION_CHANNEL_NEWSTATUS_ID,
- name, NotificationManager.IMPORTANCE_LOW);
-
- mChannel.setDescription(getString(R.string.channel_description_status));
- mChannel.enableLights(true);
-
- mChannel.setLightColor(Color.BLUE);
- mNotificationManager.createNotificationChannel(mChannel);
- }
-} \ No newline at end of file
diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
index ea782e00..4e92f67b 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
@@ -322,7 +322,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
PreferenceHelper.setAlwaysOn(this, false);
} else {
/* The intent is null when we are set as always-on or the service has been restarted. */
- mProfile = ProfileManager.getLastConnectedProfile(this);
+ mProfile = VpnStatus.getLastConnectedVpnProfile(this);
VpnStatus.logInfo(R.string.service_restarted);
if (mProfile != null) {
@@ -344,9 +344,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
/* start the OpenVPN process itself in a background thread */
new Thread(this::startOpenVPN).start();
-
- ProfileManager.setConnectedVpnProfile(this, mProfile);
- VpnStatus.setConnectedVPNProfile(mProfile.getUUIDString());
+ VpnStatus.setLastConnectedVpnProfile(getApplicationContext(), mProfile);
return START_STICKY;
}
diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNStatusService.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNStatusService.java
index 6df1379a..b52e39a2 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNStatusService.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNStatusService.java
@@ -114,7 +114,7 @@ public class OpenVPNStatusService extends Service implements VpnStatus.LogListen
@Override
public String getLastConnectedVPN() throws RemoteException {
- return VpnStatus.getLastConnectedVPNProfile();
+ return VpnStatus.getLastConnectedVPNProfileId();
}
@Override
diff --git a/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java b/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java
deleted file mode 100644
index d897e91f..00000000
--- a/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright (c) 2012-2016 Arne Schwabe
- * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
- */
-
-package de.blinkt.openvpn.core;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
-
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Locale;
-import java.util.Set;
-import java.util.UUID;
-
-import de.blinkt.openvpn.VpnProfile;
-
-public class ProfileManager {
- private static final String PREFS_NAME = "VPNList";
-
- private static final String LAST_CONNECTED_PROFILE = "lastConnectedProfile";
- private static final String TEMPORARY_PROFILE_FILENAME = "temporary-vpn-profile";
- private static ProfileManager instance;
-
- private static VpnProfile mLastConnectedVpn = null;
- private HashMap<String, VpnProfile> profiles = new HashMap<>();
- private static VpnProfile tmpprofile = null;
-
-
- private static VpnProfile get(String key) {
- if (tmpprofile != null && tmpprofile.getUUIDString().equals(key))
- return tmpprofile;
-
- if (instance == null)
- return null;
- return instance.profiles.get(key);
-
- }
-
-
- private ProfileManager() {
- }
-
- private static void checkInstance(Context context) {
- if (instance == null) {
- instance = new ProfileManager();
- instance.loadVPNList(context);
- }
- }
-
- synchronized public static ProfileManager getInstance(Context context) {
- checkInstance(context);
- return instance;
- }
-
- public static void setConntectedVpnProfileDisconnected(Context c) {
- SharedPreferences prefs = Preferences.getDefaultSharedPreferences(c);
- Editor prefsedit = prefs.edit();
- prefsedit.putString(LAST_CONNECTED_PROFILE, null);
- prefsedit.apply();
-
- }
-
- /**
- * Sets the profile that is connected (to connect if the service restarts)
- */
- public static void setConnectedVpnProfile(Context c, VpnProfile connectedProfile) {
- SharedPreferences prefs = Preferences.getDefaultSharedPreferences(c);
- Editor prefsedit = prefs.edit();
-
- prefsedit.putString(LAST_CONNECTED_PROFILE, connectedProfile.toJson());
- prefsedit.apply();
- mLastConnectedVpn = connectedProfile;
-
- }
-
- /**
- * Returns the profile that was last connected (to connect if the service restarts)
- */
- public static VpnProfile getLastConnectedProfile(Context c) {
- SharedPreferences prefs = Preferences.getDefaultSharedPreferences(c);
-
- String lastConnectedProfileJson = prefs.getString(LAST_CONNECTED_PROFILE, null);
- return VpnProfile.fromJson(lastConnectedProfileJson);
- }
-
-
- public Collection<VpnProfile> getProfiles() {
- return profiles.values();
- }
-
- public VpnProfile getProfileByName(String name) {
- for (VpnProfile vpnp : profiles.values()) {
- if (vpnp.getName().equals(name)) {
- return vpnp;
- }
- }
- return null;
- }
-
- public void saveProfileList(Context context) {
- SharedPreferences sharedprefs = Preferences.getSharedPreferencesMulti(PREFS_NAME, context);
- Editor editor = sharedprefs.edit();
- editor.putStringSet("vpnlist", profiles.keySet());
-
- // For reasing I do not understand at all
- // Android saves my prefs file only one time
- // if I remove the debug code below :(
- int counter = sharedprefs.getInt("counter", 0);
- editor.putInt("counter", counter + 1);
- editor.apply();
-
- }
-
- public void addProfile(VpnProfile profile) {
- profiles.put(profile.getUUID().toString(), profile);
-
- }
-
- public static void setTemporaryProfile(Context c, VpnProfile tmp) {
- tmp.mTemporaryProfile = true;
- ProfileManager.tmpprofile = tmp;
- saveProfile(c, tmp, true, true);
- }
-
- public static boolean isTempProfile() {
- return mLastConnectedVpn != null && mLastConnectedVpn == tmpprofile;
- }
-
- public void saveProfile(Context context, VpnProfile profile) {
- saveProfile(context, profile, true, false);
- }
-
- private static void saveProfile(Context context, VpnProfile profile, boolean updateVersion, boolean isTemporary) {
-
- if (updateVersion)
- profile.mVersion += 1;
- ObjectOutputStream vpnFile;
-
- String filename = profile.getUUID().toString() + ".vp";
- if (isTemporary)
- filename = TEMPORARY_PROFILE_FILENAME + ".vp";
-
- try {
- vpnFile = new ObjectOutputStream(context.openFileOutput(filename, Activity.MODE_PRIVATE));
-
- vpnFile.writeObject(profile);
- vpnFile.flush();
- vpnFile.close();
- } catch (IOException e) {
- VpnStatus.logException("saving VPN profile", e);
- throw new RuntimeException(e);
- }
- }
-
-
- private void loadVPNList(Context context) {
- profiles = new HashMap<>();
- SharedPreferences listpref = Preferences.getSharedPreferencesMulti(PREFS_NAME, context);
- Set<String> vlist = listpref.getStringSet("vpnlist", null);
- if (vlist == null) {
- vlist = new HashSet<>();
- }
- // Always try to load the temporary profile
- vlist.add(TEMPORARY_PROFILE_FILENAME);
-
- for (String vpnentry : vlist) {
- ObjectInputStream vpnfile=null;
- try {
- vpnfile = new ObjectInputStream(context.openFileInput(vpnentry + ".vp"));
- VpnProfile vp = ((VpnProfile) vpnfile.readObject());
-
- // Sanity check
- if (vp == null || vp.mName == null || vp.getUUID() == null)
- continue;
-
- vp.upgradeProfile();
- if (vpnentry.equals(TEMPORARY_PROFILE_FILENAME)) {
- tmpprofile = vp;
- } else {
- profiles.put(vp.getUUID().toString(), vp);
- }
-
-
- } catch (IOException | ClassNotFoundException e) {
- if (!vpnentry.equals(TEMPORARY_PROFILE_FILENAME))
- VpnStatus.logException("Loading VPN List", e);
- } finally {
- if (vpnfile!=null) {
- try {
- vpnfile.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
- }
-
-
- public void removeProfile(Context context, VpnProfile profile) {
- String vpnentry = profile.getUUID().toString();
- profiles.remove(vpnentry);
- saveProfileList(context);
- context.deleteFile(vpnentry + ".vp");
- if (mLastConnectedVpn == profile)
- mLastConnectedVpn = null;
-
- }
-
- public static VpnProfile get(Context context, String profileUUID) {
- return get(context, profileUUID, 0, 10);
- }
-
- public static VpnProfile get(Context context, String profileUUID, int version, int tries) {
- checkInstance(context);
- VpnProfile profile = get(profileUUID);
- int tried = 0;
- while ((profile == null || profile.mVersion < version) && (tried++ < tries)) {
- try {
- Thread.sleep(100);
- } catch (InterruptedException ignored) {
- }
- instance.loadVPNList(context);
- profile = get(profileUUID);
- int ver = profile == null ? -1 : profile.mVersion;
- }
-
- if (tried > 5)
-
- {
- int ver = profile == null ? -1 : profile.mVersion;
- VpnStatus.logError(String.format(Locale.US, "Used x %d tries to get current version (%d/%d) of the profile", tried, ver, version));
- }
- return profile;
- }
-
- public static VpnProfile getLastConnectedVpn() {
- return mLastConnectedVpn;
- }
-
- public static VpnProfile getAlwaysOnVPN(Context context) {
- checkInstance(context);
- SharedPreferences prefs = Preferences.getDefaultSharedPreferences(context);
-
- String uuid = prefs.getString("alwaysOnVpn", null);
- return get(uuid);
- }
-
- public static void updateLRU(Context c, VpnProfile profile) {
- profile.mLastUsed = System.currentTimeMillis();
- // LRU does not change the profile, no need for the service to refresh
- if (profile!=tmpprofile)
- saveProfile(c, profile, false, false);
- }
-}
diff --git a/app/src/main/java/de/blinkt/openvpn/core/StatusListener.java b/app/src/main/java/de/blinkt/openvpn/core/StatusListener.java
deleted file mode 100644
index 5d0b7037..00000000
--- a/app/src/main/java/de/blinkt/openvpn/core/StatusListener.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (c) 2012-2016 Arne Schwabe
- * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
- */
-
-package de.blinkt.openvpn.core;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-
-import java.io.DataInputStream;
-import java.io.File;
-import java.io.IOException;
-
-/**
- * Created by arne on 09.11.16.
- */
-
-public class StatusListener {
- private File mCacheDir;
- private ServiceConnection mConnection = new ServiceConnection() {
-
-
- @Override
- public void onServiceConnected(ComponentName className,
- IBinder service) {
- // We've bound to LocalService, cast the IBinder and get LocalService instance
- IServiceStatus serviceStatus = IServiceStatus.Stub.asInterface(service);
- try {
- /* Check if this a local service ... */
- if (service.queryLocalInterface("de.blinkt.openvpn.core.IServiceStatus") == null) {
- // Not a local service
- VpnStatus.setConnectedVPNProfile(serviceStatus.getLastConnectedVPN());
- VpnStatus.setTrafficHistory(serviceStatus.getTrafficHistory());
- ParcelFileDescriptor pfd = serviceStatus.registerStatusCallback(mCallback);
- DataInputStream fd = new DataInputStream(new ParcelFileDescriptor.AutoCloseInputStream(pfd));
-
- short len = fd.readShort();
- byte[] buf = new byte[65336];
- while (len != 0x7fff) {
- fd.readFully(buf, 0, len);
- LogItem logitem = new LogItem(buf, len);
- VpnStatus.newLogItem(logitem, false);
- len = fd.readShort();
- }
- fd.close();
-
-
-
- } else {
- VpnStatus.initLogCache(mCacheDir);
- }
-
- } catch (RemoteException | IOException e) {
- e.printStackTrace();
- VpnStatus.logException(e);
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName arg0) {
-
- }
-
- };
-
- void init(Context c) {
-
- Intent intent = new Intent(c, OpenVPNStatusService.class);
- intent.setAction(OpenVPNService.START_SERVICE);
- mCacheDir = c.getCacheDir();
-
- c.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
-
-
- }
-
-
- private IStatusCallbacks mCallback = new IStatusCallbacks.Stub()
-
- {
- @Override
- public void newLogItem(LogItem item) throws RemoteException {
- VpnStatus.newLogItem(item);
- }
-
- @Override
- public void updateStateString(String state, String msg, int resid, ConnectionStatus
- level) throws RemoteException {
- VpnStatus.updateStateString(state, msg, resid, level);
- }
-
- @Override
- public void updateByteCount(long inBytes, long outBytes) throws RemoteException {
- VpnStatus.updateByteCount(inBytes, outBytes);
- }
-
- @Override
- public void connectedVPN(String uuid) throws RemoteException {
- VpnStatus.setConnectedVPNProfile(uuid);
- }
- };
-
-}
diff --git a/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java b/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java
index fa28a875..33851b8f 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java
@@ -18,7 +18,9 @@ import java.util.Locale;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;
+import de.blinkt.openvpn.VpnProfile;
import se.leap.bitmaskclient.R;
+import se.leap.bitmaskclient.utils.PreferenceHelper;
import static se.leap.bitmaskclient.utils.ConfigHelper.getProviderFormattedString;
@@ -39,10 +41,10 @@ public class VpnStatus {
private static HandlerThread mHandlerThread;
- private static String mLastConnectedVPNUUID;
static boolean readFileLog =false;
final static java.lang.Object readFileLock = new Object();
+ private static VpnProfile lastConnectedProfile;
public static TrafficHistory trafficHistory;
@@ -136,17 +138,40 @@ public class VpnStatus {
}
public static void setConnectedVPNProfile(String uuid) {
- mLastConnectedVPNUUID = uuid;
for (StateListener sl: stateListener)
sl.setConnectedVPN(uuid);
}
- public static String getLastConnectedVPNProfile()
+ public static String getLastConnectedVPNProfileId()
{
- return mLastConnectedVPNUUID;
+ return lastConnectedProfile != null ? lastConnectedProfile.getUUIDString() : null;
}
+ public static VpnProfile getLastConnectedVpnProfile() {
+ return lastConnectedProfile;
+ }
+
+ public static VpnProfile getLastConnectedVpnProfile(Context context) {
+ return PreferenceHelper.getLastConnectedVpnProfile(context);
+ }
+
+
+ /**
+ * Sets the profile that is connected (to connect if the service restarts)
+ */
+ public static void setLastConnectedVpnProfile(Context context, VpnProfile connectedProfile) {
+ PreferenceHelper.setLastUsedVpnProfile(context, connectedProfile);
+ lastConnectedProfile = connectedProfile;
+ setConnectedVPNProfile(lastConnectedProfile.getUUIDString());
+ }
+
+
+ public static String getLastConnectedVpnName() {
+ return lastConnectedProfile != null ? lastConnectedProfile.mName : null;
+ }
+
+
public static void setTrafficHistory(TrafficHistory trafficHistory) {
VpnStatus.trafficHistory = trafficHistory;
}