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:
Robin Lee
2015-05-19 10:25:58 +00:00
committed by Android Git Automerger
5 changed files with 99 additions and 104 deletions

View File

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

View File

@@ -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);
} }
} }
} }

View File

@@ -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);

View File

@@ -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);
} }
} }
}; };

View File

@@ -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()));
} }