am b5c48a4c
: Merge "VPN: UI for forgetting disconnected VPNs" into mnc-dev
* commit 'b5c48a4c347f7aa64b99e80370a63b3f60d3203c': VPN: UI for forgetting disconnected VPNs
This commit is contained in:
@@ -41,28 +41,25 @@ import com.android.settings.R;
|
|||||||
*/
|
*/
|
||||||
class AppDialog extends AlertDialog implements DialogInterface.OnClickListener {
|
class AppDialog extends AlertDialog implements DialogInterface.OnClickListener {
|
||||||
private final Listener mListener;
|
private final Listener mListener;
|
||||||
private final PackageInfo mPkgInfo;
|
private final PackageInfo mPackageInfo;
|
||||||
private final String mLabel;
|
private final String mLabel;
|
||||||
private final boolean mConnected;
|
|
||||||
|
|
||||||
AppDialog(Context context, Listener listener, PackageInfo pkgInfo, String label,
|
AppDialog(Context context, Listener listener, PackageInfo pkgInfo, String label) {
|
||||||
boolean connected) {
|
|
||||||
super(context);
|
super(context);
|
||||||
|
|
||||||
mListener = listener;
|
mListener = listener;
|
||||||
mPkgInfo = pkgInfo;
|
mPackageInfo = pkgInfo;
|
||||||
mLabel = label;
|
mLabel = label;
|
||||||
mConnected = connected;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final PackageInfo getPackageInfo() {
|
public final PackageInfo getPackageInfo() {
|
||||||
return mPkgInfo;
|
return mPackageInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedState) {
|
protected void onCreate(Bundle savedState) {
|
||||||
setTitle(mLabel);
|
setTitle(mLabel);
|
||||||
setMessage(getContext().getString(R.string.vpn_version, mPkgInfo.versionName));
|
setMessage(getContext().getString(R.string.vpn_version, mPackageInfo.versionName));
|
||||||
|
|
||||||
createButtons();
|
createButtons();
|
||||||
super.onCreate(savedState);
|
super.onCreate(savedState);
|
||||||
@@ -71,11 +68,9 @@ class AppDialog extends AlertDialog implements DialogInterface.OnClickListener {
|
|||||||
protected void createButtons() {
|
protected void createButtons() {
|
||||||
Context context = getContext();
|
Context context = getContext();
|
||||||
|
|
||||||
if (mConnected) {
|
|
||||||
// Forget the network
|
// Forget the network
|
||||||
setButton(DialogInterface.BUTTON_NEGATIVE,
|
setButton(DialogInterface.BUTTON_NEGATIVE,
|
||||||
context.getString(R.string.vpn_forget), this);
|
context.getString(R.string.vpn_forget), this);
|
||||||
}
|
|
||||||
|
|
||||||
// Dismiss
|
// Dismiss
|
||||||
setButton(DialogInterface.BUTTON_POSITIVE,
|
setButton(DialogInterface.BUTTON_POSITIVE,
|
||||||
|
@@ -27,6 +27,7 @@ import android.net.IConnectivityManager;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
|
import android.os.UserHandle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.internal.net.VpnConfig;
|
import com.android.internal.net.VpnConfig;
|
||||||
@@ -41,18 +42,20 @@ public class AppDialogFragment extends DialogFragment implements AppDialog.Liste
|
|||||||
|
|
||||||
private static final String ARG_MANAGING = "managing";
|
private static final String ARG_MANAGING = "managing";
|
||||||
private static final String ARG_LABEL = "label";
|
private static final String ARG_LABEL = "label";
|
||||||
private static final String ARG_PACKAGE = "package";
|
|
||||||
private static final String ARG_CONNECTED = "connected";
|
private static final String ARG_CONNECTED = "connected";
|
||||||
|
private static final String ARG_PACKAGE = "package";
|
||||||
|
|
||||||
|
private PackageInfo mPackageInfo;
|
||||||
|
|
||||||
private final IConnectivityManager mService = IConnectivityManager.Stub.asInterface(
|
private final IConnectivityManager mService = IConnectivityManager.Stub.asInterface(
|
||||||
ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
|
ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
|
||||||
|
|
||||||
public static void show(VpnSettings parent, PackageInfo pkgInfo, String label, boolean managing,
|
public static void show(VpnSettings parent, PackageInfo packageInfo, String label,
|
||||||
boolean connected) {
|
boolean managing, boolean connected) {
|
||||||
if (!parent.isAdded()) return;
|
if (!parent.isAdded()) return;
|
||||||
|
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putParcelable(ARG_PACKAGE, pkgInfo);
|
args.putParcelable(ARG_PACKAGE, packageInfo);
|
||||||
args.putString(ARG_LABEL, label);
|
args.putString(ARG_LABEL, label);
|
||||||
args.putBoolean(ARG_MANAGING, managing);
|
args.putBoolean(ARG_MANAGING, managing);
|
||||||
args.putBoolean(ARG_CONNECTED, connected);
|
args.putBoolean(ARG_CONNECTED, connected);
|
||||||
@@ -66,13 +69,13 @@ public class AppDialogFragment extends DialogFragment implements AppDialog.Liste
|
|||||||
@Override
|
@Override
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
Bundle args = getArguments();
|
Bundle args = getArguments();
|
||||||
PackageInfo pkgInfo = (PackageInfo) args.getParcelable(ARG_PACKAGE);
|
final String label = args.getString(ARG_LABEL);
|
||||||
String label = args.getString(ARG_LABEL);
|
|
||||||
boolean managing = args.getBoolean(ARG_MANAGING);
|
boolean managing = args.getBoolean(ARG_MANAGING);
|
||||||
boolean connected = args.getBoolean(ARG_CONNECTED);
|
boolean connected = args.getBoolean(ARG_CONNECTED);
|
||||||
|
mPackageInfo = (PackageInfo) args.getParcelable(ARG_PACKAGE);
|
||||||
|
|
||||||
if (managing) {
|
if (managing) {
|
||||||
return new AppDialog(getActivity(), this, pkgInfo, label, connected);
|
return new AppDialog(getActivity(), this, mPackageInfo, label);
|
||||||
} else {
|
} else {
|
||||||
// Build an AlertDialog with an option to disconnect.
|
// Build an AlertDialog with an option to disconnect.
|
||||||
AlertDialog.Builder dlog = new AlertDialog.Builder(getActivity())
|
AlertDialog.Builder dlog = new AlertDialog.Builder(getActivity())
|
||||||
@@ -93,12 +96,6 @@ public class AppDialogFragment extends DialogFragment implements AppDialog.Liste
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dismiss() {
|
|
||||||
((VpnSettings) getTargetFragment()).update();
|
|
||||||
super.dismiss();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCancel(DialogInterface dialog) {
|
public void onCancel(DialogInterface dialog) {
|
||||||
dismiss();
|
dismiss();
|
||||||
@@ -107,25 +104,29 @@ public class AppDialogFragment extends DialogFragment implements AppDialog.Liste
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onForget(final DialogInterface dialog) {
|
public void onForget(final DialogInterface dialog) {
|
||||||
PackageInfo pkgInfo = (PackageInfo) getArguments().getParcelable(ARG_PACKAGE);
|
final int userId = UserHandle.getUserId(mPackageInfo.applicationInfo.uid);
|
||||||
final String pkg = pkgInfo.packageName;
|
|
||||||
try {
|
try {
|
||||||
VpnConfig vpnConfig = mService.getVpnConfig();
|
mService.setVpnPackageAuthorization(mPackageInfo.packageName, userId, false);
|
||||||
if (vpnConfig != null && pkg.equals(vpnConfig.user) && !vpnConfig.legacy) {
|
|
||||||
mService.setVpnPackageAuthorization(false);
|
|
||||||
onDisconnect(dialog);
|
onDisconnect(dialog);
|
||||||
}
|
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(TAG, "Failed to forget authorization for " + pkg, e);
|
Log.e(TAG, "Failed to forget authorization of " + mPackageInfo.packageName +
|
||||||
|
" for user " + userId, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDisconnect(final DialogInterface dialog) {
|
private void onDisconnect(final DialogInterface dialog) {
|
||||||
PackageInfo pkgInfo = (PackageInfo) getArguments().getParcelable(ARG_PACKAGE);
|
final int userId = UserHandle.getUserId(mPackageInfo.applicationInfo.uid);
|
||||||
try {
|
try {
|
||||||
mService.prepareVpn(pkgInfo.packageName, VpnConfig.LEGACY_VPN);
|
final VpnConfig vpnConfig = mService.getVpnConfig(userId);
|
||||||
|
if (vpnConfig == null || vpnConfig.legacy) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mPackageInfo.packageName.equals(vpnConfig.user)) {
|
||||||
|
mService.prepareVpn(mPackageInfo.packageName, VpnConfig.LEGACY_VPN, userId);
|
||||||
|
}
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(TAG, "Failed to disconnect package " + pkgInfo.packageName, e);
|
Log.e(TAG, "Failed to disconnect package " + mPackageInfo.packageName +
|
||||||
|
" for user " + userId, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,6 +24,7 @@ import android.net.IConnectivityManager;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
|
import android.os.UserHandle;
|
||||||
import android.security.Credentials;
|
import android.security.Credentials;
|
||||||
import android.security.KeyStore;
|
import android.security.KeyStore;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -127,12 +128,6 @@ public class ConfigDialogFragment extends DialogFragment implements
|
|||||||
dismiss();
|
dismiss();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dismiss() {
|
|
||||||
((VpnSettings) getTargetFragment()).update();
|
|
||||||
super.dismiss();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCancel(DialogInterface dialog) {
|
public void onCancel(DialogInterface dialog) {
|
||||||
dismiss();
|
dismiss();
|
||||||
@@ -151,7 +146,8 @@ public class ConfigDialogFragment extends DialogFragment implements
|
|||||||
try {
|
try {
|
||||||
LegacyVpnInfo connected = mService.getLegacyVpnInfo();
|
LegacyVpnInfo connected = mService.getLegacyVpnInfo();
|
||||||
if (connected != null && profile.key.equals(connected.key)) {
|
if (connected != null && profile.key.equals(connected.key)) {
|
||||||
mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
|
mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN,
|
||||||
|
UserHandle.myUserId());
|
||||||
}
|
}
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(TAG, "Failed to disconnect", e);
|
Log.e(TAG, "Failed to disconnect", e);
|
||||||
|
@@ -71,6 +71,9 @@ public class VpnSettings extends SettingsPreferenceFragment implements
|
|||||||
Handler.Callback, Preference.OnPreferenceClickListener {
|
Handler.Callback, Preference.OnPreferenceClickListener {
|
||||||
private static final String LOG_TAG = "VpnSettings";
|
private static final String LOG_TAG = "VpnSettings";
|
||||||
|
|
||||||
|
private static final int RESCAN_MESSAGE = 0;
|
||||||
|
private static final int RESCAN_INTERVAL_MS = 1000;
|
||||||
|
|
||||||
private static final String EXTRA_PICK_LOCKDOWN = "android.net.vpn.PICK_LOCKDOWN";
|
private static final String EXTRA_PICK_LOCKDOWN = "android.net.vpn.PICK_LOCKDOWN";
|
||||||
private static final NetworkRequest VPN_REQUEST = new NetworkRequest.Builder()
|
private static final NetworkRequest VPN_REQUEST = new NetworkRequest.Builder()
|
||||||
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
|
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
|
||||||
@@ -90,7 +93,6 @@ public class VpnSettings extends SettingsPreferenceFragment implements
|
|||||||
|
|
||||||
private Handler mUpdater;
|
private Handler mUpdater;
|
||||||
private LegacyVpnInfo mConnectedLegacyVpn;
|
private LegacyVpnInfo mConnectedLegacyVpn;
|
||||||
private HashSet<String> mConnectedVpns = new HashSet<>();
|
|
||||||
|
|
||||||
private boolean mUnavailable;
|
private boolean mUnavailable;
|
||||||
|
|
||||||
@@ -111,7 +113,6 @@ public class VpnSettings extends SettingsPreferenceFragment implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
mConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
|
mConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
mConnectivityManager.registerNetworkCallback(VPN_REQUEST, mNetworkCallback);
|
|
||||||
|
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
addPreferencesFromResource(R.xml.vpn_settings2);
|
addPreferencesFromResource(R.xml.vpn_settings2);
|
||||||
@@ -179,10 +180,32 @@ public class VpnSettings extends SettingsPreferenceFragment implements
|
|||||||
LockdownConfigFragment.show(this);
|
LockdownConfigFragment.show(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
update();
|
// Start monitoring
|
||||||
|
mConnectivityManager.registerNetworkCallback(VPN_REQUEST, mNetworkCallback);
|
||||||
|
|
||||||
|
// Trigger a refresh
|
||||||
|
if (mUpdater == null) {
|
||||||
|
mUpdater = new Handler(this);
|
||||||
|
}
|
||||||
|
mUpdater.sendEmptyMessage(RESCAN_MESSAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update() {
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
// Pause monitoring
|
||||||
|
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
|
||||||
|
|
||||||
|
if (mUpdater != null) {
|
||||||
|
mUpdater.removeCallbacksAndMessages(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handleMessage(Message message) {
|
||||||
|
mUpdater.removeMessages(RESCAN_MESSAGE);
|
||||||
|
|
||||||
// Pref group within which to list VPNs
|
// Pref group within which to list VPNs
|
||||||
PreferenceGroup vpnGroup = getPreferenceScreen();
|
PreferenceGroup vpnGroup = getPreferenceScreen();
|
||||||
vpnGroup.removeAll();
|
vpnGroup.removeAll();
|
||||||
@@ -200,18 +223,43 @@ public class VpnSettings extends SettingsPreferenceFragment implements
|
|||||||
|
|
||||||
// 3rd-party VPN apps can change elsewhere. Reload them every time.
|
// 3rd-party VPN apps can change elsewhere. Reload them every time.
|
||||||
for (AppOpsManager.PackageOps pkg : getVpnApps()) {
|
for (AppOpsManager.PackageOps pkg : getVpnApps()) {
|
||||||
|
String key = getVpnIdentifier(UserHandle.getUserId(pkg.getUid()), pkg.getPackageName());
|
||||||
final AppPreference pref = new AppPreference(getActivity(), mManageListener,
|
final AppPreference pref = new AppPreference(getActivity(), mManageListener,
|
||||||
pkg.getPackageName(), pkg.getUid());
|
pkg.getPackageName(), pkg.getUid());
|
||||||
pref.setOnPreferenceClickListener(this);
|
pref.setOnPreferenceClickListener(this);
|
||||||
mAppPreferences.put(pkg.getPackageName(), pref);
|
mAppPreferences.put(key, pref);
|
||||||
vpnGroup.addPreference(pref);
|
vpnGroup.addPreference(pref);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start monitoring.
|
// Mark out connections with a subtitle
|
||||||
if (mUpdater == null) {
|
try {
|
||||||
mUpdater = new Handler(this);
|
// Legacy VPNs
|
||||||
|
LegacyVpnInfo info = mConnectivityService.getLegacyVpnInfo();
|
||||||
|
if (info != null) {
|
||||||
|
ConfigPreference preference = mConfigPreferences.get(info.key);
|
||||||
|
if (preference != null) {
|
||||||
|
preference.setState(info.state);
|
||||||
|
mConnectedLegacyVpn = info;
|
||||||
}
|
}
|
||||||
mUpdater.sendEmptyMessage(0);
|
}
|
||||||
|
|
||||||
|
// Third-party VPNs
|
||||||
|
for (UserHandle profile : mUserManager.getUserProfiles()) {
|
||||||
|
VpnConfig cfg = mConnectivityService.getVpnConfig(profile.getIdentifier());
|
||||||
|
if (cfg != null) {
|
||||||
|
final String key = getVpnIdentifier(profile.getIdentifier(), cfg.user);
|
||||||
|
final AppPreference preference = mAppPreferences.get(key);
|
||||||
|
if (preference != null) {
|
||||||
|
preference.setState(AppPreference.STATE_CONNECTED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
mUpdater.sendEmptyMessageDelayed(RESCAN_MESSAGE, RESCAN_INTERVAL_MS);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -275,68 +323,22 @@ public class VpnSettings extends SettingsPreferenceFragment implements
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@Override
|
private static String getVpnIdentifier(int userId, String packageName) {
|
||||||
public boolean handleMessage(Message message) {
|
return Integer.toString(userId)+ "_" + packageName;
|
||||||
mUpdater.removeMessages(0);
|
|
||||||
|
|
||||||
if (isResumed()) {
|
|
||||||
try {
|
|
||||||
// Legacy VPNs
|
|
||||||
LegacyVpnInfo info = mConnectivityService.getLegacyVpnInfo();
|
|
||||||
if (mConnectedLegacyVpn != null) {
|
|
||||||
ConfigPreference preference = mConfigPreferences.get(mConnectedLegacyVpn.key);
|
|
||||||
if (preference != null) {
|
|
||||||
preference.setState(-1);
|
|
||||||
}
|
|
||||||
mConnectedLegacyVpn = null;
|
|
||||||
}
|
|
||||||
if (info != null) {
|
|
||||||
ConfigPreference preference = mConfigPreferences.get(info.key);
|
|
||||||
if (preference != null) {
|
|
||||||
preference.setState(info.state);
|
|
||||||
mConnectedLegacyVpn = info;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// VPN apps
|
|
||||||
for (String key : mConnectedVpns) {
|
|
||||||
AppPreference preference = mAppPreferences.get(key);
|
|
||||||
if (preference != null) {
|
|
||||||
preference.setState(AppPreference.STATE_DISCONNECTED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mConnectedVpns.clear();
|
|
||||||
// TODO: also query VPN services in user profiles STOPSHIP
|
|
||||||
VpnConfig cfg = mConnectivityService.getVpnConfig();
|
|
||||||
if (cfg != null) {
|
|
||||||
mConnectedVpns.add(cfg.user);
|
|
||||||
}
|
|
||||||
for (String key : mConnectedVpns) {
|
|
||||||
AppPreference preference = mAppPreferences.get(key);
|
|
||||||
if (preference != null) {
|
|
||||||
preference.setState(AppPreference.STATE_CONNECTED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
mUpdater.sendEmptyMessageDelayed(0, 1000);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private NetworkCallback mNetworkCallback = new NetworkCallback() {
|
private NetworkCallback mNetworkCallback = new NetworkCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onAvailable(Network network) {
|
public void onAvailable(Network network) {
|
||||||
if (mUpdater != null) {
|
if (mUpdater != null) {
|
||||||
mUpdater.sendEmptyMessage(0);
|
mUpdater.sendEmptyMessage(RESCAN_MESSAGE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLost(Network network) {
|
public void onLost(Network network) {
|
||||||
if (mUpdater != null) {
|
if (mUpdater != null) {
|
||||||
mUpdater.sendEmptyMessage(0);
|
mUpdater.sendEmptyMessage(RESCAN_MESSAGE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -22,6 +22,7 @@ import android.os.Bundle;
|
|||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
|
import android.os.UserHandle;
|
||||||
import android.security.Credentials;
|
import android.security.Credentials;
|
||||||
import android.security.KeyStore;
|
import android.security.KeyStore;
|
||||||
import android.security.NetworkSecurityPolicy;
|
import android.security.NetworkSecurityPolicy;
|
||||||
@@ -214,7 +215,7 @@ public class VpnTests extends InstrumentationTestCase {
|
|||||||
*/
|
*/
|
||||||
private void disconnect() throws Exception {
|
private void disconnect() throws Exception {
|
||||||
try {
|
try {
|
||||||
mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
|
mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, UserHandle.myUserId());
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(TAG, String.format("disconnect VPN exception: %s", e.toString()));
|
Log.e(TAG, String.format("disconnect VPN exception: %s", e.toString()));
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user