OSDN Git Service

VPN: UI for forgetting disconnected VPNs
authorRobin Lee <rgl@google.com>
Tue, 12 May 2015 17:35:37 +0000 (18:35 +0100)
committerRobin Lee <rgl@google.com>
Mon, 18 May 2015 21:55:15 +0000 (22:55 +0100)
Made possible by expanded internal VPN APIs.

Bug: 20872408
Bug: 20692490
Bug: 20747154
Change-Id: Idc87084a130afa18b27c85cc9d4b5033dcedaabe

src/com/android/settings/vpn2/AppDialog.java
src/com/android/settings/vpn2/AppDialogFragment.java
src/com/android/settings/vpn2/ConfigDialogFragment.java
src/com/android/settings/vpn2/VpnSettings.java
tests/src/com/android/settings/vpn2/VpnTests.java

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