summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorParménides GV <parmegv@sdf.org>2014-08-03 05:27:24 +0200
committerParménides GV <parmegv@sdf.org>2014-08-03 05:27:24 +0200
commit9b6f5499e6d5e9efd0b7b372c28b5c71d940e785 (patch)
tree6d8f9bf881b801282f89d2f263b6f35026ceb778
parent3790d1aef8feedd27ac7ca2a780b2475109315ed (diff)
Just a small glitch after cancelling a disconnect.
I don't use the eip status receiver because it wasn't reliable on timing: updated messages were arriving before the receiver was notifying a new state. Current and last ConnectionStatus is now managed at EIP. More refactoring on the eip fragment, now there are separate methods for setting up the UI depending on the message received.
-rw-r--r--app/src/main/AndroidManifest.xml9
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/Dashboard.java17
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/EIP.java17
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/EipServiceFragment.java249
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/EipStatusReceiver.java17
-rw-r--r--app/src/main/res/values/strings.xml4
6 files changed, 163 insertions, 150 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a90d6396..da710080 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -53,15 +53,6 @@
</intent-filter>
</receiver>
- <receiver
- android:name="se.leap.bitmaskclient.EipStatusReceiver"
- android:enabled="true"
- android:permission="android.permission.ACCESS_NETWORK_STATE" >
- <intent-filter>
- <action android:name="de.blinkt.openvpn.VPN_STATUS" />
- </intent-filter>
- </receiver>
-
<activity
android:theme="@android:style/Theme.DeviceDefault.Light.Dialog"
android:name="de.blinkt.openvpn.activities.DisconnectVPN" />
diff --git a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
index 8aa09de5..851cd3c4 100644
--- a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
+++ b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
@@ -66,11 +66,12 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
final public static String ON_BOOT = "dashboard on boot";
public static final String APP_VERSION = "bitmask version";
-
+
+ private EipServiceFragment eipFragment;
private ProgressBar mProgressBar;
private TextView eipStatus;
private static Context app;
- private static SharedPreferences preferences;
+ protected static SharedPreferences preferences;
private static Provider provider;
private TextView providerNameTV;
@@ -86,9 +87,6 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
app = this;
PRNGFixes.apply();
- // mProgressBar = (ProgressBar) findViewById(R.id.progressbar_dashboard);
- // mProgressBar = (ProgressBar) findViewById(R.id.eipProgress);
- // eipStatus = (TextView) findViewById(R.id.eipStatus);
mProgressBar = (ProgressBar) findViewById(R.id.eipProgress);
@@ -109,7 +107,6 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
if(lastDetectedVersion == 0) // New install
getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE).edit().putInt(APP_VERSION, versionCode);
else if(lastDetectedVersion < versionCode) {
- preferences.edit().remove(EIP.STATUS).commit();
}
} catch (NameNotFoundException e) {
}
@@ -193,7 +190,7 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
FragmentManager fragMan = getFragmentManager();
if ( provider.hasEIP()){
- EipServiceFragment eipFragment = new EipServiceFragment();
+ eipFragment = new EipServiceFragment();
if (hide_and_turn_on_eip) {
getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE).edit().remove(Dashboard.START_ON_BOOT).commit();
Bundle arguments = new Bundle();
@@ -596,10 +593,4 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
mProgressBar = (ProgressBar) findViewById(R.id.eipProgress);
mProgressBar.setVisibility(visibility);
}
-
- protected void setEipStatus(int status) {
- if(eipStatus == null)
- eipStatus = (TextView) findViewById(R.id.eipStatus);
- eipStatus.setText(status);
- }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/EIP.java b/app/src/main/java/se/leap/bitmaskclient/EIP.java
index 07bc7f9a..7b2a6d95 100644
--- a/app/src/main/java/se/leap/bitmaskclient/EIP.java
+++ b/app/src/main/java/se/leap/bitmaskclient/EIP.java
@@ -41,6 +41,7 @@ import de.blinkt.openvpn.core.OpenVpnManagementThread;
import de.blinkt.openvpn.core.OpenVpnService.LocalBinder;
import de.blinkt.openvpn.core.OpenVpnService;
import de.blinkt.openvpn.core.ProfileManager;
+import de.blinkt.openvpn.core.VpnStatus.ConnectionStatus;
import java.io.IOException;
import java.io.StringReader;
import java.security.cert.CertificateExpiredException;
@@ -109,6 +110,10 @@ public final class EIP extends IntentService {
private static JSONObject eipDefinition = null;
private static OVPNGateway activeGateway = null;
+
+ protected static ConnectionStatus lastConnectionStatusLevel;
+ protected static boolean mIsDisconnecting = false;
+ protected static boolean mIsStarting = false;
public EIP(){
super("LEAPEIP");
@@ -169,10 +174,6 @@ public final class EIP extends IntentService {
Log.d(TAG, "isRunning() = " + is_connected);
}
-
- private boolean isConnected() {
- return getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE).getString(STATUS, "").equalsIgnoreCase("LEVEL_CONNECTED");
- }
/**
* Initiates an EIP connection by selecting a gateway and preparing and sending an
@@ -207,7 +208,9 @@ public final class EIP extends IntentService {
Intent disconnect_vpn = new Intent(this, DisconnectVPN.class);
disconnect_vpn.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(disconnect_vpn);
- // getSharedPreferences(Dashboard.SHARED_PREFERENCES, Activity.MODE_PRIVATE).edit().remove(EIP.STATUS).commit();
+ mIsDisconnecting = true;
+ lastConnectionStatusLevel = ConnectionStatus.UNKNOWN_LEVEL; // Wait for the decision of the user
+ Log.d(TAG, "mIsDisconnecting = true");
}
if (mReceiver != null){
@@ -217,6 +220,10 @@ public final class EIP extends IntentService {
}
}
+ protected static boolean isConnected() {
+ return lastConnectionStatusLevel != null && lastConnectionStatusLevel.equals(ConnectionStatus.LEVEL_CONNECTED) && !mIsDisconnecting;
+ }
+
/**
* Loads eip-service.json from SharedPreferences and calls {@link updateGateways()}
* to parse gateway definitions.
diff --git a/app/src/main/java/se/leap/bitmaskclient/EipServiceFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipServiceFragment.java
index 04cf18b2..a41f0d19 100644
--- a/app/src/main/java/se/leap/bitmaskclient/EipServiceFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/EipServiceFragment.java
@@ -40,10 +40,6 @@ public class EipServiceFragment extends Fragment implements StateListener, OnChe
private View eipDetail;
private TextView eipStatus;
- // private boolean eipAutoSwitched = true;
-
- private boolean mEipStartPending = false;
-
private static EIPReceiver mEIPReceiver;
@@ -60,8 +56,8 @@ public class EipServiceFragment extends Fragment implements StateListener, OnChe
View eipSettings = eipFragment.findViewById(R.id.eipSettings);
eipSettings.setVisibility(View.GONE); // FIXME too!
- if (mEipStartPending)
- eipFragment.findViewById(R.id.eipProgress).setVisibility(View.VISIBLE);
+ if (EIP.mIsStarting)
+ eipFragment.findViewById(R.id.eipProgress).setVisibility(View.VISIBLE);
eipStatus = (TextView) eipFragment.findViewById(R.id.eipStatus);
@@ -81,7 +77,7 @@ public class EipServiceFragment extends Fragment implements StateListener, OnChe
mEIPReceiver = new EIPReceiver(new Handler());
if (savedInstanceState != null)
- mEipStartPending = savedInstanceState.getBoolean(IS_EIP_PENDING);
+ EIP.mIsStarting = savedInstanceState.getBoolean(IS_EIP_PENDING);
}
@Override
@@ -91,11 +87,19 @@ public class EipServiceFragment extends Fragment implements StateListener, OnChe
VpnStatus.addStateListener(this);
eipCommand(EIP.ACTION_CHECK_CERT_VALIDITY);
-
- if(isEipConnected()) {
- eipSwitch.setChecked(true);
- }
}
+
+ private void adjustSwitch() {
+ if(EIP.isConnected()) {
+ if(!eipSwitch.isChecked()) {
+ eipSwitch.setChecked(true);
+ }
+ } else {
+ if(eipSwitch.isChecked()) {
+ eipSwitch.setChecked(false);
+ }
+ }
+ }
@Override
public void onPause() {
@@ -107,7 +111,7 @@ public class EipServiceFragment extends Fragment implements StateListener, OnChe
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- outState.putBoolean(IS_EIP_PENDING, mEipStartPending);
+ outState.putBoolean(IS_EIP_PENDING, EIP.mIsStarting);
}
protected void saveEipStatus() {
@@ -118,87 +122,86 @@ public class EipServiceFragment extends Fragment implements StateListener, OnChe
}
if(getActivity() != null)
- getActivity().getSharedPreferences(Dashboard.SHARED_PREFERENCES, Activity.MODE_PRIVATE).edit().putBoolean(Dashboard.START_ON_BOOT, eip_is_on).commit();
+ Dashboard.preferences.edit().putBoolean(Dashboard.START_ON_BOOT, eip_is_on).commit();
}
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (buttonView.equals(eipSwitch)){
- handleEipSwitch(isChecked);
+ handleSwitch(isChecked);
}
}
-
- private boolean isAllowedAnon() {
- return getActivity().getSharedPreferences(Dashboard.SHARED_PREFERENCES, Activity.MODE_PRIVATE).getBoolean(EIP.ALLOWED_ANON, false);
- }
- private boolean isEipConnected() {
- return getEIPString(EIP.STATUS).equalsIgnoreCase("LEVEL_CONNECTED");
- }
- private String getEIPString(String feature) {
- return getActivity().getSharedPreferences(Dashboard.SHARED_PREFERENCES, Activity.MODE_PRIVATE).getString(feature, "");
- }
-
+
private boolean canStartEIP() {
- return (isAllowedAnon() || !getEIPString(EIP.CERTIFICATE).isEmpty()) && !mEipStartPending && !isEipConnected();
+ boolean certificateExists = !Dashboard.preferences.getString(EIP.CERTIFICATE, "").isEmpty();
+ boolean isAllowedAnon = Dashboard.preferences.getBoolean(EIP.ALLOWED_ANON, false);
+ return (isAllowedAnon || certificateExists) && !EIP.mIsStarting && !EIP.isConnected();
}
- private void handleEipSwitch(boolean isChecked) {
- if(isChecked) {
- handleEipSwitchOn();
- } else {
- handleEipSwitchOff();
- }
+ private void handleSwitch(boolean isChecked) {
+ if(isChecked)
+ handleSwitchOn();
+ else
+ handleSwitchOff();
+
saveEipStatus();
}
- private void handleEipSwitchOn() {
- if(canStartEIP()) {
+ private void handleSwitchOn() {
+ if(canStartEIP())
startEipFromScratch();
- }
}
- private void handleEipSwitchOff() {
- if(mEipStartPending) {
- AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getActivity());
- alertBuilder.setTitle(getResources().getString(R.string.eip_cancel_connect_title))
- .setMessage(getResources().getString(R.string.eip_cancel_connect_text))
- .setPositiveButton((R.string.eip_cancel_connect_cancel), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- stopEIP();
- }
- })
- .setNegativeButton(getResources().getString(R.string.eip_cancel_connect_cancel), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- eipSwitch.setChecked(true);
- }
- })
- .show();
- } else if(isEipConnected()) {
+ private void handleSwitchOff() {
+ if(EIP.mIsStarting) {
+ askPendingStartCancellation();
+ } else if(EIP.isConnected()) {
Log.d(TAG, "Stopping EIP");
stopEIP();
}
}
+ private void askPendingStartCancellation() {
+ AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getActivity());
+ alertBuilder.setTitle(getResources().getString(R.string.eip_cancel_connect_title))
+ .setMessage(getResources().getString(R.string.eip_cancel_connect_text))
+ .setPositiveButton((R.string.yes), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ stopEIP();
+ }
+ })
+ .setNegativeButton(getResources().getString(R.string.no), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Log.d(TAG, "askPendingStartCancellation checks the switch to true");
+ eipSwitch.setChecked(true);
+ }
+ })
+ .show();
+ }
+
public void startEipFromScratch() {
- mEipStartPending = true;
- eipFragment.findViewById(R.id.eipProgress).setVisibility(View.VISIBLE);
- eipStatus.setText(R.string.eip_status_start_pending);
+ EIP.mIsStarting = true;
+ eipFragment.findViewById(R.id.eipProgress).setVisibility(View.VISIBLE);
+ String status = getResources().getString(R.string.eip_status_start_pending);
+ setEipStatus(status);
if(!eipSwitch.isChecked()) {
- eipSwitch.setChecked(true);
+ Log.d(TAG, "startEipFromScratch checks the switch to true");
+ eipSwitch.setChecked(true);
saveEipStatus();
}
eipCommand(EIP.ACTION_START_EIP);
}
private void stopEIP() {
- mEipStartPending = false;
+ EIP.mIsStarting = false;
View eipProgressBar = getActivity().findViewById(R.id.eipProgress);
if(eipProgressBar != null)
eipProgressBar.setVisibility(View.GONE);
- if(eipStatus != null)
- eipStatus.setText(R.string.eip_state_not_connected);
+
+ String status = getResources().getString(R.string.eip_state_not_connected);
+ setEipStatus(status);
eipCommand(EIP.ACTION_STOP_EIP);
}
@@ -216,45 +219,86 @@ public class EipServiceFragment extends Fragment implements StateListener, OnChe
getActivity().startService(vpn_intent);
}
- @Override
- public void updateState(final String state, final String logmessage, final int localizedResId, final ConnectionStatus level) {
- // Note: "states" are not organized anywhere...collected state strings:
- // NOPROCESS,NONETWORK,BYTECOUNT,AUTH_FAILED + some parsing thing ( WAIT(?),AUTH,GET_CONFIG,ASSIGN_IP,CONNECTED,SIGINT )
- getActivity().runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- if (eipStatus != null) {
- boolean switchState = true;
- String statusMessage = "";
- String prefix = getString(localizedResId);
- if (level == ConnectionStatus.LEVEL_CONNECTED){
- statusMessage = getString(R.string.eip_state_connected);
- getActivity().findViewById(R.id.eipProgress).setVisibility(View.GONE);
- mEipStartPending = false; //TODO This should be done in the onReceiveResult from START_EIP command, but right now LaunchVPN isn't notifying anybody the resultcode of the request so we need to listen the states with this listener.
- } else if ( (level == ConnectionStatus.LEVEL_NONETWORK || level == ConnectionStatus.LEVEL_NOTCONNECTED || level == ConnectionStatus.LEVEL_AUTH_FAILED) && !mEipStartPending) {
- Log.d(TAG, "Not connected updated state");
- statusMessage = getString(R.string.eip_state_not_connected);
- if(getActivity() != null && getActivity().findViewById(R.id.eipProgress) != null)
- getActivity().findViewById(R.id.eipProgress).setVisibility(View.GONE);
- mEipStartPending = false; //TODO See above
- switchState = false;
- } else if (level == ConnectionStatus.LEVEL_CONNECTING_SERVER_REPLIED) {
- if(state.equals("AUTH") || state.equals("GET_CONFIG"))
- statusMessage = prefix + " " + logmessage;
- } else if (level == ConnectionStatus.LEVEL_CONNECTING_NO_SERVER_REPLY_YET) {
- statusMessage = prefix + " " + logmessage;
- }
-
- // eipAutoSwitched = true;
- // eipSwitch.setChecked(switchState);
- // eipAutoSwitched = false;
- eipStatus.setText(statusMessage);
- }
- }
+ @Override
+ public void updateState(final String state, final String logmessage, final int localizedResId, final ConnectionStatus level) {
+ boolean isNewLevel = EIP.lastConnectionStatusLevel != level;
+ boolean justDecidedOnDisconnect = EIP.lastConnectionStatusLevel == ConnectionStatus.UNKNOWN_LEVEL;
+ Log.d(TAG, "update state with level " + level);
+ if(isNewLevel && !justDecidedOnDisconnect) {
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ EIP.lastConnectionStatusLevel = level;
+ handleNewState(state, logmessage, localizedResId, level);
+ }
});
+ } else if(justDecidedOnDisconnect && level == ConnectionStatus.LEVEL_CONNECTED) {
+ EIP.lastConnectionStatusLevel = ConnectionStatus.LEVEL_NOTCONNECTED;
+ updateState(state, logmessage, localizedResId, level);
}
+ }
+ private void handleNewState(final String state, final String logmessage, final int localizedResId, final ConnectionStatus level) {
+ if (level == ConnectionStatus.LEVEL_CONNECTED)
+ setConnectedUI();
+ else if (isDisconnectedLevel(level) && !EIP.mIsStarting)
+ setDisconnectedUI();
+ else if (level == ConnectionStatus.LEVEL_CONNECTING_NO_SERVER_REPLY_YET)
+ setNoServerReplyUI(localizedResId, logmessage);
+ else if (level == ConnectionStatus.LEVEL_CONNECTING_SERVER_REPLIED)
+ setServerReplyUI(state, localizedResId, logmessage);
+ }
+
+ private boolean isDisconnectedLevel(final ConnectionStatus level) {
+ return level == ConnectionStatus.LEVEL_NONETWORK || level == ConnectionStatus.LEVEL_NOTCONNECTED || level == ConnectionStatus.LEVEL_AUTH_FAILED;
+ }
+
+ private void setConnectedUI() {
+ hideProgressBar();
+ Log.d(TAG, "mIsDisconnecting = false in setConnectedUI");
+ EIP.mIsStarting = false; //TODO This should be done in the onReceiveResult from START_EIP command, but right now LaunchVPN isn't notifying anybody the resultcode of the request so we need to listen the states with this listener.
+ EIP.mIsDisconnecting = false; //TODO See comment above
+ String status = getString(R.string.eip_state_connected);
+ setEipStatus(status);
+ adjustSwitch();
+ }
+
+ private void setDisconnectedUI(){
+ hideProgressBar();
+ EIP.mIsStarting = false; //TODO See comment in setConnectedUI()
+ Log.d(TAG, "mIsDisconnecting = false in setDisconnectedUI");
+ EIP.mIsDisconnecting = false; //TODO See comment in setConnectedUI()
+
+ String status = getString(R.string.eip_state_not_connected);
+ setEipStatus(status);
+ adjustSwitch();
+ }
+
+ private void setNoServerReplyUI(int localizedResId, String logmessage) {
+ if(eipStatus != null) {
+ String prefix = getString(localizedResId);
+ setEipStatus(prefix + " " + logmessage);
+ }
+ }
+
+ private void setServerReplyUI(String state, int localizedResId, String logmessage) {
+ if(eipStatus != null)
+ if(state.equals("AUTH") || state.equals("GET_CONFIG")) {
+ String prefix = getString(localizedResId);
+ setEipStatus(prefix + " " + logmessage);
+ }
+ }
+
+ protected void setEipStatus(String status) {
+ if(eipStatus == null)
+ eipStatus = (TextView) getActivity().findViewById(R.id.eipStatus);
+ eipStatus.setText(status);
+ }
+
+ private void hideProgressBar() {
+ if(getActivity() != null && getActivity().findViewById(R.id.eipProgress) != null)
+ getActivity().findViewById(R.id.eipProgress).setVisibility(View.GONE);
+ }
/**
* Inner class for handling messages related to EIP status and control requests
@@ -289,7 +333,7 @@ public class EipServiceFragment extends Fragment implements StateListener, OnChe
Log.d(TAG, "Action start eip = Result OK");
checked = true;
eipFragment.findViewById(R.id.eipProgress).setVisibility(View.VISIBLE);
- mEipStartPending = false;
+ EIP.mIsStarting = false;
break;
case Activity.RESULT_CANCELED:
checked = false;
@@ -322,9 +366,10 @@ public class EipServiceFragment extends Fragment implements StateListener, OnChe
break;
case Activity.RESULT_CANCELED:
Dashboard dashboard = (Dashboard) getActivity();
-
+
dashboard.setProgressBarVisibility(ProgressBar.VISIBLE);
- dashboard.setEipStatus(R.string.updating_certificate_message);
+ String status = getResources().getString(R.string.updating_certificate_message);
+ setEipStatus(status);
Intent provider_API_command = new Intent(getActivity(), ProviderAPI.class);
if(dashboard.providerAPI_result_receiver == null) {
@@ -338,10 +383,6 @@ public class EipServiceFragment extends Fragment implements StateListener, OnChe
break;
}
}
-
- // eipAutoSwitched = true;
- // eipSwitch.setChecked(checked);
- // eipAutoSwitched = false;
}
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/EipStatusReceiver.java b/app/src/main/java/se/leap/bitmaskclient/EipStatusReceiver.java
deleted file mode 100644
index 8793cf36..00000000
--- a/app/src/main/java/se/leap/bitmaskclient/EipStatusReceiver.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package se.leap.bitmaskclient;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Log;
-
-
-public class EipStatusReceiver extends BroadcastReceiver {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals("de.blinkt.openvpn.VPN_STATUS")) {
- context.getSharedPreferences(Dashboard.SHARED_PREFERENCES, Context.MODE_PRIVATE).edit().putString(EIP.STATUS, intent.getStringExtra("status")).commit();
- }
- }
-}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 1dbe8fee..cecb4ea1 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -67,8 +67,8 @@
<string name="eip_status_start_pending">Initiating connection</string>
<string name="eip_cancel_connect_title">Cancel connection?</string>
<string name="eip_cancel_connect_text">There is a connection attempt in progress. Do you wish to cancel it?</string>
- <string name="eip_cancel_connect_cancel">Yes</string>
- <string name="eip_cancel_connect_false">No</string>
+ <string name="yes">Yes</string>
+ <string name="no">No</string>
<string name="eip_state_not_connected">"Not running! Connection not secure!"</string>
<string name="eip_state_connected">Connection Secure.</string>
</resources>