Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a0a87e81 authored by Charles He's avatar Charles He
Browse files

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 3673863f)
parent 8c3e12a9
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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";
  }
+1 −0
Original line number Diff line number Diff line
@@ -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";
  }
+1 −0
Original line number Diff line number Diff line
@@ -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";
  }
+9 −18
Original line number Diff line number Diff line
@@ -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.
     * <p>
     * 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
     * <ul>
     *     <li>declare a {@link android.net.VpnService} in its manifest, guarded by
     *         {@link android.Manifest.permission#BIND_VPN_SERVICE};</li>
     *     <li>target {@link android.os.Build.VERSION_CODES#N API 24} or above; and</li>
     *     <li><i>not</i> explicitly opt out of the feature through
     *         {@link android.net.VpnService#METADATA_SUPPORTS_ALWAYS_ON}.</li>
     * </ul>
     * 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.
+23 −0
Original line number Diff line number Diff line
@@ -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
     * <ul>
     *     <li>target {@link VERSION_CODES#N API 24} or above, and
     *     <li>not opt out through the {@link VpnService#METADATA_SUPPORTS_ALWAYS_ON} meta-data
     *         field.
     * </ul>
     *
     * @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.
Loading