Idle whitelist always-on VPN host apps while VPN services are launching

Bug 35144039

Change-Id: I2d576776a83c99f7e00e609c8623d0f0756e40ef
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index a5876dd..b963555 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -85,6 +85,8 @@
 import com.android.internal.net.VpnConfig;
 import com.android.internal.net.VpnInfo;
 import com.android.internal.net.VpnProfile;
+import com.android.server.DeviceIdleController;
+import com.android.server.LocalServices;
 import com.android.server.net.BaseNetworkObserver;
 
 import libcore.io.IoUtils;
@@ -115,6 +117,10 @@
     private static final String TAG = "Vpn";
     private static final boolean LOGD = true;
 
+    // Length of time (in milliseconds) that an app hosting an always-on VPN is placed on
+    // the device idle whitelist during service launch and VPN bootstrap.
+    private static final long VPN_LAUNCH_IDLE_WHITELIST_DURATION = 60 * 1000;
+
     // TODO: create separate trackers for each unique VPN to support
     // automated reconnection
 
@@ -389,14 +395,26 @@
             }
         }
 
-        // Start the VPN service declared in the app's manifest.
-        Intent serviceIntent = new Intent(VpnConfig.SERVICE_INTERFACE);
-        serviceIntent.setPackage(alwaysOnPackage);
+        // Tell the OS that background services in this app need to be allowed for
+        // a short time, so we can bootstrap the VPN service.
+        final long oldId = Binder.clearCallingIdentity();
         try {
-            return mContext.startServiceAsUser(serviceIntent, UserHandle.of(mUserHandle)) != null;
-        } catch (RuntimeException e) {
-            Log.e(TAG, "VpnService " + serviceIntent + " failed to start", e);
-            return false;
+            DeviceIdleController.LocalService idleController =
+                    LocalServices.getService(DeviceIdleController.LocalService.class);
+            idleController.addPowerSaveTempWhitelistApp(Process.myUid(), alwaysOnPackage,
+                    VPN_LAUNCH_IDLE_WHITELIST_DURATION, mUserHandle, false, "vpn");
+
+            // Start the VPN service declared in the app's manifest.
+            Intent serviceIntent = new Intent(VpnConfig.SERVICE_INTERFACE);
+            serviceIntent.setPackage(alwaysOnPackage);
+            try {
+                return mContext.startServiceAsUser(serviceIntent, UserHandle.of(mUserHandle)) != null;
+            } catch (RuntimeException e) {
+                Log.e(TAG, "VpnService " + serviceIntent + " failed to start", e);
+                return false;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
         }
     }