Add lockdownEnabled parameter to always-on VPN API

Allows callers to opt-out of blockading network traffic during boot and
on VPN app failure.

Bug: 26694104
Change-Id: Ibfbd43ad09a25f2e38053fcd6306df3711f8bde2
diff --git a/api/current.txt b/api/current.txt
index 5e4b2d4..2cee862 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5965,7 +5965,7 @@
     method public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrievePreRebootSecurityLogs(android.content.ComponentName);
     method public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrieveSecurityLogs(android.content.ComponentName);
     method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
-    method public void setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
+    method public void setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String, boolean) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
     method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
     method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
     method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
diff --git a/api/system-current.txt b/api/system-current.txt
index bde3a8c..6d8b909 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6126,7 +6126,7 @@
     method public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrieveSecurityLogs(android.content.ComponentName);
     method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
     method public deprecated boolean setActiveProfileOwner(android.content.ComponentName, java.lang.String) throws java.lang.IllegalArgumentException;
-    method public void setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
+    method public void setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String, boolean) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
     method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
     method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
     method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
diff --git a/api/test-current.txt b/api/test-current.txt
index 9141506..1f64a0f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5969,7 +5969,7 @@
     method public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrievePreRebootSecurityLogs(android.content.ComponentName);
     method public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrieveSecurityLogs(android.content.ComponentName);
     method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
-    method public void setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
+    method public void setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String, boolean) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
     method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
     method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
     method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 0ca2e14..b0e86f4 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2998,6 +2998,19 @@
 
     /**
      * 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
+     */
+    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>
@@ -3006,7 +3019,10 @@
      * fail.
      *
      * @param vpnPackage The package name for an installed VPN app on the device, or {@code null} to
-     *            remove an existing always-on VPN configuration.
+     *        remove an existing always-on VPN configuration.
+     * @param lockdownEnabled {@code true} to disallow networking when the VPN is not connected or
+     *        {@code false} otherwise. This carries the risk that any failure of the VPN provider
+     *        could break networking for all apps. This has no effect when clearing.
      * @return {@code true} if the package is set as always-on VPN controller; {@code false}
      *         otherwise.
      * @throws SecurityException if {@code admin} is not a device or a profile owner.
@@ -3014,12 +3030,13 @@
      * @throws UnsupportedOperationException if {@code vpnPackage} exists but does not support being
      *         set as always-on, or if always-on VPN is not available.
      */
-    public void setAlwaysOnVpnPackage(@NonNull ComponentName admin, @Nullable String vpnPackage)
+    public void setAlwaysOnVpnPackage(@NonNull ComponentName admin, @Nullable String vpnPackage,
+            boolean lockdownEnabled)
             throws NameNotFoundException, UnsupportedOperationException {
         throwIfParentInstance("setAlwaysOnVpnPackage");
         if (mService != null) {
             try {
-                if (!mService.setAlwaysOnVpnPackage(admin, vpnPackage)) {
+                if (!mService.setAlwaysOnVpnPackage(admin, vpnPackage, lockdownEnabled)) {
                     throw new NameNotFoundException(vpnPackage);
                 }
             } catch (RemoteException e) {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 989e613..447ee29 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -157,7 +157,7 @@
     void setCertInstallerPackage(in ComponentName who, String installerPackage);
     String getCertInstallerPackage(in ComponentName who);
 
-    boolean setAlwaysOnVpnPackage(in ComponentName who, String vpnPackage);
+    boolean setAlwaysOnVpnPackage(in ComponentName who, String vpnPackage, boolean lockdown);
     String getAlwaysOnVpnPackage(in ComponentName who);
 
     void addPersistentPreferredActivity(in ComponentName admin, in IntentFilter filter, in ComponentName activity);
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index faf5c64..e65462b 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -792,14 +792,16 @@
      * @param userId The identifier of the user to set an always-on VPN for.
      * @param vpnPackage The package name for an installed VPN app on the device, or {@code null}
      *                   to remove an existing always-on VPN configuration.
-
+     * @param lockdownEnabled {@code true} to disallow networking when the VPN is not connected or
+     *        {@code false} otherwise.
      * @return {@code true} if the package is set as always-on VPN controller;
      *         {@code false} otherwise.
      * @hide
      */
-    public boolean setAlwaysOnVpnPackageForUser(int userId, @Nullable String vpnPackage) {
+    public boolean setAlwaysOnVpnPackageForUser(int userId, @Nullable String vpnPackage,
+            boolean lockdownEnabled) {
         try {
-            return mService.setAlwaysOnVpnPackage(userId, vpnPackage);
+            return mService.setAlwaysOnVpnPackage(userId, vpnPackage, lockdownEnabled);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index c897c45..e7a072c 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -122,7 +122,7 @@
     VpnInfo[] getAllVpnInfo();
 
     boolean updateLockdownVpn();
-    boolean setAlwaysOnVpnPackage(int userId, String packageName);
+    boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown);
     String getAlwaysOnVpnPackage(int userId);
 
     int checkMobileProvisioning(int suggestedTimeOutMs);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d85827e..100ada8 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3309,7 +3309,7 @@
     }
 
     @Override
-    public boolean setAlwaysOnVpnPackage(int userId, String packageName) {
+    public boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown) {
         enforceConnectivityInternalPermission();
         enforceCrossUserPermission(userId);
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 93fbe5c..cbec270 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4459,7 +4459,7 @@
      * @throws UnsupportedException if the package does not support being set as always-on.
      */
     @Override
-    public boolean setAlwaysOnVpnPackage(ComponentName admin, String vpnPackage)
+    public boolean setAlwaysOnVpnPackage(ComponentName admin, String vpnPackage, boolean lockdown)
             throws SecurityException {
         synchronized (this) {
             getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
@@ -4473,7 +4473,7 @@
             }
             ConnectivityManager connectivityManager = (ConnectivityManager)
                     mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-            if (!connectivityManager.setAlwaysOnVpnPackageForUser(userId, vpnPackage)) {
+            if (!connectivityManager.setAlwaysOnVpnPackageForUser(userId, vpnPackage, lockdown)) {
                 throw new UnsupportedOperationException();
             }
         } finally {