Per vpn setting change in VPN list

- Show admin support details when user taps on a cell and user restriction is on
- Show always-on-vpn active status in preference summary
- User can still open non-configurable per-VPN info page even when user restriction is applied
- Rename ConfigPreference to LegacyVpnPreference
- Move summary String handling into ManageablePreference
- ManageablePreference inherits GearPreference to reuse the code
- Don't show disconnect dialog when always-on is enabled

BUG=26950700

Change-Id: I37b087879cf3b674df528e7787d7bb1eead3310f
This commit is contained in:
Victor Chang
2016-03-15 18:48:08 +00:00
parent 16da2aa450
commit 14c2ac4dcb
6 changed files with 118 additions and 125 deletions

View File

@@ -1,39 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
<View
android:id="@+id/divider_manage"
android:layout_width="2dip"
android:layout_height="match_parent"
android:layout_marginTop="5dip"
android:layout_marginBottom="5dip"
android:background="@android:drawable/divider_horizontal_dark" />
<ImageView
android:id="@+id/manage"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:paddingStart="16dip"
android:paddingEnd="16dip"
android:src="@drawable/ic_sysbar_quicksettings"
android:contentDescription="@string/settings_label"
android:layout_gravity="center"
android:background="?android:attr/selectableItemBackground" />
</LinearLayout>

View File

@@ -3396,6 +3396,8 @@
because it runs in the same process as <xliff:g id="additional_apps_list">%2$s</xliff:g>:</string> because it runs in the same process as <xliff:g id="additional_apps_list">%2$s</xliff:g>:</string>
<!-- [CHAR_LIMIT=NONE] Format to put together two items in a list. --> <!-- [CHAR_LIMIT=NONE] Format to put together two items in a list. -->
<string name="join_two_items"><xliff:g id="first_item">%1$s</xliff:g> and <xliff:g id="second_item">%2$s</xliff:g></string> <string name="join_two_items"><xliff:g id="first_item">%1$s</xliff:g> and <xliff:g id="second_item">%2$s</xliff:g></string>
<!-- [CHAR_LIMIT=NONE] Format to put together two unrelated items in a list when "and" is not an appropriate conjunction for these 2 items -->
<string name="join_two_unrelated_items"><xliff:g id="first_item">%1$s</xliff:g>, <xliff:g id="second_item">%2$s</xliff:g></string>
<!-- [CHAR_LIMIT=NONE] Format to put the last item at the end of a series of 3 or more items in a list --> <!-- [CHAR_LIMIT=NONE] Format to put the last item at the end of a series of 3 or more items in a list -->
<string name="join_many_items_last"><xliff:g id="all_but_last_item">%1$s</xliff:g> and <xliff:g id="last_item">%2$s</xliff:g></string> <string name="join_many_items_last"><xliff:g id="all_but_last_item">%1$s</xliff:g> and <xliff:g id="last_item">%2$s</xliff:g></string>
<!-- [CHAR_LIMIT=NONE] Format to put the first item at the start of a series of 3 or more items in a list --> <!-- [CHAR_LIMIT=NONE] Format to put the first item at the start of a series of 3 or more items in a list -->
@@ -5207,6 +5209,8 @@
<string name="vpn_menu_lockdown">Always-on VPN</string> <string name="vpn_menu_lockdown">Always-on VPN</string>
<!-- Placeholder when VPN settings is open but no VPNs have been created. [CHAR LIMIT=100] --> <!-- Placeholder when VPN settings is open but no VPNs have been created. [CHAR LIMIT=100] -->
<string name="vpn_no_vpns_added">No VPNs added.</string> <string name="vpn_no_vpns_added">No VPNs added.</string>
<!-- Preference summary for active always-on vpn [CHAR LIMIT=40] -->
<string name="vpn_always_on_active">Always-on active</string>
<!-- Summary describing the always-on VPN feature. [CHAR LIMIT=NONE] --> <!-- Summary describing the always-on VPN feature. [CHAR LIMIT=NONE] -->
<string name="vpn_lockdown_summary">Select a VPN profile to always remain connected to. Network traffic will only be allowed when connected to this VPN.</string> <string name="vpn_lockdown_summary">Select a VPN profile to always remain connected to. Network traffic will only be allowed when connected to this VPN.</string>

View File

