• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.cts.deviceandprofileowner;
18 
19 import static com.android.cts.deviceandprofileowner.vpn.VpnTestHelper.TEST_ADDRESS;
20 import static com.android.cts.deviceandprofileowner.vpn.VpnTestHelper.VPN_PACKAGE;
21 
22 import android.os.Bundle;
23 import android.os.UserManager;
24 import android.util.Log;
25 
26 import com.android.compatibility.common.util.BlockingBroadcastReceiver;
27 import com.android.cts.deviceandprofileowner.vpn.VpnTestHelper;
28 
29 import java.io.IOException;
30 import java.net.Socket;
31 
32 /**
33  * Validates that a device owner or profile owner can set an always-on VPN without user action.
34  *
35  * A trivial VPN app is installed which reflects ping packets back to the sender. One ping packet is
36  * sent, received after its round-trip, and compared to the original packet to make sure nothing
37  * strange happened on the way through the VPN.
38  *
39  * All of the addresses in this test are fictional and any resemblance to real addresses is the
40  * result of a misconfigured network.
41  */
42 public class AlwaysOnVpnTest extends BaseDeviceAdminTest {
43     private static final String TAG = "AlwaysOnVpnTest";
44 
45     /** @see com.android.cts.vpnfirewall.ReflectorVpnService */
46     private static final String RESTRICTION_ALLOWED = "vpn.allowed";
47     private static final String RESTRICTION_DISALLOWED = "vpn.disallowed";
48     private static final String RESTRICTION_DONT_ESTABLISH = "vpn.dont_establish";
49     private static final String CONNECTIVITY_CHECK_HOST = "connectivitycheck.gstatic.com";
50     private static final int VPN_ON_START_TIMEOUT_MS = 5_000;
51     private static final long CONNECTIVITY_WAIT_TIME_NS = 30_000_000_000L;
52 
53     private String mPackageName;
54 
55     @Override
setUp()56     public void setUp() throws Exception {
57         super.setUp();
58         // Always-on is null by default.
59         assertNull(mDevicePolicyManager.getAlwaysOnVpnPackage(ADMIN_RECEIVER_COMPONENT));
60         mPackageName = mContext.getPackageName();
61     }
62 
63     @Override
tearDown()64     public void tearDown() throws Exception {
65         mDevicePolicyManager.setAlwaysOnVpnPackage(ADMIN_RECEIVER_COMPONENT, null, false);
66         mDevicePolicyManager.setApplicationRestrictions(ADMIN_RECEIVER_COMPONENT, VPN_PACKAGE,
67                 /* restrictions */ null);
68         super.tearDown();
69     }
70 
testAlwaysOnVpn()71     public void testAlwaysOnVpn() throws Exception {
72         // test always-on is null by default
73         assertNull(mDevicePolicyManager.getAlwaysOnVpnPackage(ADMIN_RECEIVER_COMPONENT));
74 
75         VpnTestHelper.waitForVpn(mContext, VPN_PACKAGE,
76                 /* usable */ true, /* lockdown */ true, /* allowlist */ false);
77 
78         VpnTestHelper.checkPing(TEST_ADDRESS);
79     }
80 
testDisallowConfigVpn()81     public void testDisallowConfigVpn() throws Exception {
82         mDevicePolicyManager.addUserRestriction(
83                 ADMIN_RECEIVER_COMPONENT, UserManager.DISALLOW_CONFIG_VPN);
84         try {
85             testAlwaysOnVpn();
86         } finally {
87             // clear the user restriction
88             mDevicePolicyManager.clearUserRestriction(ADMIN_RECEIVER_COMPONENT,
89                     UserManager.DISALLOW_CONFIG_VPN);
90         }
91     }
92 
testAllowedApps()93     public void testAllowedApps() throws Exception {
94         final Bundle restrictions = new Bundle();
95         restrictions.putStringArray(RESTRICTION_ALLOWED, new String[] {mPackageName});
96         mDevicePolicyManager.setApplicationRestrictions(ADMIN_RECEIVER_COMPONENT, VPN_PACKAGE,
97                 restrictions);
98         VpnTestHelper.waitForVpn(mContext, VPN_PACKAGE,
99                 /* usable */ true,  /* lockdown */ true, /* allowlist */ false);
100         assertTrue(VpnTestHelper.isNetworkVpn(mContext));
101     }
102 
testDisallowedApps()103     public void testDisallowedApps() throws Exception {
104         final Bundle restrictions = new Bundle();
105         restrictions.putStringArray(RESTRICTION_DISALLOWED, new String[] {mPackageName});
106         mDevicePolicyManager.setApplicationRestrictions(ADMIN_RECEIVER_COMPONENT, VPN_PACKAGE,
107                 restrictions);
108         VpnTestHelper.waitForVpn(mContext, VPN_PACKAGE,
109                 /* usable */ false,  /* lockdown */ true, /* allowlist */ false);
110         assertFalse(VpnTestHelper.isNetworkVpn(mContext));
111     }
112 
113     // Tests that changes to lockdown allowlist are applied correctly.
testVpnLockdownUpdateAllowlist()114     public void testVpnLockdownUpdateAllowlist() throws Exception {
115         waitForConnectivity("VPN is off");
116 
117         // VPN won't start.
118         final Bundle restrictions = new Bundle();
119         restrictions.putBoolean(RESTRICTION_DONT_ESTABLISH, true);
120         mDevicePolicyManager.setApplicationRestrictions(
121                 ADMIN_RECEIVER_COMPONENT, VPN_PACKAGE, restrictions);
122 
123         // VPN service is started asynchronously, we need to wait for it to avoid stale service
124         // instance interfering with the next test.
125         final BlockingBroadcastReceiver receiver = VpnTestHelper.registerOnStartReceiver(mContext);
126 
127         VpnTestHelper.setAlwaysOnVpn(
128                 mContext, VPN_PACKAGE, /* lockdown */ false, /* allowlist */ false);
129         waitForConnectivity("VPN service not started, no lockdown");
130         assertNotNull(receiver.awaitForBroadcast(VPN_ON_START_TIMEOUT_MS));
131 
132         VpnTestHelper.setAlwaysOnVpn(
133                 mContext, VPN_PACKAGE, /* lockdown */ true, /* allowlist */ false);
134         // Wait for loss of connectivity instead of assertConnectivity(false)
135         // to mitigate flakiness due to asynchronicity.
136         waitForNoConnectivity("VPN in lockdown, service not started");
137         assertNotNull(receiver.awaitForBroadcast(VPN_ON_START_TIMEOUT_MS));
138 
139         VpnTestHelper.setAlwaysOnVpn(
140                 mContext, VPN_PACKAGE, /* lockdown */ true, /* allowlist */ true);
141         waitForConnectivity("VPN in lockdown, service not started, app allowlisted");
142         assertNotNull(receiver.awaitForBroadcast(VPN_ON_START_TIMEOUT_MS));
143 
144         VpnTestHelper.setAlwaysOnVpn(
145                 mContext, VPN_PACKAGE, /* lockdown */ true, /* allowlist */ false);
146         // Wait for loss of connectivity instead of assertConnectivity(false)
147         // to mitigate flakiness due to asynchronicity.
148         waitForNoConnectivity("VPN in lockdown, service not started");
149         assertNotNull(receiver.awaitForBroadcast(VPN_ON_START_TIMEOUT_MS));
150 
151         receiver.unregisterQuietly();
152     }
153 
154     // Tests that when VPN comes up, allowlisted app switches over to it.
testVpnLockdownAllowlistVpnComesUp()155     public void testVpnLockdownAllowlistVpnComesUp() throws Exception {
156         waitForConnectivity("VPN is off");
157 
158         // VPN won't start initially.
159         final Bundle restrictions = new Bundle();
160         restrictions.putBoolean(RESTRICTION_DONT_ESTABLISH, true);
161         mDevicePolicyManager.setApplicationRestrictions(
162                 ADMIN_RECEIVER_COMPONENT, VPN_PACKAGE, restrictions);
163 
164         // VPN service is started asynchronously, we need to wait for it to avoid stale service
165         // instance interfering with the next test.
166         final BlockingBroadcastReceiver receiver = VpnTestHelper.registerOnStartReceiver(mContext);
167 
168         VpnTestHelper.setAlwaysOnVpn(
169                 mContext, VPN_PACKAGE,  /* lockdown */ true, /* allowlist */ true);
170         waitForConnectivity("VPN in lockdown, service not started, app allowlisted");
171         assertNotNull(receiver.awaitForBroadcast(VPN_ON_START_TIMEOUT_MS));
172 
173         // Make VPN workable again and restart.
174         mDevicePolicyManager.setApplicationRestrictions(
175                 ADMIN_RECEIVER_COMPONENT, VPN_PACKAGE, null);
176         VpnTestHelper.waitForVpn(mContext, VPN_PACKAGE,
177                 /* usable */ true,  /* lockdown */ true, /* allowlist */ true);
178 
179         // Now we should be on VPN.
180         VpnTestHelper.checkPing(TEST_ADDRESS);
181 
182         receiver.unregisterQuietly();
183     }
184 
testSetNonVpnAlwaysOn()185     public void testSetNonVpnAlwaysOn() throws Exception {
186         // Treat this CTS DPC as an non-vpn app, since it doesn't register
187         // android.net.VpnService intent filter in AndroidManifest.xml.
188         try {
189             mDevicePolicyManager.setAlwaysOnVpnPackage(
190                     ADMIN_RECEIVER_COMPONENT, mPackageName, true);
191             fail("setAlwaysOnVpnPackage should not accept non-vpn package");
192         } catch (UnsupportedOperationException e) {
193             // success
194         }
195         assertNull(mDevicePolicyManager.getAlwaysOnVpnPackage(ADMIN_RECEIVER_COMPONENT));
196     }
197 
waitForConnectivity(String message)198     private void waitForConnectivity(String message) throws InterruptedException {
199         long deadline = System.nanoTime() + CONNECTIVITY_WAIT_TIME_NS;
200         while (System.nanoTime() < deadline) {
201             try {
202                 new Socket(CONNECTIVITY_CHECK_HOST, 80);
203                 // Domain resolved, we have connectivity.
204                 return;
205             } catch (IOException e) {
206                 // Log.e(String, String, Throwable) will swallow UnknownHostException,
207                 // so manually print it out here.
208                 Log.e(TAG, "No connectivity yet: " + e.toString());
209                 Thread.sleep(2000);
210             }
211         }
212         fail("Connectivity isn't available: " + message);
213     }
214 
waitForNoConnectivity(String message)215     private void waitForNoConnectivity(String message) throws Exception {
216         long deadline = System.nanoTime() + CONNECTIVITY_WAIT_TIME_NS;
217         while (System.nanoTime() < deadline) {
218             try {
219                 new Socket(CONNECTIVITY_CHECK_HOST, 80);
220                 // Domain resolved, we have connectivity.
221             } catch (IOException e) {
222                 // No connectivity
223                 return;
224             }
225             Thread.sleep(2000);
226         }
227         fail("Connectivity still available after deadline: " + message);
228     }
229 
assertConnectivity(boolean shouldHaveConnectivity, String message)230     private void assertConnectivity(boolean shouldHaveConnectivity, String message) {
231         try {
232             new Socket(CONNECTIVITY_CHECK_HOST, 80);
233             if (!shouldHaveConnectivity) {
234                 fail("Connectivity available while not expected: " + message);
235             }
236         } catch (IOException e) {
237             if (shouldHaveConnectivity) {
238                 Log.e(TAG, "Connectivity check failed", e);
239                 fail("Connectivity isn't available while expected: " + message);
240             }
241         }
242     }
243 }
244