Loading src/com/android/settings/vpn2/AppVpnInfo.java 0 → 100644 +46 −0 Original line number Diff line number Diff line package com.android.settings.vpn2; import android.annotation.NonNull; import com.android.internal.util.Preconditions; import java.util.Objects; /** * Holds packageName:userId pairs without any heavyweight fields. * {@see ApplicationInfo} */ class AppVpnInfo implements Comparable { public final int userId; public final String packageName; public AppVpnInfo(int userId, @NonNull String packageName) { this.userId = userId; this.packageName = Preconditions.checkNotNull(packageName); } @Override public int compareTo(Object other) { AppVpnInfo that = (AppVpnInfo) other; int result = packageName.compareTo(that.packageName); if (result == 0) { result = that.userId - userId; } return result; } @Override public boolean equals(Object other) { if (other instanceof AppVpnInfo) { AppVpnInfo that = (AppVpnInfo) other; return userId == that.userId && Objects.equals(packageName, that.packageName); } return false; } @Override public int hashCode() { return Objects.hash(packageName, userId); } } src/com/android/settings/vpn2/LockdownConfigFragment.java +46 −5 Original line number Diff line number Diff line Loading @@ -21,22 +21,28 @@ import android.app.Dialog; import android.app.DialogFragment; import android.content.Context; import android.content.DialogInterface; import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.ConnectivityManager; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; import android.security.Credentials; import android.security.KeyStore; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.Toast; import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnProfile; import com.android.settings.R; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** Loading @@ -44,10 +50,12 @@ import java.util.List; */ public class LockdownConfigFragment extends DialogFragment { private List<VpnProfile> mProfiles; private List<AppVpnInfo> mApps; private List<CharSequence> mTitles; private int mCurrentIndex; private static final String TAG_LOCKDOWN = "lockdown"; private static final String LOG_TAG = "LockdownConfigFragment"; private static class TitleAdapter extends ArrayAdapter<CharSequence> { public TitleAdapter(Context context, List<CharSequence> objects) { Loading @@ -69,19 +77,43 @@ public class LockdownConfigFragment extends DialogFragment { } private void initProfiles(KeyStore keyStore, Resources res) { final ConnectivityManager cm = ConnectivityManager.from(getActivity()); final String lockdownKey = getStringOrNull(keyStore, Credentials.LOCKDOWN_VPN); final String alwaysOnPackage = cm.getAlwaysOnVpnPackageForUser(UserHandle.myUserId()); // Legacy VPN has a separate always-on mechanism which takes over the whole device, so // this option is restricted to the primary user only. if (UserManager.get(getContext()).isPrimaryUser()) { mProfiles = VpnSettings.loadVpnProfiles(keyStore, VpnProfile.TYPE_PPTP); mTitles = new ArrayList<>(1 + mProfiles.size()); mTitles.add(res.getText(R.string.vpn_lockdown_none)); } else { mProfiles = Collections.<VpnProfile>emptyList(); } mApps = VpnSettings.getVpnApps(getActivity(), /* includeProfiles */ false); mTitles = new ArrayList<>(1 + mProfiles.size() + mApps.size()); mTitles.add(res.getText(R.string.vpn_lockdown_none)); mCurrentIndex = 0; // Add true lockdown VPNs to the list first. for (VpnProfile profile : mProfiles) { if (TextUtils.equals(profile.key, lockdownKey)) { mCurrentIndex = mTitles.size(); } mTitles.add(profile.name); } // Add third-party app VPNs (VpnService) for the current profile to set as always-on. for (AppVpnInfo app : mApps) { try { String appName = VpnConfig.getVpnLabel(getContext(), app.packageName).toString(); if (TextUtils.equals(app.packageName, alwaysOnPackage)) { mCurrentIndex = mTitles.size(); } mTitles.add(appName); } catch (PackageManager.NameNotFoundException pkgNotFound) { Log.w(LOG_TAG, "VPN package not found: '" + app.packageName + "'", pkgNotFound); } } } @Override Loading Loading @@ -109,21 +141,30 @@ public class LockdownConfigFragment extends DialogFragment { final int newIndex = listView.getCheckedItemPosition(); if (mCurrentIndex == newIndex) return; final ConnectivityManager conn = ConnectivityManager.from(getActivity()); if (newIndex == 0) { keyStore.delete(Credentials.LOCKDOWN_VPN); } else { conn.setAlwaysOnVpnPackageForUser(UserHandle.myUserId(), null); } else if (newIndex <= mProfiles.size()) { final VpnProfile profile = mProfiles.get(newIndex - 1); if (!profile.isValidLockdownProfile()) { Toast.makeText(context, R.string.vpn_lockdown_config_error, Toast.LENGTH_LONG).show(); return; } conn.setAlwaysOnVpnPackageForUser(UserHandle.myUserId(), null); keyStore.put(Credentials.LOCKDOWN_VPN, profile.key.getBytes(), KeyStore.UID_SELF, /* flags */ 0); } else { keyStore.delete(Credentials.LOCKDOWN_VPN); final AppVpnInfo appVpn = mApps.get(newIndex - 1 - mProfiles.size()); conn.setAlwaysOnVpnPackageForUser(appVpn.userId, appVpn.packageName); } // kick profiles since we changed them ConnectivityManager.from(getActivity()).updateLockdownVpn(); conn.updateLockdownVpn(); } }); Loading src/com/android/settings/vpn2/VpnSettings.java +15 −35 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.settings.vpn2; import android.annotation.NonNull; import android.annotation.UiThread; import android.annotation.WorkerThread; import android.app.AppOpsManager; Loading Loading @@ -219,7 +218,7 @@ public class VpnSettings extends SettingsPreferenceFragment implements mUpdater.removeMessages(RESCAN_MESSAGE); final List<VpnProfile> vpnProfiles = loadVpnProfiles(mKeyStore); final List<AppVpnInfo> vpnApps = getVpnApps(); final List<AppVpnInfo> vpnApps = getVpnApps(getActivity(), /* includeProfiles */ true); final List<LegacyVpnInfo> connectedLegacyVpns = getConnectedLegacyVpns(); final List<AppVpnInfo> connectedAppVpns = getConnectedAppVpns(); Loading Loading @@ -418,22 +417,26 @@ public class VpnSettings extends SettingsPreferenceFragment implements return connections; } private List<AppVpnInfo> getVpnApps() { static List<AppVpnInfo> getVpnApps(Context context, boolean includeProfiles) { List<AppVpnInfo> result = Lists.newArrayList(); // Build a filter of currently active user profiles. Set<Integer> currentProfileIds = new ArraySet<>(); for (UserHandle profile : mUserManager.getUserProfiles()) { currentProfileIds.add(profile.getIdentifier()); final Set<Integer> profileIds; if (includeProfiles) { profileIds = new ArraySet<>(); for (UserHandle profile : UserManager.get(context).getUserProfiles()) { profileIds.add(profile.getIdentifier()); } } else { profileIds = Collections.singleton(UserHandle.myUserId()); } // Fetch VPN-enabled apps from AppOps. AppOpsManager aom = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE); AppOpsManager aom = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); List<AppOpsManager.PackageOps> apps = aom.getPackagesForOps(new int[] {OP_ACTIVATE_VPN}); if (apps != null) { for (AppOpsManager.PackageOps pkg : apps) { int userId = UserHandle.getUserId(pkg.getUid()); if (!currentProfileIds.contains(userId)) { if (!profileIds.contains(userId)) { // Skip packages for users outside of our profile group. continue; } Loading @@ -450,10 +453,12 @@ public class VpnSettings extends SettingsPreferenceFragment implements } } } Collections.sort(result); return result; } protected static List<VpnProfile> loadVpnProfiles(KeyStore keyStore, int... excludeTypes) { static List<VpnProfile> loadVpnProfiles(KeyStore keyStore, int... excludeTypes) { final ArrayList<VpnProfile> result = Lists.newArrayList(); for (String key : keyStore.list(Credentials.VPN)) { Loading @@ -464,29 +469,4 @@ public class VpnSettings extends SettingsPreferenceFragment implements } return result; } /** Utility holder for packageName:userId pairs */ private static class AppVpnInfo { public int userId; public String packageName; public AppVpnInfo(int userId, @NonNull String packageName) { this.userId = userId; this.packageName = packageName; } @Override public boolean equals(Object other) { if (other instanceof AppVpnInfo) { AppVpnInfo that = (AppVpnInfo) other; return userId == that.userId && packageName.equals(that.packageName); } return false; } @Override public int hashCode() { return (packageName != null ? packageName.hashCode() : 0) * 31 + userId; } } } Loading
src/com/android/settings/vpn2/AppVpnInfo.java 0 → 100644 +46 −0 Original line number Diff line number Diff line package com.android.settings.vpn2; import android.annotation.NonNull; import com.android.internal.util.Preconditions; import java.util.Objects; /** * Holds packageName:userId pairs without any heavyweight fields. * {@see ApplicationInfo} */ class AppVpnInfo implements Comparable { public final int userId; public final String packageName; public AppVpnInfo(int userId, @NonNull String packageName) { this.userId = userId; this.packageName = Preconditions.checkNotNull(packageName); } @Override public int compareTo(Object other) { AppVpnInfo that = (AppVpnInfo) other; int result = packageName.compareTo(that.packageName); if (result == 0) { result = that.userId - userId; } return result; } @Override public boolean equals(Object other) { if (other instanceof AppVpnInfo) { AppVpnInfo that = (AppVpnInfo) other; return userId == that.userId && Objects.equals(packageName, that.packageName); } return false; } @Override public int hashCode() { return Objects.hash(packageName, userId); } }
src/com/android/settings/vpn2/LockdownConfigFragment.java +46 −5 Original line number Diff line number Diff line Loading @@ -21,22 +21,28 @@ import android.app.Dialog; import android.app.DialogFragment; import android.content.Context; import android.content.DialogInterface; import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.ConnectivityManager; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; import android.security.Credentials; import android.security.KeyStore; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.Toast; import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnProfile; import com.android.settings.R; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** Loading @@ -44,10 +50,12 @@ import java.util.List; */ public class LockdownConfigFragment extends DialogFragment { private List<VpnProfile> mProfiles; private List<AppVpnInfo> mApps; private List<CharSequence> mTitles; private int mCurrentIndex; private static final String TAG_LOCKDOWN = "lockdown"; private static final String LOG_TAG = "LockdownConfigFragment"; private static class TitleAdapter extends ArrayAdapter<CharSequence> { public TitleAdapter(Context context, List<CharSequence> objects) { Loading @@ -69,19 +77,43 @@ public class LockdownConfigFragment extends DialogFragment { } private void initProfiles(KeyStore keyStore, Resources res) { final ConnectivityManager cm = ConnectivityManager.from(getActivity()); final String lockdownKey = getStringOrNull(keyStore, Credentials.LOCKDOWN_VPN); final String alwaysOnPackage = cm.getAlwaysOnVpnPackageForUser(UserHandle.myUserId()); // Legacy VPN has a separate always-on mechanism which takes over the whole device, so // this option is restricted to the primary user only. if (UserManager.get(getContext()).isPrimaryUser()) { mProfiles = VpnSettings.loadVpnProfiles(keyStore, VpnProfile.TYPE_PPTP); mTitles = new ArrayList<>(1 + mProfiles.size()); mTitles.add(res.getText(R.string.vpn_lockdown_none)); } else { mProfiles = Collections.<VpnProfile>emptyList(); } mApps = VpnSettings.getVpnApps(getActivity(), /* includeProfiles */ false); mTitles = new ArrayList<>(1 + mProfiles.size() + mApps.size()); mTitles.add(res.getText(R.string.vpn_lockdown_none)); mCurrentIndex = 0; // Add true lockdown VPNs to the list first. for (VpnProfile profile : mProfiles) { if (TextUtils.equals(profile.key, lockdownKey)) { mCurrentIndex = mTitles.size(); } mTitles.add(profile.name); } // Add third-party app VPNs (VpnService) for the current profile to set as always-on. for (AppVpnInfo app : mApps) { try { String appName = VpnConfig.getVpnLabel(getContext(), app.packageName).toString(); if (TextUtils.equals(app.packageName, alwaysOnPackage)) { mCurrentIndex = mTitles.size(); } mTitles.add(appName); } catch (PackageManager.NameNotFoundException pkgNotFound) { Log.w(LOG_TAG, "VPN package not found: '" + app.packageName + "'", pkgNotFound); } } } @Override Loading Loading @@ -109,21 +141,30 @@ public class LockdownConfigFragment extends DialogFragment { final int newIndex = listView.getCheckedItemPosition(); if (mCurrentIndex == newIndex) return; final ConnectivityManager conn = ConnectivityManager.from(getActivity()); if (newIndex == 0) { keyStore.delete(Credentials.LOCKDOWN_VPN); } else { conn.setAlwaysOnVpnPackageForUser(UserHandle.myUserId(), null); } else if (newIndex <= mProfiles.size()) { final VpnProfile profile = mProfiles.get(newIndex - 1); if (!profile.isValidLockdownProfile()) { Toast.makeText(context, R.string.vpn_lockdown_config_error, Toast.LENGTH_LONG).show(); return; } conn.setAlwaysOnVpnPackageForUser(UserHandle.myUserId(), null); keyStore.put(Credentials.LOCKDOWN_VPN, profile.key.getBytes(), KeyStore.UID_SELF, /* flags */ 0); } else { keyStore.delete(Credentials.LOCKDOWN_VPN); final AppVpnInfo appVpn = mApps.get(newIndex - 1 - mProfiles.size()); conn.setAlwaysOnVpnPackageForUser(appVpn.userId, appVpn.packageName); } // kick profiles since we changed them ConnectivityManager.from(getActivity()).updateLockdownVpn(); conn.updateLockdownVpn(); } }); Loading
src/com/android/settings/vpn2/VpnSettings.java +15 −35 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.settings.vpn2; import android.annotation.NonNull; import android.annotation.UiThread; import android.annotation.WorkerThread; import android.app.AppOpsManager; Loading Loading @@ -219,7 +218,7 @@ public class VpnSettings extends SettingsPreferenceFragment implements mUpdater.removeMessages(RESCAN_MESSAGE); final List<VpnProfile> vpnProfiles = loadVpnProfiles(mKeyStore); final List<AppVpnInfo> vpnApps = getVpnApps(); final List<AppVpnInfo> vpnApps = getVpnApps(getActivity(), /* includeProfiles */ true); final List<LegacyVpnInfo> connectedLegacyVpns = getConnectedLegacyVpns(); final List<AppVpnInfo> connectedAppVpns = getConnectedAppVpns(); Loading Loading @@ -418,22 +417,26 @@ public class VpnSettings extends SettingsPreferenceFragment implements return connections; } private List<AppVpnInfo> getVpnApps() { static List<AppVpnInfo> getVpnApps(Context context, boolean includeProfiles) { List<AppVpnInfo> result = Lists.newArrayList(); // Build a filter of currently active user profiles. Set<Integer> currentProfileIds = new ArraySet<>(); for (UserHandle profile : mUserManager.getUserProfiles()) { currentProfileIds.add(profile.getIdentifier()); final Set<Integer> profileIds; if (includeProfiles) { profileIds = new ArraySet<>(); for (UserHandle profile : UserManager.get(context).getUserProfiles()) { profileIds.add(profile.getIdentifier()); } } else { profileIds = Collections.singleton(UserHandle.myUserId()); } // Fetch VPN-enabled apps from AppOps. AppOpsManager aom = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE); AppOpsManager aom = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); List<AppOpsManager.PackageOps> apps = aom.getPackagesForOps(new int[] {OP_ACTIVATE_VPN}); if (apps != null) { for (AppOpsManager.PackageOps pkg : apps) { int userId = UserHandle.getUserId(pkg.getUid()); if (!currentProfileIds.contains(userId)) { if (!profileIds.contains(userId)) { // Skip packages for users outside of our profile group. continue; } Loading @@ -450,10 +453,12 @@ public class VpnSettings extends SettingsPreferenceFragment implements } } } Collections.sort(result); return result; } protected static List<VpnProfile> loadVpnProfiles(KeyStore keyStore, int... excludeTypes) { static List<VpnProfile> loadVpnProfiles(KeyStore keyStore, int... excludeTypes) { final ArrayList<VpnProfile> result = Lists.newArrayList(); for (String key : keyStore.list(Credentials.VPN)) { Loading @@ -464,29 +469,4 @@ public class VpnSettings extends SettingsPreferenceFragment implements } return result; } /** Utility holder for packageName:userId pairs */ private static class AppVpnInfo { public int userId; public String packageName; public AppVpnInfo(int userId, @NonNull String packageName) { this.userId = userId; this.packageName = packageName; } @Override public boolean equals(Object other) { if (other instanceof AppVpnInfo) { AppVpnInfo that = (AppVpnInfo) other; return userId == that.userId && packageName.equals(that.packageName); } return false; } @Override public int hashCode() { return (packageName != null ? packageName.hashCode() : 0) * 31 + userId; } } }