@@ -22,11 +22,9 @@ import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.UserHandle; import android.os.UserHandle;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.view.View.OnClickListener;
import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnConfig;
import com.android.settings.R;
/** /**
* {@link android.support.v7.preference.Preference} containing information about a VPN * {@link android.support.v7.preference.Preference} containing information about a VPN
@@ -39,10 +37,15 @@ public class AppPreference extends ManageablePreference {
private int mState = STATE_DISCONNECTED; private int mState = STATE_DISCONNECTED;
private String mPackageName; private String mPackageName;
private String mName; private String mName;
private int mUserId = UserHandle.USER_NULL;
public AppPreference(Context context, OnClickListener onManage) { public AppPreference(Context context) {
super(context, null /* attrs */, onManage); super(context, null /* attrs */);
}
@Override
public void setUserId(int userId) {
super.setUserId(userId);
update();
} }
public PackageInfo getPackageInfo() { public PackageInfo getPackageInfo() {
@@ -67,15 +70,6 @@ public class AppPreference extends ManageablePreference {
update(); update();
} }
public int getUserId() {
return mUserId;
}
public void setUserId(int userId) {
mUserId = userId;
update();
}
public int getState() { public int getState() {
return mState; return mState;
} }
@@ -90,8 +84,7 @@ public class AppPreference extends ManageablePreference {
return; return;
} }
final String[] states = getContext().getResources().getStringArray(R.array.vpn_states); setSummary(getSummaryString(mState == STATE_DISCONNECTED ? STATE_NONE : mState));
setSummary(mState != STATE_DISCONNECTED ? states[mState] : "");
mName = mPackageName; mName = mPackageName;
Drawable icon = null; Drawable icon = null;
@@ -140,9 +133,9 @@ public class AppPreference extends ManageablePreference {
result = mUserId - another.mUserId; result = mUserId - another.mUserId;
} }
return result; return result;
} else if (preference instanceof ConfigPreference) { } else if (preference instanceof LegacyVpnPreference) {
// Use comparator from ConfigPreference // Use comparator from ConfigPreference
ConfigPreference another = (ConfigPreference) preference; LegacyVpnPreference another = (LegacyVpnPreference) preference;
return -another.compareTo(this); return -another.compareTo(this);
} else { } else {
return super.compareTo(preference); return super.compareTo(preference);

View File

@@ -18,28 +18,24 @@ package com.android.settings.vpn2;
import android.content.Context; import android.content.Context;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.view.View.OnClickListener; import android.view.View;
import com.android.internal.net.VpnProfile; import com.android.internal.net.VpnProfile;
import com.android.settings.R; import com.android.settings.R;
import static com.android.internal.net.LegacyVpnInfo.STATE_CONNECTED; import static com.android.internal.net.LegacyVpnInfo.STATE_CONNECTED;
/** /**
* {@link android.support.v7.preference.Preference} referencing a VPN * {@link android.support.v7.preference.Preference} tracks the underlying legacy vpn profile and
* configuration. Tracks the underlying profile and its connection * its connection state.
* state.
*/ */
public class ConfigPreference extends ManageablePreference { public class LegacyVpnPreference extends ManageablePreference {
public static int STATE_NONE = -1;
private VpnProfile mProfile; private VpnProfile mProfile;
/** One of the STATE_* fields from LegacyVpnInfo, or STATE_NONE */ /** One of the STATE_* fields from LegacyVpnInfo, or STATE_NONE */
private int mState = STATE_NONE; private int mState = STATE_NONE;
ConfigPreference(Context context, OnClickListener onManage) { LegacyVpnPreference(Context context) {
super(context, null /* attrs */, onManage); super(context, null /* attrs */);
} }
public VpnProfile getProfile() { public VpnProfile getProfile() {
@@ -57,12 +53,7 @@ public class ConfigPreference extends ManageablePreference {
} }
private void update() { private void update() {
if (mState == STATE_NONE) { setSummary(getSummaryString(mState));
setSummary("");
} else {
final String[] states = getContext().getResources().getStringArray(R.array.vpn_states);
setSummary(states[mState]);
}
if (mProfile != null) { if (mProfile != null) {
setIcon(R.mipmap.ic_launcher_settings); setIcon(R.mipmap.ic_launcher_settings);
setTitle(mProfile.name); setTitle(mProfile.name);
@@ -72,8 +63,8 @@ public class ConfigPreference extends ManageablePreference {
@Override @Override
public int compareTo(Preference preference) { public int compareTo(Preference preference) {
if (preference instanceof ConfigPreference) { if (preference instanceof LegacyVpnPreference) {
ConfigPreference another = (ConfigPreference) preference; LegacyVpnPreference another = (LegacyVpnPreference) preference;
int result; int result;
if ((result = another.mState - mState) == 0 && if ((result = another.mState - mState) == 0 &&
(result = mProfile.name.compareToIgnoreCase(another.mProfile.name)) == 0 && (result = mProfile.name.compareToIgnoreCase(another.mProfile.name)) == 0 &&
@@ -93,5 +84,14 @@ public class ConfigPreference extends ManageablePreference {
return super.compareTo(preference); return super.compareTo(preference);
} }
} }
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.settings_button && isDisabledByAdmin()) {
performClick();
return;
}
super.onClick(v);
}
}

View File

@@ -17,35 +17,63 @@
package com.android.settings.vpn2; package com.android.settings.vpn2;
import android.content.Context; import android.content.Context;
import android.support.v7.preference.Preference; import android.content.res.Resources;
import android.support.v7.preference.PreferenceViewHolder; import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
import com.android.settings.GearPreference;
import com.android.settings.R; import com.android.settings.R;
/** /**
* Preference with an additional gear icon. Touching the gear icon triggers an * This class sets appropriate enabled state and user admin message when userId is set
* onChange event.
*/ */
public class ManageablePreference extends Preference { public abstract class ManageablePreference extends GearPreference {
OnClickListener mListener;
View mManageView;
public ManageablePreference(Context context, AttributeSet attrs, OnClickListener onManage) { public static int STATE_NONE = -1;
boolean mIsAlwaysOn = false;
int mUserId;
public ManageablePreference(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
mListener = onManage;
setPersistent(false); setPersistent(false);
setOrder(0); setOrder(0);
setWidgetLayoutResource(R.layout.preference_vpn); setUserId(UserHandle.myUserId());
} }
@Override public int getUserId() {
public void onBindViewHolder(PreferenceViewHolder view) { return mUserId;
mManageView = view.findViewById(R.id.manage); }
mManageView.setOnClickListener(mListener);
mManageView.setTag(this); public void setUserId(int userId) {
super.onBindViewHolder(view); mUserId = userId;
checkRestrictionAndSetDisabled(UserManager.DISALLOW_CONFIG_VPN, userId);
}
public boolean isAlwaysOn() {
return mIsAlwaysOn;
}
public void setAlwaysOn(boolean isEnabled) {
mIsAlwaysOn = isEnabled;
}
/**
* State is not shown for {@code STATE_NONE}
*
* @return summary string showing current connection state and always-on-vpn state
*/
protected String getSummaryString(int state) {
final Resources res = getContext().getResources();
final String[] states = res.getStringArray(R.array.vpn_states);
String summary = state == STATE_NONE ? "" : states[state];
if (mIsAlwaysOn) {
final String alwaysOnString = res.getString(R.string.vpn_always_on_active);
summary = TextUtils.isEmpty(summary) ? alwaysOnString : res.getString(
R.string.join_two_unrelated_items, summary, alwaysOnString);
}
return summary;
} }
} }

View File

@@ -48,13 +48,14 @@ import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View;
import com.android.internal.logging.MetricsProto.MetricsEvent; import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile; import com.android.internal.net.VpnProfile;
import com.android.internal.util.ArrayUtils; import com.android.internal.util.ArrayUtils;
import com.android.settings.GearPreference;
import com.android.settings.GearPreference.OnGearClickListener;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.RestrictedSettingsFragment; import com.android.settings.RestrictedSettingsFragment;
import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils;
@@ -93,7 +94,7 @@ public class VpnSettings extends RestrictedSettingsFragment implements
private final KeyStore mKeyStore = KeyStore.getInstance(); private final KeyStore mKeyStore = KeyStore.getInstance();
private Map<String, ConfigPreference> mConfigPreferences = new ArrayMap<>(); private Map<String, LegacyVpnPreference> mLegacyVpnPreferences = new ArrayMap<>();
private Map<AppVpnInfo, AppPreference> mAppPreferences = new ArrayMap<>(); private Map<AppVpnInfo, AppPreference> mAppPreferences = new ArrayMap<>();
private Handler mUpdater; private Handler mUpdater;
@@ -155,7 +156,7 @@ public class VpnSettings extends RestrictedSettingsFragment implements
case R.id.vpn_create: { case R.id.vpn_create: {
// Generate a new key. Here we just use the current time. // Generate a new key. Here we just use the current time.
long millis = System.currentTimeMillis(); long millis = System.currentTimeMillis();
while (mConfigPreferences.containsKey(Long.toHexString(millis))) { while (mLegacyVpnPreferences.containsKey(Long.toHexString(millis))) {
++millis; ++millis;
} }
VpnProfile profile = new VpnProfile(Long.toHexString(millis)); VpnProfile profile = new VpnProfile(Long.toHexString(millis));
@@ -229,7 +230,8 @@ public class VpnSettings extends RestrictedSettingsFragment implements
final List<LegacyVpnInfo> connectedLegacyVpns = getConnectedLegacyVpns(); final List<LegacyVpnInfo> connectedLegacyVpns = getConnectedLegacyVpns();
final List<AppVpnInfo> connectedAppVpns = getConnectedAppVpns(); final List<AppVpnInfo> connectedAppVpns = getConnectedAppVpns();
final Set<Integer> readOnlyUsers = getReadOnlyUserProfiles(); final Set<AppVpnInfo> alwaysOnAppVpnInfos = getAlwaysOnAppVpnInfos();
final String lockdownVpnKey = VpnUtils.getLockdownVpn();
// Refresh list of VPNs // Refresh list of VPNs
getActivity().runOnUiThread(new Runnable() { getActivity().runOnUiThread(new Runnable() {
@@ -244,20 +246,20 @@ public class VpnSettings extends RestrictedSettingsFragment implements
final Set<Preference> updates = new ArraySet<>(); final Set<Preference> updates = new ArraySet<>();
for (VpnProfile profile : vpnProfiles) { for (VpnProfile profile : vpnProfiles) {
ConfigPreference p = findOrCreatePreference(profile); LegacyVpnPreference p = findOrCreatePreference(profile);
p.setState(ConfigPreference.STATE_NONE); p.setState(LegacyVpnPreference.STATE_NONE);
p.setEnabled(!readOnlyUsers.contains(UserHandle.myUserId())); p.setAlwaysOn(lockdownVpnKey != null && lockdownVpnKey.equals(profile.key));
updates.add(p); updates.add(p);
} }
for (AppVpnInfo app : vpnApps) { for (AppVpnInfo app : vpnApps) {
AppPreference p = findOrCreatePreference(app); AppPreference p = findOrCreatePreference(app);
p.setState(AppPreference.STATE_DISCONNECTED); p.setState(AppPreference.STATE_DISCONNECTED);
p.setEnabled(!readOnlyUsers.contains(app.userId)); p.setAlwaysOn(alwaysOnAppVpnInfos.contains(app));
updates.add(p); updates.add(p);
} }
// Trim preferences for deleted VPNs // Trim preferences for deleted VPNs
mConfigPreferences.values().retainAll(updates); mLegacyVpnPreferences.values().retainAll(updates);
mAppPreferences.values().retainAll(updates); mAppPreferences.values().retainAll(updates);
final PreferenceGroup vpnGroup = getPreferenceScreen(); final PreferenceGroup vpnGroup = getPreferenceScreen();
@@ -277,7 +279,7 @@ public class VpnSettings extends RestrictedSettingsFragment implements
// Mark connected VPNs // Mark connected VPNs
for (LegacyVpnInfo info : connectedLegacyVpns) { for (LegacyVpnInfo info : connectedLegacyVpns) {
final ConfigPreference preference = mConfigPreferences.get(info.key); final LegacyVpnPreference preference = mLegacyVpnPreferences.get(info.key);
if (preference != null) { if (preference != null) {
preference.setState(info.state); preference.setState(info.state);
} }
@@ -297,8 +299,9 @@ public class VpnSettings extends RestrictedSettingsFragment implements
@Override @Override
public boolean onPreferenceClick(Preference preference) { public boolean onPreferenceClick(Preference preference) {
if (preference instanceof ConfigPreference) { if (preference instanceof LegacyVpnPreference) {
VpnProfile profile = ((ConfigPreference) preference).getProfile(); LegacyVpnPreference pref = (LegacyVpnPreference) preference;
VpnProfile profile = pref.getProfile();
if (mConnectedLegacyVpn != null && profile.key.equals(mConnectedLegacyVpn.key) && if (mConnectedLegacyVpn != null && profile.key.equals(mConnectedLegacyVpn.key) &&
mConnectedLegacyVpn.state == LegacyVpnInfo.STATE_CONNECTED) { mConnectedLegacyVpn.state == LegacyVpnInfo.STATE_CONNECTED) {
try { try {
@@ -312,6 +315,11 @@ public class VpnSettings extends RestrictedSettingsFragment implements
return true; return true;
} else if (preference instanceof AppPreference) { } else if (preference instanceof AppPreference) {
AppPreference pref = (AppPreference) preference; AppPreference pref = (AppPreference) preference;
if (pref.isAlwaysOn()) {
// User can't disconnect vpn when always-on is enabled
return true;
}
boolean connected = (pref.getState() == AppPreference.STATE_CONNECTED); boolean connected = (pref.getState() == AppPreference.STATE_CONNECTED);
if (!connected) { if (!connected) {
@@ -343,18 +351,15 @@ public class VpnSettings extends RestrictedSettingsFragment implements
return R.string.help_url_vpn; return R.string.help_url_vpn;
} }
private View.OnClickListener mManageListener = new View.OnClickListener() { private OnGearClickListener mGearListener = new OnGearClickListener() {
@Override @Override
public void onClick(View view) { public void onGearClick(GearPreference p) {
Object tag = view.getTag(); if (p instanceof LegacyVpnPreference) {
LegacyVpnPreference pref = (LegacyVpnPreference) p;
if (tag instanceof ConfigPreference) {
ConfigPreference pref = (ConfigPreference) tag;
ConfigDialogFragment.show(VpnSettings.this, pref.getProfile(), true /* editing */, ConfigDialogFragment.show(VpnSettings.this, pref.getProfile(), true /* editing */,
true /* exists */); true /* exists */);
} else if (tag instanceof AppPreference) { } else if (p instanceof AppPreference) {
AppPreference pref = (AppPreference) tag; AppPreference pref = (AppPreference) p;;
boolean connected = (pref.getState() == AppPreference.STATE_CONNECTED);
AppManagementFragment.show(getPrefContext(), pref); AppManagementFragment.show(getPrefContext(), pref);
} }
} }
@@ -377,12 +382,13 @@ public class VpnSettings extends RestrictedSettingsFragment implements
}; };
@UiThread @UiThread
private ConfigPreference findOrCreatePreference(VpnProfile profile) { private LegacyVpnPreference findOrCreatePreference(VpnProfile profile) {
ConfigPreference pref = mConfigPreferences.get(profile.key); LegacyVpnPreference pref = mLegacyVpnPreferences.get(profile.key);
if (pref == null) { if (pref == null) {
pref = new ConfigPreference(getPrefContext(), mManageListener); pref = new LegacyVpnPreference(getPrefContext());
pref.setOnGearClickListener(mGearListener);
pref.setOnPreferenceClickListener(this); pref.setOnPreferenceClickListener(this);
mConfigPreferences.put(profile.key, pref); mLegacyVpnPreferences.put(profile.key, pref);
} }
pref.setProfile(profile); pref.setProfile(profile);
return pref; return pref;
@@ -392,7 +398,8 @@ public class VpnSettings extends RestrictedSettingsFragment implements
private AppPreference findOrCreatePreference(AppVpnInfo app) { private AppPreference findOrCreatePreference(AppVpnInfo app) {
AppPreference pref = mAppPreferences.get(app); AppPreference pref = mAppPreferences.get(app);
if (pref == null) { if (pref == null) {
pref = new AppPreference(getPrefContext(), mManageListener); pref = new AppPreference(getPrefContext());
pref.setOnGearClickListener(mGearListener);
pref.setOnPreferenceClickListener(this); pref.setOnPreferenceClickListener(this);
mAppPreferences.put(app, pref); mAppPreferences.put(app, pref);
} }
@@ -432,13 +439,13 @@ public class VpnSettings extends RestrictedSettingsFragment implements
} }
@WorkerThread @WorkerThread
private Set<Integer> getReadOnlyUserProfiles() { private Set<AppVpnInfo> getAlwaysOnAppVpnInfos() {
Set<Integer> result = new ArraySet<>(); Set<AppVpnInfo> result = new ArraySet<>();
for (UserHandle profile : mUserManager.getUserProfiles()) { for (UserHandle profile : mUserManager.getUserProfiles()) {
final int profileId = profile.getIdentifier(); final int profileId = profile.getIdentifier();
if (mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN, profile) final String packageName = mConnectivityManager.getAlwaysOnVpnPackageForUser(profileId);
|| mConnectivityManager.getAlwaysOnVpnPackageForUser(profileId) != null) { if (packageName != null) {
result.add(profileId); result.add(new AppVpnInfo(profileId, packageName));
} }
} }
return result; return result;