Be more aggressive caching Vpn preference attrs

As any change to the preference title will cause it to lose focus,
best not to do this too often.

Change-Id: Ibac27ee1de42fd7ca05f3e3685b84f37dac39517
Fix: 28191965
diff --git a/src/com/android/settings/vpn2/AppPreference.java b/src/com/android/settings/vpn2/AppPreference.java
index fa9daf6..3369970 100644
--- a/src/com/android/settings/vpn2/AppPreference.java
+++ b/src/com/android/settings/vpn2/AppPreference.java
@@ -34,17 +34,43 @@
     public static final int STATE_CONNECTED = LegacyVpnInfo.STATE_CONNECTED;
     public static final int STATE_DISCONNECTED = STATE_NONE;
 
-    private String mPackageName;
-    private String mName;
+    private final String mPackageName;
+    private final String mName;
 
-    public AppPreference(Context context) {
+    public AppPreference(Context context, int userId, String packageName) {
         super(context, null /* attrs */);
-    }
-
-    @Override
-    public void setUserId(int userId) {
         super.setUserId(userId);
-        update();
+
+        mPackageName = packageName;
+
+        // Fetch icon and VPN label
+        String label = packageName;
+        Drawable icon = null;
+        try {
+            // Make all calls to the package manager as the appropriate user.
+            Context userContext = getUserContext();
+            PackageManager pm = userContext.getPackageManager();
+            // The nested catch block is for the case that the app doesn't exist, so we can fall
+            // back to the default activity icon.
+            try {
+                PackageInfo pkgInfo = pm.getPackageInfo(mPackageName, 0 /* flags */);
+                if (pkgInfo != null) {
+                    icon = pkgInfo.applicationInfo.loadIcon(pm);
+                    label = VpnConfig.getVpnLabel(userContext, mPackageName).toString();
+                }
+            } catch (PackageManager.NameNotFoundException pkgNotFound) {
+                // Use default app label and icon as fallback
+            }
+            if (icon == null) {
+                icon = pm.getDefaultActivityIcon();
+            }
+        } catch (PackageManager.NameNotFoundException userNotFound) {
+            // No user, no useful information to obtain. Quietly fail.
+        }
+        mName = label;
+
+        setTitle(mName);
+        setIcon(icon);
     }
 
     public PackageInfo getPackageInfo() {
@@ -64,48 +90,6 @@
         return mPackageName;
     }
 
-    public void setPackageName(String name) {
-        mPackageName = name;
-        update();
-    }
-
-    private void update() {
-        if (mPackageName == null || mUserId == UserHandle.USER_NULL) {
-            return;
-        }
-
-        mName = mPackageName;
-        Drawable icon = null;
-
-        try {
-            // Make all calls to the package manager as the appropriate user.
-            Context userContext = getUserContext();
-            PackageManager pm = userContext.getPackageManager();
-            // Fetch icon and VPN label- the nested catch block is for the case that the app doesn't
-            // exist, in which case we can fall back to the default activity icon for an activity in
-            // that user.
-            try {
-                PackageInfo pkgInfo = pm.getPackageInfo(mPackageName, 0 /* flags */);
-                if (pkgInfo != null) {
-                    icon = pkgInfo.applicationInfo.loadIcon(pm);
-                    mName = VpnConfig.getVpnLabel(userContext, mPackageName).toString();
-                }
-            } catch (PackageManager.NameNotFoundException pkgNotFound) {
-                // Use default app label and icon as fallback
-            }
-            if (icon == null) {
-                icon = pm.getDefaultActivityIcon();
-            }
-        } catch (PackageManager.NameNotFoundException userNotFound) {
-            // No user, no useful information to obtain. Quietly fail.
-        }
-        setTitle(mName);
-        setIcon(icon);
-        updateSummary();
-
-        notifyHierarchyChanged();
-    }
-
     private Context getUserContext() throws PackageManager.NameNotFoundException {
         UserHandle user = UserHandle.of(mUserId);
         return getContext().createPackageContextAsUser(
diff --git a/src/com/android/settings/vpn2/LegacyVpnPreference.java b/src/com/android/settings/vpn2/LegacyVpnPreference.java
index 2ce22d4..c1550e2 100644
--- a/src/com/android/settings/vpn2/LegacyVpnPreference.java
+++ b/src/com/android/settings/vpn2/LegacyVpnPreference.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.support.v7.preference.Preference;
+import android.text.TextUtils;
 import android.view.View;
 
 import com.android.internal.net.VpnProfile;
@@ -33,6 +34,7 @@
 
     LegacyVpnPreference(Context context) {
         super(context, null /* attrs */);
+        setIcon(R.mipmap.ic_launcher_settings);
     }
 
     public VpnProfile getProfile() {
@@ -40,12 +42,13 @@
     }
 
     public void setProfile(VpnProfile profile) {
-        mProfile = profile;
-        if (mProfile != null) {
-            setIcon(R.mipmap.ic_launcher_settings);
-            setTitle(mProfile.name);
+        final String oldLabel = (mProfile != null ? mProfile.name : null);
+        final String newLabel = (profile != null ? profile.name : null);
+        if (!TextUtils.equals(oldLabel, newLabel)) {
+            setTitle(newLabel);
+            notifyHierarchyChanged();
         }
-        notifyHierarchyChanged();
+        mProfile = profile;
     }
 
     @Override
diff --git a/src/com/android/settings/vpn2/ManageablePreference.java b/src/com/android/settings/vpn2/ManageablePreference.java
index 7c07e20..e31a396 100644
--- a/src/com/android/settings/vpn2/ManageablePreference.java
+++ b/src/com/android/settings/vpn2/ManageablePreference.java
@@ -62,13 +62,18 @@
     }
 
     public void setState(int state) {
-        mState = state;
-        updateSummary();
+        if (mState != state) {
+            mState = state;
+            updateSummary();
+            notifyHierarchyChanged();
+        }
     }
 
     public void setAlwaysOn(boolean isEnabled) {
-        mIsAlwaysOn = isEnabled;
-        updateSummary();
+        if (mIsAlwaysOn != isEnabled) {
+            mIsAlwaysOn = isEnabled;
+            updateSummary();
+        }
     }
 
     /**
diff --git a/src/com/android/settings/vpn2/VpnSettings.java b/src/com/android/settings/vpn2/VpnSettings.java
index 6c47b43..a675779 100644
--- a/src/com/android/settings/vpn2/VpnSettings.java
+++ b/src/com/android/settings/vpn2/VpnSettings.java
@@ -34,7 +34,6 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.security.Credentials;
@@ -211,8 +210,8 @@
         final List<VpnProfile> vpnProfiles = loadVpnProfiles(mKeyStore);
         final List<AppVpnInfo> vpnApps = getVpnApps(getActivity(), /* includeProfiles */ true);
 
-        final List<LegacyVpnInfo> connectedLegacyVpns = getConnectedLegacyVpns();
-        final List<AppVpnInfo> connectedAppVpns = getConnectedAppVpns();
+        final Map<String, LegacyVpnInfo> connectedLegacyVpns = getConnectedLegacyVpns();
+        final Set<AppVpnInfo> connectedAppVpns = getConnectedAppVpns();
 
         final Set<AppVpnInfo> alwaysOnAppVpnInfos = getAlwaysOnAppVpnInfos();
         final String lockdownVpnKey = VpnUtils.getLockdownVpn();
@@ -231,18 +230,26 @@
 
                 for (VpnProfile profile : vpnProfiles) {
                     LegacyVpnPreference p = findOrCreatePreference(profile);
-                    p.setState(LegacyVpnPreference.STATE_NONE);
+                    if (connectedLegacyVpns.containsKey(profile.key)) {
+                        p.setState(connectedLegacyVpns.get(profile.key).state);
+                    } else {
+                        p.setState(LegacyVpnPreference.STATE_NONE);
+                    }
                     p.setAlwaysOn(lockdownVpnKey != null && lockdownVpnKey.equals(profile.key));
                     updates.add(p);
                 }
                 for (AppVpnInfo app : vpnApps) {
                     AppPreference p = findOrCreatePreference(app);
-                    p.setState(AppPreference.STATE_DISCONNECTED);
+                    if (connectedAppVpns.contains(app)) {
+                        p.setState(AppPreference.STATE_CONNECTED);
+                    } else {
+                        p.setState(AppPreference.STATE_DISCONNECTED);
+                    }
                     p.setAlwaysOn(alwaysOnAppVpnInfos.contains(app));
                     updates.add(p);
                 }
 
-                // Trim preferences for deleted VPNs
+                // Trim out deleted VPN preferences
                 mLegacyVpnPreferences.values().retainAll(updates);
                 mAppPreferences.values().retainAll(updates);
 
@@ -260,20 +267,6 @@
                 for (Preference pref : updates) {
                     vpnGroup.addPreference(pref);
                 }
-
-                // Mark connected VPNs
-                for (LegacyVpnInfo info : connectedLegacyVpns) {
-                    final LegacyVpnPreference preference = mLegacyVpnPreferences.get(info.key);
-                    if (preference != null) {
-                        preference.setState(info.state);
-                    }
-                }
-                for (AppVpnInfo app : connectedAppVpns) {
-                    final AppPreference preference = mAppPreferences.get(app);
-                    if (preference != null) {
-                        preference.setState(AppPreference.STATE_CONNECTED);
-                    }
-                }
             }
         });
 
