1 /* 2 * Copyright (C) 2018 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.compatibility.common.util.ShellIdentityUtils.invokeStaticMethodWithShellPermissions; 20 21 import static org.junit.Assert.assertNotEquals; 22 23 import android.content.Intent; 24 import android.net.ConnectivityManager; 25 import android.net.Network; 26 import android.net.NetworkInfo; 27 import android.net.NetworkInfo.DetailedState; 28 import android.net.NetworkInfo.State; 29 import android.net.wifi.WifiInfo; 30 import android.net.wifi.WifiManager; 31 import android.os.Bundle; 32 import android.os.Handler; 33 import android.os.Looper; 34 import android.os.Message; 35 import android.os.Messenger; 36 import android.os.SystemClock; 37 import android.util.Log; 38 39 import com.android.bedstead.dpmwrapper.TestAppSystemServiceFactory; 40 import com.android.compatibility.common.util.SystemUtil; 41 42 import org.junit.After; 43 import org.junit.Before; 44 45 import java.util.ArrayList; 46 import java.util.List; 47 import java.util.concurrent.BlockingQueue; 48 import java.util.concurrent.LinkedBlockingQueue; 49 import java.util.concurrent.TimeUnit; 50 51 public class MeteredDataRestrictionTest extends BaseDeviceAdminTest { 52 private static final String TAG = MeteredDataRestrictionTest.class.getSimpleName(); 53 54 private static final String METERED_DATA_APP_PKG = 55 "com.android.cts.devicepolicy.metereddatatestapp"; 56 private static final String METERED_DATA_APP_MAIN_ACTIVITY = METERED_DATA_APP_PKG 57 + ".MainActivity"; 58 59 private static final long WAIT_FOR_NETWORK_RECONNECTION_TIMEOUT_SEC = 10; 60 private static final long WAIT_FOR_NETWORK_INFO_TIMEOUT_SEC = 8; 61 62 private static final int NUM_TRIES_METERED_STATUS_CHECK = 20; 63 private static final long INTERVAL_METERED_STATUS_CHECK_MS = 500; 64 65 private static final String EXTRA_MESSENGER = "messenger"; 66 private static final int MSG_NOTIFY_NETWORK_STATE = 1; 67 68 private final Messenger mCallbackMessenger = new Messenger(new CallbackHandler()); 69 private final BlockingQueue<NetworkInfo> mNetworkInfos = new LinkedBlockingQueue<>(1); 70 71 private ConnectivityManager mCm; 72 private WifiManager mWm; 73 private String mMeteredWifi; 74 75 @Before setUp()76 public void setUp() throws Exception { 77 super.setUp(); 78 79 mCm = mContext.getSystemService(ConnectivityManager.class); 80 mWm = TestAppSystemServiceFactory.getWifiManager(mContext, BasicAdminReceiver.class); 81 setMeteredNetwork(); 82 } 83 84 @After tearDown()85 public void tearDown() throws Exception { 86 super.tearDown(); 87 resetMeteredNetwork(); 88 } 89 testSetMeteredDataDisabledPackages()90 public void testSetMeteredDataDisabledPackages() { 91 final List<String> restrictedPkgs = new ArrayList<>(); 92 restrictedPkgs.add(METERED_DATA_APP_PKG); 93 final List<String> excludedPkgs = mDevicePolicyManager.setMeteredDataDisabledPackages( 94 ADMIN_RECEIVER_COMPONENT, restrictedPkgs); 95 assertTrue("Packages not restricted: " + excludedPkgs, excludedPkgs.isEmpty()); 96 97 List<String> actualRestrictedPkgs = mDevicePolicyManager.getMeteredDataDisabledPackages( 98 ADMIN_RECEIVER_COMPONENT); 99 assertEquals("Actual restricted pkgs: " + actualRestrictedPkgs, 100 1, actualRestrictedPkgs.size()); 101 assertTrue("Actual restricted pkgs: " + actualRestrictedPkgs, 102 actualRestrictedPkgs.contains(METERED_DATA_APP_PKG)); 103 verifyAppNetworkState(true); 104 105 restrictedPkgs.clear(); 106 mDevicePolicyManager.setMeteredDataDisabledPackages(ADMIN_RECEIVER_COMPONENT, 107 restrictedPkgs); 108 actualRestrictedPkgs = mDevicePolicyManager.getMeteredDataDisabledPackages( 109 ADMIN_RECEIVER_COMPONENT); 110 assertTrue("Actual restricted pkgs: " + actualRestrictedPkgs, 111 actualRestrictedPkgs.isEmpty()); 112 verifyAppNetworkState(false); 113 } 114 verifyAppNetworkState(boolean blocked)115 private void verifyAppNetworkState(boolean blocked) { 116 final Bundle extras = new Bundle(); 117 extras.putBinder(EXTRA_MESSENGER, mCallbackMessenger.getBinder()); 118 mNetworkInfos.clear(); 119 final Intent launchIntent = new Intent() 120 .setClassName(METERED_DATA_APP_PKG, METERED_DATA_APP_MAIN_ACTIVITY) 121 .putExtras(extras) 122 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 123 mContext.startActivity(launchIntent); 124 125 try { 126 final NetworkInfo networkInfo = mNetworkInfos.poll(WAIT_FOR_NETWORK_INFO_TIMEOUT_SEC, 127 TimeUnit.SECONDS); 128 if (networkInfo == null) { 129 fail("Timed out waiting for the network info"); 130 } 131 132 final String expectedState = (blocked ? State.DISCONNECTED : State.CONNECTED).name(); 133 final String expectedDetailedState = (blocked ? DetailedState.BLOCKED 134 : DetailedState.CONNECTED).name(); 135 assertEquals("Wrong state: " + networkInfo, 136 expectedState, networkInfo.getState().name()); 137 assertEquals("Wrong detailed state: " + networkInfo, 138 expectedDetailedState, networkInfo.getDetailedState().name()); 139 } catch (InterruptedException e) { 140 fail("Waiting for networkinfo got interrupted: " + e); 141 } 142 } 143 144 private class CallbackHandler extends Handler { CallbackHandler()145 public CallbackHandler() { 146 super(Looper.getMainLooper()); 147 } 148 149 @Override handleMessage(Message msg)150 public void handleMessage(Message msg) { 151 if (msg.what == MSG_NOTIFY_NETWORK_STATE) { 152 final NetworkInfo networkInfo = (NetworkInfo) msg.obj; 153 if (!mNetworkInfos.offer(networkInfo)) { 154 Log.e(TAG, "Error while adding networkinfo"); 155 } 156 } else { 157 Log.e(TAG, "Unknown msg type: " + msg.what); 158 } 159 } 160 } 161 setMeteredNetwork()162 private void setMeteredNetwork() throws Exception { 163 final int oldNetId = getActiveNetworkNetId(); 164 final boolean oldMeteredState = mCm.isActiveNetworkMetered(); 165 final NetworkInfo networkInfo = mCm.getActiveNetworkInfo(); 166 Log.d(TAG, "setMeteredNetwork(): oldNetId=" + oldNetId 167 + ", oldMeteredState=" + oldMeteredState + ", activeNetworkInfo=" + networkInfo); 168 if (networkInfo == null) { 169 fail("Active network is not available"); 170 } else if (networkInfo.getType() != ConnectivityManager.TYPE_WIFI) { 171 fail("Active network doesn't support setting metered status: " + networkInfo); 172 } 173 final String ssid = setWifiMeteredStatus(true); 174 175 // Set flag so status is reverted on resetMeteredNetwork(); 176 mMeteredWifi = ssid; 177 178 // When transitioning from unmetered to metered, the network stack will discconect 179 // the current WiFi connection and reconnect it. In this case we need to wait for 180 // the new network to come up. 181 if (!oldMeteredState) { 182 waitForReconnection(oldNetId); 183 } 184 assertWifiMeteredStatus(ssid, true); 185 assertActiveNetworkMetered(true); 186 } 187 resetMeteredNetwork()188 private void resetMeteredNetwork() throws Exception { 189 if (mMeteredWifi != null) { 190 Log.i(TAG, "Resetting metered status for netId=" + mMeteredWifi); 191 setWifiMeteredStatus(mMeteredWifi, /* metered= */ null); 192 assertWifiMeteredStatus(mMeteredWifi, /* metered= */ null); 193 assertActiveNetworkMetered(false); 194 } 195 } 196 setWifiMeteredStatus(Boolean metered)197 private String setWifiMeteredStatus(Boolean metered) throws Exception { 198 // Must use Shell permissions to get the connection info because on headless system user 199 // mode the method would be called by the device owner on system user, which have location 200 // disabled (and hence the returned connectionInfo would have the SSID redacted). 201 WifiInfo connectionInfo = invokeStaticMethodWithShellPermissions( 202 () -> mWm.getConnectionInfo()); 203 204 String ssid = connectionInfo.getSSID(); 205 assertNotNull("null SSID", ssid); 206 assertNotEquals("unknown SSID", WifiManager.UNKNOWN_SSID, ssid); 207 208 final String netId = ssid.trim().replaceAll("\"", ""); // remove quotes, if any. 209 assertFalse("empty SSID", ssid.isEmpty()); 210 211 Log.d(TAG, "setWifiMeteredStatus(" + metered + "): setting " + connectionInfo); 212 setWifiMeteredStatus(netId, metered); 213 return netId; 214 } 215 setWifiMeteredStatus(String ssid, Boolean metered)216 private void setWifiMeteredStatus(String ssid, Boolean metered) throws Exception { 217 Log.i(TAG, "Setting wi-fi network " + ssid + " metered status to " + metered); 218 executeCmd("cmd netpolicy set metered-network " + ssid + " " + 219 (metered != null ? metered.toString() : "undefined")); 220 } 221 assertWifiMeteredStatus(String ssid, Boolean metered)222 private void assertWifiMeteredStatus(String ssid, Boolean metered) throws Exception { 223 final String cmd = "cmd netpolicy list wifi-networks"; 224 final String expectedResult = ssid + ";" + (metered != null ? metered.toString() : "none"); 225 String cmdResult = null; 226 for (int i = 0; i < NUM_TRIES_METERED_STATUS_CHECK; ++i) { 227 cmdResult = executeCmd(cmd); 228 if (cmdResult.contains(expectedResult)) { 229 return; 230 } 231 SystemClock.sleep(INTERVAL_METERED_STATUS_CHECK_MS); 232 } 233 fail("Timed out waiting for wifi metered status to change. expected=" + expectedResult 234 + ", actual status=" + cmdResult); 235 } 236 assertActiveNetworkMetered(boolean metered)237 private void assertActiveNetworkMetered(boolean metered) { 238 boolean actualMeteredStatus = !metered; 239 for (int i = 0; i < NUM_TRIES_METERED_STATUS_CHECK; ++i) { 240 actualMeteredStatus = mCm.isActiveNetworkMetered(); 241 if (actualMeteredStatus == metered) { 242 return; 243 } 244 SystemClock.sleep(INTERVAL_METERED_STATUS_CHECK_MS); 245 } 246 fail("Timed out waiting for active network metered status to change. expected=" 247 + metered + "; actual=" + actualMeteredStatus 248 + "; networkInfo=" + mCm.getActiveNetwork()); 249 } 250 executeCmd(String cmd)251 private String executeCmd(String cmd) throws Exception { 252 final String result = SystemUtil.runShellCommand(getInstrumentation(), cmd); 253 Log.i(TAG, "Cmd '" + cmd + "' result: " + result); 254 return result; 255 } 256 getActiveNetworkNetId()257 private int getActiveNetworkNetId() { 258 Network network = mCm.getActiveNetwork(); 259 if (network == null) { 260 return 0; 261 } 262 return network.getNetId(); 263 } 264 waitForReconnection(int oldNetId)265 private void waitForReconnection(int oldNetId) throws InterruptedException { 266 long pollingDeadline = System.currentTimeMillis() 267 + WAIT_FOR_NETWORK_RECONNECTION_TIMEOUT_SEC * 1000; 268 int latestNetId; 269 do { 270 Thread.sleep(1000); 271 if (System.currentTimeMillis() >= pollingDeadline) { 272 fail("Timeout waiting for network reconnection"); 273 } 274 latestNetId = getActiveNetworkNetId(); 275 // NetId will be 0 while old network is disconnected but new network 276 // has not come up yet. 277 } while (latestNetId == 0 || latestNetId == oldNetId); 278 } 279 } 280