From a0a87e810870b696239f0371d33de924f84cb431 Mon Sep 17 00:00:00 2001 From: Charles He Date: Mon, 15 May 2017 17:07:18 +0100 Subject: [PATCH 1/2] Opt-out for always-on VPN Always-on VPN is a feature introduced in N. Since then, all VPN apps targeting N+ are assumed to support the feature, and the user or the DPC can turn on / off always-on for any such VPN app. However, a few VPN apps are not designed to support the always-on feature. Enabling always-on for these apps will result in undefined behavior and confusing "Always-on VPN disconnected" notification. This feature provides a new manifest meta-data field through which a VPN app can opt out of the always-on feature explicitly. This will stop the always-on feature from being enabled for the app, both by the user and by the DPC, and will clear its existing always-on state. A @hide API is provided to check whether an app supports always-on VPN. Documentation is updated to reflect the behavior change. Bug: 36650087 Test: runtest --path java/com/android/server/connectivity/VpnTest.java Test: cts-tradefed run cts --module CtsDevicePolicyManagerTestCases --test 'com.android.cts.devicepolicy.MixedDeviceOwnerTest#testAlwaysOnVpnUnsupportedPackage' Test: cts-tradefed run cts --module CtsDevicePolicyManagerTestCases --test 'com.android.cts.devicepolicy.MixedDeviceOwnerTest#testAlwaysOnVpnUnsupportedPackageReplaced' Test: cts-tradefed run cts --module CtsDevicePolicyManagerTestCases --test 'com.android.cts.devicepolicy.MixedProfileOwnerTest#testAlwaysOnVpnUnsupportedPackage' Test: cts-tradefed run cts --module CtsDevicePolicyManagerTestCases --test 'com.android.cts.devicepolicy.MixedProfileOwnerTest#testAlwaysOnVpnUnsupportedPackageReplaced' Test: cts-tradefed run cts --module CtsDevicePolicyManagerTestCases --test 'com.android.cts.devicepolicy.MixedManagedProfileOwnerTest#testAlwaysOnVpnUnsupportedPackage' Test: cts-tradefed run cts --module CtsDevicePolicyManagerTestCases --test 'com.android.cts.devicepolicy.MixedManagedProfileOwnerTest#testAlwaysOnVpnUnsupportedPackageReplaced' Change-Id: I477897a29175e3994d4ecf8ec546e26043c90f13 Merged-In: I477897a29175e3994d4ecf8ec546e26043c90f13 (cherry picked from commit 3673863f3b2ec692201b7fb3089a1c05973e08e4) --- api/current.txt | 1 + api/system-current.txt | 1 + api/test-current.txt | 1 + .../app/admin/DevicePolicyManager.java | 27 +++----- .../java/android/net/ConnectivityManager.java | 23 +++++++ .../android/net/IConnectivityManager.aidl | 1 + core/java/android/net/VpnService.java | 31 ++++++++- .../android/server/ConnectivityService.java | 23 ++++++- .../com/android/server/connectivity/Vpn.java | 68 +++++++++++++++++++ .../android/server/connectivity/VpnTest.java | 57 +++++++++++++--- 10 files changed, 202 insertions(+), 31 deletions(-) diff --git a/api/current.txt b/api/current.txt index b2181657da7d4..0fcd1599b1a6b 100644 --- a/api/current.txt +++ b/api/current.txt @@ -26001,6 +26001,7 @@ package android.net { method public boolean protect(java.net.Socket); method public boolean protect(java.net.DatagramSocket); method public boolean setUnderlyingNetworks(android.net.Network[]); + field public static final java.lang.String METADATA_SUPPORTS_ALWAYS_ON = "android.net.VpnService.SUPPORTS_ALWAYS_ON"; field public static final java.lang.String SERVICE_INTERFACE = "android.net.VpnService"; } diff --git a/api/system-current.txt b/api/system-current.txt index 928346b0ac07f..a32ef4215ff86 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -28265,6 +28265,7 @@ package android.net { method public boolean protect(java.net.Socket); method public boolean protect(java.net.DatagramSocket); method public boolean setUnderlyingNetworks(android.net.Network[]); + field public static final java.lang.String METADATA_SUPPORTS_ALWAYS_ON = "android.net.VpnService.SUPPORTS_ALWAYS_ON"; field public static final java.lang.String SERVICE_INTERFACE = "android.net.VpnService"; } diff --git a/api/test-current.txt b/api/test-current.txt index 1dfca8ee14f34..5a708cf3d34ce 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -26110,6 +26110,7 @@ package android.net { method public boolean protect(java.net.Socket); method public boolean protect(java.net.DatagramSocket); method public boolean setUnderlyingNetworks(android.net.Network[]); + field public static final java.lang.String METADATA_SUPPORTS_ALWAYS_ON = "android.net.VpnService.SUPPORTS_ALWAYS_ON"; field public static final java.lang.String SERVICE_INTERFACE = "android.net.VpnService"; } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 01c46569d1e24..2463b6a39bf43 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -55,7 +55,6 @@ import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.provider.ContactsContract.Directory; -import android.provider.Settings; import android.security.Credentials; import android.service.restrictions.RestrictionsReceiver; import android.telephony.TelephonyManager; @@ -3902,28 +3901,20 @@ public class DevicePolicyManager { return null; } - /** - * Called by a device or profile owner to configure an always-on VPN connection through a - * specific application for the current user. - * - * @deprecated this version only exists for compability with previous developer preview builds. - * TODO: delete once there are no longer any live references. - * @hide - */ - @Deprecated - public void setAlwaysOnVpnPackage(@NonNull ComponentName admin, @Nullable String vpnPackage) - throws NameNotFoundException, UnsupportedOperationException { - setAlwaysOnVpnPackage(admin, vpnPackage, /* lockdownEnabled */ true); - } - /** * Called by a device or profile owner to configure an always-on VPN connection through a * specific application for the current user. This connection is automatically granted and * persisted after a reboot. *

- * The designated package should declare a {@link android.net.VpnService} in its manifest - * guarded by {@link android.Manifest.permission#BIND_VPN_SERVICE}, otherwise the call will - * fail. + * To support the always-on feature, an app must + *

+ * The call will fail if called with the package name of an unsupported VPN app. * * @param vpnPackage The package name for an installed VPN app on the device, or {@code null} to * remove an existing always-on VPN configuration. diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index d91482331f097..195ec3f84eecb 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -834,6 +834,29 @@ public class ConnectivityManager { } } + /** + * Checks if a VPN app supports always-on mode. + * + * In order to support the always-on feature, an app has to + * + * + * @param userId The identifier of the user for whom the VPN app is installed. + * @param vpnPackage The canonical package name of the VPN app. + * @return {@code true} if and only if the VPN app exists and supports always-on mode. + * @hide + */ + public boolean isAlwaysOnVpnPackageSupportedForUser(int userId, @Nullable String vpnPackage) { + try { + return mService.isAlwaysOnVpnPackageSupported(userId, vpnPackage); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Configures an always-on VPN connection through a specific application. * This connection is automatically granted and persisted after a reboot. diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 27729dcce7794..b9dd207aaa420 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -123,6 +123,7 @@ interface IConnectivityManager VpnInfo[] getAllVpnInfo(); boolean updateLockdownVpn(); + boolean isAlwaysOnVpnPackageSupported(int userId, String packageName); boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown); String getAlwaysOnVpnPackage(int userId); diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java index 2d9860cf0e40f..1f5f17d41038a 100644 --- a/core/java/android/net/VpnService.java +++ b/core/java/android/net/VpnService.java @@ -28,8 +28,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; -import android.net.Network; -import android.net.NetworkUtils; import android.os.Binder; import android.os.IBinder; import android.os.Parcel; @@ -123,6 +121,35 @@ public class VpnService extends Service { */ public static final String SERVICE_INTERFACE = VpnConfig.SERVICE_INTERFACE; + /** + * Key for boolean meta-data field indicating whether this VpnService supports always-on mode. + * + *

For a VPN app targeting {@link android.os.Build.VERSION_CODES#N API 24} or above, Android + * provides users with the ability to set it as always-on, so that VPN connection is + * persisted after device reboot and app upgrade. Always-on VPN can also be enabled by device + * owner and profile owner apps through + * {@link android.app.admin.DevicePolicyManager#setAlwaysOnVpnPackage}. + * + *

VPN apps not supporting this feature should opt out by adding this meta-data field to the + * {@code VpnService} component of {@code AndroidManifest.xml}. In case there is more than one + * {@code VpnService} component defined in {@code AndroidManifest.xml}, opting out any one of + * them will opt out the entire app. For example, + *

 {@code
+     * 
+     *     
+     *         
+     *     
+     *     
+     * 
+     * } 
+ * + *

This meta-data field defaults to {@code true} if absent. + */ + public static final String METADATA_SUPPORTS_ALWAYS_ON = + "android.net.VpnService.SUPPORTS_ALWAYS_ON"; + /** * Use IConnectivityManager since those methods are hidden and not * available in ConnectivityManager. diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 1703bd55d6672..ff13ef619628c 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -128,9 +128,9 @@ import com.android.server.LocalServices; import com.android.server.am.BatteryStatsService; import com.android.server.connectivity.DataConnectionStats; import com.android.server.connectivity.KeepaliveTracker; +import com.android.server.connectivity.LingerMonitor; import com.android.server.connectivity.MockableSystemProperties; import com.android.server.connectivity.Nat464Xlat; -import com.android.server.connectivity.LingerMonitor; import com.android.server.connectivity.NetworkAgentInfo; import com.android.server.connectivity.NetworkDiagnostics; import com.android.server.connectivity.NetworkMonitor; @@ -1515,6 +1515,12 @@ public class ConnectivityService extends IConnectivityManager.Stub ConnectivityManager.enforceChangePermission(mContext); } + private void enforceSettingsPermission() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.NETWORK_SETTINGS, + "ConnectivityService"); + } + private void enforceTetherAccessPermission() { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE, @@ -3645,6 +3651,21 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + @Override + public boolean isAlwaysOnVpnPackageSupported(int userId, String packageName) { + enforceSettingsPermission(); + enforceCrossUserPermission(userId); + + synchronized (mVpns) { + Vpn vpn = mVpns.get(userId); + if (vpn == null) { + Slog.w(TAG, "User " + userId + " has no Vpn configuration"); + return false; + } + return vpn.isAlwaysOnPackageSupported(packageName); + } + } + @Override public boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown) { enforceConnectivityInternalPermission(); diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 27968a99cb028..e82eabfe5ad9b 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -36,6 +36,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; @@ -56,7 +57,10 @@ import android.net.NetworkMisc; import android.net.RouteInfo; import android.net.UidRange; import android.net.Uri; +import android.net.VpnService; import android.os.Binder; +import android.os.Build.VERSION_CODES; +import android.os.Bundle; import android.os.FileUtils; import android.os.IBinder; import android.os.INetworkManagementService; @@ -295,6 +299,56 @@ public class Vpn { } } + /** + * Checks if a VPN app supports always-on mode. + * + * In order to support the always-on feature, an app has to + *

+ * + * @param packageName the canonical package name of the VPN app + * @return {@code true} if and only if the VPN app exists and supports always-on mode + */ + public boolean isAlwaysOnPackageSupported(String packageName) { + enforceSettingsPermission(); + + if (packageName == null) { + return false; + } + + PackageManager pm = mContext.getPackageManager(); + ApplicationInfo appInfo = null; + try { + appInfo = pm.getApplicationInfoAsUser(packageName, 0 /*flags*/, mUserHandle); + } catch (NameNotFoundException unused) { + Log.w(TAG, "Can't find \"" + packageName + "\" when checking always-on support"); + } + if (appInfo == null || appInfo.targetSdkVersion < VERSION_CODES.N) { + return false; + } + + final Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE); + intent.setPackage(packageName); + List services = + pm.queryIntentServicesAsUser(intent, PackageManager.GET_META_DATA, mUserHandle); + if (services == null || services.size() == 0) { + return false; + } + + for (ResolveInfo rInfo : services) { + final Bundle metaData = rInfo.serviceInfo.metaData; + if (metaData != null + && !metaData.getBoolean(VpnService.METADATA_SUPPORTS_ALWAYS_ON, true)) { + return false; + } + } + + return true; + } + /** * Configures an always-on VPN connection through a specific application. * This connection is automatically granted and persisted after a reboot. @@ -303,6 +357,10 @@ public class Vpn { * manifest guarded by {@link android.Manifest.permission.BIND_VPN_SERVICE}, * otherwise the call will fail. * + *

Note that this method does not check if the VPN app supports always-on mode. The check is + * delayed to {@link #startAlwaysOnVpn()}, which is always called immediately after this + * method in {@link android.net.IConnectivityManager#setAlwaysOnVpnPackage}. + * * @param packageName the package to designate as always-on VPN supplier. * @param lockdown whether to prevent traffic outside of a VPN, for example while connecting. * @return {@code true} if the package has been set as always-on, {@code false} otherwise. @@ -443,6 +501,11 @@ public class Vpn { if (alwaysOnPackage == null) { return true; } + // Remove always-on VPN if it's not supported. + if (!isAlwaysOnPackageSupported(alwaysOnPackage)) { + setAlwaysOnPackage(null, false); + return false; + } // Skip if the service is already established. This isn't bulletproof: it's not bound // until after establish(), so if it's mid-setup onStartCommand will be sent twice, // which may restart the connection. @@ -1219,6 +1282,11 @@ public class Vpn { "Unauthorized Caller"); } + private void enforceSettingsPermission() { + mContext.enforceCallingOrSelfPermission(Manifest.permission.NETWORK_SETTINGS, + "Unauthorized Caller"); + } + private class Connection implements ServiceConnection { private IBinder mService; diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index 506d9e5043f4b..f0b3724955aaf 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -27,13 +27,16 @@ import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.app.NotificationManager; import android.content.Context; -import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; import android.net.NetworkInfo.DetailedState; import android.net.UidRange; -import android.os.Build; +import android.net.VpnService; +import android.os.Build.VERSION_CODES; +import android.os.Bundle; import android.os.INetworkManagementService; import android.os.Looper; import android.os.UserHandle; @@ -45,22 +48,22 @@ import android.util.ArraySet; import com.android.internal.net.VpnConfig; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Map; -import java.util.Set; - import org.mockito.Answers; -import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + /** * Tests for {@link Vpn}. * * Build, install and run with: - * runtest --path src/com/android/server/connectivity/VpnTest.java + * runtest --path java/com/android/server/connectivity/VpnTest.java */ public class VpnTest extends AndroidTestCase { private static final String TAG = "VpnTest"; @@ -116,7 +119,7 @@ public class VpnTest extends AndroidTestCase { // Used by {@link Notification.Builder} ApplicationInfo applicationInfo = new ApplicationInfo(); - applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; + applicationInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT; when(mContext.getApplicationInfo()).thenReturn(applicationInfo); doNothing().when(mNetService).registerObserver(any()); @@ -314,6 +317,40 @@ public class VpnTest extends AndroidTestCase { order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser)); } + @SmallTest + public void testIsAlwaysOnPackageSupported() throws Exception { + final Vpn vpn = createVpn(primaryUser.id); + + ApplicationInfo appInfo = new ApplicationInfo(); + when(mPackageManager.getApplicationInfoAsUser(eq(PKGS[0]), anyInt(), eq(primaryUser.id))) + .thenReturn(appInfo); + + ServiceInfo svcInfo = new ServiceInfo(); + ResolveInfo resInfo = new ResolveInfo(); + resInfo.serviceInfo = svcInfo; + when(mPackageManager.queryIntentServicesAsUser(any(), eq(PackageManager.GET_META_DATA), + eq(primaryUser.id))) + .thenReturn(Collections.singletonList(resInfo)); + + // null package name should return false + assertFalse(vpn.isAlwaysOnPackageSupported(null)); + + // Pre-N apps are not supported + appInfo.targetSdkVersion = VERSION_CODES.M; + assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0])); + + // N+ apps are supported by default + appInfo.targetSdkVersion = VERSION_CODES.N; + assertTrue(vpn.isAlwaysOnPackageSupported(PKGS[0])); + + // Apps that opt out explicitly are not supported + appInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT; + Bundle metaData = new Bundle(); + metaData.putBoolean(VpnService.METADATA_SUPPORTS_ALWAYS_ON, false); + svcInfo.metaData = metaData; + assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0])); + } + @SmallTest public void testNotificationShownForAlwaysOnApp() { final UserHandle userHandle = UserHandle.of(primaryUser.id); From 5da5ae3b96769735a2dc5abe265101df545be828 Mon Sep 17 00:00:00 2001 From: Charles He Date: Tue, 15 Aug 2017 15:30:22 +0100 Subject: [PATCH 2/2] Opt-out for always-on VPN: rename API. Rename the opt-out flag in AndroidManifest to SERVICE_META_DATA_SUPPORTS_ALWAYS_ON as directed by the API Council. Bug: 64331776 Bug: 36650087 Test: runtest --path java/com/android/server/connectivity/VpnTest.java Change-Id: I24326fad7a89083a2409134640bda81ee0359d08 Merged-In: I24326fad7a89083a2409134640bda81ee0359d08 (cherry picked from commit c57a01c16616f938eaa84ae5349a53efdec6e4cc) --- api/current.txt | 2 +- api/system-current.txt | 2 +- api/test-current.txt | 2 +- core/java/android/app/admin/DevicePolicyManager.java | 2 +- core/java/android/net/ConnectivityManager.java | 4 ++-- core/java/android/net/VpnService.java | 5 +++-- .../core/java/com/android/server/connectivity/Vpn.java | 8 ++++---- .../net/java/com/android/server/connectivity/VpnTest.java | 2 +- 8 files changed, 14 insertions(+), 13 deletions(-) diff --git a/api/current.txt b/api/current.txt index 0fcd1599b1a6b..3503f2445e7a5 100644 --- a/api/current.txt +++ b/api/current.txt @@ -26001,8 +26001,8 @@ package android.net { method public boolean protect(java.net.Socket); method public boolean protect(java.net.DatagramSocket); method public boolean setUnderlyingNetworks(android.net.Network[]); - field public static final java.lang.String METADATA_SUPPORTS_ALWAYS_ON = "android.net.VpnService.SUPPORTS_ALWAYS_ON"; field public static final java.lang.String SERVICE_INTERFACE = "android.net.VpnService"; + field public static final java.lang.String SERVICE_META_DATA_SUPPORTS_ALWAYS_ON = "android.net.VpnService.SUPPORTS_ALWAYS_ON"; } public class VpnService.Builder { diff --git a/api/system-current.txt b/api/system-current.txt index a32ef4215ff86..3696b605c8af1 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -28265,8 +28265,8 @@ package android.net { method public boolean protect(java.net.Socket); method public boolean protect(java.net.DatagramSocket); method public boolean setUnderlyingNetworks(android.net.Network[]); - field public static final java.lang.String METADATA_SUPPORTS_ALWAYS_ON = "android.net.VpnService.SUPPORTS_ALWAYS_ON"; field public static final java.lang.String SERVICE_INTERFACE = "android.net.VpnService"; + field public static final java.lang.String SERVICE_META_DATA_SUPPORTS_ALWAYS_ON = "android.net.VpnService.SUPPORTS_ALWAYS_ON"; } public class VpnService.Builder { diff --git a/api/test-current.txt b/api/test-current.txt index 5a708cf3d34ce..e7280eccd8f9d 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -26110,8 +26110,8 @@ package android.net { method public boolean protect(java.net.Socket); method public boolean protect(java.net.DatagramSocket); method public boolean setUnderlyingNetworks(android.net.Network[]); - field public static final java.lang.String METADATA_SUPPORTS_ALWAYS_ON = "android.net.VpnService.SUPPORTS_ALWAYS_ON"; field public static final java.lang.String SERVICE_INTERFACE = "android.net.VpnService"; + field public static final java.lang.String SERVICE_META_DATA_SUPPORTS_ALWAYS_ON = "android.net.VpnService.SUPPORTS_ALWAYS_ON"; } public class VpnService.Builder { diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 2463b6a39bf43..2931467add56a 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -3912,7 +3912,7 @@ public class DevicePolicyManager { * {@link android.Manifest.permission#BIND_VPN_SERVICE}; *

  • target {@link android.os.Build.VERSION_CODES#N API 24} or above; and
  • *
  • not explicitly opt out of the feature through - * {@link android.net.VpnService#METADATA_SUPPORTS_ALWAYS_ON}.
  • + * {@link android.net.VpnService#SERVICE_META_DATA_SUPPORTS_ALWAYS_ON}. * * The call will fail if called with the package name of an unsupported VPN app. * diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 195ec3f84eecb..3460f564fcb83 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -840,8 +840,8 @@ public class ConnectivityManager { * In order to support the always-on feature, an app has to * * * @param userId The identifier of the user for whom the VPN app is installed. diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java index 1f5f17d41038a..3cc52a6b769cc 100644 --- a/core/java/android/net/VpnService.java +++ b/core/java/android/net/VpnService.java @@ -145,9 +145,10 @@ public class VpnService extends Service { * * } * - *

    This meta-data field defaults to {@code true} if absent. + *

    This meta-data field defaults to {@code true} if absent. It will only have effect on + * {@link android.os.Build.VERSION_CODES#O_MR1} or higher. */ - public static final String METADATA_SUPPORTS_ALWAYS_ON = + public static final String SERVICE_META_DATA_SUPPORTS_ALWAYS_ON = "android.net.VpnService.SUPPORTS_ALWAYS_ON"; /** diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index e82eabfe5ad9b..56cff7c715d63 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -305,8 +305,8 @@ public class Vpn { * In order to support the always-on feature, an app has to *

    * * @param packageName the canonical package name of the VPN app @@ -340,8 +340,8 @@ public class Vpn { for (ResolveInfo rInfo : services) { final Bundle metaData = rInfo.serviceInfo.metaData; - if (metaData != null - && !metaData.getBoolean(VpnService.METADATA_SUPPORTS_ALWAYS_ON, true)) { + if (metaData != null && + !metaData.getBoolean(VpnService.SERVICE_META_DATA_SUPPORTS_ALWAYS_ON, true)) { return false; } } diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index f0b3724955aaf..296cb76560af4 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -346,7 +346,7 @@ public class VpnTest extends AndroidTestCase { // Apps that opt out explicitly are not supported appInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT; Bundle metaData = new Bundle(); - metaData.putBoolean(VpnService.METADATA_SUPPORTS_ALWAYS_ON, false); + metaData.putBoolean(VpnService.SERVICE_META_DATA_SUPPORTS_ALWAYS_ON, false); svcInfo.metaData = metaData; assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0])); }