@@ -369,6 +362,7 @@
             pref.setOnPreferenceClickListener(this);
             mLegacyVpnPreferences.put(profile.key, pref);
         }
+        // This may change as the profile can update and keep the same key.
         pref.setProfile(profile);
         return pref;
     }
@@ -377,33 +371,31 @@
     private AppPreference findOrCreatePreference(AppVpnInfo app) {
         AppPreference pref = mAppPreferences.get(app);
         if (pref == null) {
-            pref = new AppPreference(getPrefContext());
+            pref = new AppPreference(getPrefContext(), app.userId, app.packageName);
             pref.setOnGearClickListener(mGearListener);
             pref.setOnPreferenceClickListener(this);
             mAppPreferences.put(app, pref);
         }
-        pref.setUserId(app.userId);
-        pref.setPackageName(app.packageName);
         return pref;
     }
 
     @WorkerThread
-    private List<LegacyVpnInfo> getConnectedLegacyVpns() {
+    private Map<String, LegacyVpnInfo> getConnectedLegacyVpns() {
         try {
             mConnectedLegacyVpn = mConnectivityService.getLegacyVpnInfo(UserHandle.myUserId());
             if (mConnectedLegacyVpn != null) {
-                return Collections.singletonList(mConnectedLegacyVpn);
+                return Collections.singletonMap(mConnectedLegacyVpn.key, mConnectedLegacyVpn);
             }
         } catch (RemoteException e) {
             Log.e(LOG_TAG, "Failure updating VPN list with connected legacy VPNs", e);
         }
-        return Collections.emptyList();
+        return Collections.emptyMap();
     }
 
     @WorkerThread
-    private List<AppVpnInfo> getConnectedAppVpns() {
+    private Set<AppVpnInfo> getConnectedAppVpns() {
         // Mark connected third-party services
-        List<AppVpnInfo> connections = new ArrayList<>();
+        Set<AppVpnInfo> connections = new ArraySet<>();
         try {
             for (UserHandle profile : mUserManager.getUserProfiles()) {
                 VpnConfig config = mConnectivityService.getVpnConfig(profile.getIdentifier());