1 /* 2 * Copyright (C) 2019 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.net.hostside; 18 19 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED; 20 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED; 21 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED; 22 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; 23 import static android.net.NetworkCapabilities.TRANSPORT_WIFI; 24 25 import static com.android.compatibility.common.util.SystemUtil.runShellCommand; 26 import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG; 27 28 import static org.junit.Assert.assertEquals; 29 import static org.junit.Assert.assertFalse; 30 import static org.junit.Assert.assertNotEquals; 31 import static org.junit.Assert.assertTrue; 32 import static org.junit.Assert.fail; 33 34 import android.app.ActivityManager; 35 import android.app.Instrumentation; 36 import android.content.Context; 37 import android.location.LocationManager; 38 import android.net.ConnectivityManager; 39 import android.net.ConnectivityManager.NetworkCallback; 40 import android.net.Network; 41 import android.net.NetworkCapabilities; 42 import android.net.wifi.WifiManager; 43 import android.os.Process; 44 import android.text.TextUtils; 45 import android.util.Log; 46 import android.util.Pair; 47 48 import com.android.compatibility.common.util.AppStandbyUtils; 49 import com.android.compatibility.common.util.BatteryUtils; 50 51 import java.util.concurrent.CountDownLatch; 52 import java.util.concurrent.TimeUnit; 53 54 import androidx.test.platform.app.InstrumentationRegistry; 55 56 public class NetworkPolicyTestUtils { 57 58 private static final int TIMEOUT_CHANGE_METEREDNESS_MS = 5000; 59 60 private static ConnectivityManager mCm; 61 private static WifiManager mWm; 62 63 private static Boolean mBatterySaverSupported; 64 private static Boolean mDataSaverSupported; 65 private static Boolean mDozeModeSupported; 66 private static Boolean mAppStandbySupported; 67 NetworkPolicyTestUtils()68 private NetworkPolicyTestUtils() {} 69 isBatterySaverSupported()70 public static boolean isBatterySaverSupported() { 71 if (mBatterySaverSupported == null) { 72 mBatterySaverSupported = BatteryUtils.isBatterySaverSupported(); 73 } 74 return mBatterySaverSupported; 75 } 76 77 /** 78 * As per CDD requirements, if the device doesn't support data saver mode then 79 * ConnectivityManager.getRestrictBackgroundStatus() will always return 80 * RESTRICT_BACKGROUND_STATUS_DISABLED. So, enable the data saver mode and check if 81 * ConnectivityManager.getRestrictBackgroundStatus() for an app in background returns 82 * RESTRICT_BACKGROUND_STATUS_DISABLED or not. 83 */ isDataSaverSupported()84 public static boolean isDataSaverSupported() { 85 if (mDataSaverSupported == null) { 86 assertMyRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED); 87 try { 88 setRestrictBackground(true); 89 mDataSaverSupported = !isMyRestrictBackgroundStatus( 90 RESTRICT_BACKGROUND_STATUS_DISABLED); 91 } finally { 92 setRestrictBackground(false); 93 } 94 } 95 return mDataSaverSupported; 96 } 97 isDozeModeSupported()98 public static boolean isDozeModeSupported() { 99 if (mDozeModeSupported == null) { 100 final String result = executeShellCommand("cmd deviceidle enabled deep"); 101 mDozeModeSupported = result.equals("1"); 102 } 103 return mDozeModeSupported; 104 } 105 isAppStandbySupported()106 public static boolean isAppStandbySupported() { 107 if (mAppStandbySupported == null) { 108 mAppStandbySupported = AppStandbyUtils.isAppStandbyEnabled(); 109 } 110 return mAppStandbySupported; 111 } 112 isLowRamDevice()113 public static boolean isLowRamDevice() { 114 final ActivityManager am = (ActivityManager) getContext().getSystemService( 115 Context.ACTIVITY_SERVICE); 116 return am.isLowRamDevice(); 117 } 118 isLocationEnabled()119 public static boolean isLocationEnabled() { 120 final LocationManager lm = (LocationManager) getContext().getSystemService( 121 Context.LOCATION_SERVICE); 122 return lm.isLocationEnabled(); 123 } 124 setLocationEnabled(boolean enabled)125 public static void setLocationEnabled(boolean enabled) { 126 final LocationManager lm = (LocationManager) getContext().getSystemService( 127 Context.LOCATION_SERVICE); 128 lm.setLocationEnabledForUser(enabled, Process.myUserHandle()); 129 assertEquals("Couldn't change location enabled state", lm.isLocationEnabled(), enabled); 130 Log.d(TAG, "Changed location enabled state to " + enabled); 131 } 132 isActiveNetworkMetered(boolean metered)133 public static boolean isActiveNetworkMetered(boolean metered) { 134 return getConnectivityManager().isActiveNetworkMetered() == metered; 135 } 136 canChangeActiveNetworkMeteredness()137 public static boolean canChangeActiveNetworkMeteredness() { 138 final Network activeNetwork = getConnectivityManager().getActiveNetwork(); 139 final NetworkCapabilities networkCapabilities 140 = getConnectivityManager().getNetworkCapabilities(activeNetwork); 141 return networkCapabilities.hasTransport(TRANSPORT_WIFI); 142 } 143 setupMeteredNetwork(boolean metered)144 public static Pair<String, Boolean> setupMeteredNetwork(boolean metered) throws Exception { 145 if (isActiveNetworkMetered(metered)) { 146 return null; 147 } 148 final boolean isLocationEnabled = isLocationEnabled(); 149 try { 150 if (!isLocationEnabled) { 151 setLocationEnabled(true); 152 } 153 final String ssid = unquoteSSID(getWifiManager().getConnectionInfo().getSSID()); 154 assertNotEquals(WifiManager.UNKNOWN_SSID, ssid); 155 setWifiMeteredStatus(ssid, metered); 156 return Pair.create(ssid, !metered); 157 } finally { 158 // Reset the location enabled state 159 if (!isLocationEnabled) { 160 setLocationEnabled(false); 161 } 162 } 163 } 164 resetMeteredNetwork(String ssid, boolean metered)165 public static void resetMeteredNetwork(String ssid, boolean metered) throws Exception { 166 setWifiMeteredStatus(ssid, metered); 167 } 168 setWifiMeteredStatus(String ssid, boolean metered)169 public static void setWifiMeteredStatus(String ssid, boolean metered) throws Exception { 170 assertFalse("SSID should not be empty", TextUtils.isEmpty(ssid)); 171 final String cmd = "cmd netpolicy set metered-network " + ssid + " " + metered; 172 executeShellCommand(cmd); 173 assertWifiMeteredStatus(ssid, metered); 174 assertActiveNetworkMetered(metered); 175 } 176 assertWifiMeteredStatus(String ssid, boolean expectedMeteredStatus)177 public static void assertWifiMeteredStatus(String ssid, boolean expectedMeteredStatus) { 178 final String result = executeShellCommand("cmd netpolicy list wifi-networks"); 179 final String expectedLine = ssid + ";" + expectedMeteredStatus; 180 assertTrue("Expected line: " + expectedLine + "; Actual result: " + result, 181 result.contains(expectedLine)); 182 } 183 184 // Copied from cts/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java assertActiveNetworkMetered(boolean expectedMeteredStatus)185 public static void assertActiveNetworkMetered(boolean expectedMeteredStatus) throws Exception { 186 final CountDownLatch latch = new CountDownLatch(1); 187 final NetworkCallback networkCallback = new NetworkCallback() { 188 @Override 189 public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) { 190 final boolean metered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED); 191 if (metered == expectedMeteredStatus) { 192 latch.countDown(); 193 } 194 } 195 }; 196 // Registering a callback here guarantees onCapabilitiesChanged is called immediately 197 // with the current setting. Therefore, if the setting has already been changed, 198 // this method will return right away, and if not it will wait for the setting to change. 199 getConnectivityManager().registerDefaultNetworkCallback(networkCallback); 200 if (!latch.await(TIMEOUT_CHANGE_METEREDNESS_MS, TimeUnit.MILLISECONDS)) { 201 fail("Timed out waiting for active network metered status to change to " 202 + expectedMeteredStatus + " ; network = " 203 + getConnectivityManager().getActiveNetwork()); 204 } 205 getConnectivityManager().unregisterNetworkCallback(networkCallback); 206 } 207 setRestrictBackground(boolean enabled)208 public static void setRestrictBackground(boolean enabled) { 209 executeShellCommand("cmd netpolicy set restrict-background " + enabled); 210 final String output = executeShellCommand("cmd netpolicy get restrict-background"); 211 final String expectedSuffix = enabled ? "enabled" : "disabled"; 212 assertTrue("output '" + output + "' should end with '" + expectedSuffix + "'", 213 output.endsWith(expectedSuffix)); 214 } 215 isMyRestrictBackgroundStatus(int expectedStatus)216 public static boolean isMyRestrictBackgroundStatus(int expectedStatus) { 217 final int actualStatus = getConnectivityManager().getRestrictBackgroundStatus(); 218 if (expectedStatus != actualStatus) { 219 Log.d(TAG, "MyRestrictBackgroundStatus: " 220 + "Expected: " + restrictBackgroundValueToString(expectedStatus) 221 + "; Actual: " + restrictBackgroundValueToString(actualStatus)); 222 return false; 223 } 224 return true; 225 } 226 227 // Copied from cts/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java unquoteSSID(String ssid)228 private static String unquoteSSID(String ssid) { 229 // SSID is returned surrounded by quotes if it can be decoded as UTF-8. 230 // Otherwise it's guaranteed not to start with a quote. 231 if (ssid.charAt(0) == '"') { 232 return ssid.substring(1, ssid.length() - 1); 233 } else { 234 return ssid; 235 } 236 } 237 restrictBackgroundValueToString(int status)238 public static String restrictBackgroundValueToString(int status) { 239 switch (status) { 240 case RESTRICT_BACKGROUND_STATUS_DISABLED: 241 return "DISABLED"; 242 case RESTRICT_BACKGROUND_STATUS_WHITELISTED: 243 return "WHITELISTED"; 244 case RESTRICT_BACKGROUND_STATUS_ENABLED: 245 return "ENABLED"; 246 default: 247 return "UNKNOWN_STATUS_" + status; 248 } 249 } 250 executeShellCommand(String command)251 public static String executeShellCommand(String command) { 252 final String result = runShellCommand(command).trim(); 253 Log.d(TAG, "Output of '" + command + "': '" + result + "'"); 254 return result; 255 } 256 assertMyRestrictBackgroundStatus(int expectedStatus)257 public static void assertMyRestrictBackgroundStatus(int expectedStatus) { 258 final int actualStatus = getConnectivityManager().getRestrictBackgroundStatus(); 259 assertEquals(restrictBackgroundValueToString(expectedStatus), 260 restrictBackgroundValueToString(actualStatus)); 261 } 262 getConnectivityManager()263 public static ConnectivityManager getConnectivityManager() { 264 if (mCm == null) { 265 mCm = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE); 266 } 267 return mCm; 268 } 269 getWifiManager()270 public static WifiManager getWifiManager() { 271 if (mWm == null) { 272 mWm = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE); 273 } 274 return mWm; 275 } 276 getContext()277 public static Context getContext() { 278 return getInstrumentation().getContext(); 279 } 280 getInstrumentation()281 public static Instrumentation getInstrumentation() { 282 return InstrumentationRegistry.getInstrumentation(); 283 } 284 } 285