Display current vpn for Vpn preference summary text

Change-Id: Id9917a07e519d16e2000b4fa9888f783171e55f8
Fix: 34974598
Test: RunSettingsRoboTests
diff --git a/src/com/android/settings/network/MobileNetworkPreferenceController.java b/src/com/android/settings/network/MobileNetworkPreferenceController.java
index f55e753..c7abf90 100644
--- a/src/com/android/settings/network/MobileNetworkPreferenceController.java
+++ b/src/com/android/settings/network/MobileNetworkPreferenceController.java
@@ -26,7 +26,6 @@
 
 import com.android.settings.Utils;
 import com.android.settings.core.PreferenceController;
-import com.android.settings.core.lifecycle.Lifecycle;
 import com.android.settings.core.lifecycle.LifecycleObserver;
 import com.android.settings.core.lifecycle.events.OnPause;
 import com.android.settings.core.lifecycle.events.OnResume;
diff --git a/src/com/android/settings/network/NetworkDashboardFragment.java b/src/com/android/settings/network/NetworkDashboardFragment.java
index 9a811b8..f52230b 100644
--- a/src/com/android/settings/network/NetworkDashboardFragment.java
+++ b/src/com/android/settings/network/NetworkDashboardFragment.java
@@ -83,17 +83,20 @@
                 new WifiMasterSwitchPreferenceController(context, mMetricsFeatureProvider);
         final MobileNetworkPreferenceController mobileNetworkPreferenceController =
                 new MobileNetworkPreferenceController(context);
+        final VpnPreferenceController vpnPreferenceController =
+                new VpnPreferenceController(context);
         final Lifecycle lifecycle = getLifecycle();
         lifecycle.addObserver(airplaneModePreferenceController);
         lifecycle.addObserver(mobilePlanPreferenceController);
         lifecycle.addObserver(wifiPreferenceController);
         lifecycle.addObserver(mobileNetworkPreferenceController);
+        lifecycle.addObserver(vpnPreferenceController);
 
         final List<PreferenceController> controllers = new ArrayList<>();
         controllers.add(airplaneModePreferenceController);
         controllers.add(mobileNetworkPreferenceController);
         controllers.add(new TetherPreferenceController(context));
-        controllers.add(new VpnPreferenceController(context));
+        controllers.add(vpnPreferenceController);
         controllers.add(new ProxyPreferenceController(context));
         controllers.add(mobilePlanPreferenceController);
         controllers.add(wifiPreferenceController);
diff --git a/src/com/android/settings/network/VpnPreferenceController.java b/src/com/android/settings/network/VpnPreferenceController.java
index f7e230f..86ff175 100644
--- a/src/com/android/settings/network/VpnPreferenceController.java
+++ b/src/com/android/settings/network/VpnPreferenceController.java
@@ -16,38 +16,74 @@
 package com.android.settings.network;
 
 import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.net.ConnectivityManager;
+import android.net.IConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
+import android.util.Log;
+import android.util.SparseArray;
 
+import com.android.internal.net.LegacyVpnInfo;
+import com.android.internal.net.VpnConfig;
+import com.android.settings.R;
 import com.android.settings.core.PreferenceController;
+import com.android.settings.core.lifecycle.LifecycleObserver;
+import com.android.settings.core.lifecycle.events.OnPause;
+import com.android.settings.core.lifecycle.events.OnResume;
 import com.android.settingslib.RestrictedLockUtils;
 
+import java.util.List;
 
-public class VpnPreferenceController extends PreferenceController {
+
+public class VpnPreferenceController extends PreferenceController implements LifecycleObserver,
+        OnResume, OnPause {
 
     private static final String KEY_VPN_SETTINGS = "vpn_settings";
+    private static final NetworkRequest REQUEST = new NetworkRequest.Builder()
+            .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+            .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+            .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+            .build();
+    private static final String TAG = "VpnPreferenceController";
 
     private final String mToggleable;
-    private final boolean mIsSecondaryUser;
+    private final UserManager mUserManager;
+    private final ConnectivityManager mConnectivityManager;
+    private final IConnectivityManager mConnectivityManagerService;
+    private Preference mPreference;
 
     public VpnPreferenceController(Context context) {
         super(context);
         mToggleable = Settings.Global.getString(context.getContentResolver(),
                 Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
-        mIsSecondaryUser = !UserManager.get(context).isAdminUser();
+        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        mConnectivityManager =
+                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        mConnectivityManagerService = IConnectivityManager.Stub.asInterface(
+                ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
     }
 
     @Override
     public void displayPreference(PreferenceScreen screen) {
         super.displayPreference(screen);
+        mPreference = screen.findPreference(KEY_VPN_SETTINGS);
         // Manually set dependencies for Wifi when not toggleable.
         if (mToggleable == null || !mToggleable.contains(Settings.Global.RADIO_WIFI)) {
-            final Preference pref = screen.findPreference(KEY_VPN_SETTINGS);
-            if (pref != null) {
-                pref.setDependency(AirplaneModePreferenceController.KEY_TOGGLE_AIRPLANE);
+            if (mPreference != null) {
+                mPreference.setDependency(AirplaneModePreferenceController.KEY_TOGGLE_AIRPLANE);
             }
         }
     }
@@ -62,4 +98,96 @@
     public String getPreferenceKey() {
         return KEY_VPN_SETTINGS;
     }
+
+    @Override
+    public void onPause() {
+        if (isAvailable()) {
+            mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+        }
+    }
+
+    @Override
+    public void onResume() {
+        if (isAvailable()) {
+            mConnectivityManager.registerNetworkCallback(REQUEST, mNetworkCallback);
+        }
+    }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    void updateSummary() {
+        if (mPreference == null) {
+            return;
+        }
+        // Copied from SystemUI::SecurityControllerImpl
+        SparseArray<VpnConfig> vpns = new SparseArray<>();
+        try {
+            final List<UserInfo> users = mUserManager.getUsers();
+            for (UserInfo user : users) {
+                VpnConfig cfg = mConnectivityManagerService.getVpnConfig(user.id);
+                if (cfg == null) {
+                    continue;
+                } else if (cfg.legacy) {
+                    // Legacy VPNs should do nothing if the network is disconnected. Third-party
+                    // VPN warnings need to continue as traffic can still go to the app.
+                    final LegacyVpnInfo legacyVpn =
+                            mConnectivityManagerService.getLegacyVpnInfo(user.id);
+                    if (legacyVpn == null || legacyVpn.state != LegacyVpnInfo.STATE_CONNECTED) {
+                        continue;
+                    }
+                }
+                vpns.put(user.id, cfg);
+            }
+        } catch (RemoteException rme) {
+            // Roll back to previous state
+            Log.e(TAG, "Unable to list active VPNs", rme);
+            return;
+        }
+        final UserInfo userInfo = mUserManager.getUserInfo(UserHandle.myUserId());
+        final int uid;
+        if (userInfo.isRestricted()) {
+            uid = userInfo.restrictedProfileParentId;
+        } else {
+            uid = userInfo.id;
+        }
+        VpnConfig vpn = vpns.get(uid);
+        final String vpnName;
+        if (vpn == null) {
+            vpnName = null;
+        } else {
+            vpnName = getNameForVpnConfig(vpn, UserHandle.of(uid));
+        }
+        new Handler(Looper.getMainLooper()).post(() -> mPreference.setSummary(vpnName));
+    }
+
+    private String getNameForVpnConfig(VpnConfig cfg, UserHandle user) {
+        if (cfg.legacy) {
+            return mContext.getString(R.string.bluetooth_connected);
+        }
+        // The package name for an active VPN is stored in the 'user' field of its VpnConfig
+        final String vpnPackage = cfg.user;
+        try {
+            Context userContext = mContext.createPackageContextAsUser(mContext.getPackageName(),
+                    0 /* flags */, user);
+            return VpnConfig.getVpnLabel(userContext, vpnPackage).toString();
+        } catch (PackageManager.NameNotFoundException nnfe) {
+            Log.e(TAG, "Package " + vpnPackage + " is not present", nnfe);
+            return null;
+        }
+    }
+
+    // Copied from SystemUI::SecurityControllerImpl
+    private final ConnectivityManager.NetworkCallback
+            mNetworkCallback = new ConnectivityManager.NetworkCallback() {
+        @Override
+        public void onAvailable(Network network) {
+            Log.d(TAG, "onAvailable " + network.netId);
+            updateSummary();
+        }
+
+        @Override
+        public void onLost(Network network) {
+            Log.d(TAG, "onLost " + network.netId);
+            updateSummary();
+        }
+    };
 }
diff --git a/tests/robotests/src/com/android/settings/network/VpnPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/VpnPreferenceControllerTest.java
new file mode 100644
index 0000000..2a0b873
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/VpnPreferenceControllerTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network;
+
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.IConnectivityManager;
+import android.net.NetworkRequest;
+import android.os.IBinder;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowServiceManager;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class VpnPreferenceControllerTest {
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private ConnectivityManager mConnectivityManager;
+    @Mock
+    private IBinder mBinder;
+    @Mock
+    private IConnectivityManager mConnectivityManagerService;
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private Preference mPreference;
+    private VpnPreferenceController mController;
+    private Lifecycle mLifecycle;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
+                .thenReturn(mConnectivityManager);
+        when(mBinder.queryLocalInterface("android.net.IConnectivityManager"))
+                .thenReturn(mConnectivityManagerService);
+        ShadowServiceManager.addService(Context.CONNECTIVITY_SERVICE, mBinder);
+        when(mScreen.findPreference(anyString())).thenReturn(mPreference);
+
+        mController = spy(new VpnPreferenceController(mContext));
+        mLifecycle = new Lifecycle();
+        mLifecycle.addObserver(mController);
+    }
+
+    @Test
+    public void displayPreference_available_shouldSetDependency() {
+
+        doReturn(true).when(mController).isAvailable();
+        mController.displayPreference(mScreen);
+
+        verify(mPreference).setDependency(AirplaneModePreferenceController.KEY_TOGGLE_AIRPLANE);
+    }
+
+    @Test
+    public void goThroughLifecycle_shouldRegisterUnregisterListener() {
+        doReturn(true).when(mController).isAvailable();
+
+        mLifecycle.onResume();
+        verify(mConnectivityManager).registerNetworkCallback(
+                any(NetworkRequest.class), any(ConnectivityManager.NetworkCallback.class));
+
+        mLifecycle.onPause();
+        verify(mConnectivityManager).unregisterNetworkCallback(
+                any(ConnectivityManager.NetworkCallback.class));
+    }
+
+}